• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components/select/select_element.h"
17 
18 #include "core/components/box/render_box.h"
19 #include "core/components/select/render_select.h"
20 #include "core/components/text/render_text.h"
21 #include "core/event/ace_event_helper.h"
22 
23 namespace OHOS::Ace {
24 
PerformBuild()25 void SelectElement::PerformBuild()
26 {
27     RefPtr<ClickRecognizer> clickRecognizer = AceType::MakeRefPtr<ClickRecognizer>();
28     clickRecognizer->SetOnClick([weak = AceType::WeakClaim(this)](const ClickInfo&) {
29         auto element = weak.Upgrade();
30         if (element) {
31             element->HandleClickedEvent();
32         }
33     });
34     RefPtr<RawRecognizer> rawRecognizer = AceType::MakeRefPtr<RawRecognizer>();
35     rawRecognizer->SetOnTouchDown([weak = AceType::WeakClaim(this)](const TouchEventInfo&) {
36         auto element = weak.Upgrade();
37         if (element) {
38             element->HandleTouchEvent(true);
39         }
40     });
41     rawRecognizer->SetOnTouchUp([weak = AceType::WeakClaim(this)](const TouchEventInfo&) {
42         auto element = weak.Upgrade();
43         if (element) {
44             element->HandleTouchEvent(false);
45         }
46     });
47     rawRecognizer->SetOnTouchCancel([weak = AceType::WeakClaim(this)](const TouchEventInfo&) {
48         auto element = weak.Upgrade();
49         if (element) {
50             element->HandleTouchEvent(false);
51         }
52     });
53 
54     RefPtr<RenderSelect> render = AceType::DynamicCast<RenderSelect>(renderNode_);
55     if (render) {
56         render->SetClickRecognizer(clickRecognizer);
57         render->SetRawRecognizer(rawRecognizer);
58     } else {
59         LOGE("select: can not get render node of select by dynamic cast failed.");
60         return;
61     }
62 
63     RefPtr<SelectComponent> component = AceType::DynamicCast<SelectComponent>(component_);
64     if (!component || !component->Initialize()) {
65         LOGE("select: can not get component of select by dynamic cast failed or initialize failed.");
66         return;
67     }
68 
69     normalPadding_ = component->GetNormalPadding();
70     auto weak = AceType::WeakClaim(this);
71     component->SetOptionClickedCallback([weak](std::size_t index) {
72         auto refPtr = weak.Upgrade();
73         if (refPtr) {
74             refPtr->HandleOptionClickedEvent(index);
75         }
76     });
77     component->SetOptionModifiedCallback([weak](std::size_t index) {
78         auto refPtr = weak.Upgrade();
79         if (refPtr) {
80             refPtr->HandleOptionModifiedEvent(index);
81         }
82     });
83     render->SetFocusCallback([weak] {
84         auto ref = weak.Upgrade();
85         if (ref && ref->IsCurrentFocus()) {
86             ref->OnFocus();
87         }
88     });
89 
90     if (context_.Invalid() || component->GetOnChanged().IsEmpty()) {
91         LOGE("select: can not set callback of onchange for it is null.");
92     } else {
93         onChangeCallback_ = AceAsyncEvent<void(const std::string&)>::Create(component->GetOnChanged(), context_);
94     }
95 
96     if (component->GetOnSelected()) {
97         onSelected_ = *component->GetOnSelected();
98     } else {
99         onSelected_ = nullptr;
100     }
101 
102     dataComponent_ = component;
103 
104     auto focusNode = AceType::DynamicCast<FocusNode>(this);
105     if (!focusNode) {
106         LOGE("select: can not dynamicCast to focusNode.");
107         return;
108     }
109     focusNode->SetFocusable(!component->GetDisabled());
110 
111     SoleChildElement::PerformBuild();
112 }
113 
HandleClickedEvent()114 void SelectElement::HandleClickedEvent()
115 {
116     const auto pipeline = context_.Upgrade();
117     if (!pipeline) {
118         LOGE("select: can not show dialog, inner pipeline is null.");
119         return;
120     }
121 
122     auto stackElement = pipeline->GetLastStack();
123     if (!stackElement) {
124         LOGE("select: can not get last stack from pipeline context.");
125         return;
126     }
127 
128     RefPtr<SelectComponent> select = AceType::DynamicCast<SelectComponent>(dataComponent_);
129     if (!select || select->GetDisabled()) {
130         LOGE("select: the component of select is null or disabled now.");
131         return;
132     }
133 
134     RefPtr<SelectPopupComponent> selectPopup = AceType::DynamicCast<SelectPopupComponent>(select->GetPopup());
135     if (!selectPopup) {
136         LOGE("select: the type of popup component is not SelectPopupComponent.");
137         return;
138     }
139 
140     RefPtr<RenderSelect> render = AceType::DynamicCast<RenderSelect>(renderNode_);
141     if (!render) {
142         LOGE("select: can not get render node of select by dynamic cast failed.");
143         return;
144     }
145 
146     if (!RequestFocusImmediately()) {
147         LOGE("OnClick can not request SelectElement's focus successfully");
148     }
149 
150     if (selectPopup->GetDialogShowed()) {
151         // hide
152         selectPopup->HideDialog(SELECT_INVALID_INDEX);
153     } else {
154         // show
155         Offset leftTop = render->GetOffsetToStage();
156         Offset rightBottom = leftTop + render->GetLayoutSize();
157         selectPopup->SetDefaultSelecting();
158         selectPopup->ShowDialog(stackElement, leftTop, rightBottom, false);
159     }
160 }
161 
HandleTouchEvent(bool isDown)162 void SelectElement::HandleTouchEvent(bool isDown)
163 {
164     auto component = AceType::DynamicCast<SelectComponent>(dataComponent_);
165     if (!component) {
166         LOGE("select: the component of select is null now.");
167         return;
168     }
169     if (component->GetDisabled()) {
170         LOGW("select: the component of select is disabled now.");
171         return;
172     }
173     auto endColor = isDown ? component->GetClickedColor() : Color::TRANSPARENT;
174     PlayEventEffectAnimation(isDown, endColor);
175 }
176 
HandleOptionModifiedEvent(std::size_t index)177 void SelectElement::HandleOptionModifiedEvent(std::size_t index)
178 {
179     RefPtr<SelectComponent> component = AceType::DynamicCast<SelectComponent>(dataComponent_);
180     if (!component) {
181         LOGE("select: the component of select is null now.");
182         return;
183     }
184 
185     auto option = component->GetSelectOption(index);
186     if (!option) {
187         LOGE("select: can not get option of index[%{public}u].", static_cast<int32_t>(index));
188         return;
189     }
190 
191     auto tagText = component->GetTipText();
192     if (!tagText) {
193         LOGE("select: can not get current text.");
194         return;
195     }
196 
197     auto selText = option->GetText();
198     if (!selText) {
199         LOGE("select: can not get select text.");
200         return;
201     }
202 
203     tagText->SetData(selText->GetData());
204 
205     RefPtr<RenderNode> render = GetRenderText();
206     if (!render) {
207         LOGE("select:can not get render text now.");
208         return;
209     }
210 
211     render->Update(tagText);
212 }
213 
FlushRefresh()214 void SelectElement::FlushRefresh()
215 {
216     if (children_.empty()) {
217         return;
218     }
219     RefPtr<SelectComponent> component = AceType::DynamicCast<SelectComponent>(dataComponent_);
220     if (!component) {
221         LOGE("select: the component of select is null now.");
222         return;
223     }
224     component->Initialize();
225     const auto& child = children_.front();
226     RemoveChild(child);
227     InflateComponent(dataComponent_, DEFAULT_ELEMENT_SLOT, DEFAULT_RENDER_SLOT);
228 }
229 
HandleOptionClickedEvent(std::size_t index)230 void SelectElement::HandleOptionClickedEvent(std::size_t index)
231 {
232     HandleOptionModifiedEvent(index);
233 
234     RefPtr<SelectComponent> component = AceType::DynamicCast<SelectComponent>(dataComponent_);
235     if (!component) {
236         LOGE("select: the component of select is null now.");
237         return;
238     }
239 
240     auto option = component->GetSelectOption(index);
241     if (!option) {
242         LOGE("select: can not get option of index[%{public}u].", static_cast<int32_t>(index));
243         return;
244     }
245 
246     if (onChangeCallback_) {
247         std::string param = std::string("\"change\",{\"newValue\":\"").append(option->GetValue().append("\"},null"));
248         onChangeCallback_(param);
249     }
250 
251     std::string value = option->GetValue();
252     if (onSelected_) {
253         onSelected_(index, value);
254     }
255 }
256 
GetRenderText() const257 RefPtr<RenderNode> SelectElement::GetRenderText() const
258 {
259     auto box = GetRenderBox();
260     if (!box) {
261         LOGE("select: can not get render node of box by function[GetRenderBox].");
262         return nullptr;
263     }
264 
265     if (box->GetChildren().empty()) {
266         LOGE("select: there has no child in box.");
267         return nullptr;
268     }
269 
270     auto inner = box->GetChildren().front();
271     if (!inner) {
272         LOGE("inner component is null");
273         return nullptr;
274     }
275 
276     if (inner->GetChildren().empty()) {
277         LOGE("inner component is empty");
278         return nullptr;
279     }
280 
281     auto row = inner->GetChildren().front();
282     if (!row) {
283         LOGE("select: can not get render node of row by first child.");
284         return nullptr;
285     }
286 
287     if (row->GetChildren().empty()) {
288         LOGE("select: there has no child in box.");
289         return nullptr;
290     }
291 
292     for (const auto& child : row->GetChildren()) {
293         auto textItem = AceType::DynamicCast<RenderFlexItem>(child);
294         if (textItem) {
295             for (const auto& text : textItem->GetChildren()) {
296                 auto renderText = AceType::DynamicCast<RenderText>(text);
297                 if (renderText) {
298                     return renderText;
299                 }
300             }
301         }
302     }
303 
304     LOGE("select: there has no child in row's all children.");
305     return nullptr;
306 }
307 
GetRenderBox() const308 RefPtr<RenderNode> SelectElement::GetRenderBox() const
309 {
310     RefPtr<RenderSelect> select = AceType::DynamicCast<RenderSelect>(renderNode_);
311     if (!select) {
312         LOGE("select: can not get render node of select by dynamic cast failed.");
313         return nullptr;
314     }
315 
316     if (select->GetChildren().empty()) {
317         LOGE("select: there has no child in render select.");
318         return nullptr;
319     }
320 
321     auto box = select->GetChildren().front();
322     if (!box) {
323         LOGE("select: can not get render node of box by first child.");
324         return nullptr;
325     }
326 
327     auto renderBox = AceType::DynamicCast<RenderBox>(box);
328     if (!renderBox) {
329         LOGE("select: can not get render node of box by dynamic cast.");
330         return nullptr;
331     }
332 
333     return renderBox;
334 }
335 
OnClick()336 void SelectElement::OnClick()
337 {
338     HandleClickedEvent();
339 }
340 
GetCorner() const341 Corner SelectElement::GetCorner() const
342 {
343     RefPtr<SelectComponent> component = AceType::DynamicCast<SelectComponent>(dataComponent_);
344     Corner corner;
345     if (!component) {
346         LOGE("select: the component of select is null now.");
347         return corner;
348     }
349 
350     const auto& border = component->GetInnerBorder();
351     corner.topLeftRadius = border.TopLeftRadius();
352     corner.topRightRadius = border.TopRightRadius();
353     corner.bottomLeftRadius = border.BottomLeftRadius();
354     corner.bottomRightRadius = border.BottomRightRadius();
355     return corner;
356 }
357 
OnFocus()358 void SelectElement::OnFocus()
359 {
360     auto render = GetRenderBox();
361     if (!render) {
362         return;
363     }
364     auto pipe = context_.Upgrade();
365     if (!pipe) {
366         return;
367     }
368     RefPtr<SelectComponent> data = AceType::DynamicCast<SelectComponent>(dataComponent_);
369     if (!data) {
370         return;
371     }
372     Size size = render->GetLayoutSize();
373     Rect rect(0.0, 0.0, size.Width(), size.Height());
374     RRect rrect;
375     rrect.SetRect(rect);
376     auto corner = GetCorner();
377     rrect.SetCorner(corner);
378     Offset offset = render->GetGlobalOffset();
379     pipe->ShowFocusAnimation(rrect, data->GetClickedColor(), offset, true);
380 }
381 
OnBlur()382 void SelectElement::OnBlur()
383 {
384     HandleTouchEvent(false);
385 }
386 
SetBackgroundColor(bool isDown,const Color & color)387 void SelectElement::SetBackgroundColor(bool isDown, const Color& color)
388 {
389     auto component = AceType::DynamicCast<SelectComponent>(dataComponent_);
390     if (!component) {
391         LOGE("select: the component of select is null now.");
392         return;
393     }
394     if (component->GetDisabled()) {
395         return;
396     }
397     component->SetClicked(isDown, color);
398     auto boxComponent = component->GetBoxComponent();
399     if (!boxComponent) {
400         LOGE("select: can not get box component of select.");
401         return;
402     }
403     auto boxRender = GetRenderBox();
404     if (!boxRender) {
405         LOGE("select: can not get box render by function[GetRenderBox].");
406         return;
407     }
408     // Change background color of box of select.
409     boxRender->Update(boxComponent);
410 }
411 
CreateColorAnimation(RefPtr<KeyframeAnimation<Color>> & animation,const Color & from,const Color & to,bool isDown)412 void SelectElement::CreateColorAnimation(RefPtr<KeyframeAnimation<Color>>& animation, const Color& from,
413     const Color& to, bool isDown)
414 {
415     if (!animation) {
416         return;
417     }
418     auto start = AceType::MakeRefPtr<Keyframe<Color>>(0.0f, from);
419     auto end = AceType::MakeRefPtr<Keyframe<Color>>(1.0f, to);
420     end->SetCurve(Curves::SHARP);
421     animation->AddKeyframe(start);
422     animation->AddKeyframe(end);
423     animation->AddListener([weak = AceType::WeakClaim(this), isDown](const Color& value) {
424         auto select = weak.Upgrade();
425         if (select) {
426             select->eventEffectColor_ = value;
427             select->SetBackgroundColor(isDown, value);
428         }
429     });
430 }
431 
PlayEventEffectAnimation(bool isDown,const Color & endColor)432 void SelectElement::PlayEventEffectAnimation(bool isDown, const Color& endColor)
433 {
434     if (!eventEffectController_) {
435         eventEffectController_ = CREATE_ANIMATOR(context_);
436     }
437     if (!eventEffectController_->IsStopped()) {
438         eventEffectController_->Stop();
439     }
440     auto colorAnimation = AceType::MakeRefPtr<KeyframeAnimation<Color>>();
441     CreateColorAnimation(colorAnimation, eventEffectColor_, endColor, isDown);
442     eventEffectController_->ClearInterpolators();
443     eventEffectController_->ClearStopListeners();
444     eventEffectController_->AddInterpolator(colorAnimation);
445     eventEffectController_->SetDuration(PRESS_DURATION);
446     eventEffectController_->SetFillMode(FillMode::FORWARDS);
447     eventEffectController_->Forward();
448 }
449 
450 } // namespace OHOS::Ace
451