1 /*
2 * Copyright (c) 2021 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/stepper/render_stepper.h"
17
18 #include "base/i18n/localization.h"
19 #include "core/components/stepper/render_stepper_item.h"
20
21 namespace OHOS::Ace {
22 namespace {
23
24 // button params
25 const char STEPPER_STATUS_NORMAL[] = "normal";
26 const char STEPPER_STATUS_DISABLED[] = "disabled";
27 const char STEPPER_STATUS_WAITING[] = "waiting";
28 const char STEPPER_STATUS_SKIP[] = "skip";
29 constexpr int32_t LEAST_STEPPER_ITEM_COUNT = 2;
30
31 // for focus
32 constexpr Dimension STEPPER_FOCUS_DEL_OFFSET = 4.0_vp;
33 constexpr Dimension STEPPER_FOCUS_DEL_SIZE = 8.0_vp;
34 constexpr Dimension STEPPER_FOCUS_RADIUS_DEL_SIZE = 3.0_vp;
35
36 } // namespace
37
Update(const RefPtr<Component> & component)38 void RenderStepper::Update(const RefPtr<Component>& component)
39 {
40 stepperComponent_ = AceType::DynamicCast<StepperComponent>(component);
41 if (!stepperComponent_) {
42 LOGW("stepper component is null");
43 return;
44 }
45 childrenArray_.clear();
46 needReverse_ = (stepperComponent_->GetTextDirection() == TextDirection::RTL);
47 totalItemCount_ = static_cast<int32_t>(stepperComponent_->GetChildren().size());
48
49 // currentIndex_ should be updated only for the first time
50 int32_t index = stepperComponent_->GetIndex();
51 if (currentIndex_ != index) {
52 if (index >= 0 && index < totalItemCount_) {
53 currentIndex_ = index;
54 } else if (index < 0) {
55 currentIndex_ = 0;
56 } else {
57 currentIndex_ = totalItemCount_ - 1;
58 }
59 }
60
61 const auto& stepperController = stepperComponent_->GetStepperController();
62 if (stepperController) {
63 auto weak = AceType::WeakClaim(this);
64 stepperController->SetRightButtonStatusImpl([weak](const std::string& status, const std::string& label) {
65 auto stepper = weak.Upgrade();
66 if (stepper) {
67 stepper->SetRightButtonStatus(status, label);
68 }
69 });
70 }
71 if (stepperComponent_->GetOnFinish()) {
72 onFinish_ = *stepperComponent_->GetOnFinish();
73 }
74 if (stepperComponent_->GetOnSkip()) {
75 onSkip_ = *stepperComponent_->GetOnSkip();
76 }
77 if (stepperComponent_->GetOnChange()) {
78 onChange_ = *stepperComponent_->GetOnChange();
79 }
80 if (stepperComponent_->GetOnNext()) {
81 onNext_ = *stepperComponent_->GetOnNext();
82 }
83 if (stepperComponent_->GetOnPrevious()) {
84 onPrevious_ = *stepperComponent_->GetOnPrevious();
85 }
86 finishEvent_ = AceAsyncEvent<void(const std::string&)>::Create(stepperComponent_->GetFinishEventId(), context_);
87 skipEvent_ = AceAsyncEvent<void(const std::string&)>::Create(stepperComponent_->GetSkipEventId(), context_);
88 changeEvent_ = AceAsyncEvent<void(const std::string&)>::Create(stepperComponent_->GetChangeEventId(), context_);
89 nextEvent_ = AceSyncEvent<void(const std::string&, std::string&)>::Create(
90 stepperComponent_->GetNextEventId(), context_);
91 backEvent_ = AceSyncEvent<void(const std::string&, std::string&)>::Create(
92 stepperComponent_->GetBackEventId(), context_);
93 if (!stepperAnimationController_ && totalItemCount_ >= LEAST_STEPPER_ITEM_COUNT) {
94 stepperAnimationController_ = AceType::MakeRefPtr<StepperAnimationController>(GetContext());
95 stepperAnimationController_->SetRenderStepper(AceType::WeakClaim(this));
96 }
97 Initialize();
98 MarkNeedLayout();
99 }
100
Initialize()101 void RenderStepper::Initialize()
102 {
103 InitAttr();
104 InitRecognizer();
105 UpdateButtonStatus();
106 leftButtonData_.isLeft = true;
107 InitButton(leftButtonData_);
108 rightButtonData_.isLeft = false;
109 InitButton(rightButtonData_);
110 InitProgress(renderProgress_);
111 InitAccessibilityEventListener();
112 }
113
InitAttr()114 void RenderStepper::InitAttr()
115 {
116 stepperLabels_ = stepperComponent_->GetStepperLabels();
117 defaultPaddingStart_ = NormalizeToPx(stepperComponent_->GetDefaultPaddingStart());
118 defaultPaddingEnd_ = NormalizeToPx(stepperComponent_->GetDefaultPaddingEnd());
119 progressColor_ = stepperComponent_->GetProgressColor();
120 progressDiameter_ = stepperComponent_->GetProgressDiameter();
121 arrowWidth_ = NormalizeToPx(stepperComponent_->GetArrowWidth());
122 arrowHeight_ = NormalizeToPx(stepperComponent_->GetArrowHeight());
123 arrowColor_ = stepperComponent_->GetArrowColor();
124 disabledAlpha_ = stepperComponent_->GetDisabledAlpha();
125 disabledColor_ = stepperComponent_->GetDisabledColor().ChangeOpacity(disabledAlpha_);
126 rrectRadius_ = NormalizeToPx(stepperComponent_->GetRadius());
127 buttonPressedColor_ = stepperComponent_->GetButtonPressedColor();
128 buttonPressedHeight_ = NormalizeToPx(stepperComponent_->GetButtonPressedHeight());
129 controlPanelHeight_ = NormalizeToPx(stepperComponent_->GetControlHeight());
130 controlMargin_ = NormalizeToPx(stepperComponent_->GetControlMargin());
131 controlPadding_ = NormalizeToPx(stepperComponent_->GetControlPadding());
132 focusColor_ = stepperComponent_->GetFocusColor();
133 focusBorderWidth_ = NormalizeToPx(stepperComponent_->GetFocusBorderWidth());
134 mouseHoverColor_ = stepperComponent_->GetMouseHoverColor();
135 textStyles_ = stepperComponent_->GetLabelStyles();
136 textColors_.clear();
137 for (auto& textStyle : textStyles_) {
138 textColors_.emplace_back(textStyle.GetTextColor());
139 }
140 }
141
InitAccessibilityEventListener()142 void RenderStepper::InitAccessibilityEventListener()
143 {
144 auto refNode = accessibilityNode_.Upgrade();
145 if (!refNode) {
146 return;
147 }
148 refNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
149 refNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
150 auto weakPtr = AceType::WeakClaim(this);
151 refNode->SetActionScrollForward([weakPtr]() {
152 auto stepper = weakPtr.Upgrade();
153 if (stepper) {
154 stepper->StepperNext();
155 return true;
156 }
157 return false;
158 });
159 refNode->SetActionScrollBackward([weakPtr]() {
160 auto stepper = weakPtr.Upgrade();
161 if (stepper) {
162 stepper->StepperPrev();
163 return true;
164 }
165 return false;
166 });
167 }
168
InitRecognizer()169 void RenderStepper::InitRecognizer()
170 {
171 auto wp = AceType::WeakClaim(this);
172 touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
173 touchRecognizer_->SetOnTouchDown([wp](const TouchEventInfo& info) {
174 auto client = wp.Upgrade();
175 if (client) {
176 client->HandleTouchDown(info);
177 }
178 });
179 touchRecognizer_->SetOnTouchUp([wp](const TouchEventInfo& info) {
180 auto client = wp.Upgrade();
181 if (client) {
182 client->HandleTouchUp(info);
183 }
184 });
185 touchRecognizer_->SetOnTouchMove([wp](const TouchEventInfo& info) {
186 auto client = wp.Upgrade();
187 if (client) {
188 client->HandleTouchMove(info);
189 }
190 });
191
192 clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
193 clickRecognizer_->SetOnClick([wp](const ClickInfo& info) {
194 auto client = wp.Upgrade();
195 if (client) {
196 client->HandleClick(info);
197 }
198 });
199 }
200
InitProgress(RefPtr<RenderLoadingProgress> & renderProgress)201 void RenderStepper::InitProgress(RefPtr<RenderLoadingProgress>& renderProgress)
202 {
203 if (!renderProgress) {
204 auto progressComponent = AceType::MakeRefPtr<LoadingProgressComponent>();
205 progressComponent->SetDiameter(progressDiameter_);
206 progressComponent->SetProgressColor(progressColor_);
207 renderProgress = AceType::DynamicCast<RenderLoadingProgress>(progressComponent->CreateRenderNode());
208 renderProgress->Attach(GetContext());
209 renderProgress->Update(progressComponent);
210 AddChild(renderProgress);
211 }
212 }
213
InitButton(ControlPanelData & buttonData)214 void RenderStepper::InitButton(ControlPanelData& buttonData)
215 {
216 if (!buttonData.displayRender) {
217 buttonData.displayComponent = AceType::MakeRefPtr<DisplayComponent>();
218 buttonData.displayRender = AceType::DynamicCast<RenderDisplay>(buttonData.displayComponent->CreateRenderNode());
219 AddChild(buttonData.displayRender);
220 buttonData.displayRender->Attach(GetContext());
221 }
222
223 if (!buttonData.hotBoxRender) {
224 buttonData.hotBoxComponent = AceType::MakeRefPtr<BoxComponent>();
225 buttonData.hotBoxComponent->SetPadding(Edge(controlPadding_));
226 LayoutParam constraints;
227 constraints.SetMinSize(Size(0, buttonPressedHeight_));
228 constraints.SetMaxSize(Size(Size::INFINITE_SIZE, Size::INFINITE_SIZE));
229 buttonData.hotBoxComponent->SetConstraints(constraints);
230 buttonData.hotBoxRender = AceType::DynamicCast<RenderBox>(buttonData.hotBoxComponent->CreateRenderNode());
231 buttonData.displayRender->AddChild(buttonData.hotBoxRender);
232 buttonData.hotBoxRender->Attach(GetContext());
233 }
234
235 if (!buttonData.flexRender) {
236 auto row = AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_START,
237 FlexAlign::CENTER, std::list<RefPtr<Component>>());
238 row->SetMainAxisSize(MainAxisSize::MIN);
239 buttonData.flexRender = AceType::DynamicCast<RenderFlex>(row->CreateRenderNode());
240 buttonData.hotBoxRender->AddChild(buttonData.flexRender);
241 buttonData.flexRender->Attach(GetContext());
242 InitHotArea(buttonData);
243 buttonData.flexRender->Update(row);
244 buttonData.hotBoxRender->Update(buttonData.hotBoxComponent);
245 buttonData.displayRender->Update(buttonData.displayComponent);
246 }
247 }
248
InitHotArea(ControlPanelData & buttonData)249 void RenderStepper::InitHotArea(ControlPanelData& buttonData)
250 {
251 if (!buttonData.textBoxRender) {
252 auto textBoxComponent = AceType::MakeRefPtr<BoxComponent>();
253 buttonData.textBoxRender = AceType::DynamicCast<RenderBox>(textBoxComponent->CreateRenderNode());
254 buttonData.textBoxRender->Attach(GetContext());
255
256 buttonData.textComponent = AceType::MakeRefPtr<TextComponent>("");
257 buttonData.textComponent->SetData(buttonData.text);
258
259 buttonData.textRender = AceType::DynamicCast<RenderText>(buttonData.textComponent->CreateRenderNode());
260 buttonData.textBoxRender->AddChild(buttonData.textRender);
261 buttonData.textRender->Attach(GetContext());
262 buttonData.textRender->Update(buttonData.textComponent);
263 buttonData.textBoxRender->Update(textBoxComponent);
264 }
265
266 if (!buttonData.imageBoxRender) {
267 auto imageBoxComponent = AceType::MakeRefPtr<BoxComponent>();
268 imageBoxComponent->SetPadding(buttonData.isLeft ? Edge(0, 0, controlPadding_, 0, DimensionUnit::PX)
269 : Edge(controlPadding_, 0, 0, 0, DimensionUnit::PX));
270 buttonData.imageBoxRender = AceType::DynamicCast<RenderBox>(imageBoxComponent->CreateRenderNode());
271 buttonData.imageBoxRender->Attach(GetContext());
272
273 auto imageComponent = AceType::MakeRefPtr<ImageComponent>();
274 if (buttonData.isLeft) {
275 buttonData.imageComponentLeft = imageComponent;
276 } else {
277 buttonData.imageComponentRight = imageComponent;
278 }
279 imageComponent->SetResourceId(buttonData.isLeft ? InternalResource::ResourceId::STEPPER_BACK_ARROW
280 : InternalResource::ResourceId::STEPPER_NEXT_ARROW);
281 imageComponent->SetWidth(stepperComponent_->GetArrowWidth());
282 imageComponent->SetHeight(stepperComponent_->GetArrowHeight());
283 // this color is only effect svg image path
284 imageComponent->SetImageFill(arrowColor_);
285
286 auto renderImage = AceType::DynamicCast<RenderImage>(imageComponent->CreateRenderNode());
287 if (buttonData.isLeft) {
288 buttonData.imageRenderLeft = renderImage;
289 } else {
290 buttonData.imageRenderRight = renderImage;
291 }
292 buttonData.imageBoxRender->AddChild(renderImage);
293 renderImage->Attach(GetContext());
294 renderImage->Update(imageComponent);
295 buttonData.imageBoxRender->Update(imageBoxComponent);
296 }
297 }
298
UpdateButton(ControlPanelData & buttonData)299 void RenderStepper::UpdateButton(ControlPanelData& buttonData)
300 {
301 // update disabled button
302 if (!buttonData.isLeft) {
303 if (buttonData.buttonStatus == StepperButtonStatus::DISABLED) {
304 textStyles_[currentIndex_].SetTextColor(disabledColor_);
305 buttonData.imageComponentRight->SetImageFill(arrowColor_.ChangeOpacity(disabledAlpha_));
306 } else {
307 textStyles_[currentIndex_].SetTextColor(textColors_[currentIndex_]);
308 buttonData.imageComponentRight->SetImageFill(arrowColor_);
309 }
310 if (buttonData.buttonType == StepperButtonType::TEXT_ARROW) {
311 buttonData.imageRenderRight->Update(buttonData.imageComponentRight);
312 }
313 } else {
314 textStyles_[currentIndex_].SetTextColor(textColors_[currentIndex_]);
315 }
316
317 // update hot area
318 auto decoration = AceType::MakeRefPtr<Decoration>();
319 if (buttonData.isHovered) {
320 decoration->SetBackgroundColor(mouseHoverColor_);
321 } else {
322 decoration->SetBackgroundColor(Color::TRANSPARENT);
323 }
324 if (buttonData.isClicked && buttonData.buttonStatus != StepperButtonStatus::DISABLED) {
325 decoration->SetBackgroundColor(buttonPressedColor_);
326 }
327 Border border;
328 border.SetBorderRadius(Radius(rrectRadius_));
329 decoration->SetBorder(border);
330 buttonData.hotBoxRender->SetBackDecoration(decoration);
331
332 // update text
333 Size maxSize = GetLayoutParam().GetMaxSize().IsInfinite() ? viewPort_ : GetLayoutParam().GetMaxSize();
334 double maxTextWidth = 0.0;
335 if (buttonData.buttonType == StepperButtonType::TEXT_ARROW) {
336 maxTextWidth = maxSize.Width() / 2 - defaultPaddingStart_ - controlMargin_ - 3 * controlPadding_ - arrowWidth_;
337 } else if (buttonData.buttonType == StepperButtonType::TEXT) {
338 maxTextWidth = maxSize.Width() / 2 - defaultPaddingEnd_ - controlMargin_ - 2 * controlPadding_;
339 }
340 LayoutParam constraints;
341 constraints.SetMinSize(Size(0, 0));
342 constraints.SetMaxSize(Size(maxTextWidth, Size::INFINITE_SIZE));
343 buttonData.textBoxRender->SetConstraints(constraints);
344 buttonData.textComponent->SetData(buttonData.text);
345 textStyles_[currentIndex_].SetTextAlign(buttonData.isLeft ? TextAlign::LEFT : TextAlign::RIGHT);
346 buttonData.textComponent->SetTextStyle(textStyles_[currentIndex_]);
347 buttonData.textRender->Update(buttonData.textComponent);
348 // update flex children
349 if (buttonData.isLeft) {
350 buttonData.flexRender->AddChild(buttonData.imageBoxRender);
351 buttonData.flexRender->AddChild(buttonData.textBoxRender);
352 } else {
353 if (buttonData.buttonType == StepperButtonType::TEXT_ARROW) {
354 buttonData.flexRender->AddChild(buttonData.textBoxRender);
355 buttonData.flexRender->AddChild(buttonData.imageBoxRender);
356 } else if (buttonData.buttonType == StepperButtonType::TEXT) {
357 buttonData.flexRender->AddChild(buttonData.textBoxRender);
358 buttonData.flexRender->RemoveChild(buttonData.imageBoxRender);
359 }
360 }
361 }
362
UpdateButtonStatus()363 void RenderStepper::UpdateButtonStatus()
364 {
365 if (totalItemCount_ == 0) {
366 leftButtonData_.buttonType = StepperButtonType::NONE;
367 rightButtonData_.buttonType = StepperButtonType::NONE;
368 return;
369 }
370 if (needReverse_) {
371 LoadDefaultButtonStatus(rightButtonData_, leftButtonData_);
372 } else {
373 LoadDefaultButtonStatus(leftButtonData_, rightButtonData_);
374 }
375
376 UpdateRightButtonStatus(stepperLabels_[currentIndex_].initialStatus,
377 Localization::GetInstance()->GetEntryLetters("stepper.skip"));
378 }
379
UpdateRightButtonStatus(const std::string & status,const std::string & label)380 void RenderStepper::UpdateRightButtonStatus(const std::string& status, const std::string& label)
381 {
382 if (status == STEPPER_STATUS_NORMAL) {
383 rightButtonData_.buttonStatus = StepperButtonStatus::NORMAL;
384 } else if (status == STEPPER_STATUS_DISABLED) {
385 rightButtonData_.buttonStatus = StepperButtonStatus::DISABLED;
386 } else if (status == STEPPER_STATUS_WAITING) {
387 rightButtonData_.buttonStatus = StepperButtonStatus::WAITING;
388 } else if (status == STEPPER_STATUS_SKIP) {
389 rightButtonData_.buttonType = StepperButtonType::TEXT;
390 rightButtonData_.buttonStatus = StepperButtonStatus::SKIP;
391 rightButtonData_.text = label;
392 }
393 }
394
LoadDefaultButtonStatus(ControlPanelData & buttonDataPrev,ControlPanelData & buttonDataNext)395 void RenderStepper::LoadDefaultButtonStatus(ControlPanelData& buttonDataPrev, ControlPanelData& buttonDataNext)
396 {
397 std::string leftLabel = stepperLabels_[currentIndex_].leftLabel;
398 std::string rightLabel = stepperLabels_[currentIndex_].rightLabel;
399 if (totalItemCount_ == 1) {
400 buttonDataPrev.buttonType = StepperButtonType::NONE;
401 buttonDataNext.buttonType = StepperButtonType::TEXT;
402 buttonDataNext.buttonStatus = StepperButtonStatus::NORMAL;
403 buttonDataNext.text = rightLabel.empty() ?
404 Localization::GetInstance()->GetEntryLetters("stepper.start") : rightLabel;
405 } else if (totalItemCount_ > 1) {
406 if (currentIndex_ == 0) {
407 buttonDataPrev.buttonType = StepperButtonType::NONE;
408 buttonDataNext.buttonType = StepperButtonType::TEXT_ARROW;
409 buttonDataNext.buttonStatus = StepperButtonStatus::NORMAL;
410 buttonDataNext.text = rightLabel.empty() ?
411 Localization::GetInstance()->GetEntryLetters("stepper.next") : rightLabel;
412 } else if (currentIndex_ == totalItemCount_ - 1) {
413 buttonDataPrev.buttonType = StepperButtonType::TEXT_ARROW;
414 buttonDataPrev.buttonStatus = StepperButtonStatus::NORMAL;
415 buttonDataPrev.text = leftLabel.empty() ?
416 Localization::GetInstance()->GetEntryLetters("stepper.back") : leftLabel;
417 buttonDataNext.buttonType = StepperButtonType::TEXT;
418 buttonDataNext.text = rightLabel.empty() ?
419 Localization::GetInstance()->GetEntryLetters("stepper.start") : rightLabel;
420 buttonDataNext.buttonStatus = StepperButtonStatus::NORMAL;
421 } else {
422 buttonDataPrev.buttonType = StepperButtonType::TEXT_ARROW;
423 buttonDataPrev.buttonStatus = StepperButtonStatus::NORMAL;
424 buttonDataPrev.text = leftLabel.empty() ?
425 Localization::GetInstance()->GetEntryLetters("stepper.back") : leftLabel;
426 buttonDataNext.buttonType = StepperButtonType::TEXT_ARROW;
427 buttonDataNext.buttonStatus = StepperButtonStatus::NORMAL;
428 buttonDataNext.text = rightLabel.empty() ?
429 Localization::GetInstance()->GetEntryLetters("stepper.next") : rightLabel;
430 }
431 }
432 }
433
SetRightButtonStatus(const std::string & status,const std::string & label)434 void RenderStepper::SetRightButtonStatus(const std::string& status, const std::string& label)
435 {
436 UpdateRightButtonStatus(status, label);
437 MarkNeedLayout();
438 }
439
PerformLayout()440 void RenderStepper::PerformLayout()
441 {
442 // layout stepper item
443 InitChildrenArr();
444 if (!isAnimation_) {
445 LayoutParam innerLayout = GetLayoutParam();
446 Size minSize = GetLayoutParam().GetMinSize();
447 Size maxSize = GetLayoutParam().GetMaxSize().IsInfinite() ? viewPort_ : GetLayoutParam().GetMaxSize();
448 innerLayout.SetMaxSize(Size(maxSize.Width(), maxSize.Height() - controlPanelHeight_));
449 double maxWidth = minSize.Width();
450 double maxHeight = minSize.Height();
451 int32_t childrenSize = static_cast<int32_t>(childrenArray_.size());
452 for (int32_t i = 0; i < childrenSize; i++) {
453 const auto& childItem = childrenArray_[i];
454 childItem->Layout(innerLayout);
455 maxWidth = std::max(maxWidth, childItem->GetLayoutSize().Width());
456 maxHeight = std::max(maxHeight, childItem->GetLayoutSize().Height());
457 }
458 SetLayoutSize(maxSize);
459 stepperWidth_ = maxWidth;
460 prevItemOffset_ = (needReverse_ ? maxWidth : -maxWidth);
461 nextItemOffset_ = (needReverse_ ? -maxWidth : maxWidth);
462 Offset prevItemPosition = GetMainAxisOffset(prevItemOffset_);
463 Offset nextItemPosition = GetMainAxisOffset(nextItemOffset_);
464
465 for (int32_t i = 0; i < childrenSize; i++) {
466 const auto& childItem = childrenArray_[i];
467 if (i < currentIndex_) {
468 childItem->SetPosition(prevItemPosition);
469 } else if (i == currentIndex_) {
470 childItem->SetPosition(Offset::Zero());
471 auto display = AceType::DynamicCast<RenderDisplay>(childItem->GetFirstChild());
472 if (!display) {
473 return;
474 }
475 display->UpdateOpacity(255.0);
476 } else {
477 childItem->SetPosition(nextItemPosition);
478 }
479 }
480 }
481 // layout buttons
482 LayoutButton(leftButtonData_);
483 LayoutButton(rightButtonData_);
484 LayoutProgress();
485 }
486
LayoutButton(ControlPanelData & buttonData)487 void RenderStepper::LayoutButton(ControlPanelData& buttonData)
488 {
489 if (buttonData.buttonType == StepperButtonType::NONE || buttonData.buttonStatus == StepperButtonStatus::WAITING) {
490 buttonData.displayRender->UpdateVisibleType(VisibleType::GONE);
491 return;
492 } else {
493 buttonData.displayRender->UpdateVisibleType(VisibleType::VISIBLE);
494 }
495
496 UpdateButton(buttonData);
497 auto maxSize = GetLayoutParam().GetMaxSize().IsInfinite() ? viewPort_ : GetLayoutParam().GetMaxSize();
498 double maxWidth = 0.0;
499 if (buttonData.isLeft) {
500 maxWidth = maxSize.Width() / 2 - defaultPaddingStart_ - controlMargin_;
501 } else {
502 maxWidth = maxSize.Width() / 2 - defaultPaddingEnd_ - controlMargin_;
503 }
504 LayoutParam layoutParam = GetLayoutParam();
505 layoutParam.SetMaxSize(Size(maxWidth, maxSize.Height()));
506 buttonData.displayRender->Layout(layoutParam);
507
508 double deltaX = 0.0;
509 if (buttonData.isLeft) {
510 deltaX = defaultPaddingStart_ + controlMargin_;
511 } else {
512 deltaX = maxSize.Width() - (defaultPaddingEnd_ + controlMargin_
513 + buttonData.displayRender->GetLayoutSize().Width());
514 }
515 auto deltaY = maxSize.Height() - controlMargin_ - buttonData.displayRender->GetLayoutSize().Height();
516 buttonData.displayRender->SetPosition(Offset(deltaX, deltaY));
517 }
518
LayoutProgress()519 void RenderStepper::LayoutProgress()
520 {
521 if (renderProgress_ && rightButtonData_.buttonStatus == StepperButtonStatus::WAITING) {
522 renderProgress_->PerformLayout();
523 Size maxSize = GetLayoutParam().GetMaxSize().IsInfinite() ? viewPort_ : GetLayoutParam().GetMaxSize();
524 auto deltaX = maxSize.Width() - (
525 defaultPaddingEnd_ + controlMargin_ + controlPadding_ + renderProgress_->GetLayoutSize().Width());
526 auto deltaY = maxSize.Height() - (
527 controlMargin_ + (buttonPressedHeight_ + renderProgress_->GetLayoutSize().Height()) / 2);
528 renderProgress_->SetPosition(Offset(deltaX, deltaY));
529 }
530 }
531
RegisterChangeEndListener(int32_t listenerId,const StepperChangeEndListener & listener)532 void RenderStepper::RegisterChangeEndListener(int32_t listenerId, const StepperChangeEndListener& listener)
533 {
534 if (listener) {
535 changeEndListeners_[listenerId] = listener;
536 }
537 }
538
UnRegisterChangeEndListener(int32_t listenerId)539 void RenderStepper::UnRegisterChangeEndListener(int32_t listenerId)
540 {
541 changeEndListeners_.erase(listenerId);
542 }
543
OnStatusChanged(RenderStatus renderStatus)544 void RenderStepper::OnStatusChanged(RenderStatus renderStatus)
545 {
546 if (renderStatus == RenderStatus::FOCUS) {
547 onFocus_ = true;
548 } else if (renderStatus == RenderStatus::BLUR) {
549 onFocus_ = false;
550 }
551 }
552
FireFinishEvent() const553 void RenderStepper::FireFinishEvent() const
554 {
555 if (finishEvent_) {
556 std::string param = std::string("\"finish\",null");
557 finishEvent_(param);
558 }
559 if (onFinish_) {
560 onFinish_();
561 }
562 }
563
FireSkipEvent() const564 void RenderStepper::FireSkipEvent() const
565 {
566 if (skipEvent_) {
567 std::string param = std::string("\"skip\",null");
568 skipEvent_(param);
569 }
570 if (onSkip_) {
571 onSkip_();
572 }
573 }
574
FireChangedEvent(int32_t oldIndex,int32_t newIndex) const575 void RenderStepper::FireChangedEvent(int32_t oldIndex, int32_t newIndex) const
576 {
577 if (changeEvent_) {
578 std::string param = std::string("\"change\",{\"prevIndex\":")
579 .append(std::to_string(oldIndex))
580 .append(",\"index\":")
581 .append(std::to_string(newIndex))
582 .append("},null");
583 changeEvent_(param);
584 }
585 for (const auto& [first, second] : changeEndListeners_) {
586 if (second) {
587 second(currentIndex_);
588 }
589 }
590 if (onChange_) {
591 onChange_(oldIndex, newIndex);
592 }
593 }
594
FireNextEvent(int32_t currentIndex,int32_t & pendingIndex)595 void RenderStepper::FireNextEvent(int32_t currentIndex, int32_t& pendingIndex)
596 {
597 if (nextEvent_) {
598 std::string result;
599 std::string param = std::string("\"next\",{\"index\":")
600 .append(std::to_string(currentIndex))
601 .append(",\"pendingIndex\":")
602 .append(std::to_string(pendingIndex))
603 .append("},");
604 nextEvent_(param, result);
605 if (!result.empty() && isdigit(result[0])) {
606 pendingIndex = StringUtils::StringToInt(result);
607 }
608 }
609 if (onNext_) {
610 onNext_(currentIndex, pendingIndex);
611 }
612 }
613
FireBackEvent(int32_t currentIndex,int32_t & pendingIndex)614 void RenderStepper::FireBackEvent(int32_t currentIndex, int32_t& pendingIndex)
615 {
616 if (backEvent_) {
617 std::string result;
618 std::string param = std::string("\"back\",{\"index\":")
619 .append(std::to_string(currentIndex))
620 .append(",\"pendingIndex\":")
621 .append(std::to_string(pendingIndex))
622 .append("},");
623 backEvent_(param, result);
624 if (!result.empty() && isdigit(result[0])) {
625 pendingIndex = StringUtils::StringToInt(result);
626 }
627 }
628 if (onPrevious_) {
629 onPrevious_(currentIndex, pendingIndex);
630 }
631 }
632
FireItemEvent(int32_t index,bool isAppear) const633 void RenderStepper::FireItemEvent(int32_t index, bool isAppear) const
634 {
635 int32_t childrenCount = static_cast<int32_t>(childrenArray_.size());
636 if (index < 0 || index >= childrenCount) {
637 LOGW("index is error, index = %{public}d", index);
638 return;
639 }
640 const auto& item = childrenArray_[index];
641 auto stepperItem = AceType::DynamicCast<RenderStepperItem>(item);
642 if (!stepperItem) {
643 LOGW("Get Stepper Item Is Null");
644 return;
645 }
646 if (isAppear) {
647 stepperItem->FireAppearEvent();
648 } else {
649 stepperItem->FireDisappearEvent();
650 }
651 }
652
GetPrevIndex() const653 int32_t RenderStepper::GetPrevIndex() const
654 {
655 int32_t index;
656 if (needReverse_) {
657 index = currentIndex_ + 1;
658 if (index >= totalItemCount_) {
659 index = totalItemCount_ - 1;
660 }
661 } else {
662 index = currentIndex_ - 1;
663 if (index < 0) {
664 index = 0;
665 }
666 }
667 return index;
668 }
669
GetNextIndex() const670 int32_t RenderStepper::GetNextIndex() const
671 {
672 int32_t index;
673 if (needReverse_) {
674 index = currentIndex_ - 1;
675 if (index < 0) {
676 index = 0;
677 }
678 } else {
679 index = currentIndex_ + 1;
680 if (index >= totalItemCount_) {
681 index = totalItemCount_ - 1;
682 }
683 }
684 return index;
685 }
686
StepperPrev()687 void RenderStepper::StepperPrev()
688 {
689 int32_t toIndex = GetPrevIndex();
690 FireBackEvent(currentIndex_, toIndex);
691 StepperTo(toIndex, false);
692 }
693
StepperNext()694 void RenderStepper::StepperNext()
695 {
696 int32_t toIndex = GetNextIndex();
697 FireNextEvent(currentIndex_, toIndex);
698 StepperTo(toIndex, false);
699 }
700
StepperTo(int32_t index,bool reverse)701 void RenderStepper::StepperTo(int32_t index, bool reverse)
702 {
703 int32_t fromIndex = currentIndex_;
704 if (index >= totalItemCount_) {
705 currentIndex_ = totalItemCount_ - 1;
706 } else if (index < 0) {
707 currentIndex_ = 0;
708 } else {
709 currentIndex_ = index;
710 }
711 if (fromIndex != currentIndex_) {
712 outItemIndex_ = fromIndex;
713 DoStepperToAnimation(outItemIndex_, currentIndex_, reverse);
714 }
715 }
716
DoStepperToAnimation(int32_t fromIndex,int32_t toIndex,bool reverse)717 void RenderStepper::DoStepperToAnimation(int32_t fromIndex, int32_t toIndex, bool reverse)
718 {
719 if (!stepperAnimationController_) {
720 return;
721 }
722 if (onFocus_) {
723 auto context = GetContext().Upgrade();
724 if (context) {
725 context->CancelFocusAnimation();
726 }
727 }
728
729 auto weak = AceType::WeakClaim(this);
730 stepperAnimationController_->SetAnimationStopCallback([weak, fromIndex, toIndex]() {
731 auto stepper = weak.Upgrade();
732 if (stepper) {
733 stepper->FireChangedEvent(stepper->outItemIndex_, stepper->currentIndex_);
734 stepper->FireItemEvent(stepper->outItemIndex_, false);
735 stepper->FireItemEvent(stepper->currentIndex_, true);
736 stepper->isAnimation_ = false;
737 stepper->MarkNeedLayout();
738 }
739 });
740 isAnimation_ = true;
741 stepperAnimationController_->PlayStepperToAnimation(fromIndex, toIndex, reverse);
742 UpdateButtonStatus();
743 MarkNeedLayout();
744 }
745
UpdateItemOpacity(uint8_t opacity,int32_t index)746 void RenderStepper::UpdateItemOpacity(uint8_t opacity, int32_t index)
747 {
748 auto child = childrenArray_[index];
749 if (!child) {
750 return;
751 }
752 auto display = AceType::DynamicCast<RenderDisplay>(child->GetFirstChild());
753 if (!display) {
754 return;
755 }
756 display->UpdateOpacity(opacity);
757 }
758
InitChildrenArr()759 void RenderStepper::InitChildrenArr()
760 {
761 if (!childrenArray_.empty()) {
762 return;
763 }
764 const auto& children = GetChildren();
765 for (const auto& child : children) {
766 if (AceType::DynamicCast<RenderStepperItem>(child)) {
767 childrenArray_.emplace_back(child);
768 }
769 }
770 }
771
UpdateItemPosition(double offset,int32_t index)772 void RenderStepper::UpdateItemPosition(double offset, int32_t index)
773 {
774 int32_t childrenCount = static_cast<int32_t>(childrenArray_.size());
775 if (index < 0 || index >= childrenCount) {
776 LOGE("index is error, index = %{public}d", index);
777 return;
778 }
779 const auto& childItem = childrenArray_[index];
780 childItem->SetPosition(GetMainAxisOffset(offset));
781 MarkNeedRender();
782 }
783
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)784 void RenderStepper::OnTouchTestHit(
785 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
786 {
787 if (touchRecognizer_) {
788 touchRecognizer_->SetCoordinateOffset(coordinateOffset);
789 result.emplace_back(touchRecognizer_);
790 }
791 if (clickRecognizer_) {
792 result.emplace_back(clickRecognizer_);
793 }
794 }
795
MouseHoverTest(const Point & parentLocalPoint)796 bool RenderStepper::MouseHoverTest(const Point& parentLocalPoint)
797 {
798 auto context = context_.Upgrade();
799 if (!context) {
800 return false;
801 }
802 bool isInRegion = InTouchRectList(parentLocalPoint, GetTouchRectList());
803 if (isInRegion) {
804 context->AddToHoverList(AceType::WeakClaim(this).Upgrade());
805 }
806 const auto localPoint = parentLocalPoint - GetPosition();
807 const auto& children = GetChildren();
808 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
809 auto& child = *iter;
810 child->MouseHoverTest(localPoint);
811 }
812 if (leftButtonData_.displayRender->GetPaintRect().IsInRegion(parentLocalPoint)) {
813 leftButtonData_.isHovered = true;
814 leftOldHover_ = true;
815 } else {
816 leftButtonData_.isHovered = false;
817 }
818
819 if (rightButtonData_.displayRender->GetPaintRect().IsInRegion(parentLocalPoint)) {
820 rightButtonData_.isHovered = true;
821 rightOldHover_ = true;
822 } else {
823 rightButtonData_.isHovered = false;
824 }
825
826 if (!leftOldHover_ && !rightOldHover_) {
827 return isInRegion;
828 }
829 leftOldHover_ = leftButtonData_.isHovered;
830 rightOldHover_ = rightButtonData_.isHovered;
831
832 MarkNeedLayout();
833 context->AddToHoverList(AceType::WeakClaim(this).Upgrade());
834 return isInRegion;
835 }
836
HandleClick(const ClickInfo & clickInfo)837 void RenderStepper::HandleClick(const ClickInfo& clickInfo)
838 {
839 Point clickPoint = Point(clickInfo.GetGlobalLocation().GetX(), clickInfo.GetGlobalLocation().GetY());
840 if (fingerId_ >= 0 && clickInfo.GetFingerId() != fingerId_) {
841 return;
842 }
843
844 leftHotRect_ = leftButtonData_.displayRender->GetPaintRect() + GetGlobalOffset();
845 if (leftHotRect_.IsInRegion(clickPoint)) {
846 if (needReverse_) {
847 HandleLeftButtonClick();
848 } else {
849 StepperPrev();
850 }
851 }
852
853 rightHotRect_ = rightButtonData_.displayRender->GetPaintRect() + GetGlobalOffset();
854 if (rightHotRect_.IsInRegion(clickPoint)) {
855 if (needReverse_) {
856 StepperNext();
857 } else {
858 HandleRightButtonClick();
859 }
860 }
861 }
862
HandleLeftButtonClick()863 void RenderStepper::HandleLeftButtonClick()
864 {
865 switch (leftButtonData_.buttonStatus) {
866 case StepperButtonStatus::NORMAL:
867 if (currentIndex_ == totalItemCount_ - 1) {
868 FireFinishEvent();
869 } else {
870 StepperPrev();
871 }
872 break;
873 case StepperButtonStatus::SKIP:
874 FireSkipEvent();
875 break;
876 case StepperButtonStatus::DISABLED:
877 break;
878 case StepperButtonStatus::WAITING:
879 break;
880 default:
881 break;
882 }
883 }
884
HandleRightButtonClick()885 void RenderStepper::HandleRightButtonClick()
886 {
887 switch (rightButtonData_.buttonStatus) {
888 case StepperButtonStatus::NORMAL:
889 if (currentIndex_ == totalItemCount_ - 1) {
890 FireFinishEvent();
891 } else {
892 StepperNext();
893 }
894 break;
895 case StepperButtonStatus::SKIP:
896 FireSkipEvent();
897 break;
898 case StepperButtonStatus::DISABLED:
899 break;
900 case StepperButtonStatus::WAITING:
901 break;
902 default:
903 break;
904 }
905 }
906
HandleTouchDown(const TouchEventInfo & info)907 void RenderStepper::HandleTouchDown(const TouchEventInfo& info)
908 {
909 if (info.GetTouches().empty()) {
910 return;
911 }
912 const auto& locationInfo = info.GetTouches().front();
913 Point touchPoint = Point(locationInfo.GetGlobalLocation().GetX(), locationInfo.GetGlobalLocation().GetY());
914 if (fingerId_ >= 0 && locationInfo.GetFingerId() != fingerId_) {
915 return;
916 }
917
918 leftHotRect_ = leftButtonData_.displayRender->GetPaintRect() + GetGlobalOffset();
919 if (leftHotRect_.IsInRegion(touchPoint)) {
920 fingerId_ = locationInfo.GetFingerId();
921 leftButtonData_.isClicked = true;
922 MarkNeedLayout();
923 }
924
925 rightHotRect_ = rightButtonData_.displayRender->GetPaintRect() + GetGlobalOffset();
926 if (rightHotRect_.IsInRegion(touchPoint)) {
927 fingerId_ = locationInfo.GetFingerId();
928 rightButtonData_.isClicked = true;
929 MarkNeedLayout();
930 }
931 }
932
HandleTouchUp(const TouchEventInfo & info)933 void RenderStepper::HandleTouchUp(const TouchEventInfo& info)
934 {
935 int32_t fingerId = -1;
936 if (!info.GetTouches().empty()) {
937 fingerId = info.GetTouches().front().GetFingerId();
938 } else if (!info.GetChangedTouches().empty()) {
939 fingerId = info.GetChangedTouches().front().GetFingerId();
940 }
941
942 if (fingerId_ >= 0 && fingerId != fingerId_) {
943 return;
944 } else {
945 fingerId_ = -1;
946 leftButtonData_.isClicked = false;
947 rightButtonData_.isClicked = false;
948 MarkNeedLayout();
949 }
950 }
951
HandleTouchMove(const TouchEventInfo & info)952 void RenderStepper::HandleTouchMove(const TouchEventInfo& info)
953 {
954 if (info.GetTouches().empty()) {
955 return;
956 }
957 const auto& locationInfo = info.GetTouches().front();
958 Point touchPoint = Point(locationInfo.GetGlobalLocation().GetX(), locationInfo.GetGlobalLocation().GetY());
959 if (fingerId_ >= 0 && locationInfo.GetFingerId() != fingerId_) {
960 return;
961 }
962 if (!leftHotRect_.IsInRegion(touchPoint)) {
963 leftButtonData_.isClicked = false;
964 MarkNeedLayout();
965 }
966 if (!rightHotRect_.IsInRegion(touchPoint)) {
967 rightButtonData_.isClicked = false;
968 MarkNeedLayout();
969 }
970 }
971
UpdateButtonFocus(bool focus,bool isLeft)972 void RenderStepper::UpdateButtonFocus(bool focus, bool isLeft)
973 {
974 auto context = context_.Upgrade();
975 if (!context) {
976 LOGE("pipeline is null.");
977 return;
978 }
979
980 if ((isLeft && leftButtonData_.buttonStatus == StepperButtonStatus::DISABLED) ||
981 (!isLeft && rightButtonData_.buttonStatus == StepperButtonStatus::DISABLED) ||
982 (!isLeft && rightButtonData_.buttonStatus == StepperButtonStatus::WAITING)) {
983 context->CancelFocusAnimation();
984 return;
985 }
986
987 Offset offset;
988 Size layoutSize;
989 Offset globalOffset;
990 if (isLeft) {
991 offset = leftButtonData_.displayRender->GetPosition();
992 layoutSize = leftButtonData_.displayRender->GetLayoutSize();
993 globalOffset = leftButtonData_.displayRender->GetGlobalOffset();
994 } else {
995 offset = rightButtonData_.displayRender->GetPosition();
996 layoutSize = rightButtonData_.displayRender->GetLayoutSize();
997 globalOffset = rightButtonData_.displayRender->GetGlobalOffset();
998 }
999 if (focus) {
1000 offset += Offset(NormalizeToPx(STEPPER_FOCUS_DEL_OFFSET), NormalizeToPx(STEPPER_FOCUS_DEL_OFFSET));
1001 layoutSize -= Size(NormalizeToPx(STEPPER_FOCUS_DEL_SIZE), NormalizeToPx(STEPPER_FOCUS_DEL_SIZE));
1002 globalOffset += Offset(NormalizeToPx(STEPPER_FOCUS_DEL_OFFSET), NormalizeToPx(STEPPER_FOCUS_DEL_OFFSET));
1003 Radius radius = Radius(rrectRadius_) - Radius(NormalizeToPx(STEPPER_FOCUS_RADIUS_DEL_SIZE));
1004 context->ShowFocusAnimation(RRect::MakeRRect(Rect(offset, layoutSize), radius),
1005 focusColor_, globalOffset);
1006 } else {
1007 context->CancelFocusAnimation();
1008 }
1009 }
1010
HandleButtonClick(bool isLeft)1011 void RenderStepper::HandleButtonClick(bool isLeft)
1012 {
1013 if (isLeft) {
1014 if (needReverse_) {
1015 HandleLeftButtonClick();
1016 } else {
1017 StepperPrev();
1018 }
1019 } else {
1020 if (needReverse_) {
1021 StepperNext();
1022 } else {
1023 HandleRightButtonClick();
1024 }
1025 }
1026 }
1027
1028 } // namespace OHOS::Ace
1029