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