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