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