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