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 "base/utils/utf_helper.h"
17 #include "core/components_ng/pattern/button/toggle_button_pattern.h"
18
19 #include "core/components/toggle/toggle_theme.h"
20 #include "core/components_ng/pattern/toggle/toggle_model.h"
21 #include "core/components/text/text_theme.h"
22
23 namespace OHOS::Ace::NG {
24 namespace {
25 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
26 constexpr int32_t TOUCH_DURATION = 100;
27 constexpr int32_t MOUSE_HOVER_DURATION = 250;
28 constexpr int32_t TYPE_TOUCH = 0;
29 constexpr int32_t TYPE_HOVER = 1;
30 constexpr int32_t TYPE_CANCEL = 2;
31 }
32
OnAttachToFrameNode()33 void ToggleButtonPattern::OnAttachToFrameNode()
34 {
35 InitParameters();
36 }
37
InitParameters()38 void ToggleButtonPattern::InitParameters()
39 {
40 auto host = GetHost();
41 CHECK_NULL_VOID(host);
42 auto context = host->GetContext();
43 CHECK_NULL_VOID(context);
44 toggleTheme_ = context->GetTheme<ToggleTheme>(host->GetThemeScopeId());
45 CHECK_NULL_VOID(toggleTheme_);
46 checkedColor_ = toggleTheme_->GetCheckedColor();
47 unCheckedColor_ = toggleTheme_->GetBackgroundColor();
48 textMargin_ = toggleTheme_->GetTextMargin();
49 buttonRadius_ = toggleTheme_->GetButtonRadius();
50 textFontSize_ = toggleTheme_->GetTextFontSize();
51 textColor_ = toggleTheme_->GetTextColor();
52 }
53
OnModifyDone()54 void ToggleButtonPattern::OnModifyDone()
55 {
56 Pattern::CheckLocalized();
57 CheckLocalizedBorderRadiuses();
58 InitParameters();
59 auto host = GetHost();
60 CHECK_NULL_VOID(host);
61 auto layoutProperty = host->GetLayoutProperty();
62 CHECK_NULL_VOID(layoutProperty);
63 if (layoutProperty->GetPositionProperty()) {
64 layoutProperty->UpdateAlignment(
65 layoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER));
66 } else {
67 layoutProperty->UpdateAlignment(Alignment::CENTER);
68 }
69 auto buttonPaintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
70 CHECK_NULL_VOID(buttonPaintProperty);
71 if (!isOn_.has_value()) {
72 isOn_ = buttonPaintProperty->GetIsOnValue();
73 }
74 bool changed = false;
75 if (buttonPaintProperty->HasIsOn()) {
76 bool isOn = buttonPaintProperty->GetIsOnValue();
77 changed = isOn ^ isOn_.value();
78 isOn_ = isOn;
79 }
80 const auto& renderContext = host->GetRenderContext();
81 CHECK_NULL_VOID(renderContext);
82 if (!UseContentModifier()) {
83 if (isOn_.value_or(false)) {
84 auto selectedColor = buttonPaintProperty->GetSelectedColor().value_or(checkedColor_);
85 renderContext->UpdateBackgroundColor(selectedColor);
86 } else {
87 auto bgColor = buttonPaintProperty->GetBackgroundColor().value_or(unCheckedColor_);
88 renderContext->UpdateBackgroundColor(bgColor);
89 }
90 HandleOnOffStyle(!isOn_.value(), isFocus_);
91 }
92 if (changed) {
93 auto toggleButtonEventHub = GetOrCreateEventHub<ToggleButtonEventHub>();
94 CHECK_NULL_VOID(toggleButtonEventHub);
95 toggleButtonEventHub->UpdateChangeEvent(isOn_.value());
96 }
97 GetIsTextFade();
98 FireBuilder();
99 InitEvent();
100 SetAccessibilityAction();
101 }
102
InitEvent()103 void ToggleButtonPattern::InitEvent()
104 {
105 InitButtonAndText();
106 HandleEnabled();
107 InitClickEvent();
108 HandleOverlayStyle();
109 InitTouchEvent();
110 InitHoverEvent();
111 InitOnKeyEvent();
112 }
113
GetIsTextFade()114 void ToggleButtonPattern::GetIsTextFade()
115 {
116 auto host = GetHost();
117 CHECK_NULL_VOID(host);
118 auto pipeline = host->GetContextRefPtr();
119 CHECK_NULL_VOID(pipeline);
120 auto textTheme = pipeline->GetTheme<TextTheme>();
121 CHECK_NULL_VOID(textTheme);
122 isTextFadeOut_ = textTheme->GetIsTextFadeout();
123 }
124
UpdateTexOverflow(bool isMarqueeStart,RefPtr<TextLayoutProperty> & textLayoutProperty)125 void ToggleButtonPattern::UpdateTexOverflow(bool isMarqueeStart, RefPtr<TextLayoutProperty>& textLayoutProperty)
126 {
127 if (isTextFadeOut_ && textLayoutProperty) {
128 textLayoutProperty->UpdateTextOverflow(TextOverflow::MARQUEE);
129 textLayoutProperty->UpdateTextMarqueeFadeout(true);
130 textLayoutProperty->UpdateTextMarqueeStart(isMarqueeStart);
131 textLayoutProperty->UpdateTextMarqueeStartPolicy(MarqueeStartPolicy::DEFAULT);
132 }
133 }
134
InitHoverEvent()135 void ToggleButtonPattern::InitHoverEvent()
136 {
137 if (UseContentModifier()) {
138 return;
139 }
140 auto host = GetHost();
141 CHECK_NULL_VOID(host);
142 auto eventHub = host->GetOrCreateEventHub<ToggleButtonEventHub>();
143 CHECK_NULL_VOID(eventHub);
144 auto inputHub = eventHub->GetOrCreateInputEventHub();
145 CHECK_NULL_VOID(inputHub);
146 auto hoverEffect = inputHub->GetHoverEffect();
147 inputHub->SetHoverEffect(hoverEffect == HoverEffectType::BOARD ? HoverEffectType::AUTO : hoverEffect);
148 if (hoverListener_) {
149 return;
150 }
151 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
152 TAG_LOGI(AceLogTag::ACE_SELECT_COMPONENT, "button hover %{public}d", isHover);
153 auto pattern = weak.Upgrade();
154 if (pattern) {
155 pattern->HandleHoverEvent(isHover);
156 }
157 };
158
159 hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverTask));
160 inputHub->AddOnHoverEvent(hoverListener_);
161 }
162
HandleHoverEvent(bool isHover)163 void ToggleButtonPattern::HandleHoverEvent(bool isHover)
164 {
165 isHover_ = isHover;
166 auto host = GetHost();
167 CHECK_NULL_VOID(host);
168 auto eventHub = host->GetOrCreateEventHub<EventHub>();
169 CHECK_NULL_VOID(eventHub);
170 auto enabled = eventHub->IsEnabled();
171 auto inputEventHub = host->GetOrCreateInputEventHub();
172 CHECK_NULL_VOID(inputEventHub);
173 auto hoverEffect = inputEventHub->GetHoverEffect();
174 if (hoverEffect == HoverEffectType::NONE || hoverEffect == HoverEffectType::SCALE) {
175 return;
176 }
177 if (!isPress_ && (enabled || !isHover)) {
178 auto renderContext = host->GetRenderContext();
179 CHECK_NULL_VOID(renderContext);
180 AnimateTouchAndHover(renderContext, isHover ? TYPE_CANCEL : TYPE_HOVER, isHover ? TYPE_HOVER : TYPE_CANCEL,
181 MOUSE_HOVER_DURATION, Curves::FRICTION);
182 if (isHover) {
183 SetToggleScale(renderContext);
184 } else {
185 if (isScale_) {
186 isScale_ = false;
187 renderContext->SetScale(1.0f, 1.0f);
188 }
189 }
190 }
191 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
192 CHECK_NULL_VOID(textNode);
193 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
194 CHECK_NULL_VOID(textLayoutProperty);
195 UpdateTexOverflow((isHover_ || isFocus_), textLayoutProperty);
196 textNode->MarkDirtyNode();
197 }
198
HandleOverlayStyle()199 void ToggleButtonPattern::HandleOverlayStyle()
200 {
201 HandleBorderAndShadow();
202 HandleFocusStyle();
203 }
204
HandleBorderAndShadow()205 void ToggleButtonPattern::HandleBorderAndShadow()
206 {
207 CHECK_NULL_VOID(toggleTheme_);
208 auto host = GetHost();
209 CHECK_NULL_VOID(host);
210 auto renderContext = host->GetRenderContext();
211 CHECK_NULL_VOID(renderContext);
212 auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
213 CHECK_NULL_VOID(paintProperty);
214 auto layoutProperty = GetLayoutProperty<ButtonLayoutProperty>();
215 CHECK_NULL_VOID(layoutProperty);
216 if (!layoutProperty->GetBorderWidthProperty()) {
217 if (!renderContext->HasBorderWidth()) {
218 BorderWidthProperty borderWidth;
219 borderWidth.SetBorderWidth(toggleTheme_->GetBorderWidth());
220 layoutProperty->UpdateBorderWidth(borderWidth);
221 renderContext->UpdateBorderWidth(borderWidth);
222 }
223 if (!renderContext->HasBorderColor()) {
224 BorderColorProperty borderColor;
225 Color color = paintProperty->GetIsOnValue(false) ?
226 toggleTheme_->GetBorderColorChecked() : toggleTheme_->GetBorderColorUnchecked();
227 borderColor.SetColor(color);
228 renderContext->UpdateBorderColor(borderColor);
229 }
230 }
231
232 auto&& graphics = renderContext->GetOrCreateGraphics();
233 CHECK_NULL_VOID(graphics);
234 CHECK_NULL_VOID(paintProperty->GetIsOn());
235 if (!graphics->HasBackShadow()) {
236 ShadowStyle shadowStyle = ShadowStyle::None;
237 if (paintProperty->GetIsOnValue(false)) {
238 shadowStyle = static_cast<ShadowStyle>(toggleTheme_->GetShadowNormal());
239 }
240 Shadow shadow = Shadow::CreateShadow(shadowStyle);
241 renderContext->UpdateBackShadow(shadow);
242 }
243 }
244
AddIsFocusActiveUpdateEvent()245 void ToggleButtonPattern::AddIsFocusActiveUpdateEvent()
246 {
247 if (!isFocusActiveUpdateEvent_) {
248 isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusAcitve) {
249 auto pattern = weak.Upgrade();
250 CHECK_NULL_VOID(pattern);
251 pattern->SetIsFocus(isFocusAcitve);
252 pattern->UpdateButtonStyle();
253 };
254 }
255 auto host = GetHost();
256 CHECK_NULL_VOID(host);
257 auto pipeline = host->GetContextRefPtr();
258 CHECK_NULL_VOID(pipeline);
259 pipeline->AddIsFocusActiveUpdateEvent(host, isFocusActiveUpdateEvent_);
260 }
261
RemoveIsFocusActiveUpdateEvent()262 void ToggleButtonPattern::RemoveIsFocusActiveUpdateEvent()
263 {
264 auto host = GetHost();
265 CHECK_NULL_VOID(host);
266 auto pipeline = host->GetContextRefPtr();
267 CHECK_NULL_VOID(pipeline);
268 pipeline->RemoveIsFocusActiveUpdateEvent(host);
269 }
270
UpdateButtonStyle()271 void ToggleButtonPattern::UpdateButtonStyle()
272 {
273 auto host = GetHost();
274 CHECK_NULL_VOID(host);
275 auto renderContext = host->GetRenderContext();
276 CHECK_NULL_VOID(renderContext);
277 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
278 CHECK_NULL_VOID(textNode);
279 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
280 CHECK_NULL_VOID(textLayoutProperty);
281 auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
282 CHECK_NULL_VOID(paintProperty);
283 bool isOffState = paintProperty->GetIsOnValue(false);
284 UpdateTexOverflow((isHover_ || isFocus_), textLayoutProperty);
285 if (isFocus_) {
286 SetFocusButtonStyle(textNode, textLayoutProperty, isOffState, renderContext);
287 } else {
288 SetBlurButtonStyle(textNode, textLayoutProperty, isOffState, renderContext);
289 }
290 textNode->MarkDirtyNode();
291 }
292
SetBlurButtonStyle(RefPtr<FrameNode> & textNode,RefPtr<TextLayoutProperty> & textLayoutProperty,bool isOffState,RefPtr<RenderContext> & renderContext)293 void ToggleButtonPattern::SetBlurButtonStyle(RefPtr<FrameNode>& textNode,
294 RefPtr<TextLayoutProperty>& textLayoutProperty,
295 bool isOffState, RefPtr<RenderContext>& renderContext)
296 {
297 CHECK_NULL_VOID(toggleTheme_);
298 CHECK_NULL_VOID(renderContext);
299 if (isCheckedShadow_ && isOn_.value_or(false)) {
300 isCheckedShadow_ = false;
301 ShadowStyle shadowStyle = static_cast<ShadowStyle>(toggleTheme_->GetShadowNormal());
302 renderContext->UpdateBackShadow(Shadow::CreateShadow(shadowStyle));
303 }
304 if (isShadow_ && !isOn_.value_or(false)) {
305 isShadow_ = false;
306 renderContext->UpdateBackShadow(Shadow::CreateShadow(ShadowStyle::None));
307 }
308 if (isScale_) {
309 isScale_ = false;
310 renderContext->SetScale(1.0, 1.0);
311 }
312 isbgColorFocus_ = renderContext->GetBackgroundColor() == (isOffState ?
313 toggleTheme_->GetBackgroundColorFocusChecked() : toggleTheme_->GetBackgroundColorFocusUnchecked());
314 if (isbgColorFocus_) {
315 Color color =
316 isOffState ? toggleTheme_->GetCheckedColor() : toggleTheme_->GetBackgroundColor();
317 renderContext->UpdateBackgroundColor(color);
318 }
319 isTextColor_ = textLayoutProperty->GetTextColor() == toggleTheme_->GetTextColorFocus();
320 if (isTextColor_) {
321 textLayoutProperty->UpdateTextColor(toggleTheme_->GetTextColor());
322 textNode->MarkModifyDone();
323 textNode->MarkDirtyNode();
324 }
325 }
326
SetFocusButtonStyle(RefPtr<FrameNode> & textNode,RefPtr<TextLayoutProperty> & textLayoutProperty,bool isOffState,RefPtr<RenderContext> & renderContext)327 void ToggleButtonPattern::SetFocusButtonStyle(RefPtr<FrameNode>& textNode,
328 RefPtr<TextLayoutProperty>& textLayoutProperty,
329 bool isOffState, RefPtr<RenderContext>& renderContext)
330 {
331 CHECK_NULL_VOID(textNode);
332 CHECK_NULL_VOID(textLayoutProperty);
333 CHECK_NULL_VOID(toggleTheme_);
334 CHECK_NULL_VOID(renderContext);
335
336 isTextColor_ = textLayoutProperty->GetTextColor() == toggleTheme_->GetTextColor();
337 if (isTextColor_) {
338 textLayoutProperty->UpdateTextColor(toggleTheme_->GetTextColorFocus());
339 textNode->MarkModifyDone();
340 textNode->MarkDirtyNode();
341 }
342 SetToggleScale(renderContext);
343 Shadow shadowValue = isOffState
344 ? Shadow::CreateShadow(static_cast<ShadowStyle>(toggleTheme_->GetShadowNormal()))
345 : Shadow::CreateShadow(ShadowStyle::None);
346 isbgColorFocus_ = renderContext->GetBackgroundColor() ==
347 (isOffState ? toggleTheme_->GetCheckedColor() : toggleTheme_->GetBackgroundColor());
348 HandleFocusShadowStyle(isOffState, renderContext, shadowValue, isbgColorFocus_);
349 }
350
HandleFocusShadowStyle(bool isOffState,RefPtr<RenderContext> & renderContext,Shadow shadowValue,bool isbgColorFocus)351 void ToggleButtonPattern::HandleFocusShadowStyle(bool isOffState, RefPtr<RenderContext>& renderContext,
352 Shadow shadowValue, bool isbgColorFocus)
353 {
354 CHECK_NULL_VOID(renderContext);
355 auto&& graphics = renderContext->GetOrCreateGraphics();
356 CHECK_NULL_VOID(graphics);
357 auto isNeedShadow = !graphics->HasBackShadow() || graphics->GetBackShadowValue() == shadowValue;
358 if (isNeedShadow) {
359 isOffState ? isCheckedShadow_ = isNeedShadow : isShadow_ = isNeedShadow;
360 ShadowStyle focusShadowStyle = static_cast<ShadowStyle>(toggleTheme_->GetShadowFocus());
361 renderContext->UpdateBackShadow(Shadow::CreateShadow(focusShadowStyle));
362 }
363 if (isbgColorFocus) {
364 renderContext->UpdateBackgroundColor(isOffState ? toggleTheme_->GetBackgroundColorFocusChecked()
365 : toggleTheme_->GetBackgroundColorFocusUnchecked());
366 }
367 }
368
HandleFocusStyle()369 void ToggleButtonPattern::HandleFocusStyle()
370 {
371 auto host = GetHost();
372 CHECK_NULL_VOID(host);
373 auto focusHub = host->GetOrCreateFocusHub();
374 CHECK_NULL_VOID(focusHub);
375 auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
376 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "status button handle focus event");
377 auto pattern = weak.Upgrade();
378 CHECK_NULL_VOID(pattern);
379 pattern->HandleFocusEvent();
380 };
381 focusHub->SetOnFocusInternal(focusTask);
382 auto blurTask = [weak = WeakClaim(this)]() {
383 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "status button handle blur event");
384 auto pattern = weak.Upgrade();
385 CHECK_NULL_VOID(pattern);
386 pattern->HandleBlurEvent();
387 };
388 focusHub->SetOnBlurInternal(blurTask);
389 }
390
391
HandleFocusEvent()392 void ToggleButtonPattern::HandleFocusEvent()
393 {
394 auto host = GetHost();
395 CHECK_NULL_VOID(host);
396 auto pipeline = host->GetContextRefPtr();
397 CHECK_NULL_VOID(pipeline);
398 AddIsFocusActiveUpdateEvent();
399 if (pipeline->GetIsFocusActive()) {
400 SetIsFocus(true);
401 UpdateButtonStyle();
402 }
403 }
404
HandleBlurEvent()405 void ToggleButtonPattern::HandleBlurEvent()
406 {
407 SetIsFocus(false);
408 RemoveIsFocusActiveUpdateEvent();
409 UpdateButtonStyle();
410 }
411
SetToggleScale(RefPtr<RenderContext> & renderContext)412 void ToggleButtonPattern::SetToggleScale(RefPtr<RenderContext>& renderContext)
413 {
414 CHECK_NULL_VOID(toggleTheme_);
415 auto&& transform = renderContext->GetOrCreateTransform();
416 CHECK_NULL_VOID(transform);
417 float sacleHoverOrFocus = toggleTheme_->GetScaleHoverOrFocus();
418 VectorF scale(sacleHoverOrFocus, sacleHoverOrFocus);
419 if (!transform->HasTransformScale() || transform->GetTransformScaleValue() == scale) {
420 isScale_ = true;
421 renderContext->SetScale(sacleHoverOrFocus, sacleHoverOrFocus);
422 }
423 }
424
SetAccessibilityAction()425 void ToggleButtonPattern::SetAccessibilityAction()
426 {
427 auto host = GetHost();
428 CHECK_NULL_VOID(host);
429 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
430 CHECK_NULL_VOID(accessibilityProperty);
431 accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
432 const auto& pattern = weakPtr.Upgrade();
433 CHECK_NULL_VOID(pattern);
434 pattern->UpdateSelectStatus(true);
435 });
436
437 accessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
438 const auto& pattern = weakPtr.Upgrade();
439 CHECK_NULL_VOID(pattern);
440 pattern->UpdateSelectStatus(false);
441 });
442 }
443
UpdateSelectStatus(bool isSelected)444 void ToggleButtonPattern::UpdateSelectStatus(bool isSelected)
445 {
446 auto host = GetHost();
447 CHECK_NULL_VOID(host);
448 auto context = host->GetRenderContext();
449 CHECK_NULL_VOID(context);
450 MarkIsSelected(isSelected);
451 context->OnMouseSelectUpdate(isSelected, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
452 }
453
MarkIsSelected(bool isSelected)454 void ToggleButtonPattern::MarkIsSelected(bool isSelected)
455 {
456 if (isOn_ == isSelected) {
457 return;
458 }
459 isOn_ = isSelected;
460 auto eventHub = GetOrCreateEventHub<ToggleButtonEventHub>();
461 CHECK_NULL_VOID(eventHub);
462 eventHub->UpdateChangeEvent(isSelected);
463 auto host = GetHost();
464 CHECK_NULL_VOID(host);
465 if (isSelected) {
466 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
467 host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
468 } else {
469 eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
470 host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
471 }
472 }
473
OnAfterModifyDone()474 void ToggleButtonPattern::OnAfterModifyDone()
475 {
476 auto host = GetHost();
477 CHECK_NULL_VOID(host);
478 auto inspectorId = host->GetInspectorId().value_or("");
479 if (!inspectorId.empty()) {
480 Recorder::NodeDataCache::Get().PutBool(host, inspectorId, isOn_.value_or(false));
481 }
482 }
483
InitTouchEvent()484 void ToggleButtonPattern::InitTouchEvent()
485 {
486 if (touchListener_) {
487 return;
488 }
489 auto host = GetHost();
490 CHECK_NULL_VOID(host);
491 auto gesture = host->GetOrCreateGestureEventHub();
492 CHECK_NULL_VOID(gesture);
493 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
494 auto buttonPattern = weak.Upgrade();
495 CHECK_NULL_VOID(buttonPattern);
496 if (info.GetTouches().empty()) {
497 return;
498 }
499 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
500 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "button touch down");
501 buttonPattern->OnTouchDown();
502 }
503 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
504 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
505 TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "button touch up");
506 buttonPattern->OnTouchUp();
507 }
508 };
509 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
510 gesture->AddTouchEvent(touchListener_);
511 }
512
OnTouchDown()513 void ToggleButtonPattern::OnTouchDown()
514 {
515 isPress_ = true;
516 FireBuilder();
517 if (UseContentModifier()) {
518 return;
519 }
520 auto host = GetHost();
521 CHECK_NULL_VOID(host);
522 auto buttonEventHub = GetOrCreateEventHub<ButtonEventHub>();
523 CHECK_NULL_VOID(buttonEventHub);
524 if (buttonEventHub->GetStateEffect()) {
525 auto renderContext = host->GetRenderContext();
526 CHECK_NULL_VOID(renderContext);
527 backgroundColor_ = renderContext->GetBackgroundColor().value_or(Color::TRANSPARENT);
528 if (clickedColor_.has_value()) {
529 // for user self-defined
530 renderContext->UpdateBackgroundColor(clickedColor_.value());
531 return;
532 }
533 // for system default
534 auto isNeedToHandleHoverOpacity = false;
535 AnimateTouchAndHover(renderContext, isNeedToHandleHoverOpacity ? TYPE_HOVER : TYPE_CANCEL, TYPE_TOUCH,
536 TOUCH_DURATION, isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
537 }
538 }
539
OnTouchUp()540 void ToggleButtonPattern::OnTouchUp()
541 {
542 isPress_ = false;
543 FireBuilder();
544 if (UseContentModifier()) {
545 return;
546 }
547 auto host = GetHost();
548 CHECK_NULL_VOID(host);
549 auto buttonEventHub = GetOrCreateEventHub<ButtonEventHub>();
550 CHECK_NULL_VOID(buttonEventHub);
551 if (buttonEventHub->GetStateEffect()) {
552 auto renderContext = host->GetRenderContext();
553 if (clickedColor_.has_value()) {
554 renderContext->UpdateBackgroundColor(backgroundColor_);
555 return;
556 }
557 if (buttonEventHub->IsEnabled()) {
558 auto isNeedToHandleHoverOpacity = false;
559 AnimateTouchAndHover(renderContext, TYPE_TOUCH, isNeedToHandleHoverOpacity ? TYPE_HOVER : TYPE_CANCEL,
560 TOUCH_DURATION, isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
561 } else {
562 AnimateTouchAndHover(renderContext, TYPE_TOUCH, TYPE_CANCEL, TOUCH_DURATION, Curves::FRICTION);
563 }
564 }
565 }
566
InitClickEvent()567 void ToggleButtonPattern::InitClickEvent()
568 {
569 if (clickListener_) {
570 return;
571 }
572 auto host = GetHost();
573 CHECK_NULL_VOID(host);
574 auto gesture = host->GetOrCreateGestureEventHub();
575 CHECK_NULL_VOID(gesture);
576 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
577 auto buttonPattern = weak.Upgrade();
578 CHECK_NULL_VOID(buttonPattern);
579 buttonPattern->OnClick();
580 };
581 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
582 gesture->AddClickEvent(clickListener_);
583 }
584
OnClick()585 void ToggleButtonPattern::OnClick()
586 {
587 if (UseContentModifier()) {
588 return;
589 }
590 auto host = GetHost();
591 CHECK_NULL_VOID(host);
592 auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
593 CHECK_NULL_VOID(paintProperty);
594 bool isLastSelected = false;
595 if (paintProperty->HasIsOn()) {
596 isLastSelected = paintProperty->GetIsOnValue();
597 }
598 const auto& renderContext = host->GetRenderContext();
599 CHECK_NULL_VOID(renderContext);
600 Color selectedColor;
601 auto buttonPaintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
602 CHECK_NULL_VOID(buttonPaintProperty);
603 if (isLastSelected) {
604 selectedColor = buttonPaintProperty->GetBackgroundColor().value_or(unCheckedColor_);
605 } else {
606 selectedColor = buttonPaintProperty->GetSelectedColor().value_or(checkedColor_);
607 }
608 paintProperty->UpdateIsOn(!isLastSelected);
609 isOn_ = !isLastSelected;
610 renderContext->UpdateBackgroundColor(selectedColor);
611 auto buttonEventHub = GetOrCreateEventHub<ToggleButtonEventHub>();
612 CHECK_NULL_VOID(buttonEventHub);
613 buttonEventHub->UpdateChangeEvent(!isLastSelected);
614 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
615 HandleOnOffStyle(!isOn_.value(), isFocus_);
616 }
617
HandleOnOffStyle(bool isOnToOff,bool isFocus)618 void ToggleButtonPattern::HandleOnOffStyle(bool isOnToOff, bool isFocus)
619 {
620 auto host = GetHost();
621 CHECK_NULL_VOID(host);
622 auto renderContext = host->GetRenderContext();
623 CHECK_NULL_VOID(renderContext);
624 CHECK_NULL_VOID(toggleTheme_);
625 UpdateButtonStyle();
626 BorderColorProperty borderColor;
627 borderColor.SetColor(isOnToOff ? toggleTheme_->GetBorderColorChecked() : toggleTheme_->GetBorderColorUnchecked());
628 if (!renderContext->HasBorderColor() || renderContext->GetBorderColor() == borderColor) {
629 BorderColorProperty color;
630 color.SetColor(isOnToOff ? toggleTheme_->GetBorderColorUnchecked() : toggleTheme_->GetBorderColorChecked());
631 renderContext->UpdateBorderColor(color);
632 }
633 }
634
InitButtonAndText()635 void ToggleButtonPattern::InitButtonAndText()
636 {
637 auto host = GetHost();
638 CHECK_NULL_VOID(host);
639 auto layoutProperty = host->GetLayoutProperty<ButtonLayoutProperty>();
640 CHECK_NULL_VOID(layoutProperty);
641 if (host->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
642 layoutProperty->UpdateType(ButtonType::ROUNDED_RECTANGLE);
643 } else {
644 layoutProperty->UpdateType(ButtonType::CAPSULE);
645 }
646
647 auto renderContext = host->GetRenderContext();
648 CHECK_NULL_VOID(renderContext);
649 if (!renderContext->HasBorderRadius()) {
650 renderContext->UpdateBorderRadius({ buttonRadius_, buttonRadius_, buttonRadius_, buttonRadius_ });
651 }
652 if (!host->GetFirstChild()) {
653 return;
654 }
655 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
656 CHECK_NULL_VOID(textNode);
657 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
658 CHECK_NULL_VOID(textLayoutProperty);
659 if (textLayoutProperty->HasFontSize()) {
660 layoutProperty->UpdateFontSize(textLayoutProperty->GetFontSizeValue(textFontSize_));
661 }
662 layoutProperty->UpdateLabel(UtfUtils::Str16ToStr8(textLayoutProperty->GetContentValue(u"")));
663 if (!textLayoutProperty->GetTextColorFlagByUserValue(false)) {
664 textLayoutProperty->UpdateTextColor(textColor_);
665 }
666
667 if (!textLayoutProperty->GetMarginProperty()) {
668 MarginProperty margin;
669 margin.left = CalcLength(textMargin_.ConvertToPx());
670 margin.right = CalcLength(textMargin_.ConvertToPx());
671 textLayoutProperty->UpdateMargin(margin);
672 }
673 UpdateTexOverflow(false, textLayoutProperty);
674 textNode->MarkModifyDone();
675 textNode->MarkDirtyNode();
676 }
677
InitOnKeyEvent()678 void ToggleButtonPattern::InitOnKeyEvent()
679 {
680 auto host = GetHost();
681 CHECK_NULL_VOID(host);
682 auto focusHub = host->GetOrCreateFocusHub();
683 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
684 auto pattern = wp.Upgrade();
685 if (!pattern) {
686 return false;
687 }
688 return pattern->OnKeyEvent(event);
689 };
690 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
691 }
692
OnKeyEvent(const KeyEvent & event)693 bool ToggleButtonPattern::OnKeyEvent(const KeyEvent& event)
694 {
695 if (event.action != KeyAction::DOWN) {
696 return false;
697 }
698 if (event.code == KeyCode::KEY_SPACE || event.code == KeyCode::KEY_ENTER) {
699 OnClick();
700 return true;
701 }
702 return false;
703 }
704
ProvideRestoreInfo()705 std::string ToggleButtonPattern::ProvideRestoreInfo()
706 {
707 auto jsonObj = JsonUtil::Create(true);
708 jsonObj->Put("IsOn", isOn_.value_or(false));
709 return jsonObj->ToString();
710 }
711
OnRestoreInfo(const std::string & restoreInfo)712 void ToggleButtonPattern::OnRestoreInfo(const std::string& restoreInfo)
713 {
714 auto toggleButtonPaintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
715 CHECK_NULL_VOID(toggleButtonPaintProperty);
716 auto info = JsonUtil::ParseJsonString(restoreInfo);
717 if (!info->IsValid() || !info->IsObject()) {
718 return;
719 }
720 auto jsonIsOn = info->GetValue("IsOn");
721 toggleButtonPaintProperty->UpdateIsOn(jsonIsOn->GetBool());
722 OnModifyDone();
723 }
724
OnColorConfigurationUpdate()725 void ToggleButtonPattern::OnColorConfigurationUpdate()
726 {
727 OnModifyDone();
728 if (SystemProperties::ConfigChangePerform()) {
729 auto host = GetHost();
730 CHECK_NULL_VOID(host);
731 auto pipeline = host->GetContext();
732 CHECK_NULL_VOID(pipeline);
733 auto theme = pipeline->GetTheme<ToggleTheme>();
734 CHECK_NULL_VOID(theme);
735 auto pops = host->GetPaintProperty<ToggleButtonPaintProperty>();
736 CHECK_NULL_VOID(pops);
737 if (!pops->GetSelectedColorSetByUserValue(false)) {
738 Color color = theme->GetCheckedColor();
739 pops->UpdateSelectedColor(color);
740 }
741 host->MarkModifyDone();
742 host->MarkDirtyNode();
743 }
744 }
745
OnThemeScopeUpdate(int32_t themeScopeId)746 bool ToggleButtonPattern::OnThemeScopeUpdate(int32_t themeScopeId)
747 {
748 InitParameters();
749 bool result = false;
750 auto host = GetHost();
751 CHECK_NULL_RETURN(host, false);
752 auto renderContext = host->GetRenderContext();
753 CHECK_NULL_RETURN(renderContext, false);
754
755 auto paintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
756 CHECK_NULL_RETURN(paintProperty, false);
757
758 if (isOn_.value_or(false) && !paintProperty->HasSelectedColor()) {
759 renderContext->UpdateBackgroundColor(checkedColor_);
760 result = true;
761 }
762 if (!isOn_.value_or(false) && !paintProperty->HasBackgroundColor()) {
763 renderContext->UpdateBackgroundColor(unCheckedColor_);
764 result = true;
765 }
766 host->MarkDirtyNode();
767 return result;
768 }
769
SetButtonPress(bool isSelected)770 void ToggleButtonPattern::SetButtonPress(bool isSelected)
771 {
772 auto host = GetHost();
773 CHECK_NULL_VOID(host);
774 auto eventHub = host->GetOrCreateEventHub<EventHub>();
775 CHECK_NULL_VOID(eventHub);
776 auto enabled = eventHub->IsEnabled();
777 if (!enabled) {
778 return;
779 }
780 auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
781 CHECK_NULL_VOID(paintProperty);
782 paintProperty->UpdateIsOn(isSelected);
783 OnModifyDone();
784 }
785
FireBuilder()786 void ToggleButtonPattern::FireBuilder()
787 {
788 auto host = GetHost();
789 CHECK_NULL_VOID(host);
790 if (!toggleMakeFunc_.has_value()) {
791 auto children = host->GetChildren();
792 for (const auto& child : children) {
793 if (child->GetId() == nodeId_) {
794 host->RemoveChildAndReturnIndex(child);
795 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
796 break;
797 }
798 }
799 return;
800 }
801 auto node = BuildContentModifierNode();
802 if (contentModifierNode_ == node) {
803 return;
804 }
805 auto renderContext = host->GetRenderContext();
806 CHECK_NULL_VOID(renderContext);
807 renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
808 host->RemoveChildAndReturnIndex(contentModifierNode_);
809 contentModifierNode_ = node;
810 CHECK_NULL_VOID(contentModifierNode_);
811 nodeId_ = contentModifierNode_->GetId();
812 host->AddChild(contentModifierNode_, 0);
813 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
814 }
815
BuildContentModifierNode()816 RefPtr<FrameNode> ToggleButtonPattern::BuildContentModifierNode()
817 {
818 if (!toggleMakeFunc_.has_value()) {
819 return nullptr;
820 }
821 auto host = GetHost();
822 CHECK_NULL_RETURN(host, nullptr);
823 auto eventHub = host->GetOrCreateEventHub<EventHub>();
824 CHECK_NULL_RETURN(eventHub, nullptr);
825 auto enabled = eventHub->IsEnabled();
826 auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
827 CHECK_NULL_RETURN(paintProperty, nullptr);
828 bool isSelected = false;
829 if (paintProperty->HasIsOn()) {
830 isSelected = paintProperty->GetIsOnValue();
831 } else {
832 isSelected = false;
833 }
834 return (toggleMakeFunc_.value())(ToggleConfiguration(enabled, isSelected));
835 }
836
SetToggleBuilderFunc(SwitchMakeCallback && toggleMakeFunc)837 void ToggleButtonPattern::SetToggleBuilderFunc(SwitchMakeCallback&& toggleMakeFunc)
838 {
839 if (toggleMakeFunc == nullptr) {
840 toggleMakeFunc_ = std::nullopt;
841 contentModifierNode_ = nullptr;
842 auto host = GetHost();
843 CHECK_NULL_VOID(host);
844 for (auto child : host->GetChildren()) {
845 auto childNode = DynamicCast<FrameNode>(child);
846 if (childNode) {
847 childNode->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
848 }
849 }
850 OnModifyDone();
851 return;
852 }
853 toggleMakeFunc_ = std::move(toggleMakeFunc);
854 }
855
ToTreeJson(std::unique_ptr<JsonValue> & json,const InspectorConfig & config) const856 void ToggleButtonPattern::ToTreeJson(std::unique_ptr<JsonValue>& json, const InspectorConfig& config) const
857 {
858 Pattern::ToTreeJson(json, config);
859 json->Put(TreeKey::CHECKED, isOn_ ? "true" : "false");
860 }
861 } // namespace OHOS::Ace::NG
862