• 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/option/option_pattern.h"
17 
18 #include "base/memory/ace_type.h"
19 #include "base/utils/utils.h"
20 #include "core/components/select/select_theme.h"
21 #include "core/components_ng/base/ui_node.h"
22 #include "core/components_ng/pattern/image/image_layout_property.h"
23 #include "core/components_ng/pattern/menu/menu_pattern.h"
24 #include "core/components_ng/pattern/option/option_paint_property.h"
25 #include "core/components_ng/pattern/option/option_view.h"
26 #include "core/components_ng/pattern/text/text_layout_property.h"
27 #include "core/components_ng/property/property.h"
28 #include "core/event/touch_event.h"
29 #include "core/pipeline/pipeline_base.h"
30 #include "core/pipeline_ng/pipeline_context.h"
31 
32 namespace OHOS::Ace::NG {
OnAttachToFrameNode()33 void OptionPattern::OnAttachToFrameNode()
34 {
35     RegisterOnKeyEvent();
36     RegisterOnClick();
37     RegisterOnTouch();
38     RegisterOnHover();
39 }
40 
OnModifyDone()41 void OptionPattern::OnModifyDone()
42 {
43     Pattern::OnModifyDone();
44     auto context = PipelineBase::GetCurrentContext();
45     CHECK_NULL_VOID(context);
46     textTheme_ = context->GetTheme<TextTheme>();
47     CHECK_NULL_VOID(textTheme_);
48     selectTheme_ = context->GetTheme<SelectTheme>();
49     CHECK_NULL_VOID(selectTheme_);
50 
51     auto host = GetHost();
52     CHECK_NULL_VOID(host);
53     auto eventHub = host->GetEventHub<OptionEventHub>();
54     CHECK_NULL_VOID(eventHub);
55     if (!eventHub->IsEnabled()) {
56         CHECK_NULL_VOID(text_);
57         text_->GetRenderContext()->UpdateForegroundColor(selectTheme_->GetDisabledMenuFontColor());
58         text_->MarkModifyDone();
59     }
60     SetAccessibilityAction();
61 }
62 
OnSelectProcess()63 void OptionPattern::OnSelectProcess()
64 {
65     auto host = GetHost();
66     CHECK_NULL_VOID(host);
67     auto hub = host->GetEventHub<OptionEventHub>();
68     CHECK_NULL_VOID(hub);
69     auto JsAction = hub->GetJsCallback();
70     if (JsAction) {
71         LOGI("Option's callback executing");
72         JsAction();
73     }
74     auto onSelect = hub->GetOnSelect();
75     if (onSelect) {
76         LOGI("selecting option %d", index_);
77         onSelect(index_);
78     }
79     host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
80     // hide menu when option is clicked
81     auto pipeline = PipelineContext::GetCurrentContext();
82     CHECK_NULL_VOID(pipeline);
83     auto overlayManager = pipeline->GetOverlayManager();
84     CHECK_NULL_VOID(overlayManager);
85     auto menu = GetMenu().Upgrade();
86     CHECK_NULL_VOID(menu);
87     auto menuPattern = menu->GetPattern<MenuPattern>();
88     CHECK_NULL_VOID(menuPattern);
89     menuPattern->HideMenu();
90 }
91 
PlayBgColorAnimation(bool isHoverChange)92 void OptionPattern::PlayBgColorAnimation(bool isHoverChange)
93 {
94     AnimationOption option = AnimationOption();
95     if (isHoverChange) {
96         option.SetDuration(selectTheme_->GetHoverAnimationDuration());
97         option.SetCurve(Curves::FRICTION);
98     } else {
99         option.SetDuration(selectTheme_->GetPressAnimationDuration());
100         option.SetCurve(Curves::SHARP);
101     }
102 
103     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
104         auto pattern = weak.Upgrade();
105         CHECK_NULL_VOID_NOLOG(pattern);
106         auto host = pattern->GetHost();
107         CHECK_NULL_VOID_NOLOG(host);
108         auto renderContext = host->GetRenderContext();
109         CHECK_NULL_VOID_NOLOG(renderContext);
110         renderContext->BlendBgColor(pattern->GetBgBlendColor());
111     });
112 }
113 
RegisterOnClick()114 void OptionPattern::RegisterOnClick()
115 {
116     auto host = GetHost();
117     CHECK_NULL_VOID(host);
118     auto hub = host->GetEventHub<OptionEventHub>();
119 
120     auto event = [weak = WeakClaim(this)](GestureEvent& /* info */) {
121         auto pattern = weak.Upgrade();
122         CHECK_NULL_VOID_NOLOG(pattern);
123         pattern->OnSelectProcess();
124     };
125     auto clickEvent = MakeRefPtr<ClickEvent>(std::move(event));
126 
127     auto gestureHub = host->GetOrCreateGestureEventHub();
128     CHECK_NULL_VOID(gestureHub);
129     gestureHub->AddClickEvent(clickEvent);
130 }
131 
RegisterOnTouch()132 void OptionPattern::RegisterOnTouch()
133 {
134     auto host = GetHost();
135     CHECK_NULL_VOID(host);
136     auto gestureHub = host->GetOrCreateGestureEventHub();
137     CHECK_NULL_VOID(gestureHub);
138 
139     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
140         auto pattern = weak.Upgrade();
141         CHECK_NULL_VOID(pattern);
142         pattern->OnPress(info);
143     };
144     auto touchEvent = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
145     gestureHub->AddTouchEvent(touchEvent);
146 }
147 
RegisterOnHover()148 void OptionPattern::RegisterOnHover()
149 {
150     auto host = GetHost();
151     CHECK_NULL_VOID(host);
152     auto inputHub = host->GetOrCreateInputEventHub();
153     CHECK_NULL_VOID(inputHub);
154     auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
155         auto pattern = weak.Upgrade();
156         CHECK_NULL_VOID(pattern);
157         pattern->OnHover(isHover);
158     };
159     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
160     inputHub->AddOnHoverEvent(mouseEvent);
161 }
162 
RegisterOnKeyEvent()163 void OptionPattern::RegisterOnKeyEvent()
164 {
165     auto host = GetHost();
166     CHECK_NULL_VOID(host);
167     auto focusHub = host->GetOrCreateFocusHub();
168     CHECK_NULL_VOID(focusHub);
169     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
170         auto pattern = wp.Upgrade();
171         CHECK_NULL_RETURN_NOLOG(pattern, false);
172         return pattern->OnKeyEvent(event);
173     };
174     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
175 }
176 
OnKeyEvent(const KeyEvent & event)177 bool OptionPattern::OnKeyEvent(const KeyEvent& event)
178 {
179     if (event.action != KeyAction::DOWN) {
180         return false;
181     }
182     if (event.code == KeyCode::KEY_ENTER) {
183         OnSelectProcess();
184         return true;
185     }
186     return false;
187 }
188 
OnPress(const TouchEventInfo & info)189 void OptionPattern::OnPress(const TouchEventInfo& info)
190 {
191     auto host = GetHost();
192     CHECK_NULL_VOID(host);
193     const auto& renderContext = host->GetRenderContext();
194     CHECK_NULL_VOID(renderContext);
195     auto props = GetPaintProperty<OptionPaintProperty>();
196     CHECK_NULL_VOID(props);
197     auto touchType = info.GetTouches().front().GetTouchType();
198 
199     auto pipeline = PipelineBase::GetCurrentContext();
200     CHECK_NULL_VOID(pipeline);
201     auto theme = pipeline->GetTheme<SelectTheme>();
202     // enter press status
203     if (touchType == TouchType::DOWN) {
204         LOGD("triggers option press");
205         // change background color, update press status
206         SetBgBlendColor(theme->GetClickedColor());
207         PlayBgColorAnimation(false);
208 
209         props->UpdatePress(true);
210         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
211         // disable next option node's divider
212         UpdateNextNodeDivider(false);
213     }
214     // leave press status
215     else if (touchType == TouchType::UP) {
216         if (IsHover()) {
217             SetBgBlendColor(theme->GetHoverColor());
218         } else {
219             SetBgBlendColor(Color::TRANSPARENT);
220         }
221         PlayBgColorAnimation(false);
222 
223         props->UpdatePress(false);
224         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
225         UpdateNextNodeDivider(true);
226     }
227 }
228 
OnHover(bool isHover)229 void OptionPattern::OnHover(bool isHover)
230 {
231     SetIsHover(isHover);
232 
233     auto host = GetHost();
234     CHECK_NULL_VOID(host);
235     auto renderContext = host->GetRenderContext();
236     CHECK_NULL_VOID(renderContext);
237     auto props = GetPaintProperty<OptionPaintProperty>();
238     CHECK_NULL_VOID(props);
239     if (isHover) {
240         auto pipeline = PipelineContext::GetCurrentContext();
241         CHECK_NULL_VOID(pipeline);
242         auto theme = pipeline->GetTheme<SelectTheme>();
243         auto hoverColor = theme->GetHoverColor();
244         SetBgBlendColor(hoverColor);
245 
246         props->UpdateHover(true);
247         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
248         UpdateNextNodeDivider(false);
249     } else {
250         SetBgBlendColor(Color::TRANSPARENT);
251 
252         props->UpdateHover(false);
253         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
254         UpdateNextNodeDivider(true);
255     }
256     PlayBgColorAnimation();
257 }
258 
UpdateNextNodeDivider(bool needDivider)259 void OptionPattern::UpdateNextNodeDivider(bool needDivider)
260 {
261     auto host = GetHost();
262     // find next option node from parent menuNode
263     CHECK_NULL_VOID(host);
264     auto parent = host->GetParent();
265     CHECK_NULL_VOID(parent);
266     auto nextNode = parent->GetChildAtIndex(index_ + 1);
267     if (nextNode) {
268         if (!InstanceOf<FrameNode>(nextNode)) {
269             LOGW("next optionNode is not a frameNode! type = %{public}s", nextNode->GetTag().c_str());
270             return;
271         }
272         auto props = DynamicCast<FrameNode>(nextNode)->GetPaintProperty<OptionPaintProperty>();
273         CHECK_NULL_VOID(props);
274         props->UpdateNeedDivider(needDivider);
275         nextNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
276     }
277 }
278 
SetBgColor(const Color & color)279 void OptionPattern::SetBgColor(const Color& color)
280 {
281     auto renderContext = GetHost()->GetRenderContext();
282     CHECK_NULL_VOID(renderContext);
283     renderContext->UpdateBackgroundColor(color);
284     bgColor_ = color;
285 }
286 
SetFontSize(const Dimension & value)287 void OptionPattern::SetFontSize(const Dimension& value)
288 {
289     CHECK_NULL_VOID(text_);
290     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
291     CHECK_NULL_VOID(props);
292     text_->MarkModifyDone();
293     CHECK_NULL_VOID(selectTheme_);
294     props->UpdateFontSize(value.IsNegative() ? selectTheme_->GetMenuFontSize() : value);
295 }
296 
SetItalicFontStyle(const Ace::FontStyle & value)297 void OptionPattern::SetItalicFontStyle(const Ace::FontStyle& value)
298 {
299     CHECK_NULL_VOID(text_);
300     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
301     CHECK_NULL_VOID(props);
302     text_->MarkModifyDone();
303     props->UpdateItalicFontStyle(value);
304 }
305 
SetFontWeight(const FontWeight & value)306 void OptionPattern::SetFontWeight(const FontWeight& value)
307 {
308     CHECK_NULL_VOID(text_);
309     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
310     CHECK_NULL_VOID(props);
311     text_->MarkModifyDone();
312     props->UpdateFontWeight(value);
313 }
314 
SetFontFamily(const std::vector<std::string> & value)315 void OptionPattern::SetFontFamily(const std::vector<std::string>& value)
316 {
317     CHECK_NULL_VOID(text_);
318     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
319     CHECK_NULL_VOID(props);
320     text_->MarkModifyDone();
321     props->UpdateFontFamily(value);
322 }
323 
SetFontColor(const Color & color)324 void OptionPattern::SetFontColor(const Color& color)
325 {
326     CHECK_NULL_VOID(text_);
327     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
328     CHECK_NULL_VOID(props);
329     text_->MarkModifyDone();
330     props->UpdateTextColor(color);
331     auto context = text_->GetRenderContext();
332     CHECK_NULL_VOID(context);
333     context->UpdateForegroundColor(color);
334     context->UpdateForegroundColorFlag(false);
335     context->ResetForegroundColorStrategy();
336 }
337 
InspectorGetFont()338 std::string OptionPattern::InspectorGetFont()
339 {
340     CHECK_NULL_RETURN(text_, "");
341     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
342     CHECK_NULL_RETURN(props, "");
343     return props->InspectorGetTextFont();
344 }
345 
GetBgColor()346 Color OptionPattern::GetBgColor()
347 {
348     auto pipeline = PipelineContext::GetCurrentContext();
349     CHECK_NULL_RETURN(pipeline, Color());
350     auto theme = pipeline->GetTheme<SelectTheme>();
351     CHECK_NULL_RETURN(theme, Color());
352     auto bgColor = theme->GetBackgroundColor();
353     return bgColor_.value_or(bgColor);
354 }
355 
GetFontSize()356 Dimension OptionPattern::GetFontSize()
357 {
358     CHECK_NULL_RETURN(text_, Dimension());
359     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
360     CHECK_NULL_RETURN(props, Dimension());
361     CHECK_NULL_RETURN(selectTheme_, Dimension());
362     auto defaultSize = selectTheme_->GetMenuFontSize();
363     return props->GetFontSizeValue(defaultSize);
364 }
365 
GetItalicFontStyle()366 Ace::FontStyle OptionPattern::GetItalicFontStyle()
367 {
368     CHECK_NULL_RETURN(text_, Ace::FontStyle());
369     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
370     CHECK_NULL_RETURN(props, Ace::FontStyle());
371     auto defaultStyle = textTheme_->GetTextStyle().GetFontStyle();
372     return props->GetItalicFontStyleValue(defaultStyle);
373 }
374 
GetFontWeight()375 FontWeight OptionPattern::GetFontWeight()
376 {
377     CHECK_NULL_RETURN(text_, FontWeight());
378     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
379     CHECK_NULL_RETURN(props, FontWeight());
380     auto defaultWeight = textTheme_->GetTextStyle().GetFontWeight();
381     return props->GetFontWeightValue(defaultWeight);
382 }
383 
GetFontFamily()384 std::vector<std::string> OptionPattern::GetFontFamily()
385 {
386     CHECK_NULL_RETURN(text_, std::vector<std::string>());
387     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
388     CHECK_NULL_RETURN(props, std::vector<std::string>());
389     auto defaultFamily = textTheme_->GetTextStyle().GetFontFamilies();
390     return props->GetFontFamilyValue(defaultFamily);
391 }
392 
GetFontColor()393 Color OptionPattern::GetFontColor()
394 {
395     CHECK_NULL_RETURN(text_, Color::TRANSPARENT);
396     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
397     CHECK_NULL_RETURN(props, Color::TRANSPARENT);
398     auto defaultColor = selectTheme_->GetMenuFontColor();
399     return props->GetTextColorValue(defaultColor);
400 }
401 
GetText()402 std::string OptionPattern::GetText()
403 {
404     CHECK_NULL_RETURN(text_, std::string());
405     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
406     CHECK_NULL_RETURN(textProps, std::string());
407     return textProps->GetContentValue();
408 }
409 
UpdateText(const std::string & content)410 void OptionPattern::UpdateText(const std::string& content)
411 {
412     CHECK_NULL_VOID(text_);
413     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
414     CHECK_NULL_VOID(props);
415     props->UpdateContent(content);
416     text_->MarkModifyDone();
417     text_->MarkDirtyNode();
418 }
419 
UpdateIcon(const std::string & src)420 void OptionPattern::UpdateIcon(const std::string& src)
421 {
422     iconSrc_ = src;
423     auto host = GetHost();
424     CHECK_NULL_VOID(host);
425     RefPtr<FrameNode> row =
426         host->GetChildAtIndex(0) ? AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(0)) : nullptr;
427     CHECK_NULL_VOID(row);
428     if (src.empty()) {
429         row->RemoveChild(icon_); // it's safe even if icon_ is nullptr
430         row->MarkModifyDone();
431         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
432         icon_ = nullptr;
433         return;
434     }
435     if (!icon_) {
436         icon_ = OptionView::CreateIcon(src, row);
437         row->MarkModifyDone();
438         row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
439         return;
440     }
441 
442     auto props = icon_->GetLayoutProperty<ImageLayoutProperty>();
443     CHECK_NULL_VOID(props);
444     auto imageSrcInfo = props->GetImageSourceInfo();
445     CHECK_NULL_VOID(imageSrcInfo);
446     imageSrcInfo->SetSrc(src);
447     props->UpdateImageSourceInfo(imageSrcInfo.value());
448     icon_->MarkModifyDone();
449     icon_->MarkDirtyNode();
450 }
451 
SetAccessibilityAction()452 void OptionPattern::SetAccessibilityAction()
453 {
454     auto host = GetHost();
455     CHECK_NULL_VOID(host);
456     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
457     CHECK_NULL_VOID(accessibilityProperty);
458     accessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
459         const auto& pattern = weakPtr.Upgrade();
460         CHECK_NULL_VOID(pattern);
461         pattern->OnSelectProcess();
462     });
463 }
464 } // namespace OHOS::Ace::NG
465