1 /*
2 * Copyright (c) 2022 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/components/button/button_theme.h"
21 #include "core/components/common/properties/color.h"
22 #include "core/components/toggle/toggle_theme.h"
23 #include "core/components_ng/base/ui_node.h"
24 #include "core/components_ng/pattern/button/toggle_button_paint_property.h"
25 #include "core/components_ng/pattern/text/text_layout_property.h"
26 #include "core/components_ng/property/property.h"
27 #include "core/pipeline/pipeline_base.h"
28
29 namespace OHOS::Ace::NG {
30
OnAttachToFrameNode()31 void ToggleButtonPattern::OnAttachToFrameNode()
32 {
33 InitParameters();
34 }
35
InitParameters()36 void ToggleButtonPattern::InitParameters()
37 {
38 auto pipeline = PipelineBase::GetCurrentContext();
39 CHECK_NULL_VOID(pipeline);
40 auto toggleTheme = pipeline->GetTheme<ToggleTheme>();
41 CHECK_NULL_VOID(toggleTheme);
42 checkedColor_ = toggleTheme->GetCheckedColor();
43 unCheckedColor_ = toggleTheme->GetBackgroundColor();
44 textMargin_ = toggleTheme->GetTextMargin();
45 buttonMargin_ = toggleTheme->GetButtonMargin();
46 buttonHeight_ = toggleTheme->GetButtonHeight();
47 buttonRadius_ = toggleTheme->GetButtonRadius();
48 textFontSize_ = toggleTheme->GetTextFontSize();
49 textColor_ = toggleTheme->GetTextColor();
50 disabledAlpha_ = toggleTheme->GetDisabledAlpha();
51 auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
52 CHECK_NULL_VOID(buttonTheme);
53 clickedColor_ = buttonTheme->GetClickedColor();
54 }
55
OnModifyDone()56 void ToggleButtonPattern::OnModifyDone()
57 {
58 auto host = GetHost();
59 CHECK_NULL_VOID(host);
60
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
70 auto buttonPaintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
71 CHECK_NULL_VOID(buttonPaintProperty);
72 if (!isOn_.has_value()) {
73 isOn_ = buttonPaintProperty->GetIsOnValue();
74 }
75 bool changed = false;
76 if (buttonPaintProperty->HasIsOn()) {
77 bool isOn = buttonPaintProperty->GetIsOnValue();
78 changed = isOn ^ isOn_.value();
79 isOn_ = isOn;
80 }
81 const auto& renderContext = host->GetRenderContext();
82 CHECK_NULL_VOID(renderContext);
83
84 if (isOn_.value()) {
85 auto selectedColor = buttonPaintProperty->GetSelectedColor().value_or(checkedColor_);
86 renderContext->UpdateBackgroundColor(selectedColor);
87 } else {
88 auto bgColor = buttonPaintProperty->GetBackgroundColor().value_or(unCheckedColor_);
89 renderContext->UpdateBackgroundColor(bgColor);
90 }
91
92 if (changed) {
93 auto toggleButtonEventHub = GetEventHub<ToggleButtonEventHub>();
94 CHECK_NULL_VOID(toggleButtonEventHub);
95 toggleButtonEventHub->UpdateChangeEvent(isOn_.value());
96 }
97 InitButtonAndText();
98 HandleEnabled();
99 InitTouchEvent();
100 InitHoverEvent();
101 InitOnKeyEvent();
102 }
103
HandleEnabled()104 void ToggleButtonPattern::HandleEnabled()
105 {
106 auto host = GetHost();
107 CHECK_NULL_VOID(host);
108 auto eventHub = host->GetEventHub<EventHub>();
109 CHECK_NULL_VOID(eventHub);
110 auto enabled = eventHub->IsEnabled();
111 auto renderContext = host->GetRenderContext();
112 CHECK_NULL_VOID(renderContext);
113 auto pipeline = PipelineBase::GetCurrentContext();
114 CHECK_NULL_VOID(pipeline);
115 auto theme = pipeline->GetTheme<ToggleTheme>();
116 CHECK_NULL_VOID(theme);
117 auto backgroundColor = renderContext->GetBackgroundColor().value_or(theme->GetCheckedColor());
118 if (!enabled) {
119 if (host->GetFirstChild()) {
120 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
121 CHECK_NULL_VOID(textNode);
122 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
123 CHECK_NULL_VOID(textLayoutProperty);
124 auto color = textLayoutProperty->GetTextColorValue(textColor_);
125 textLayoutProperty->UpdateTextColor(color.BlendOpacity(disabledAlpha_));
126 }
127 renderContext->OnBackgroundColorUpdate(backgroundColor.BlendOpacity(disabledAlpha_));
128 } else {
129 renderContext->OnBackgroundColorUpdate(backgroundColor);
130 }
131 }
132
InitClickEvent()133 void ToggleButtonPattern::InitClickEvent()
134 {
135 if (clickListener_) {
136 return;
137 }
138 auto host = GetHost();
139 CHECK_NULL_VOID(host);
140 auto gesture = host->GetOrCreateGestureEventHub();
141 CHECK_NULL_VOID(gesture);
142 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
143 auto buttonPattern = weak.Upgrade();
144 buttonPattern->OnClick();
145 };
146 clickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
147 gesture->AddClickEvent(clickListener_);
148 }
149
OnClick()150 void ToggleButtonPattern::OnClick()
151 {
152 auto host = GetHost();
153 CHECK_NULL_VOID(host);
154 auto paintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
155 CHECK_NULL_VOID(paintProperty);
156 bool isLastSelected = false;
157 if (paintProperty->HasIsOn()) {
158 isLastSelected = paintProperty->GetIsOnValue();
159 }
160 const auto& renderContext = host->GetRenderContext();
161 CHECK_NULL_VOID(renderContext);
162 Color selectedColor;
163 auto buttonPaintProperty = host->GetPaintProperty<ToggleButtonPaintProperty>();
164 CHECK_NULL_VOID(buttonPaintProperty);
165 if (isLastSelected) {
166 selectedColor = buttonPaintProperty->GetBackgroundColor().value_or(unCheckedColor_);
167 } else {
168 selectedColor = buttonPaintProperty->GetSelectedColor().value_or(checkedColor_);
169 }
170 paintProperty->UpdateIsOn(!isLastSelected);
171 isOn_ = !isLastSelected;
172 renderContext->UpdateBackgroundColor(selectedColor);
173 auto buttonEventHub = GetEventHub<ToggleButtonEventHub>();
174 CHECK_NULL_VOID(buttonEventHub);
175 buttonEventHub->UpdateChangeEvent(!isLastSelected);
176 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
177 }
178
InitButtonAndText()179 void ToggleButtonPattern::InitButtonAndText()
180 {
181 auto host = GetHost();
182 CHECK_NULL_VOID(host);
183 auto layoutProperty = host->GetLayoutProperty<ButtonLayoutProperty>();
184 CHECK_NULL_VOID(layoutProperty);
185 if ((!layoutProperty->GetCalcLayoutConstraint() ||
186 !layoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Height().has_value())) {
187 layoutProperty->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(buttonHeight_)));
188 }
189 auto renderContext = host->GetRenderContext();
190 CHECK_NULL_VOID(renderContext);
191 if (!renderContext->HasBorderRadius()) {
192 renderContext->UpdateBorderRadius({ buttonRadius_, buttonRadius_, buttonRadius_, buttonRadius_ });
193 }
194 if (!host->GetFirstChild()) {
195 return;
196 }
197 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
198 CHECK_NULL_VOID(textNode);
199 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
200 CHECK_NULL_VOID(textLayoutProperty);
201 if (!textLayoutProperty->HasFontSize()) {
202 textLayoutProperty->UpdateFontSize(textFontSize_);
203 } else {
204 layoutProperty->UpdateFontSize(textLayoutProperty->GetFontSizeValue(textFontSize_));
205 }
206 layoutProperty->UpdateLabel(textLayoutProperty->GetContentValue(""));
207 textLayoutProperty->UpdateTextColor(textColor_);
208
209 if (!textLayoutProperty->GetMarginProperty()) {
210 MarginProperty margin;
211 margin.left = CalcLength(textMargin_.ConvertToPx());
212 margin.right = CalcLength(textMargin_.ConvertToPx());
213 textLayoutProperty->UpdateMargin(margin);
214 }
215 textNode->MarkModifyDone();
216 textNode->MarkDirtyNode();
217 }
218
InitOnKeyEvent()219 void ToggleButtonPattern::InitOnKeyEvent()
220 {
221 auto host = GetHost();
222 CHECK_NULL_VOID(host);
223 auto focusHub = host->GetOrCreateFocusHub();
224 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
225 auto pattern = wp.Upgrade();
226 if (!pattern) {
227 return false;
228 }
229 return pattern->OnKeyEvent(event);
230 };
231 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
232 }
233
OnKeyEvent(const KeyEvent & event)234 bool ToggleButtonPattern::OnKeyEvent(const KeyEvent& event)
235 {
236 if (event.action != KeyAction::DOWN) {
237 return false;
238 }
239 if (event.code == KeyCode::KEY_SPACE || event.code == KeyCode::KEY_ENTER) {
240 OnClick();
241 return true;
242 }
243 return false;
244 }
245
ProvideRestoreInfo()246 std::string ToggleButtonPattern::ProvideRestoreInfo()
247 {
248 auto jsonObj = JsonUtil::Create(true);
249 jsonObj->Put("IsOn", isOn_.value_or(false));
250 return jsonObj->ToString();
251 }
252
OnRestoreInfo(const std::string & restoreInfo)253 void ToggleButtonPattern::OnRestoreInfo(const std::string& restoreInfo)
254 {
255 auto toggleButtonPaintProperty = GetPaintProperty<ToggleButtonPaintProperty>();
256 CHECK_NULL_VOID(toggleButtonPaintProperty);
257 auto info = JsonUtil::ParseJsonString(restoreInfo);
258 if (!info->IsValid() || !info->IsObject()) {
259 return;
260 }
261 auto jsonIsOn = info->GetValue("IsOn");
262 toggleButtonPaintProperty->UpdateIsOn(jsonIsOn->GetBool());
263 OnModifyDone();
264 }
265 } // namespace OHOS::Ace::NG
266