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