• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/button/toggle_button_pattern.h"
17 
18 #include "base/memory/ace_type.h"
19 #include "base/utils/utils.h"
20 #include "core/common/recorder/node_data_cache.h"
21 #include "core/components/button/button_theme.h"
22 #include "core/components/common/properties/color.h"
23 #include "core/components/toggle/toggle_theme.h"
24 #include "core/components_ng/base/ui_node.h"
25 #include "core/components_ng/pattern/button/toggle_button_paint_property.h"
26 #include "core/components_ng/pattern/text/text_layout_property.h"
27 #include "core/components_ng/property/property.h"
28 #include "core/pipeline/pipeline_base.h"
29 
30 namespace OHOS::Ace::NG {
31 namespace {
32 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
33 constexpr int32_t TOUCH_DURATION = 100;
34 constexpr int32_t TYPE_TOUCH = 0;
35 constexpr int32_t TYPE_HOVER = 1;
36 constexpr int32_t TYPE_CANCEL = 2;
37 }
38 
OnAttachToFrameNode()39 void ToggleButtonPattern::OnAttachToFrameNode()
40 {
41     InitParameters();
42 }
43 
InitParameters()44 void ToggleButtonPattern::InitParameters()
45 {
46     auto pipeline = PipelineBase::GetCurrentContext();
47     CHECK_NULL_VOID(pipeline);
48     auto toggleTheme = pipeline->GetTheme<ToggleTheme>();
49     CHECK_NULL_VOID(toggleTheme);
50     checkedColor_ = toggleTheme->GetCheckedColor();
51     unCheckedColor_ = toggleTheme->GetBackgroundColor();
52     textMargin_ = toggleTheme->GetTextMargin();
53     buttonMargin_ = toggleTheme->GetButtonMargin();
54     buttonHeight_ = toggleTheme->GetButtonHeight();
55     buttonRadius_ = toggleTheme->GetButtonRadius();
56     textFontSize_ = toggleTheme->GetTextFontSize();
57     textColor_ = toggleTheme->GetTextColor();
58     disabledAlpha_ = toggleTheme->GetDisabledAlpha();
59     auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
60     CHECK_NULL_VOID(buttonTheme);
61     clickedColor_ = buttonTheme->GetClickedColor();
62 }
63 
OnModifyDone()64 void ToggleButtonPattern::OnModifyDone()
65 {
66     CheckLocalizedBorderRadiuses();
67     auto host = GetHost();
68     CHECK_NULL_VOID(host);
69 
70     auto layoutProperty = host->GetLayoutProperty();
71     CHECK_NULL_VOID(layoutProperty);
72     if (layoutProperty->GetPositionProperty()) {
73         layoutProperty->UpdateAlignment(
74             layoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER));
75     } else {
76         layoutProperty->UpdateAlignment(Alignment::CENTER);
77     }
78 
79     auto buttonPaintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
80     CHECK_NULL_VOID(buttonPaintProperty);
81     if (!isOn_.has_value()) {
82         isOn_ = buttonPaintProperty->GetIsOnValue();
83     }
84     bool changed = false;
85     if (buttonPaintProperty->HasIsOn()) {
86         bool isOn = buttonPaintProperty->GetIsOnValue();
87         changed = isOn ^ isOn_.value();
88         isOn_ = isOn;
89     }
90     const auto& renderContext = host->GetRenderContext();
91     CHECK_NULL_VOID(renderContext);
92 
93     if (!UseContentModifier()) {
94         if (isOn_.value()) {
95             auto selectedColor = buttonPaintProperty->GetSelectedColor().value_or(checkedColor_);
96             renderContext->UpdateBackgroundColor(selectedColor);
97         } else {
98             auto bgColor = buttonPaintProperty->GetBackgroundColor().value_or(unCheckedColor_);
99             renderContext->UpdateBackgroundColor(bgColor);
100         }
101     }
102 
103     if (changed) {
104         auto toggleButtonEventHub = GetEventHub<ToggleButtonEventHub>();
105         CHECK_NULL_VOID(toggleButtonEventHub);
106         toggleButtonEventHub->UpdateChangeEvent(isOn_.value());
107     }
108     FireBuilder();
109     InitButtonAndText();
110     HandleEnabled();
111     InitClickEvent();
112     InitTouchEvent();
113     InitHoverEvent();
114     InitOnKeyEvent();
115     SetAccessibilityAction();
116 }
117 
SetAccessibilityAction()118 void ToggleButtonPattern::SetAccessibilityAction()
119 {
120     auto host = GetHost();
121     CHECK_NULL_VOID(host);
122     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
123     CHECK_NULL_VOID(accessibilityProperty);
124     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
125         const auto& pattern = weakPtr.Upgrade();
126         CHECK_NULL_VOID(pattern);
127         pattern->UpdateSelectStatus(true);
128     });
129 
130     accessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
131         const auto& pattern = weakPtr.Upgrade();
132         CHECK_NULL_VOID(pattern);
133         pattern->UpdateSelectStatus(false);
134     });
135     FireBuilder();
136 }
137 
UpdateSelectStatus(bool isSelected)138 void ToggleButtonPattern::UpdateSelectStatus(bool isSelected)
139 {
140     auto host = GetHost();
141     CHECK_NULL_VOID(host);
142     auto context = host->GetRenderContext();
143     CHECK_NULL_VOID(context);
144     MarkIsSelected(isSelected);
145     context->OnMouseSelectUpdate(isSelected, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
146 }
147 
MarkIsSelected(bool isSelected)148 void ToggleButtonPattern::MarkIsSelected(bool isSelected)
149 {
150     if (isOn_ == isSelected) {
151         return;
152     }
153     isOn_ = isSelected;
154     auto eventHub = GetEventHub<ToggleButtonEventHub>();
155     CHECK_NULL_VOID(eventHub);
156     eventHub->UpdateChangeEvent(isSelected);
157     auto host = GetHost();
158     CHECK_NULL_VOID(host);
159     if (isSelected) {
160         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
161         host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
162     } else {
163         eventHub->SetCurrentUIState(UI_STATE_SELECTED, isSelected);
164         host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
165     }
166 }
167 
OnAfterModifyDone()168 void ToggleButtonPattern::OnAfterModifyDone()
169 {
170     auto host = GetHost();
171     CHECK_NULL_VOID(host);
172     auto inspectorId = host->GetInspectorId().value_or("");
173     if (!inspectorId.empty()) {
174         Recorder::NodeDataCache::Get().PutBool(host, inspectorId, isOn_.value_or(false));
175     }
176 }
177 
InitTouchEvent()178 void ToggleButtonPattern::InitTouchEvent()
179 {
180     if (touchListener_) {
181         return;
182     }
183     auto host = GetHost();
184     CHECK_NULL_VOID(host);
185     auto gesture = host->GetOrCreateGestureEventHub();
186     CHECK_NULL_VOID(gesture);
187     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
188         auto buttonPattern = weak.Upgrade();
189         CHECK_NULL_VOID(buttonPattern);
190         if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
191             TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "button touch down");
192             buttonPattern->OnTouchDown();
193         }
194         if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
195             info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
196             TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "button touch up");
197             buttonPattern->OnTouchUp();
198         }
199     };
200     touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
201     gesture->AddTouchEvent(touchListener_);
202 }
203 
OnTouchDown()204 void ToggleButtonPattern::OnTouchDown()
205 {
206     isPress_ = true;
207     FireBuilder();
208     if (UseContentModifier()) {
209         return;
210     }
211     auto host = GetHost();
212     CHECK_NULL_VOID(host);
213     auto buttonEventHub = GetEventHub<ButtonEventHub>();
214     CHECK_NULL_VOID(buttonEventHub);
215     if (buttonEventHub->GetStateEffect()) {
216         auto renderContext = host->GetRenderContext();
217         CHECK_NULL_VOID(renderContext);
218         backgroundColor_ = renderContext->GetBackgroundColor().value_or(Color::TRANSPARENT);
219         if (isSetClickedColor_) {
220             // for user self-defined
221             renderContext->UpdateBackgroundColor(clickedColor_);
222             return;
223         }
224         // for system default
225         auto isNeedToHandleHoverOpacity = false;
226         AnimateTouchAndHover(renderContext, isNeedToHandleHoverOpacity ? TYPE_HOVER : TYPE_CANCEL, TYPE_TOUCH,
227             TOUCH_DURATION, isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
228     }
229 }
230 
OnTouchUp()231 void ToggleButtonPattern::OnTouchUp()
232 {
233     isPress_ = false;
234     FireBuilder();
235     if (UseContentModifier()) {
236         return;
237     }
238     auto host = GetHost();
239     CHECK_NULL_VOID(host);
240     auto buttonEventHub = GetEventHub<ButtonEventHub>();
241     CHECK_NULL_VOID(buttonEventHub);
242     if (buttonEventHub->GetStateEffect()) {
243         auto renderContext = host->GetRenderContext();
244         if (isSetClickedColor_) {
245             renderContext->UpdateBackgroundColor(backgroundColor_);
246             return;
247         }
248         if (buttonEventHub->IsEnabled()) {
249             auto isNeedToHandleHoverOpacity = false;
250             AnimateTouchAndHover(renderContext, TYPE_TOUCH, isNeedToHandleHoverOpacity ? TYPE_HOVER : TYPE_CANCEL,
251                 TOUCH_DURATION, isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
252         } else {
253             AnimateTouchAndHover(renderContext, TYPE_TOUCH, TYPE_CANCEL, TOUCH_DURATION, Curves::FRICTION);
254         }
255     }
256 }
257 
InitClickEvent()258 void ToggleButtonPattern::InitClickEvent()
259 {
260     if (clickListener_) {
261         return;
262     }
263     auto host = GetHost();
264     CHECK_NULL_VOID(host);
265     auto gesture = host->GetOrCreateGestureEventHub();
266     CHECK_NULL_VOID(gesture);
267     auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
268         auto buttonPattern = weak.Upgrade();
269         buttonPattern->OnClick();
270     };
271     clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
272     gesture->AddClickEvent(clickListener_);
273 }
274 
OnClick()275 void ToggleButtonPattern::OnClick()
276 {
277     if (UseContentModifier()) {
278         return;
279     }
280     auto host = GetHost();
281     CHECK_NULL_VOID(host);
282     auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
283     CHECK_NULL_VOID(paintProperty);
284     bool isLastSelected = false;
285     if (paintProperty->HasIsOn()) {
286         isLastSelected = paintProperty->GetIsOnValue();
287     }
288     const auto& renderContext = host->GetRenderContext();
289     CHECK_NULL_VOID(renderContext);
290     Color selectedColor;
291     auto buttonPaintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
292     CHECK_NULL_VOID(buttonPaintProperty);
293     if (isLastSelected) {
294         selectedColor = buttonPaintProperty->GetBackgroundColor().value_or(unCheckedColor_);
295     } else {
296         selectedColor = buttonPaintProperty->GetSelectedColor().value_or(checkedColor_);
297     }
298     paintProperty->UpdateIsOn(!isLastSelected);
299     isOn_ = !isLastSelected;
300     renderContext->UpdateBackgroundColor(selectedColor);
301     auto buttonEventHub = GetEventHub<ToggleButtonEventHub>();
302     CHECK_NULL_VOID(buttonEventHub);
303     buttonEventHub->UpdateChangeEvent(!isLastSelected);
304     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
305 }
306 
InitButtonAndText()307 void ToggleButtonPattern::InitButtonAndText()
308 {
309     auto host = GetHost();
310     CHECK_NULL_VOID(host);
311     auto layoutProperty = host->GetLayoutProperty<ButtonLayoutProperty>();
312     CHECK_NULL_VOID(layoutProperty);
313     layoutProperty->UpdateType(ButtonType::CAPSULE);
314 
315     auto renderContext = host->GetRenderContext();
316     CHECK_NULL_VOID(renderContext);
317     if (!renderContext->HasBorderRadius()) {
318         renderContext->UpdateBorderRadius({ buttonRadius_, buttonRadius_, buttonRadius_, buttonRadius_ });
319     }
320     if (!host->GetFirstChild()) {
321         return;
322     }
323     auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
324     CHECK_NULL_VOID(textNode);
325     auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
326     CHECK_NULL_VOID(textLayoutProperty);
327     if (textLayoutProperty->HasFontSize()) {
328         layoutProperty->UpdateFontSize(textLayoutProperty->GetFontSizeValue(textFontSize_));
329     }
330     layoutProperty->UpdateLabel(textLayoutProperty->GetContentValue(""));
331     if (!textLayoutProperty->GetTextColor().has_value()) {
332         textLayoutProperty->UpdateTextColor(textColor_);
333     }
334 
335     if (!textLayoutProperty->GetMarginProperty()) {
336         MarginProperty margin;
337         margin.left = CalcLength(textMargin_.ConvertToPx());
338         margin.right = CalcLength(textMargin_.ConvertToPx());
339         textLayoutProperty->UpdateMargin(margin);
340     }
341     textNode->MarkModifyDone();
342     textNode->MarkDirtyNode();
343 }
344 
InitOnKeyEvent()345 void ToggleButtonPattern::InitOnKeyEvent()
346 {
347     auto host = GetHost();
348     CHECK_NULL_VOID(host);
349     auto focusHub = host->GetOrCreateFocusHub();
350     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
351         auto pattern = wp.Upgrade();
352         if (!pattern) {
353             return false;
354         }
355         return pattern->OnKeyEvent(event);
356     };
357     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
358 }
359 
OnKeyEvent(const KeyEvent & event)360 bool ToggleButtonPattern::OnKeyEvent(const KeyEvent& event)
361 {
362     if (event.action != KeyAction::DOWN) {
363         return false;
364     }
365     if (event.code == KeyCode::KEY_SPACE || event.code == KeyCode::KEY_ENTER) {
366         OnClick();
367         return true;
368     }
369     return false;
370 }
371 
ProvideRestoreInfo()372 std::string ToggleButtonPattern::ProvideRestoreInfo()
373 {
374     auto jsonObj = JsonUtil::Create(true);
375     jsonObj->Put("IsOn", isOn_.value_or(false));
376     return jsonObj->ToString();
377 }
378 
OnRestoreInfo(const std::string & restoreInfo)379 void ToggleButtonPattern::OnRestoreInfo(const std::string& restoreInfo)
380 {
381     auto toggleButtonPaintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
382     CHECK_NULL_VOID(toggleButtonPaintProperty);
383     auto info = JsonUtil::ParseJsonString(restoreInfo);
384     if (!info->IsValid() || !info->IsObject()) {
385         return;
386     }
387     auto jsonIsOn = info->GetValue("IsOn");
388     toggleButtonPaintProperty->UpdateIsOn(jsonIsOn->GetBool());
389     OnModifyDone();
390 }
391 
OnColorConfigurationUpdate()392 void ToggleButtonPattern::OnColorConfigurationUpdate()
393 {
394     auto host = GetHost();
395     CHECK_NULL_VOID(host);
396     auto pipeline = PipelineBase::GetCurrentContext();
397     CHECK_NULL_VOID(pipeline);
398     auto toggleTheme = pipeline->GetTheme<ToggleTheme>();
399     CHECK_NULL_VOID(toggleTheme);
400     checkedColor_ = toggleTheme->GetCheckedColor();
401     unCheckedColor_ = toggleTheme->GetBackgroundColor();
402     OnModifyDone();
403 }
404 
SetButtonPress(bool isSelected)405 void ToggleButtonPattern::SetButtonPress(bool isSelected)
406 {
407     auto host = GetHost();
408     CHECK_NULL_VOID(host);
409     auto eventHub = host->GetEventHub<EventHub>();
410     CHECK_NULL_VOID(eventHub);
411     auto enabled = eventHub->IsEnabled();
412     if (!enabled) {
413         return;
414     }
415     auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
416     CHECK_NULL_VOID(paintProperty);
417     paintProperty->UpdateIsOn(isSelected);
418     OnModifyDone();
419 }
420 
FireBuilder()421 void ToggleButtonPattern::FireBuilder()
422 {
423     auto host = GetHost();
424     CHECK_NULL_VOID(host);
425     if (!toggleMakeFunc_.has_value()) {
426         auto children = host->GetChildren();
427         for (const auto& child : children) {
428             if (child->GetId() == nodeId_) {
429                 host->RemoveChildAndReturnIndex(child);
430                 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
431                 break;
432             }
433         }
434         return;
435     }
436     auto node = BuildContentModifierNode();
437     if (contentModifierNode_ == node) {
438         return;
439     }
440     auto renderContext = host->GetRenderContext();
441     CHECK_NULL_VOID(renderContext);
442     renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
443     host->RemoveChildAndReturnIndex(contentModifierNode_);
444     contentModifierNode_ = node;
445     CHECK_NULL_VOID(contentModifierNode_);
446     nodeId_ = contentModifierNode_->GetId();
447     host->AddChild(contentModifierNode_, 0);
448     host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
449 }
450 
BuildContentModifierNode()451 RefPtr<FrameNode> ToggleButtonPattern::BuildContentModifierNode()
452 {
453     if (!toggleMakeFunc_.has_value()) {
454         return nullptr;
455     }
456     auto host = GetHost();
457     CHECK_NULL_RETURN(host, nullptr);
458     auto eventHub = host->GetEventHub<EventHub>();
459     CHECK_NULL_RETURN(eventHub, nullptr);
460     auto enabled = eventHub->IsEnabled();
461     auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
462     CHECK_NULL_RETURN(paintProperty, nullptr);
463     bool isSelected = false;
464     if (paintProperty->HasIsOn()) {
465         isSelected = paintProperty->GetIsOnValue();
466     } else {
467         isSelected = false;
468     }
469     return (toggleMakeFunc_.value())(ToggleConfiguration(enabled, isSelected));
470 }
471 
SetToggleBuilderFunc(SwitchMakeCallback && toggleMakeFunc)472 void ToggleButtonPattern::SetToggleBuilderFunc(SwitchMakeCallback&& toggleMakeFunc)
473 {
474     if (toggleMakeFunc == nullptr) {
475         toggleMakeFunc_ = std::nullopt;
476         contentModifierNode_ = nullptr;
477         auto host = GetHost();
478         CHECK_NULL_VOID(host);
479         for (auto child : host->GetChildren()) {
480             auto childNode = DynamicCast<FrameNode>(child);
481             if (childNode) {
482                 childNode->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
483             }
484         }
485         OnModifyDone();
486         return;
487     }
488     toggleMakeFunc_ = std::move(toggleMakeFunc);
489 }
490 } // namespace OHOS::Ace::NG
491