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