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