• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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