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/button_pattern.h"
17
18 #include "base/utils/utils.h"
19 #include "core/common/recorder/node_data_cache.h"
20 #include "core/components/button/button_theme.h"
21 #include "core/components/common/layout/constants.h"
22 #include "core/components/common/properties/color.h"
23 #include "core/components_ng/pattern/button/button_event_hub.h"
24 #include "core/components_ng/pattern/button/toggle_button_pattern.h"
25 #include "core/components_ng/pattern/text/text_layout_property.h"
26 #include "core/components_ng/property/property.h"
27 #include "core/event/mouse_event.h"
28 #include "core/pipeline/pipeline_base.h"
29
30 namespace OHOS::Ace::NG {
31 namespace {
32 constexpr int32_t TOUCH_DURATION = 100;
33 constexpr int32_t MOUSE_HOVER_DURATION = 250;
34 constexpr int32_t TYPE_TOUCH = 0;
35 constexpr int32_t TYPE_HOVER = 1;
36 constexpr int32_t TYPE_CANCEL = 2;
37
GetColorFromType(const RefPtr<ButtonTheme> & theme,const int32_t & type)38 Color GetColorFromType(const RefPtr<ButtonTheme>& theme, const int32_t& type)
39 {
40 if (type == TYPE_TOUCH) {
41 return theme->GetClickedColor();
42 } else if (type == TYPE_HOVER) {
43 return theme->GetHoverColor();
44 } else {
45 return Color::TRANSPARENT;
46 }
47 }
48 } // namespace
49
OnAttachToFrameNode()50 void ButtonPattern::OnAttachToFrameNode()
51 {
52 auto pipeline = PipelineBase::GetCurrentContext();
53 CHECK_NULL_VOID(pipeline);
54 auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
55 CHECK_NULL_VOID(buttonTheme);
56 clickedColor_ = buttonTheme->GetClickedColor();
57 auto host = GetHost();
58 CHECK_NULL_VOID(host);
59 auto renderContext = host->GetRenderContext();
60 CHECK_NULL_VOID(renderContext);
61 renderContext->SetAlphaOffscreen(true);
62 }
63
UpdateTextLayoutProperty(RefPtr<ButtonLayoutProperty> & layoutProperty,RefPtr<TextLayoutProperty> & textLayoutProperty)64 void ButtonPattern::UpdateTextLayoutProperty(
65 RefPtr<ButtonLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty)
66 {
67 CHECK_NULL_VOID(layoutProperty);
68 CHECK_NULL_VOID(textLayoutProperty);
69
70 auto label = layoutProperty->GetLabelValue("");
71 textLayoutProperty->UpdateContent(label);
72 if (layoutProperty->GetFontSize().has_value()) {
73 textLayoutProperty->UpdateFontSize(layoutProperty->GetFontSize().value());
74 }
75 if (layoutProperty->GetFontWeight().has_value()) {
76 textLayoutProperty->UpdateFontWeight(layoutProperty->GetFontWeight().value());
77 }
78 if (layoutProperty->GetFontColor().has_value()) {
79 textLayoutProperty->UpdateTextColor(layoutProperty->GetFontColor().value());
80 }
81 if (layoutProperty->GetFontStyle().has_value()) {
82 textLayoutProperty->UpdateItalicFontStyle(layoutProperty->GetFontStyle().value());
83 }
84 if (layoutProperty->GetFontFamily().has_value()) {
85 textLayoutProperty->UpdateFontFamily(layoutProperty->GetFontFamily().value());
86 }
87 if (layoutProperty->GetTextOverflow().has_value()) {
88 textLayoutProperty->UpdateTextOverflow(layoutProperty->GetTextOverflow().value());
89 }
90 if (layoutProperty->GetMaxLines().has_value()) {
91 textLayoutProperty->UpdateMaxLines(layoutProperty->GetMaxLines().value());
92 }
93 if (layoutProperty->GetMinFontSize().has_value()) {
94 textLayoutProperty->UpdateAdaptMinFontSize(layoutProperty->GetMinFontSize().value());
95 }
96 if (layoutProperty->GetMaxFontSize().has_value()) {
97 textLayoutProperty->UpdateAdaptMaxFontSize(layoutProperty->GetMaxFontSize().value());
98 }
99 if (layoutProperty->GetHeightAdaptivePolicy().has_value()) {
100 textLayoutProperty->UpdateHeightAdaptivePolicy(layoutProperty->GetHeightAdaptivePolicy().value());
101 }
102 // update text style defined by buttonStyle and control size
103 UpdateTextStyle(layoutProperty, textLayoutProperty);
104 }
105
UpdateTextStyle(RefPtr<ButtonLayoutProperty> & layoutProperty,RefPtr<TextLayoutProperty> & textLayoutProperty)106 void ButtonPattern::UpdateTextStyle(
107 RefPtr<ButtonLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty)
108 {
109 auto pipeline = PipelineBase::GetCurrentContext();
110 CHECK_NULL_VOID(pipeline);
111 auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
112 CHECK_NULL_VOID(buttonTheme);
113 if (!textLayoutProperty->HasTextColor()) {
114 ButtonStyleMode buttonStyle = layoutProperty->GetButtonStyle().value_or(ButtonStyleMode::EMPHASIZE);
115 Color fontColor = buttonTheme->GetTextColor(buttonStyle);
116 textLayoutProperty->UpdateTextColor(fontColor);
117 }
118 if (!textLayoutProperty->HasFontSize()) {
119 ControlSize controlSize = layoutProperty->GetControlSize().value_or(ControlSize::NORMAL);
120 Dimension fontSize = buttonTheme->GetTextSize(controlSize);
121 textLayoutProperty->UpdateFontSize(fontSize);
122 }
123 }
124
IsNeedToHandleHoverOpacity()125 bool ButtonPattern::IsNeedToHandleHoverOpacity()
126 {
127 auto host = GetHost();
128 CHECK_NULL_RETURN(host, false);
129 auto inputEventHub = host->GetOrCreateInputEventHub();
130 auto hoverEffect = inputEventHub->GetHoverEffect();
131 return isHover_ && hoverEffect != HoverEffectType::BOARD && hoverEffect != HoverEffectType::SCALE &&
132 hoverEffect != HoverEffectType::NONE;
133 }
134
InitButtonLabel()135 void ButtonPattern::InitButtonLabel()
136 {
137 auto host = GetHost();
138 CHECK_NULL_VOID(host);
139 auto focusHub = host->GetFocusHub();
140 CHECK_NULL_VOID(focusHub);
141 auto layoutProperty = GetLayoutProperty<ButtonLayoutProperty>();
142 CHECK_NULL_VOID(layoutProperty);
143 if (!layoutProperty->GetLabel().has_value()) {
144 focusHub->SetFocusType(FocusType::SCOPE);
145 return;
146 }
147 focusHub->SetFocusType(FocusType::NODE);
148 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
149 CHECK_NULL_VOID(textNode);
150 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
151 CHECK_NULL_VOID(textLayoutProperty);
152 UpdateTextLayoutProperty(layoutProperty, textLayoutProperty);
153 auto buttonRenderContext = host->GetRenderContext();
154 CHECK_NULL_VOID(buttonRenderContext);
155 auto textRenderContext = textNode->GetRenderContext();
156 CHECK_NULL_VOID(textRenderContext);
157 textRenderContext->UpdateClipEdge(buttonRenderContext->GetClipEdgeValue(true));
158 textNode->MarkModifyDone();
159 textNode->MarkDirtyNode();
160 }
161
OnModifyDone()162 void ButtonPattern::OnModifyDone()
163 {
164 Pattern::OnModifyDone();
165 InitButtonLabel();
166 HandleBackgroundColor();
167 HandleEnabled();
168 InitHoverEvent();
169 InitTouchEvent();
170 }
171
InitTouchEvent()172 void ButtonPattern::InitTouchEvent()
173 {
174 if (touchListener_) {
175 return;
176 }
177 auto host = GetHost();
178 CHECK_NULL_VOID(host);
179 auto gesture = host->GetOrCreateGestureEventHub();
180 CHECK_NULL_VOID(gesture);
181 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
182 auto buttonPattern = weak.Upgrade();
183 CHECK_NULL_VOID(buttonPattern);
184 if (info.GetTouches().front().GetTouchType() == TouchType::DOWN) {
185 buttonPattern->OnTouchDown();
186 }
187 if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
188 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
189 buttonPattern->OnTouchUp();
190 }
191 };
192 touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
193 gesture->AddTouchEvent(touchListener_);
194 }
195
OnAfterModifyDone()196 void ButtonPattern::OnAfterModifyDone()
197 {
198 auto host = GetHost();
199 CHECK_NULL_VOID(host);
200 auto inspectorId = host->GetInspectorId().value_or("");
201 if (!inspectorId.empty()) {
202 auto text = host->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetText();
203 Recorder::NodeDataCache::Get().PutString(inspectorId, text);
204 }
205 }
206
InitHoverEvent()207 void ButtonPattern::InitHoverEvent()
208 {
209 auto host = GetHost();
210 CHECK_NULL_VOID(host);
211 auto eventHub = host->GetEventHub<ButtonEventHub>();
212 auto inputHub = eventHub->GetOrCreateInputEventHub();
213 auto hoverEffect = inputHub->GetHoverEffect();
214 inputHub->SetHoverEffect(hoverEffect == HoverEffectType::BOARD ? HoverEffectType::AUTO : hoverEffect);
215 if (hoverListener_) {
216 return;
217 }
218 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
219 auto pattern = weak.Upgrade();
220 if (pattern) {
221 pattern->HandleHoverEvent(isHover);
222 }
223 };
224
225 hoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverTask));
226 inputHub->AddOnHoverEvent(hoverListener_);
227 }
228
OnTouchDown()229 void ButtonPattern::OnTouchDown()
230 {
231 isPress_ = true;
232 auto host = GetHost();
233 CHECK_NULL_VOID(host);
234 auto buttonEventHub = GetEventHub<ButtonEventHub>();
235 CHECK_NULL_VOID(buttonEventHub);
236 if (buttonEventHub->GetStateEffect()) {
237 auto renderContext = host->GetRenderContext();
238 CHECK_NULL_VOID(renderContext);
239 backgroundColor_ = renderContext->GetBackgroundColor().value_or(Color::TRANSPARENT);
240 if (isSetClickedColor_) {
241 // for user self-defined
242 renderContext->UpdateBackgroundColor(clickedColor_);
243 return;
244 }
245 // for system default
246 auto isNeedToHandleHoverOpacity = IsNeedToHandleHoverOpacity();
247 AnimateTouchAndHover(renderContext, isNeedToHandleHoverOpacity ? TYPE_HOVER : TYPE_CANCEL, TYPE_TOUCH,
248 TOUCH_DURATION, isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
249 }
250 }
251
OnTouchUp()252 void ButtonPattern::OnTouchUp()
253 {
254 isPress_ = false;
255 auto host = GetHost();
256 CHECK_NULL_VOID(host);
257 auto buttonEventHub = GetEventHub<ButtonEventHub>();
258 CHECK_NULL_VOID(buttonEventHub);
259 auto toggleButtonPattern = host->GetPattern<ToggleButtonPattern>();
260 if (toggleButtonPattern) {
261 toggleButtonPattern->OnClick();
262 }
263 if (buttonEventHub->GetStateEffect()) {
264 auto renderContext = host->GetRenderContext();
265 if (isSetClickedColor_) {
266 renderContext->UpdateBackgroundColor(backgroundColor_);
267 return;
268 }
269 if (buttonEventHub->IsEnabled()) {
270 auto isNeedToHandleHoverOpacity = IsNeedToHandleHoverOpacity();
271 AnimateTouchAndHover(renderContext, TYPE_TOUCH, isNeedToHandleHoverOpacity ? TYPE_HOVER : TYPE_CANCEL,
272 TOUCH_DURATION, isNeedToHandleHoverOpacity ? Curves::SHARP : Curves::FRICTION);
273 } else {
274 AnimateTouchAndHover(renderContext, TYPE_TOUCH, TYPE_CANCEL, TOUCH_DURATION, Curves::FRICTION);
275 }
276 }
277 }
278
HandleHoverEvent(bool isHover)279 void ButtonPattern::HandleHoverEvent(bool isHover)
280 {
281 isHover_ = isHover;
282 auto host = GetHost();
283 CHECK_NULL_VOID(host);
284 auto eventHub = host->GetEventHub<EventHub>();
285 CHECK_NULL_VOID(eventHub);
286 auto enabled = eventHub->IsEnabled();
287 auto inputEventHub = host->GetOrCreateInputEventHub();
288 auto hoverEffect = inputEventHub->GetHoverEffect();
289 if (hoverEffect == HoverEffectType::NONE || hoverEffect == HoverEffectType::SCALE) {
290 return;
291 }
292 if (!isPress_ && (enabled || !isHover)) {
293 auto renderContext = host->GetRenderContext();
294 CHECK_NULL_VOID(renderContext);
295 AnimateTouchAndHover(renderContext, isHover ? TYPE_CANCEL : TYPE_HOVER, isHover ? TYPE_HOVER : TYPE_CANCEL,
296 MOUSE_HOVER_DURATION, Curves::FRICTION);
297 }
298 }
299
HandleBackgroundColor()300 void ButtonPattern::HandleBackgroundColor()
301 {
302 auto host = GetHost();
303 CHECK_NULL_VOID(host);
304 auto pipeline = PipelineBase::GetCurrentContext();
305 CHECK_NULL_VOID(pipeline);
306 auto renderContext = host->GetRenderContext();
307 CHECK_NULL_VOID(renderContext);
308 auto layoutProperty = GetLayoutProperty<ButtonLayoutProperty>();
309 CHECK_NULL_VOID(layoutProperty);
310 auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
311 CHECK_NULL_VOID(buttonTheme);
312 ButtonStyleMode buttonStyle = layoutProperty->GetButtonStyle().value_or(ButtonStyleMode::EMPHASIZE);
313 if (!renderContext->HasBackgroundColor()) {
314 renderContext->UpdateBackgroundColor(buttonTheme->GetBgColor(buttonStyle));
315 }
316 themeBgColor_ = buttonTheme->GetBgColor(buttonStyle);
317 themeTextColor_ = buttonTheme->GetTextColor(buttonStyle);
318 }
319
HandleEnabled()320 void ButtonPattern::HandleEnabled()
321 {
322 auto host = GetHost();
323 CHECK_NULL_VOID(host);
324 auto eventHub = host->GetEventHub<EventHub>();
325 CHECK_NULL_VOID(eventHub);
326 auto enabled = eventHub->IsEnabled();
327 auto renderContext = host->GetRenderContext();
328 CHECK_NULL_VOID(renderContext);
329 auto pipeline = PipelineBase::GetCurrentContext();
330 CHECK_NULL_VOID(pipeline);
331 auto theme = pipeline->GetTheme<ButtonTheme>();
332 CHECK_NULL_VOID(theme);
333 auto alpha = theme->GetBgDisabledAlpha();
334 auto originalOpacity = renderContext->GetOpacityValue(1.0);
335 renderContext->OnOpacityUpdate(enabled ? originalOpacity : alpha * originalOpacity);
336 }
337
AnimateTouchAndHover(RefPtr<RenderContext> & renderContext,int32_t typeFrom,int32_t typeTo,int32_t duration,const RefPtr<Curve> & curve)338 void ButtonPattern::AnimateTouchAndHover(RefPtr<RenderContext>& renderContext, int32_t typeFrom, int32_t typeTo,
339 int32_t duration, const RefPtr<Curve>& curve)
340 {
341 auto pipeline = PipelineBase::GetCurrentContext();
342 CHECK_NULL_VOID(pipeline);
343 auto theme = pipeline->GetTheme<ButtonTheme>();
344 CHECK_NULL_VOID(theme);
345 Color blendColorFrom = GetColorFromType(theme, typeFrom);
346 Color blendColorTo = GetColorFromType(theme, typeTo);
347 renderContext->BlendBgColor(blendColorFrom);
348 AnimationOption option = AnimationOption();
349 option.SetDuration(duration);
350 option.SetCurve(curve);
351 AnimationUtils::Animate(option, [renderContext, blendColorTo]() { renderContext->BlendBgColor(blendColorTo); });
352 }
353
OnColorConfigurationUpdate()354 void ButtonPattern::OnColorConfigurationUpdate()
355 {
356 auto node = GetHost();
357 if (isColorUpdateFlag_) {
358 node->SetNeedCallChildrenUpdate(false);
359 return;
360 }
361 node->SetNeedCallChildrenUpdate(false);
362 auto pipeline = PipelineBase::GetCurrentContext();
363 auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
364 auto renderContext = node->GetRenderContext();
365 CHECK_NULL_VOID(renderContext);
366 auto buttonLayoutProperty = node->GetLayoutProperty<ButtonLayoutProperty>();
367 CHECK_NULL_VOID(buttonLayoutProperty);
368 ButtonStyleMode buttonStyle = buttonLayoutProperty->GetButtonStyle().value_or(ButtonStyleMode::EMPHASIZE);
369 if (renderContext->GetBackgroundColor().value_or(themeBgColor_) == themeBgColor_) {
370 auto color = buttonTheme->GetBgColor(buttonStyle);
371 renderContext->UpdateBackgroundColor(color);
372 }
373 auto textNode = DynamicCast<FrameNode>(node->GetFirstChild());
374 CHECK_NULL_VOID(textNode);
375 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
376 CHECK_NULL_VOID(textLayoutProperty);
377 if (textLayoutProperty->GetTextColor().value_or(themeTextColor_) == themeTextColor_) {
378 textLayoutProperty->UpdateTextColor(buttonTheme->GetTextColor(buttonStyle));
379 textNode->MarkDirtyNode();
380 }
381 }
382 } // namespace OHOS::Ace::NG
383