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