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