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