1 /*
2 * Copyright (c) 2022-2023 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_ng/pattern/radio/radio_pattern.h"
17
18 #include "base/utils/utils.h"
19 #include "core/common/recorder/node_data_cache.h"
20 #include "core/components/checkable/checkable_theme.h"
21 #include "core/components/theme/icon_theme.h"
22 #include "core/components_ng/pattern/image/image_layout_property.h"
23 #include "core/components_ng/pattern/image/image_pattern.h"
24 #include "core/components_ng/pattern/radio/radio_paint_property.h"
25 #include "core/components_ng/pattern/stage/page_event_hub.h"
26 #include "core/components_ng/property/property.h"
27 #include "core/event/touch_event.h"
28 #include "core/pipeline_ng/pipeline_context.h"
29
30 namespace OHOS::Ace::NG {
31
32 namespace {
33 constexpr int FOR_HOTZONESIZE_CALCULATE_MULTIPLY_TWO = 2;
34 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
35
36 constexpr int32_t DEFAULT_RADIO_ANIMATION_DURATION = 200;
37 constexpr float DEFAULT_CUSTOM_SCALE = 0.7F;
38 constexpr float INDICATOR_MIN_SCALE = 0.8F;
39 constexpr float INDICATOR_MAX_SCALE = 1.0F;
40 constexpr float INDICATOR_MIN_OPACITY = 0.0F;
41 constexpr float INDICATOR_MAX_OPACITY = 1.0F;
42 constexpr int32_t RADIO_PADDING_COUNT = 2;
43
44 constexpr float DEFAULT_INTERPOLATINGSPRING_VELOCITY = 0.0f;
45 constexpr float DEFAULT_INTERPOLATINGSPRING_MASS = 1.0f;
46 constexpr float DEFAULT_INTERPOLATINGSPRING_STIFFNESS = 728.0f;
47 constexpr float DEFAULT_INTERPOLATINGSPRING_DAMPING = 46.0f;
48
49 enum class RadioIndicatorType {
50 TICK = 0,
51 DOT,
52 CUSTOM,
53 };
54 } // namespace
55
OnAttachToFrameNode()56 void RadioPattern::OnAttachToFrameNode()
57 {
58 auto host = GetHost();
59 CHECK_NULL_VOID(host);
60 host->GetLayoutProperty()->UpdateAlignment(Alignment::CENTER);
61 }
62
OnDetachFromFrameNode(FrameNode * frameNode)63 void RadioPattern::OnDetachFromFrameNode(FrameNode* frameNode)
64 {
65 CHECK_NULL_VOID(frameNode);
66 auto groupManager = GetGroupManager();
67 CHECK_NULL_VOID(groupManager);
68 auto radioEventHub = frameNode->GetEventHub<NG::RadioEventHub>();
69 CHECK_NULL_VOID(radioEventHub);
70 groupManager->RemoveRadioFromGroup(radioEventHub->GetGroup(), frameNode->GetId());
71 }
72
SetBuilderState()73 void RadioPattern::SetBuilderState()
74 {
75 CHECK_NULL_VOID(builderChildNode_);
76 auto renderContext = builderChildNode_->GetRenderContext();
77 CHECK_NULL_VOID(renderContext);
78 renderContext->UpdateOpacity(0);
79 auto layoutProperty = builderChildNode_->GetLayoutProperty();
80 CHECK_NULL_VOID(layoutProperty);
81 layoutProperty->UpdateVisibility(VisibleType::GONE);
82 }
83
UpdateIndicatorType()84 void RadioPattern::UpdateIndicatorType()
85 {
86 auto radioPaintProperty = GetHost()->GetPaintProperty<RadioPaintProperty>();
87 auto radioIndicatorType = radioPaintProperty->GetRadioIndicator().value_or(0);
88 if (radioIndicatorType == static_cast<int32_t>(RadioIndicatorType::CUSTOM)) {
89 LoadBuilder();
90 } else {
91 ImageNodeCreate();
92 }
93 CHECK_NULL_VOID(builderChildNode_);
94 auto renderContext = builderChildNode_->GetRenderContext();
95 CHECK_NULL_VOID(renderContext);
96 renderContext->UpdateTransformScale({ INDICATOR_MAX_SCALE, INDICATOR_MAX_SCALE });
97 renderContext->UpdateOpacity(1);
98 if (!radioModifier_) {
99 radioModifier_ = AceType::MakeRefPtr<RadioModifier>();
100 }
101 if (!radioPaintProperty->HasRadioCheck()) {
102 radioPaintProperty->UpdateRadioCheck(false);
103 }
104 if (!radioPaintProperty->GetRadioCheckValue()) {
105 radioModifier_->InitOpacityScale(false);
106 SetBuilderState();
107 }
108 }
109
OnModifyDone()110 void RadioPattern::OnModifyDone()
111 {
112 Pattern::OnModifyDone();
113 FireBuilder();
114 if (!makeFunc_.has_value() && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
115 UpdateIndicatorType();
116 }
117 UpdateState();
118 auto host = GetHost();
119 CHECK_NULL_VOID(host);
120 auto* pipeline = host->GetContextWithCheck();
121 CHECK_NULL_VOID(pipeline);
122 auto radioTheme = pipeline->GetTheme<RadioTheme>();
123 CHECK_NULL_VOID(radioTheme);
124 auto layoutProperty = host->GetLayoutProperty();
125 CHECK_NULL_VOID(layoutProperty);
126 MarginProperty margin;
127 margin.left = CalcLength(radioTheme->GetHotZoneHorizontalPadding().Value());
128 margin.right = CalcLength(radioTheme->GetHotZoneHorizontalPadding().Value());
129 margin.top = CalcLength(radioTheme->GetHotZoneVerticalPadding().Value());
130 margin.bottom = CalcLength(radioTheme->GetHotZoneVerticalPadding().Value());
131 auto& setMargin = layoutProperty->GetMarginProperty();
132 if (setMargin) {
133 if (setMargin->left.has_value()) {
134 margin.left = setMargin->left;
135 }
136 if (setMargin->right.has_value()) {
137 margin.right = setMargin->right;
138 }
139 if (setMargin->top.has_value()) {
140 margin.top = setMargin->top;
141 }
142 if (setMargin->bottom.has_value()) {
143 margin.bottom = setMargin->bottom;
144 }
145 }
146 layoutProperty->UpdateMargin(margin);
147 hotZoneHorizontalPadding_ = radioTheme->GetHotZoneHorizontalPadding();
148 hotZoneVerticalPadding_ = radioTheme->GetHotZoneVerticalPadding();
149 HandleEnabled();
150 InitClickEvent();
151 InitTouchEvent();
152 InitMouseEvent();
153 auto focusHub = host->GetFocusHub();
154 CHECK_NULL_VOID(focusHub);
155 InitOnKeyEvent(focusHub);
156 SetAccessibilityAction();
157 }
158
ImageNodeCreate()159 void RadioPattern::ImageNodeCreate()
160 {
161 auto host = GetHost();
162 CHECK_NULL_VOID(host);
163 auto childNode = DynamicCast<FrameNode>(host->GetFirstChild());
164 if (preTypeIsBuilder_) {
165 host->RemoveChild(childNode);
166 }
167 if (!childNode || preTypeIsBuilder_) {
168 auto node = FrameNode::GetOrCreateFrameNode(V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
169 []() { return AceType::MakeRefPtr<ImagePattern>(); });
170 CHECK_NULL_VOID(node);
171 builderChildNode_ = AceType::DynamicCast<FrameNode>(node);
172 CHECK_NULL_VOID(builderChildNode_);
173 auto gesturehub = builderChildNode_->GetOrCreateGestureEventHub();
174 CHECK_NULL_VOID(gesturehub);
175 gesturehub->SetHitTestMode(HitTestMode::HTMNONE);
176 }
177 CHECK_NULL_VOID(builderChildNode_);
178 auto radioPaintProperty = host->GetPaintProperty<RadioPaintProperty>();
179 CHECK_NULL_VOID(radioPaintProperty);
180 auto imageProperty = builderChildNode_->GetLayoutProperty<ImageLayoutProperty>();
181 CHECK_NULL_VOID(imageProperty);
182 imageProperty->UpdateUserDefinedIdealSize(GetChildContentSize());
183 auto imageSourceInfo = GetImageSourceInfoFromTheme(radioPaintProperty->GetRadioIndicator().value_or(0));
184 UpdateInternalResource(imageSourceInfo);
185 auto* pipeline = host->GetContextWithCheck();
186 CHECK_NULL_VOID(pipeline);
187 auto radioTheme = pipeline->GetTheme<RadioTheme>();
188 CHECK_NULL_VOID(radioTheme);
189 auto indicatorColor = radioPaintProperty->GetRadioIndicatorColor().value_or(Color(radioTheme->GetPointColor()));
190 auto imageRenderProperty = builderChildNode_->GetPaintProperty<ImageRenderProperty>();
191 CHECK_NULL_VOID(imageRenderProperty);
192 imageRenderProperty->UpdateSvgFillColor(indicatorColor);
193 imageProperty->UpdateImageSourceInfo(imageSourceInfo);
194 preTypeIsBuilder_ = false;
195 builderChildNode_->MountToParent(host);
196 builderChildNode_->MarkModifyDone();
197 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
198 }
199
SetAccessibilityAction()200 void RadioPattern::SetAccessibilityAction()
201 {
202 auto host = GetHost();
203 CHECK_NULL_VOID(host);
204 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
205 CHECK_NULL_VOID(accessibilityProperty);
206 accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
207 const auto& pattern = weakPtr.Upgrade();
208 CHECK_NULL_VOID(pattern);
209 pattern->UpdateSelectStatus(true);
210 });
211
212 accessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
213 const auto& pattern = weakPtr.Upgrade();
214 CHECK_NULL_VOID(pattern);
215 pattern->UpdateSelectStatus(false);
216 });
217 }
218
UpdateSelectStatus(bool isSelected)219 void RadioPattern::UpdateSelectStatus(bool isSelected)
220 {
221 auto host = GetHost();
222 CHECK_NULL_VOID(host);
223 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d update status %d", host->GetId(), isSelected);
224 auto context = host->GetRenderContext();
225 CHECK_NULL_VOID(context);
226 MarkIsSelected(isSelected);
227 context->OnMouseSelectUpdate(isSelected, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
228 }
229
MarkIsSelected(bool isSelected)230 void RadioPattern::MarkIsSelected(bool isSelected)
231 {
232 if (preCheck_ == isSelected) {
233 return;
234 }
235 preCheck_ = isSelected;
236 auto eventHub = GetEventHub<RadioEventHub>();
237 CHECK_NULL_VOID(eventHub);
238 eventHub->UpdateChangeEvent(isSelected);
239 auto host = GetHost();
240 CHECK_NULL_VOID(host);
241 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d fire change event %{public}d", host->GetId(),
242 isSelected);
243 if (isSelected) {
244 eventHub->UpdateCurrentUIState(UI_STATE_SELECTED);
245 host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
246 } else {
247 eventHub->ResetCurrentUIState(UI_STATE_SELECTED);
248 host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
249 }
250 }
251
OnAfterModifyDone()252 void RadioPattern::OnAfterModifyDone()
253 {
254 auto host = GetHost();
255 CHECK_NULL_VOID(host);
256 auto inspectorId = host->GetInspectorId().value_or("");
257 if (inspectorId.empty()) {
258 return;
259 }
260 auto eventHub = host->GetEventHub<RadioEventHub>();
261 CHECK_NULL_VOID(eventHub);
262 Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, eventHub->GetValue(), preCheck_);
263 }
264
InitClickEvent()265 void RadioPattern::InitClickEvent()
266 {
267 if (clickListener_) {
268 return;
269 }
270 auto host = GetHost();
271 CHECK_NULL_VOID(host);
272 auto gesture = host->GetOrCreateGestureEventHub();
273 CHECK_NULL_VOID(gesture);
274 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
275 auto radioPattern = weak.Upgrade();
276 CHECK_NULL_VOID(radioPattern);
277 radioPattern->OnClick();
278 };
279 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
280 gesture->AddClickEvent(clickListener_);
281 }
282
InitTouchEvent()283 void RadioPattern::InitTouchEvent()
284 {
285 if (UseContentModifier()) {
286 return;
287 }
288 if (touchListener_) {
289 return;
290 }
291 auto host = GetHost();
292 CHECK_NULL_VOID(host);
293 auto gesture = host->GetOrCreateGestureEventHub();
294 CHECK_NULL_VOID(gesture);
295 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
296 auto radioPattern = weak.Upgrade();
297 CHECK_NULL_VOID(radioPattern);
298 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
299 radioPattern->OnTouchDown();
300 }
301 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
302 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
303 radioPattern->OnTouchUp();
304 }
305 };
306 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
307 gesture->AddTouchEvent(touchListener_);
308 }
309
InitMouseEvent()310 void RadioPattern::InitMouseEvent()
311 {
312 if (UseContentModifier()) {
313 return;
314 }
315 if (mouseEvent_) {
316 return;
317 }
318 auto host = GetHost();
319 CHECK_NULL_VOID(host);
320 auto gesture = host->GetOrCreateGestureEventHub();
321 CHECK_NULL_VOID(gesture);
322 auto eventHub = host->GetEventHub<RadioEventHub>();
323 auto inputHub = eventHub->GetOrCreateInputEventHub();
324
325 auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
326 auto pattern = weak.Upgrade();
327 if (pattern) {
328 pattern->HandleMouseEvent(isHover);
329 }
330 };
331 mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
332 inputHub->AddOnHoverEvent(mouseEvent_);
333 }
334
HandleMouseEvent(bool isHover)335 void RadioPattern::HandleMouseEvent(bool isHover)
336 {
337 if (UseContentModifier()) {
338 return;
339 }
340 isHover_ = isHover;
341 if (isHover) {
342 touchHoverType_ = TouchHoverAnimationType::HOVER;
343 } else {
344 touchHoverType_ = TouchHoverAnimationType::NONE;
345 }
346 auto host = GetHost();
347 CHECK_NULL_VOID(host);
348 TAG_LOGD(
349 AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d handle mouse hover %{public}d", host->GetId(), isHover);
350 host->MarkNeedRenderOnly();
351 }
352
OnClick()353 void RadioPattern::OnClick()
354 {
355 if (UseContentModifier()) {
356 return;
357 }
358 auto host = GetHost();
359 CHECK_NULL_VOID(host);
360 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d handle click event", host->GetId());
361 auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
362 CHECK_NULL_VOID(paintProperty);
363 bool check = false;
364 if (paintProperty->HasRadioCheck()) {
365 check = paintProperty->GetRadioCheckValue();
366 } else {
367 paintProperty->UpdateRadioCheck(false);
368 }
369 if (!preCheck_ && !check) {
370 paintProperty->UpdateRadioCheck(true);
371 UpdateState();
372 }
373 }
374
OnTouchDown()375 void RadioPattern::OnTouchDown()
376 {
377 if (UseContentModifier()) {
378 return;
379 }
380 if (isHover_) {
381 touchHoverType_ = TouchHoverAnimationType::HOVER_TO_PRESS;
382 } else {
383 touchHoverType_ = TouchHoverAnimationType::PRESS;
384 }
385 auto host = GetHost();
386 CHECK_NULL_VOID(host);
387 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d onTouch Down", host->GetId());
388 isTouch_ = true;
389 host->MarkNeedRenderOnly();
390 }
391
OnTouchUp()392 void RadioPattern::OnTouchUp()
393 {
394 if (UseContentModifier()) {
395 return;
396 }
397 if (isHover_) {
398 touchHoverType_ = TouchHoverAnimationType::PRESS_TO_HOVER;
399 } else {
400 touchHoverType_ = TouchHoverAnimationType::NONE;
401 }
402 auto host = GetHost();
403 CHECK_NULL_VOID(host);
404 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d onTouch Up", host->GetId());
405 isTouch_ = false;
406 host->MarkNeedRenderOnly();
407 }
408
CheckPageNode()409 void RadioPattern::CheckPageNode()
410 {
411 if (Container::IsInSubContainer()) {
412 return;
413 }
414 auto host = GetHost();
415 CHECK_NULL_VOID(host);
416 auto prePageId = GetPrePageId();
417 auto pipelineContext = PipelineContext::GetCurrentContext();
418 CHECK_NULL_VOID(pipelineContext);
419 auto stageManager = pipelineContext->GetStageManager();
420 CHECK_NULL_VOID(stageManager);
421 auto pageNode = stageManager->GetPageById(host->GetPageId());
422 CHECK_NULL_VOID(pageNode);
423 if (pageNode->GetId() != prePageId) {
424 auto eventHub = host->GetEventHub<RadioEventHub>();
425 CHECK_NULL_VOID(eventHub);
426 auto groupManager = GetGroupManager();
427 CHECK_NULL_VOID(groupManager);
428 auto group = eventHub->GetGroup();
429 groupManager->AddRadioToGroup(group, host->GetId());
430 auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
431 CHECK_NULL_VOID(paintProperty);
432 bool check = false;
433 if (paintProperty->HasRadioCheck()) {
434 check = paintProperty->GetRadioCheckValue();
435 }
436 UpdateGroupCheckStatus(host, groupManager, check);
437 }
438 }
439
UpdateState()440 void RadioPattern::UpdateState()
441 {
442 auto host = GetHost();
443 CHECK_NULL_VOID(host);
444 auto eventHub = host->GetEventHub<RadioEventHub>();
445 CHECK_NULL_VOID(eventHub);
446
447 auto pipelineContext = PipelineContext::GetCurrentContext();
448 CHECK_NULL_VOID(pipelineContext);
449 auto groupManager = GetGroupManager();
450 CHECK_NULL_VOID(groupManager);
451 auto preGroup = GetPreGroup();
452 auto group = eventHub->GetGroup();
453 if (!preGroup.has_value()) {
454 groupManager->AddRadioToGroup(group, host->GetId());
455 SetPrePageIdToLastPageId();
456 auto callback = [weak = WeakClaim(this)]() {
457 auto radio = weak.Upgrade();
458 if (radio) {
459 radio->CheckPageNode();
460 }
461 };
462 pipelineContext->AddBuildFinishCallBack(callback);
463 }
464 if (preGroup.has_value() && preGroup.value() != group) {
465 groupManager->RemoveRadioFromGroup(preGroup.value(), host->GetId());
466 groupManager->AddRadioToGroup(group, host->GetId());
467 SetPrePageIdToLastPageId();
468 isGroupChanged_ = true;
469 }
470 SetPreGroup(group);
471
472 auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
473 CHECK_NULL_VOID(paintProperty);
474
475 bool check = false;
476 if (paintProperty->HasRadioCheck()) {
477 check = paintProperty->GetRadioCheckValue();
478 /*
479 * Do not set isFirstCreated_ to false if the radio is set to true at creation time. The isFirstCreated_ is set
480 * to false in UpdateGroupCheckStatus because isFirstCreated_ is also required to determine if an onChange event
481 * needs to be triggered.
482 */
483 if (check) {
484 UpdateUIStatus(true);
485 isOnAnimationFlag_ = true;
486 } else {
487 // If the radio is set to false, set isFirstCreated_ to false.
488 isFirstCreated_ = false;
489 }
490 } else {
491 paintProperty->UpdateRadioCheck(false);
492 // If the radio check is not set, set isFirstCreated_ to false.
493 isFirstCreated_ = false;
494 }
495 if (preCheck_ != check || isGroupChanged_) {
496 UpdateGroupCheckStatus(host, groupManager, check);
497 }
498 preCheck_ = check;
499 isGroupChanged_ = false;
500 }
501
UpdateUncheckStatus(const RefPtr<FrameNode> & frameNode)502 void RadioPattern::UpdateUncheckStatus(const RefPtr<FrameNode>& frameNode)
503 {
504 auto radioPaintProperty = frameNode->GetPaintProperty<RadioPaintProperty>();
505 CHECK_NULL_VOID(radioPaintProperty);
506 if (radioPaintProperty->GetRadioCheckValue(false)) {
507 radioPaintProperty->UpdateRadioCheck(false);
508 FireBuilder();
509 }
510 frameNode->MarkNeedRenderOnly();
511 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
512 startExitAnimation();
513 }
514 if (preCheck_) {
515 auto radioEventHub = GetEventHub<RadioEventHub>();
516 CHECK_NULL_VOID(radioEventHub);
517 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d fire unselect event", frameNode->GetId());
518 radioEventHub->UpdateChangeEvent(false);
519 isOnAnimationFlag_ = false;
520 }
521 preCheck_ = false;
522 }
523
startEnterAnimation()524 void RadioPattern::startEnterAnimation()
525 {
526 auto springCurve = AceType::MakeRefPtr<InterpolatingSpring>(DEFAULT_INTERPOLATINGSPRING_VELOCITY,
527 DEFAULT_INTERPOLATINGSPRING_MASS, DEFAULT_INTERPOLATINGSPRING_STIFFNESS, DEFAULT_INTERPOLATINGSPRING_DAMPING);
528 AnimationOption delayOption;
529 delayOption.SetCurve(springCurve);
530 delayOption.SetDelay(DEFAULT_RADIO_ANIMATION_DURATION);
531 CHECK_NULL_VOID(builderChildNode_);
532 auto renderContext = builderChildNode_->GetRenderContext();
533 CHECK_NULL_VOID(renderContext);
534 renderContext->UpdateOpacity(INDICATOR_MIN_OPACITY);
535 renderContext->UpdateTransformScale({ INDICATOR_MIN_SCALE, INDICATOR_MIN_SCALE });
536 auto layoutProperty = builderChildNode_->GetLayoutProperty();
537 CHECK_NULL_VOID(layoutProperty);
538 layoutProperty->UpdateVisibility(VisibleType::VISIBLE);
539 auto eventHub = builderChildNode_->GetEventHub<EventHub>();
540 if (eventHub) {
541 eventHub->SetEnabled(true);
542 }
543 AnimationUtils::Animate(
544 delayOption,
545 [&]() {
546 renderContext->UpdateTransformScale({ INDICATOR_MAX_SCALE, INDICATOR_MAX_SCALE });
547 renderContext->UpdateOpacity(INDICATOR_MAX_OPACITY);
548 },
549 nullptr);
550 }
551
startExitAnimation()552 void RadioPattern::startExitAnimation()
553 {
554 auto springCurve = AceType::MakeRefPtr<InterpolatingSpring>(DEFAULT_INTERPOLATINGSPRING_VELOCITY,
555 DEFAULT_INTERPOLATINGSPRING_MASS, DEFAULT_INTERPOLATINGSPRING_STIFFNESS, DEFAULT_INTERPOLATINGSPRING_DAMPING);
556 AnimationOption delayOption;
557 delayOption.SetCurve(springCurve);
558 CHECK_NULL_VOID(builderChildNode_);
559 auto renderContext = builderChildNode_->GetRenderContext();
560 CHECK_NULL_VOID(renderContext);
561 AnimationUtils::Animate(
562 delayOption,
563 [&]() {
564 renderContext->UpdateTransformScale({ INDICATOR_MIN_SCALE, INDICATOR_MIN_SCALE });
565 renderContext->UpdateOpacity(INDICATOR_MIN_OPACITY);
566 },
567 nullptr);
568 auto eventHub = builderChildNode_->GetEventHub<EventHub>();
569 if (eventHub) {
570 eventHub->SetEnabled(false);
571 }
572 }
573
GetImageSourceInfoFromTheme(int32_t RadioIndicator)574 ImageSourceInfo RadioPattern::GetImageSourceInfoFromTheme(int32_t RadioIndicator)
575 {
576 ImageSourceInfo imageSourceInfo;
577 auto host = GetHost();
578 CHECK_NULL_RETURN(host, imageSourceInfo);
579 auto* pipeline = host->GetContextWithCheck();
580 CHECK_NULL_RETURN(pipeline, imageSourceInfo);
581 auto radioTheme = pipeline->GetTheme<RadioTheme>();
582 CHECK_NULL_RETURN(radioTheme, imageSourceInfo);
583 switch (RadioIndicator) {
584 case static_cast<int32_t>(RadioIndicatorType::TICK):
585 imageSourceInfo.SetResourceId(radioTheme->GetTickResourceId());
586 break;
587 case static_cast<int32_t>(RadioIndicatorType::DOT):
588 imageSourceInfo.SetResourceId(radioTheme->GetDotResourceId());
589 break;
590 default:
591 imageSourceInfo.SetResourceId(radioTheme->GetTickResourceId());
592 break;
593 }
594 return imageSourceInfo;
595 }
596
UpdateInternalResource(ImageSourceInfo & sourceInfo)597 void RadioPattern::UpdateInternalResource(ImageSourceInfo& sourceInfo)
598 {
599 CHECK_NULL_VOID(sourceInfo.IsInternalResource());
600 auto host = GetHost();
601 CHECK_NULL_VOID(host);
602 auto* pipeline = host->GetContextWithCheck();
603 CHECK_NULL_VOID(pipeline);
604 auto iconTheme = pipeline->GetTheme<IconTheme>();
605 CHECK_NULL_VOID(iconTheme);
606 auto radioTheme = pipeline->GetTheme<RadioTheme>();
607 CHECK_NULL_VOID(radioTheme);
608 auto iconPath = iconTheme->GetIconPath(sourceInfo.GetResourceId());
609 if (iconPath.empty()) {
610 return;
611 }
612 sourceInfo.SetSrc(iconPath);
613 }
614
LoadBuilder()615 void RadioPattern::LoadBuilder()
616 {
617 RefPtr<UINode> customNode;
618 if (builder_) {
619 auto host = GetHost();
620 CHECK_NULL_VOID(host);
621 auto childNode = DynamicCast<FrameNode>(host->GetFirstChild());
622 if (preTypeIsBuilder_) {
623 return;
624 } else {
625 if (childNode) {
626 host->RemoveChild(childNode);
627 }
628 }
629 NG::ScopedViewStackProcessor builderViewStackProcessor;
630 builder_();
631 customNode = NG::ViewStackProcessor::GetInstance()->Finish();
632 CHECK_NULL_VOID(customNode);
633 builderChildNode_ = AceType::DynamicCast<FrameNode>(customNode);
634 CHECK_NULL_VOID(builderChildNode_);
635 preTypeIsBuilder_ = true;
636 builderChildNode_->MountToParent(host);
637 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
638 }
639 }
640
InitializeParam(Dimension & defaultWidth,Dimension & defaultHeight,Dimension & horizontalPadding,Dimension & verticalPadding)641 void RadioPattern::InitializeParam(
642 Dimension& defaultWidth, Dimension& defaultHeight, Dimension& horizontalPadding, Dimension& verticalPadding)
643 {
644 auto host = GetHost();
645 CHECK_NULL_VOID(host);
646 auto* pipeline = host->GetContextWithCheck();
647 CHECK_NULL_VOID(pipeline);
648 auto radioTheme = pipeline->GetTheme<RadioTheme>();
649 CHECK_NULL_VOID(radioTheme);
650 defaultWidth = radioTheme->GetWidth();
651 defaultHeight = radioTheme->GetHeight();
652 horizontalPadding = radioTheme->GetDefaultPaddingSize();
653 verticalPadding = radioTheme->GetDefaultPaddingSize();
654 }
655
GetChildContentSize()656 CalcSize RadioPattern::GetChildContentSize()
657 {
658 auto host = GetHost();
659 auto layoutProperty = host->GetLayoutProperty<LayoutProperty>();
660 auto &&layoutConstraint = layoutProperty->GetCalcLayoutConstraint();
661 if (layoutConstraint && layoutConstraint->selfIdealSize) {
662 auto selfIdealSize = layoutConstraint->selfIdealSize;
663 if (selfIdealSize->IsValid()) {
664 auto height = selfIdealSize->Height()->GetDimension() * DEFAULT_CUSTOM_SCALE;
665 auto width = selfIdealSize->Width()->GetDimension() * DEFAULT_CUSTOM_SCALE;
666 auto length = std::min(width, height);
667 return CalcSize(NG::CalcLength(length), NG::CalcLength(length));
668 }
669 if (selfIdealSize->Width().has_value()) {
670 auto width = selfIdealSize->Width()->GetDimension() * DEFAULT_CUSTOM_SCALE;
671 return CalcSize(NG::CalcLength(width), NG::CalcLength(width));
672 }
673 if (selfIdealSize->Height().has_value()) {
674 auto height = selfIdealSize->Height()->GetDimension() * DEFAULT_CUSTOM_SCALE;
675 return CalcSize(NG::CalcLength(height), NG::CalcLength(height));
676 }
677 }
678 Dimension defaultWidth;
679 Dimension defaultHeight;
680 Dimension horizontalPadding;
681 Dimension verticalPadding;
682 InitializeParam(defaultWidth, defaultHeight, horizontalPadding, verticalPadding);
683 auto width = (defaultWidth - horizontalPadding * RADIO_PADDING_COUNT) * DEFAULT_CUSTOM_SCALE;
684 auto height = (defaultHeight - verticalPadding * RADIO_PADDING_COUNT) * DEFAULT_CUSTOM_SCALE;
685 return CalcSize(NG::CalcLength(width), NG::CalcLength(height));
686 }
687
UpdateGroupCheckStatus(const RefPtr<FrameNode> & frameNode,const RefPtr<GroupManager> & groupManager,bool check)688 void RadioPattern::UpdateGroupCheckStatus(
689 const RefPtr<FrameNode>& frameNode, const RefPtr<GroupManager>& groupManager, bool check)
690 {
691 frameNode->MarkNeedRenderOnly();
692 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
693 if (!isFirstCreated_ && check) {
694 startEnterAnimation();
695 }
696 }
697
698 auto radioEventHub = GetEventHub<RadioEventHub>();
699 CHECK_NULL_VOID(radioEventHub);
700 if (check) {
701 groupManager->UpdateRadioGroupValue(radioEventHub->GetGroup(), frameNode->GetId());
702 } else {
703 auto radioPaintProperty = frameNode->GetPaintProperty<RadioPaintProperty>();
704 CHECK_NULL_VOID(radioPaintProperty);
705 radioPaintProperty->UpdateRadioCheck(check);
706 if (!isGroupChanged_) {
707 isOnAnimationFlag_ = false;
708 }
709 }
710
711 if (!isFirstCreated_) {
712 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "radio node %{public}d fire group change event %{public}d",
713 frameNode->GetId(), check);
714 radioEventHub->UpdateChangeEvent(check);
715 }
716 }
717
UpdateUIStatus(bool check)718 void RadioPattern::UpdateUIStatus(bool check)
719 {
720 uiStatus_ = check ? UIStatus::SELECTED : UIStatus::UNSELECTED;
721 auto host = GetHost();
722 CHECK_NULL_VOID(host);
723 host->MarkNeedRenderOnly();
724 }
725
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)726 void RadioPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
727 {
728 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
729 auto pattern = wp.Upgrade();
730 if (pattern) {
731 pattern->GetInnerFocusPaintRect(paintRect);
732 }
733 };
734 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
735 }
736
GetInnerFocusPaintRect(RoundRect & paintRect)737 void RadioPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
738 {
739 auto host = GetHost();
740 CHECK_NULL_VOID(host);
741 auto* pipeline = host->GetContextWithCheck();
742 CHECK_NULL_VOID(pipeline);
743 auto radioTheme = pipeline->GetTheme<RadioTheme>();
744 CHECK_NULL_VOID(radioTheme);
745 auto focusPaintPadding = radioTheme->GetFocusPaintPadding().ConvertToPx();
746 float outCircleRadius = size_.Width() / 2 + focusPaintPadding;
747 float originX = offset_.GetX() - focusPaintPadding;
748 float originY = offset_.GetY() - focusPaintPadding;
749 float width = size_.Width() + 2 * focusPaintPadding;
750 float height = size_.Height() + 2 * focusPaintPadding;
751 paintRect.SetRect({ originX, originY, width, height });
752 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, outCircleRadius, outCircleRadius);
753 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, outCircleRadius, outCircleRadius);
754 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, outCircleRadius, outCircleRadius);
755 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, outCircleRadius, outCircleRadius);
756 }
757
GetFocusPattern() const758 FocusPattern RadioPattern::GetFocusPattern() const
759 {
760 auto pipeline = PipelineBase::GetCurrentContext();
761 CHECK_NULL_RETURN(pipeline, FocusPattern());
762 auto radioTheme = pipeline->GetTheme<RadioTheme>();
763 CHECK_NULL_RETURN(radioTheme, FocusPattern());
764 FocusPaintParam focusPaintParam;
765 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
766 auto focusColor = radioTheme->GetFocusColor();
767 focusPaintParam.SetPaintColor(focusColor);
768 } else {
769 auto activeColor = radioTheme->GetActiveColor();
770 focusPaintParam.SetPaintColor(activeColor);
771 }
772 return { FocusType::NODE, true, FocusStyleType::CUSTOM_REGION, focusPaintParam };
773 }
774
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig &)775 bool RadioPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& /*config*/)
776 {
777 auto geometryNode = dirty->GetGeometryNode();
778 offset_ = geometryNode->GetContentOffset();
779 size_ = geometryNode->GetContentSize();
780 if (!isUserSetResponseRegion_) {
781 AddHotZoneRect();
782 }
783 return true;
784 }
785
786 // Set the default hot zone for the component.
AddHotZoneRect()787 void RadioPattern::AddHotZoneRect()
788 {
789 hotZoneOffset_.SetX(offset_.GetX() - hotZoneHorizontalPadding_.ConvertToPx());
790 hotZoneOffset_.SetY(offset_.GetY() - hotZoneVerticalPadding_.ConvertToPx());
791 hotZoneSize_.SetWidth(
792 size_.Width() + FOR_HOTZONESIZE_CALCULATE_MULTIPLY_TWO * hotZoneHorizontalPadding_.ConvertToPx());
793 hotZoneSize_.SetHeight(
794 size_.Height() + FOR_HOTZONESIZE_CALCULATE_MULTIPLY_TWO * hotZoneVerticalPadding_.ConvertToPx());
795 DimensionRect hotZoneRegion;
796 hotZoneRegion.SetSize(DimensionSize(Dimension(hotZoneSize_.Width()), Dimension(hotZoneSize_.Height())));
797 hotZoneRegion.SetOffset(DimensionOffset(Dimension(hotZoneOffset_.GetX()), Dimension(hotZoneOffset_.GetY())));
798 auto host = GetHost();
799 CHECK_NULL_VOID(host);
800 auto gestureHub = host->GetOrCreateGestureEventHub();
801 CHECK_NULL_VOID(gestureHub);
802 gestureHub->SetResponseRegion(std::vector<DimensionRect>({ hotZoneRegion }));
803 }
804
RemoveLastHotZoneRect() const805 void RadioPattern::RemoveLastHotZoneRect() const
806 {
807 auto host = GetHost();
808 CHECK_NULL_VOID(host);
809 host->RemoveLastHotZoneRect();
810 }
811
ProvideRestoreInfo()812 std::string RadioPattern::ProvideRestoreInfo()
813 {
814 auto jsonObj = JsonUtil::Create(true);
815 auto radioPaintProperty = GetPaintProperty<RadioPaintProperty>();
816 CHECK_NULL_RETURN(radioPaintProperty, "");
817 jsonObj->Put("checked", radioPaintProperty->GetRadioCheck().value_or(false));
818 return jsonObj->ToString();
819 }
820
OnRestoreInfo(const std::string & restoreInfo)821 void RadioPattern::OnRestoreInfo(const std::string& restoreInfo)
822 {
823 auto radioPaintProperty = GetPaintProperty<RadioPaintProperty>();
824 CHECK_NULL_VOID(radioPaintProperty);
825 auto info = JsonUtil::ParseJsonString(restoreInfo);
826 if (!info->IsValid() || !info->IsObject()) {
827 return;
828 }
829 auto jsonChecked = info->GetValue("checked");
830 radioPaintProperty->UpdateRadioCheck(jsonChecked->GetBool());
831 OnModifyDone();
832 }
833
HandleEnabled()834 void RadioPattern::HandleEnabled()
835 {
836 auto host = GetHost();
837 CHECK_NULL_VOID(host);
838 auto eventHub = host->GetEventHub<EventHub>();
839 CHECK_NULL_VOID(eventHub);
840 auto enabled = eventHub->IsEnabled();
841 auto radioPaintProperty = GetHost()->GetPaintProperty<RadioPaintProperty>();
842 if (enabled_ != enabled) {
843 enabled_ = enabled;
844 if (!enabled_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
845 if (!radioModifier_) {
846 radioModifier_ = AceType::MakeRefPtr<RadioModifier>();
847 }
848 if (!radioPaintProperty->HasRadioCheck() || !radioPaintProperty->GetRadioCheckValue()) {
849 radioModifier_->SetUIStatus(UIStatus::UNSELECTED);
850 }
851 }
852 auto paintProperty = GetPaintProperty<RadioPaintProperty>();
853 CHECK_NULL_VOID(paintProperty);
854 paintProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_RENDER);
855 }
856 }
857
SetRadioChecked(bool check)858 void RadioPattern::SetRadioChecked(bool check)
859 {
860 auto host = GetHost();
861 CHECK_NULL_VOID(host);
862 auto eventHub = host->GetEventHub<EventHub>();
863 CHECK_NULL_VOID(eventHub);
864 auto enabled = eventHub->IsEnabled();
865 if (!enabled) {
866 return;
867 }
868 auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
869 CHECK_NULL_VOID(paintProperty);
870 paintProperty->UpdateRadioCheck(check);
871 UpdateState();
872 OnModifyDone();
873 }
874
FireBuilder()875 void RadioPattern::FireBuilder()
876 {
877 auto host = GetHost();
878 CHECK_NULL_VOID(host);
879 host->RemoveChildAndReturnIndex(customNode_);
880 if (makeFunc_.has_value()) {
881 customNode_ = BuildContentModifierNode();
882 CHECK_NULL_VOID(customNode_);
883 host->AddChild(customNode_, 0);
884 }
885 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
886 }
887
BuildContentModifierNode()888 RefPtr<FrameNode> RadioPattern::BuildContentModifierNode()
889 {
890 CHECK_NULL_RETURN(makeFunc_, nullptr);
891 auto host = GetHost();
892 CHECK_NULL_RETURN(host, nullptr);
893 auto eventHub = host->GetEventHub<RadioEventHub>();
894 CHECK_NULL_RETURN(eventHub, nullptr);
895 auto value = eventHub->GetValue();
896 auto enabled = eventHub->IsEnabled();
897 auto paintProperty = host->GetPaintProperty<RadioPaintProperty>();
898 CHECK_NULL_RETURN(paintProperty, nullptr);
899 bool isChecked = false;
900 if (paintProperty->HasRadioCheck()) {
901 isChecked = paintProperty->GetRadioCheckValue();
902 } else {
903 isChecked = false;
904 }
905 RadioConfiguration radioConfiguration(value, isChecked, enabled);
906 return (makeFunc_.value())(radioConfiguration);
907 }
908
GetGroupManager()909 RefPtr<GroupManager> RadioPattern::GetGroupManager()
910 {
911 auto manager = groupManager_.Upgrade();
912 if (manager) {
913 return manager;
914 }
915 groupManager_ = GroupManager::GetGroupManager();
916 return groupManager_.Upgrade();
917 }
918
SetPrePageIdToLastPageId()919 void RadioPattern::SetPrePageIdToLastPageId()
920 {
921 if (!Container::IsInSubContainer()) {
922 auto pipelineContext = PipelineContext::GetCurrentContext();
923 CHECK_NULL_VOID(pipelineContext);
924 auto stageManager = pipelineContext->GetStageManager();
925 CHECK_NULL_VOID(stageManager);
926 auto pageNode = stageManager->GetLastPage();
927 CHECK_NULL_VOID(pageNode);
928 SetPrePageId(pageNode->GetId());
929 }
930 }
931 } // namespace OHOS::Ace::NG
932