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