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/select/select_pattern.h"
17 
18 #include <cstdint>
19 #include <optional>
20 
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/json/json_util.h"
24 #include "base/utils/system_properties.h"
25 #include "base/utils/utils.h"
26 #include "core/animation/curves.h"
27 #include "core/common/recorder/event_recorder.h"
28 #include "core/common/recorder/node_data_cache.h"
29 #include "core/components/common/properties/color.h"
30 #include "core/components/common/properties/text_style.h"
31 #include "core/components/select/select_theme.h"
32 #include "core/components/theme/icon_theme.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/base/inspector_filter.h"
35 #include "core/components_ng/base/view_abstract.h"
36 #include "core/components_ng/base/view_stack_processor.h"
37 #include "core/components_ng/pattern/image/image_pattern.h"
38 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
39 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
40 #include "core/components_ng/pattern/menu/menu_pattern.h"
41 #include "core/components_ng/pattern/option/option_pattern.h"
42 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
43 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
44 #include "core/components_ng/pattern/select/select_event_hub.h"
45 #include "core/components_ng/pattern/select/select_properties.h"
46 #include "core/components_ng/pattern/text/text_layout_property.h"
47 #include "core/components_ng/pattern/text/text_pattern.h"
48 #include "core/components_ng/property/border_property.h"
49 #include "core/components_ng/property/measure_property.h"
50 #include "core/components_ng/property/measure_utils.h"
51 #include "core/components_ng/property/property.h"
52 #include "core/components_v2/inspector/inspector_constants.h"
53 #include "core/components_v2/inspector/utils.h"
54 #include "core/pipeline/pipeline_base.h"
55 
56 namespace OHOS::Ace::NG {
57 
58 namespace {
59 
60 constexpr uint32_t SELECT_ITSELF_TEXT_LINES = 1;
61 
62 constexpr Dimension OPTION_MARGIN = 8.0_vp;
63 
64 constexpr Dimension CALIBERATE_X = 4.0_vp;
65 
66 constexpr Dimension CALIBERATE_Y = 4.0_vp;
67 
68 constexpr Dimension SELECT_SMALL_PADDING_VP = 4.0_vp;
69 
70 constexpr Dimension SELECT_MARGIN_VP = 8.0_vp;
71 
RecordChange(RefPtr<FrameNode> host,int32_t index,const std::string & value)72 void RecordChange(RefPtr<FrameNode> host, int32_t index, const std::string& value)
73 {
74     if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
75         auto inspectorId = host->GetInspectorId().value_or("");
76         Recorder::EventParamsBuilder builder;
77         builder.SetId(inspectorId)
78             .SetType(host->GetTag())
79             .SetIndex(index)
80             .SetText(value)
81             .SetDescription(host->GetAutoEventParamValue(""));
82         Recorder::EventRecorder::Get().OnChange(std::move(builder));
83         if (!inspectorId.empty()) {
84             Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, value, index);
85         }
86     }
87 }
88 
ConvertControlSizeToString(ControlSize controlSize)89 static std::string ConvertControlSizeToString(ControlSize controlSize)
90 {
91     std::string result;
92     switch (controlSize) {
93         case ControlSize::SMALL:
94             result = "ControlSize.SMALL";
95             break;
96         case ControlSize::NORMAL:
97             result = "ControlSize.NORMAL";
98             break;
99         default:
100             break;
101     }
102     return result;
103 }
104 } // namespace
105 
OnAttachToFrameNode()106 void SelectPattern::OnAttachToFrameNode()
107 {
108     RegisterOnKeyEvent();
109     RegisterOnClick();
110     RegisterOnPress();
111     RegisterOnHover();
112 }
113 
OnModifyDone()114 void SelectPattern::OnModifyDone()
115 {
116     Pattern::OnModifyDone();
117     CreateSelectedCallback();
118 
119     auto host = GetHost();
120     CHECK_NULL_VOID(host);
121     auto eventHub = host->GetEventHub<SelectEventHub>();
122     CHECK_NULL_VOID(eventHub);
123     if (!eventHub->IsEnabled()) {
124         SetDisabledStyle();
125     }
126     auto menu = GetMenuNode();
127     CHECK_NULL_VOID(menu);
128     auto menuPattern = menu->GetPattern<MenuPattern>();
129     CHECK_NULL_VOID(menuPattern);
130     menuPattern->UpdateSelectIndex(selected_);
131 }
132 
OnAfterModifyDone()133 void SelectPattern::OnAfterModifyDone()
134 {
135     auto host = GetHost();
136     CHECK_NULL_VOID(host);
137     auto inspectorId = host->GetInspectorId().value_or("");
138     if (inspectorId.empty()) {
139         return;
140     }
141     Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, selectValue_, selected_);
142 }
143 
SetItemSelected(int32_t index,const std::string & value)144 void SelectPattern::SetItemSelected(int32_t index, const std::string& value)
145 {
146     auto host = GetHost();
147     CHECK_NULL_VOID(host);
148     auto menu = GetMenuNode();
149     CHECK_NULL_VOID(menu);
150     auto menuPattern = menu->GetPattern<MenuPattern>();
151     CHECK_NULL_VOID(menuPattern);
152     isSelected_ = true;
153     menuPattern->UpdateSelectIndex(index);
154     CHECK_NULL_VOID(text_);
155     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
156     CHECK_NULL_VOID(textProps);
157     SetSelected(index);
158     textProps->UpdateContent(value);
159     text_->MarkModifyDone();
160     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
161     menuPattern->HideMenu();
162     auto hub = host->GetEventHub<SelectEventHub>();
163     CHECK_NULL_VOID(hub);
164 
165     auto onSelect = hub->GetSelectEvent();
166     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "select choice index %{public}d", index);
167     if (onSelect) {
168         onSelect(index, value);
169     }
170     RecordChange(host, index, value);
171 }
172 
ShowSelectMenu()173 void SelectPattern::ShowSelectMenu()
174 {
175     CHECK_NULL_VOID(!options_.empty());
176     auto context = PipelineContext::GetCurrentContext();
177     CHECK_NULL_VOID(context);
178     auto overlayManager = context->GetOverlayManager();
179     CHECK_NULL_VOID(overlayManager);
180 
181     auto menu = GetMenuNode();
182     CHECK_NULL_VOID(menu);
183     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
184     CHECK_NULL_VOID(menuLayoutProps);
185     menuLayoutProps->UpdateTargetSize(selectSize_);
186 
187     auto select = GetHost();
188     CHECK_NULL_VOID(select);
189     auto selectGeometry = select->GetGeometryNode();
190     CHECK_NULL_VOID(selectGeometry);
191     auto selectProps = select->GetLayoutProperty();
192     CHECK_NULL_VOID(selectProps);
193 
194     if (isFitTrigger_) {
195         auto selectWidth = selectSize_.Width();
196         auto menuPattern = menu->GetPattern<MenuPattern>();
197         CHECK_NULL_VOID(menuPattern);
198         menuPattern->SetIsWidthModifiedBySelect(true);
199         menuLayoutProps->UpdateSelectMenuModifiedWidth(selectWidth);
200         auto scroll = DynamicCast<FrameNode>(menu->GetFirstChild());
201         CHECK_NULL_VOID(scroll);
202         auto scrollPattern = scroll->GetPattern<ScrollPattern>();
203         CHECK_NULL_VOID(scrollPattern);
204         scrollPattern->SetIsWidthModifiedBySelect(true);
205         auto scrollLayoutProps = scroll->GetLayoutProperty<ScrollLayoutProperty>();
206         CHECK_NULL_VOID(scrollLayoutProps);
207         scrollLayoutProps->UpdateScrollWidth(selectWidth);
208         UpdateOptionsWidth(selectWidth);
209     }
210 
211     auto offset = GetHost()->GetPaintRectOffset();
212     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
213         offset.AddY(selectSize_.Height() + CALIBERATE_Y.ConvertToPx());
214         offset.AddX(-CALIBERATE_X.ConvertToPx());
215     } else {
216         offset.AddY(selectSize_.Height());
217     }
218 
219     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "select click to show menu.");
220     overlayManager->ShowMenu(GetHost()->GetId(), offset, menuWrapper_);
221 }
222 
UpdateOptionsWidth(float selectWidth)223 void SelectPattern::UpdateOptionsWidth(float selectWidth)
224 {
225     for (size_t i = 0; i < options_.size(); ++i) {
226         auto optionGeoNode = options_[i]->GetGeometryNode();
227         CHECK_NULL_VOID(optionGeoNode);
228         auto optionWidth = selectWidth - OPTION_MARGIN.ConvertToPx();
229         auto optionPattern = options_[i]->GetPattern<OptionPattern>();
230         CHECK_NULL_VOID(optionPattern);
231         optionPattern->SetIsWidthModifiedBySelect(true);
232         auto optionPaintProperty = options_[i]->GetPaintProperty<OptionPaintProperty>();
233         CHECK_NULL_VOID(optionPaintProperty);
234         optionPaintProperty->UpdateSelectModifiedWidth(optionWidth);
235     }
236 }
237 
238 // add click event to show menu
RegisterOnClick()239 void SelectPattern::RegisterOnClick()
240 {
241     auto host = GetHost();
242     CHECK_NULL_VOID(host);
243 
244     GestureEventFunc callback = [weak = WeakClaim(this)](GestureEvent& /* info */) mutable {
245         auto pattern = weak.Upgrade();
246         CHECK_NULL_VOID(pattern);
247 
248         auto selected = pattern->GetSelected();
249         if (selected > -1 && selected < static_cast<int32_t>(pattern->GetOptions().size())) {
250             pattern->UpdateSelectedProps(selected);
251         }
252         pattern->ShowSelectMenu();
253     };
254     auto gestureHub = host->GetOrCreateGestureEventHub();
255     if (!gestureHub->GetTouchable()) {
256         return;
257     }
258     gestureHub->BindMenu(std::move(callback));
259 }
260 
PlayBgColorAnimation(bool isHoverChange)261 void SelectPattern::PlayBgColorAnimation(bool isHoverChange)
262 {
263     auto host = GetHost();
264     CHECK_NULL_VOID(host);
265     auto* pipeline = host->GetContextWithCheck();
266     CHECK_NULL_VOID(pipeline);
267     auto selectTheme = pipeline->GetTheme<SelectTheme>();
268     CHECK_NULL_VOID(selectTheme);
269 
270     AnimationOption option = AnimationOption();
271     if (isHoverChange) {
272         option.SetDuration(selectTheme->GetHoverAnimationDuration());
273         option.SetCurve(Curves::FRICTION);
274     } else {
275         option.SetDuration(selectTheme->GetPressAnimationDuration());
276         option.SetCurve(Curves::SHARP);
277     }
278 
279     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
280         auto pattern = weak.Upgrade();
281         CHECK_NULL_VOID(pattern);
282         auto host = pattern->GetHost();
283         CHECK_NULL_VOID(host);
284         auto renderContext = host->GetRenderContext();
285         CHECK_NULL_VOID(renderContext);
286         renderContext->BlendBgColor(pattern->GetBgBlendColor());
287     });
288 }
289 
290 // change background color when hovered
RegisterOnHover()291 void SelectPattern::RegisterOnHover()
292 {
293     auto host = GetHost();
294     CHECK_NULL_VOID(host);
295     auto inputHub = host->GetOrCreateInputEventHub();
296     CHECK_NULL_VOID(inputHub);
297     auto mouseCallback = [weak = WeakClaim(this), host](bool isHover) {
298         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "select mouse hover %{public}d", isHover);
299         auto pattern = weak.Upgrade();
300         CHECK_NULL_VOID(pattern);
301         pattern->SetIsHover(isHover);
302         auto* pipeline = host->GetContextWithCheck();
303         CHECK_NULL_VOID(pipeline);
304         auto theme = pipeline->GetTheme<SelectTheme>();
305         CHECK_NULL_VOID(theme);
306         // update hover status, repaint background color
307         if (isHover) {
308             pattern->SetBgBlendColor(theme->GetHoverColor());
309         } else {
310             pattern->SetBgBlendColor(Color::TRANSPARENT);
311         }
312         pattern->PlayBgColorAnimation();
313     };
314     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseCallback));
315     inputHub->AddOnHoverEvent(mouseEvent);
316 }
317 
318 // change background color when pressed
RegisterOnPress()319 void SelectPattern::RegisterOnPress()
320 {
321     auto host = GetHost();
322     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
323         auto pattern = weak.Upgrade();
324         CHECK_NULL_VOID(pattern);
325         auto host = pattern->GetHost();
326         CHECK_NULL_VOID(host);
327         auto context = host->GetContextRefPtr();
328         CHECK_NULL_VOID(context);
329         auto theme = context->GetTheme<SelectTheme>();
330         auto touchType = info.GetTouches().front().GetTouchType();
331         const auto& renderContext = host->GetRenderContext();
332         CHECK_NULL_VOID(renderContext);
333         // update press status, repaint background color
334         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "select touch type %{public}zu", touchType);
335         if (touchType == TouchType::DOWN) {
336             pattern->SetBgBlendColor(theme->GetClickedColor());
337             pattern->PlayBgColorAnimation(false);
338         }
339         if (touchType == TouchType::UP) {
340             if (pattern->IsHover()) {
341                 pattern->SetBgBlendColor(theme->GetHoverColor());
342             } else {
343                 pattern->SetBgBlendColor(Color::TRANSPARENT);
344             }
345             pattern->PlayBgColorAnimation(false);
346         }
347     };
348     auto touchEvent = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
349     auto gestureHub = host->GetOrCreateGestureEventHub();
350     gestureHub->AddTouchEvent(touchEvent);
351 }
352 
CreateSelectedCallback()353 void SelectPattern::CreateSelectedCallback()
354 {
355     auto host = GetHost();
356     CHECK_NULL_VOID(host);
357     auto callback = [weak = WeakClaim(RawPtr(host))](int32_t index) {
358         auto host = weak.Upgrade();
359         CHECK_NULL_VOID(host);
360         auto pattern = host->GetPattern<SelectPattern>();
361         CHECK_NULL_VOID(pattern);
362         pattern->SetSelected(index);
363         pattern->UpdateText(index);
364         pattern->isSelected_ = true;
365         auto hub = host->GetEventHub<SelectEventHub>();
366         CHECK_NULL_VOID(hub);
367         // execute change event callback
368         auto selectChangeEvent = hub->GetSelectChangeEvent();
369         if (selectChangeEvent) {
370             selectChangeEvent(index);
371         }
372         auto valueChangeEvent = hub->GetValueChangeEvent();
373         if (valueChangeEvent) {
374             auto newSelected = pattern->options_[index]->GetPattern<OptionPattern>();
375             CHECK_NULL_VOID(newSelected);
376             valueChangeEvent(newSelected->GetText());
377         }
378         // execute onSelect callback
379         auto newSelected = pattern->options_[index]->GetPattern<OptionPattern>();
380         CHECK_NULL_VOID(newSelected);
381         auto value = newSelected->GetText();
382         auto onSelect = hub->GetSelectEvent();
383         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "select choice index %{public}d", index);
384         if (onSelect) {
385             onSelect(index, value);
386         }
387         RecordChange(host, index, value);
388     };
389     for (auto&& option : options_) {
390         auto hub = option->GetEventHub<OptionEventHub>();
391         // no std::move, need to set multiple options
392         hub->SetOnSelect(callback);
393         option->MarkModifyDone();
394     }
395 }
396 
RegisterOnKeyEvent()397 void SelectPattern::RegisterOnKeyEvent()
398 {
399     auto host = GetHost();
400     CHECK_NULL_VOID(host);
401     auto focusHub = host->GetOrCreateFocusHub();
402     CHECK_NULL_VOID(focusHub);
403     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
404         auto pattern = wp.Upgrade();
405         CHECK_NULL_RETURN(pattern, false);
406         return pattern->OnKeyEvent(event);
407     };
408     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
409 }
410 
OnKeyEvent(const KeyEvent & event)411 bool SelectPattern::OnKeyEvent(const KeyEvent& event)
412 {
413     if (event.action != KeyAction::DOWN) {
414         return false;
415     }
416     if (event.code == KeyCode::KEY_ENTER) {
417         auto host = GetHost();
418         CHECK_NULL_RETURN(host, false);
419         auto focusHub = host->GetOrCreateFocusHub();
420         CHECK_NULL_RETURN(focusHub, false);
421         focusHub->OnClick(event);
422         return true;
423     }
424     return false;
425 }
426 
SetDisabledStyle()427 void SelectPattern::SetDisabledStyle()
428 {
429     auto host = GetHost();
430     CHECK_NULL_VOID(host);
431     auto pipeline = host->GetContextWithCheck();
432     CHECK_NULL_VOID(pipeline);
433     auto theme = pipeline->GetTheme<SelectTheme>();
434     CHECK_NULL_VOID(theme);
435 
436     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
437     CHECK_NULL_VOID(textProps);
438     textProps->UpdateTextColor(theme->GetDisabledFontColor());
439     text_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
440 
441     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
442         auto spinnerLayoutProperty = spinner_->GetLayoutProperty<TextLayoutProperty>();
443         CHECK_NULL_VOID(spinnerLayoutProperty);
444         spinnerLayoutProperty->UpdateSymbolColorList({theme->GetDisabledSpinnerSymbolColor()});
445     } else {
446         auto spinnerLayoutProperty = spinner_->GetLayoutProperty<ImageLayoutProperty>();
447         CHECK_NULL_VOID(spinnerLayoutProperty);
448 
449         ImageSourceInfo imageSourceInfo = spinnerLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo());
450         auto iconTheme = pipeline->GetTheme<IconTheme>();
451         CHECK_NULL_VOID(iconTheme);
452         auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::SPINNER_DISABLE);
453         imageSourceInfo.SetSrc(iconPath);
454         if (imageSourceInfo.IsSvg()) {
455             imageSourceInfo.SetFillColor(theme->GetDisabledSpinnerColor());
456         }
457         spinnerLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
458         auto spinnerRenderProperty = spinner_->GetPaintProperty<ImageRenderProperty>();
459         CHECK_NULL_VOID(spinnerRenderProperty);
460         spinnerRenderProperty->UpdateSvgFillColor(theme->GetDisabledSpinnerColor());
461     }
462     spinner_->MarkModifyDone();
463 
464     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
465         auto renderContext = host->GetRenderContext();
466         CHECK_NULL_VOID(renderContext);
467         renderContext->UpdateBackgroundColor(renderContext->GetBackgroundColor()
468                                                  .value_or(theme->GetButtonBackgroundColor())
469                                                  .BlendOpacity(theme->GetDisabledFontColorAlpha()));
470     }
471 }
472 
SetSelected(int32_t index)473 void SelectPattern::SetSelected(int32_t index)
474 {
475     // if option is already selected, do nothing
476     if (index == selected_) {
477         return;
478     }
479     if (index >= static_cast<int32_t>(options_.size()) || index < 0) {
480         selected_ = -1;
481         ResetOptionProps();
482         return;
483     }
484     UpdateLastSelectedProps(index);
485     selected_ = index;
486 }
487 
AddOptionNode(const RefPtr<FrameNode> & option)488 void SelectPattern::AddOptionNode(const RefPtr<FrameNode>& option)
489 {
490     CHECK_NULL_VOID(option);
491     options_.push_back(option);
492 }
493 
BuildChild()494 void SelectPattern::BuildChild()
495 {
496     auto select = GetHost();
497     CHECK_NULL_VOID(select);
498     // get theme from SelectThemeManager
499     auto* pipeline = select->GetContextWithCheck();
500     CHECK_NULL_VOID(pipeline);
501     auto theme = pipeline->GetTheme<SelectTheme>();
502     CHECK_NULL_VOID(theme);
503 
504     bool hasRowNode = HasRowNode();
505     bool hasTextNode = HasTextNode();
506     bool hasSpinnerNode = HasSpinnerNode();
507     auto rowId = GetRowId();
508     auto textId = GetTextId();
509     auto spinnerId = GetSpinnerId();
510 
511     auto row = FrameNode::GetOrCreateFrameNode(
512         V2::ROW_ETS_TAG, rowId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(false); });
513     CHECK_NULL_VOID(row);
514     row->SetInternal();
515     auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
516     CHECK_NULL_VOID(rowProps);
517     rowProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
518     rowProps->UpdateCrossAxisAlign(FlexAlign::CENTER);
519     rowProps->UpdateFlexDirection(FlexDirection::ROW);
520     rowProps->UpdateSpace(theme->GetContentSpinnerPadding());
521     text_ =
522         FrameNode::GetOrCreateFrameNode(V2::TEXT_ETS_TAG, textId, []() { return AceType::MakeRefPtr<TextPattern>(); });
523     CHECK_NULL_VOID(text_);
524     text_->SetInternal();
525     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
526     CHECK_NULL_VOID(textProps);
527     InitTextProps(textProps, theme);
528     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
529         spinner_ = FrameNode::GetOrCreateFrameNode(
530             V2::SYMBOL_ETS_TAG, spinnerId, []() { return AceType::MakeRefPtr<TextPattern>(); });
531         CHECK_NULL_VOID(spinner_);
532         spinner_->SetInternal();
533         InitSpinner(spinner_, theme);
534     } else {
535         spinner_ = FrameNode::GetOrCreateFrameNode(
536             V2::IMAGE_ETS_TAG, spinnerId, []() { return AceType::MakeRefPtr<ImagePattern>(); });
537         CHECK_NULL_VOID(spinner_);
538         spinner_->SetInternal();
539         auto iconTheme = pipeline->GetTheme<IconTheme>();
540         CHECK_NULL_VOID(iconTheme);
541         InitSpinner(spinner_, iconTheme, theme);
542     }
543     // mount triangle and text
544     text_->MarkModifyDone();
545     if (!hasTextNode) {
546         text_->MountToParent(row);
547     }
548     spinner_->MarkModifyDone();
549     if (!hasSpinnerNode) {
550         spinner_->MountToParent(row);
551     }
552     if (!hasRowNode) {
553         row->MountToParent(select);
554     }
555     row->MarkModifyDone();
556     row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
557 
558     // set bgColor and border
559     auto renderContext = select->GetRenderContext();
560     CHECK_NULL_VOID(renderContext);
561     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
562         renderContext->UpdateBackgroundColor(theme->GetBackgroundColor());
563     } else {
564         renderContext->UpdateBackgroundColor(theme->GetButtonBackgroundColor());
565     }
566     renderContext->SetClipToFrame(true);
567     BorderRadiusProperty border;
568     border.SetRadius(theme->GetSelectBorderRadius());
569     renderContext->UpdateBorderRadius(border);
570 }
571 
SetValue(const std::string & value)572 void SelectPattern::SetValue(const std::string& value)
573 {
574     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
575     CHECK_NULL_VOID(props);
576     props->UpdateContent(value);
577     auto pattern = text_->GetPattern<TextPattern>();
578     CHECK_NULL_VOID(pattern);
579     auto modifier = pattern->GetContentModifier();
580     CHECK_NULL_VOID(modifier);
581     modifier->ContentChange();
582     selectValue_ = value;
583 }
584 
SetFontSize(const Dimension & value)585 void SelectPattern::SetFontSize(const Dimension& value)
586 {
587     if (value.IsNegative()) {
588         return;
589     }
590     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
591     CHECK_NULL_VOID(props);
592     props->UpdateFontSize(value);
593 }
594 
SetItalicFontStyle(const Ace::FontStyle & value)595 void SelectPattern::SetItalicFontStyle(const Ace::FontStyle& value)
596 {
597     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
598     CHECK_NULL_VOID(props);
599     props->UpdateItalicFontStyle(value);
600 }
601 
SetFontWeight(const FontWeight & value)602 void SelectPattern::SetFontWeight(const FontWeight& value)
603 {
604     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
605     CHECK_NULL_VOID(props);
606     props->UpdateFontWeight(value);
607 }
608 
SetFontFamily(const std::vector<std::string> & value)609 void SelectPattern::SetFontFamily(const std::vector<std::string>& value)
610 {
611     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
612     CHECK_NULL_VOID(props);
613     props->UpdateFontFamily(value);
614 }
615 
SetFontColor(const Color & color)616 void SelectPattern::SetFontColor(const Color& color)
617 {
618     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
619     CHECK_NULL_VOID(props);
620     props->UpdateTextColor(color);
621     auto context = text_->GetRenderContext();
622     context->UpdateForegroundColor(color);
623     context->UpdateForegroundColorFlag(false);
624     context->ResetForegroundColorStrategy();
625 }
626 
SetOptionBgColor(const Color & color)627 void SelectPattern::SetOptionBgColor(const Color& color)
628 {
629     optionBgColor_ = color;
630     for (size_t i = 0; i < options_.size(); ++i) {
631         if (static_cast<int32_t>(i) == selected_ && selectedBgColor_.has_value()) {
632             continue;
633         }
634         auto pattern = options_[i]->GetPattern<OptionPattern>();
635         CHECK_NULL_VOID(pattern);
636         pattern->SetBgColor(color);
637     }
638 }
639 
SetOptionFontSize(const Dimension & value)640 void SelectPattern::SetOptionFontSize(const Dimension& value)
641 {
642     optionFont_.FontSize = value;
643     for (size_t i = 0; i < options_.size(); ++i) {
644         if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontSize.has_value()) {
645             continue;
646         }
647         auto pattern = options_[i]->GetPattern<OptionPattern>();
648         CHECK_NULL_VOID(pattern);
649         pattern->SetFontSize(value);
650     }
651 }
652 
SetOptionItalicFontStyle(const Ace::FontStyle & value)653 void SelectPattern::SetOptionItalicFontStyle(const Ace::FontStyle& value)
654 {
655     optionFont_.FontStyle = value;
656     for (size_t i = 0; i < options_.size(); ++i) {
657         if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontStyle.has_value()) {
658             continue;
659         }
660         auto pattern = options_[i]->GetPattern<OptionPattern>();
661         CHECK_NULL_VOID(pattern);
662         pattern->SetItalicFontStyle(value);
663     }
664 }
665 
SetOptionFontWeight(const FontWeight & value)666 void SelectPattern::SetOptionFontWeight(const FontWeight& value)
667 {
668     optionFont_.FontWeight = value;
669     for (size_t i = 0; i < options_.size(); ++i) {
670         if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontWeight.has_value()) {
671             continue;
672         }
673         auto pattern = options_[i]->GetPattern<OptionPattern>();
674         CHECK_NULL_VOID(pattern);
675         pattern->SetFontWeight(value);
676     }
677 }
678 
SetOptionFontFamily(const std::vector<std::string> & value)679 void SelectPattern::SetOptionFontFamily(const std::vector<std::string>& value)
680 {
681     optionFont_.FontFamily = value;
682     for (size_t i = 0; i < options_.size(); ++i) {
683         if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontFamily.has_value()) {
684             continue;
685         }
686         auto pattern = options_[i]->GetPattern<OptionPattern>();
687         CHECK_NULL_VOID(pattern);
688         pattern->SetFontFamily(value);
689     }
690 }
691 
SetOptionFontColor(const Color & color)692 void SelectPattern::SetOptionFontColor(const Color& color)
693 {
694     optionFont_.FontColor = color;
695     for (size_t i = 0; i < options_.size(); ++i) {
696         if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontColor.has_value()) {
697             continue;
698         }
699         auto pattern = options_[i]->GetPattern<OptionPattern>();
700         CHECK_NULL_VOID(pattern);
701         pattern->SetFontColor(color);
702     }
703 }
704 
705 // set props of option node when selected
SetSelectedOptionBgColor(const Color & color)706 void SelectPattern::SetSelectedOptionBgColor(const Color& color)
707 {
708     selectedBgColor_ = color;
709     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
710         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
711         CHECK_NULL_VOID(pattern);
712         pattern->SetBgColor(color);
713     }
714 }
715 
SetSelectedOptionFontSize(const Dimension & value)716 void SelectPattern::SetSelectedOptionFontSize(const Dimension& value)
717 {
718     selectedFont_.FontSize = value;
719     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
720         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
721         CHECK_NULL_VOID(pattern);
722         pattern->SetFontSize(value);
723     }
724 }
725 
SetSelectedOptionItalicFontStyle(const Ace::FontStyle & value)726 void SelectPattern::SetSelectedOptionItalicFontStyle(const Ace::FontStyle& value)
727 {
728     selectedFont_.FontStyle = value;
729     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
730         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
731         CHECK_NULL_VOID(pattern);
732         pattern->SetItalicFontStyle(value);
733     }
734 }
735 
SetSelectedOptionFontWeight(const FontWeight & value)736 void SelectPattern::SetSelectedOptionFontWeight(const FontWeight& value)
737 {
738     selectedFont_.FontWeight = value;
739     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
740         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
741         CHECK_NULL_VOID(pattern);
742         pattern->SetFontWeight(value);
743     }
744 }
745 
SetSelectedOptionFontFamily(const std::vector<std::string> & value)746 void SelectPattern::SetSelectedOptionFontFamily(const std::vector<std::string>& value)
747 {
748     selectedFont_.FontFamily = value;
749     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
750         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
751         CHECK_NULL_VOID(pattern);
752         pattern->SetFontFamily(value);
753     }
754 }
755 
SetSelectedOptionFontColor(const Color & color)756 void SelectPattern::SetSelectedOptionFontColor(const Color& color)
757 {
758     selectedFont_.FontColor = color;
759     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
760         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
761         CHECK_NULL_VOID(pattern);
762         pattern->SetFontColor(color);
763     }
764 }
765 
GetOptions()766 const std::vector<RefPtr<FrameNode>>& SelectPattern::GetOptions()
767 {
768     return options_;
769 }
770 
ResetOptionProps()771 void SelectPattern::ResetOptionProps()
772 {
773     auto host = GetHost();
774     CHECK_NULL_VOID(host);
775     auto pipeline = host->GetContextWithCheck();
776     CHECK_NULL_VOID(pipeline);
777     auto selectTheme = pipeline->GetTheme<SelectTheme>();
778     auto textTheme = pipeline->GetTheme<TextTheme>();
779     CHECK_NULL_VOID(selectTheme && textTheme);
780 
781     for (const auto& option : options_) {
782         auto pattern = option->GetPattern<OptionPattern>();
783         CHECK_NULL_VOID(pattern);
784         pattern->SetSelected(false);
785         pattern->SetBgColor(optionBgColor_.value_or(selectTheme->GetBackgroundColor()));
786         pattern->SetFontSize(optionFont_.FontSize.value_or(selectTheme->GetMenuFontSize()));
787         pattern->SetItalicFontStyle(optionFont_.FontStyle.value_or(textTheme->GetTextStyle().GetFontStyle()));
788         pattern->SetFontWeight(optionFont_.FontWeight.value_or(textTheme->GetTextStyle().GetFontWeight()));
789         pattern->SetFontFamily(optionFont_.FontFamily.value_or(textTheme->GetTextStyle().GetFontFamilies()));
790         pattern->SetFontColor(optionFont_.FontColor.value_or(selectTheme->GetMenuFontColor()));
791     }
792 }
793 
UpdateLastSelectedProps(int32_t index)794 void SelectPattern::UpdateLastSelectedProps(int32_t index)
795 {
796     CHECK_NULL_VOID(options_[index]);
797     auto newSelected = options_[index]->GetPattern<OptionPattern>();
798     CHECK_NULL_VOID(newSelected);
799     // set lastSelected option props back to default (unselected) values
800     if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
801         CHECK_NULL_VOID(options_[selected_]);
802         auto lastSelected = options_[selected_]->GetPattern<OptionPattern>();
803         CHECK_NULL_VOID(lastSelected);
804 
805         lastSelected->SetFontColor(newSelected->GetFontColor());
806         lastSelected->SetFontFamily(newSelected->GetFontFamily());
807         lastSelected->SetFontSize(newSelected->GetFontSize());
808         lastSelected->SetItalicFontStyle(newSelected->GetItalicFontStyle());
809         lastSelected->SetFontWeight(newSelected->GetFontWeight());
810 
811         lastSelected->SetBgColor(newSelected->GetBgColor());
812         lastSelected->SetSelected(false);
813         lastSelected->UpdateNextNodeDivider(true);
814         if (selected_ != 0) {
815             auto lastSelectedNode = lastSelected->GetHost();
816             CHECK_NULL_VOID(lastSelectedNode);
817             auto lastSelectedPros = lastSelectedNode->GetPaintProperty<OptionPaintProperty>();
818             CHECK_NULL_VOID(lastSelectedPros);
819             lastSelectedPros->UpdateNeedDivider(true);
820         }
821         options_[selected_]->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
822     }
823 }
824 
825 // update selected option props
UpdateSelectedProps(int32_t index)826 void SelectPattern::UpdateSelectedProps(int32_t index)
827 {
828     CHECK_NULL_VOID(options_[index]);
829     auto newSelected = options_[index]->GetPattern<OptionPattern>();
830     CHECK_NULL_VOID(newSelected);
831 
832     // set newSelected props
833     auto host = GetHost();
834     CHECK_NULL_VOID(host);
835     auto pipeline = host->GetContextRefPtr();
836     CHECK_NULL_VOID(pipeline);
837     auto theme = pipeline->GetTheme<SelectTheme>();
838     CHECK_NULL_VOID(theme);
839     if (selectedFont_.FontColor.has_value()) {
840         newSelected->SetFontColor(selectedFont_.FontColor.value());
841     } else {
842         auto selectedColorText = theme->GetSelectedColorText();
843         newSelected->SetFontColor(selectedColorText);
844     }
845     if (selectedFont_.FontFamily.has_value()) {
846         newSelected->SetFontFamily(selectedFont_.FontFamily.value());
847     }
848     if (selectedFont_.FontSize.has_value()) {
849         newSelected->SetFontSize(selectedFont_.FontSize.value());
850     }
851     if (selectedFont_.FontStyle.has_value()) {
852         newSelected->SetItalicFontStyle(selectedFont_.FontStyle.value());
853     }
854     if (selectedFont_.FontWeight.has_value()) {
855         newSelected->SetFontWeight(selectedFont_.FontWeight.value());
856     }
857     if (selectedBgColor_.has_value()) {
858         newSelected->SetBgColor(selectedBgColor_.value());
859     } else {
860         auto selectedColor = theme->GetSelectedColor();
861         newSelected->SetBgColor(selectedColor);
862     }
863     newSelected->SetSelected(true);
864     newSelected->UpdateNextNodeDivider(false);
865     auto newSelectedNode = newSelected->GetHost();
866     CHECK_NULL_VOID(newSelectedNode);
867     auto newSelectedPros = newSelectedNode->GetPaintProperty<OptionPaintProperty>();
868     CHECK_NULL_VOID(newSelectedPros);
869     newSelectedPros->UpdateNeedDivider(false);
870 }
871 
UpdateText(int32_t index)872 void SelectPattern::UpdateText(int32_t index)
873 {
874     // update text to selected option's text
875     CHECK_NULL_VOID(text_);
876     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
877     CHECK_NULL_VOID(textProps);
878     if (index >= static_cast<int32_t>(options_.size()) || index < 0) {
879         return;
880     }
881     auto newSelected = options_[index]->GetPattern<OptionPattern>();
882     CHECK_NULL_VOID(newSelected);
883     textProps->UpdateContent(newSelected->GetText());
884     text_->MarkModifyDone();
885     auto host = GetHost();
886     CHECK_NULL_VOID(host);
887     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
888     selectValue_ = newSelected->GetText();
889 }
890 
InitTextProps(const RefPtr<TextLayoutProperty> & textProps,const RefPtr<SelectTheme> & theme)891 void SelectPattern::InitTextProps(const RefPtr<TextLayoutProperty>& textProps, const RefPtr<SelectTheme>& theme)
892 {
893     textProps->UpdateFontSize(theme->GetFontSize());
894     textProps->UpdateFontWeight(FontWeight::MEDIUM);
895     textProps->UpdateTextColor(theme->GetFontColor());
896     textProps->UpdateTextDecoration(theme->GetTextDecoration());
897     textProps->UpdateTextOverflow(TextOverflow::ELLIPSIS);
898     textProps->UpdateMaxLines(SELECT_ITSELF_TEXT_LINES);
899 }
900 
InitSpinner(const RefPtr<FrameNode> & spinner,const RefPtr<IconTheme> & iconTheme,const RefPtr<SelectTheme> & selectTheme)901 void SelectPattern::InitSpinner(
902     const RefPtr<FrameNode>& spinner, const RefPtr<IconTheme>& iconTheme, const RefPtr<SelectTheme>& selectTheme)
903 {
904     ImageSourceInfo imageSourceInfo;
905     auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::SPINNER);
906     imageSourceInfo.SetSrc(iconPath);
907     imageSourceInfo.SetFillColor(selectTheme->GetSpinnerColor());
908 
909     auto spinnerLayoutProperty = spinner->GetLayoutProperty<ImageLayoutProperty>();
910     CHECK_NULL_VOID(spinnerLayoutProperty);
911     spinnerLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
912     CalcSize idealSize = { CalcLength(selectTheme->GetSpinnerWidth()), CalcLength(selectTheme->GetSpinnerHeight()) };
913     MeasureProperty layoutConstraint;
914     layoutConstraint.selfIdealSize = idealSize;
915     spinnerLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
916     auto spinnerRenderProperty = spinner->GetPaintProperty<ImageRenderProperty>();
917     CHECK_NULL_VOID(spinnerRenderProperty);
918     spinnerRenderProperty->UpdateSvgFillColor(selectTheme->GetSpinnerColor());
919 }
920 
InitSpinner(const RefPtr<FrameNode> & spinner,const RefPtr<SelectTheme> & selectTheme)921 void SelectPattern::InitSpinner(
922     const RefPtr<FrameNode>& spinner, const RefPtr<SelectTheme>& selectTheme)
923 {
924     auto spinnerLayoutProperty = spinner->GetLayoutProperty<TextLayoutProperty>();
925     CHECK_NULL_VOID(spinnerLayoutProperty);
926     uint32_t symbolId = selectTheme->GetSpinnerSource();
927     spinnerLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo{symbolId});
928     spinnerLayoutProperty->UpdateSymbolColorList({selectTheme->GetSpinnerSymbolColor()});
929     spinnerLayoutProperty->UpdateFontSize(selectTheme->GetFontSize());
930 }
931 
932 // XTS inspector code
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const933 void SelectPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
934 {
935     /* no fixed attr below, just return */
936     if (filter.IsFastFilter()) {
937         ToJsonArrowAndText(json, filter);
938         ToJsonOptionAlign(json, filter);
939         ToJsonMenuBackgroundStyle(json, filter);
940         return;
941     }
942     json->PutExtAttr("options", InspectorGetOptions().c_str(), filter);
943     json->PutExtAttr("selected", std::to_string(selected_).c_str(), filter);
944     ToJsonArrowAndText(json, filter);
945     json->PutExtAttr("selectedOptionBgColor", selectedBgColor_->ColorToString().c_str(), filter);
946     json->PutExtAttr("selectedOptionFont", InspectorGetSelectedFont().c_str(), filter);
947     json->PutExtAttr("selectedOptionFontColor",
948         selectedFont_.FontColor.value_or(Color::BLACK).ColorToString().c_str(), filter);
949 
950     if (options_.empty()) {
951         json->PutExtAttr("optionBgColor", "", filter);
952         json->PutExtAttr("optionFont", "", filter);
953         json->PutExtAttr("optionFontColor", "", filter);
954     } else {
955         auto optionPattern = options_[0]->GetPattern<OptionPattern>();
956         CHECK_NULL_VOID(optionPattern);
957         json->PutExtAttr("optionBgColor", optionPattern->GetBgColor().ColorToString().c_str(), filter);
958         json->PutExtAttr("optionFont", optionPattern->InspectorGetFont().c_str(), filter);
959         json->PutExtAttr("optionFontColor", optionPattern->GetFontColor().ColorToString().c_str(), filter);
960     }
961     ToJsonOptionAlign(json, filter);
962     for (size_t i = 0; i < options_.size(); ++i) {
963         auto optionPaintProperty = options_[i]->GetPaintProperty<OptionPaintProperty>();
964         CHECK_NULL_VOID(optionPaintProperty);
965         std::string optionWidth = std::to_string(optionPaintProperty->GetSelectModifiedWidthValue(0.0f));
966         json->PutExtAttr("optionWidth", optionWidth.c_str(), filter);
967     }
968 
969     auto menu = GetMenuNode();
970     CHECK_NULL_VOID(menu);
971     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
972     CHECK_NULL_VOID(menuLayoutProps);
973     std::string optionHeight =  std::to_string(menuLayoutProps->GetSelectModifiedHeightValue(0.0f));
974     json->PutExtAttr("optionHeight", optionHeight.c_str(), filter);
975     ToJsonMenuBackgroundStyle(json, filter);
976     ToJsonDivider(json, filter);
977 }
978 
ToJsonArrowAndText(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const979 void SelectPattern::ToJsonArrowAndText(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
980 {
981     /* no fixed attr below, just return */
982     if (filter.IsFastFilter()) {
983         return;
984     }
985     auto host = GetHost();
986     CHECK_NULL_VOID(host);
987     if (!host->GetChildren().empty()) {
988         auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
989         CHECK_NULL_VOID(row);
990         auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
991         CHECK_NULL_VOID(rowProps);
992         json->PutExtAttr("space", rowProps->GetSpace()->ToString().c_str(), filter);
993 
994         if (rowProps->GetFlexDirection().value() == FlexDirection::ROW) {
995             json->PutExtAttr("arrowPosition", "ArrowPosition.END", filter);
996         } else {
997             json->PutExtAttr("arrowPosition", "ArrowPosition.START", filter);
998         }
999     }
1000 
1001     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
1002     CHECK_NULL_VOID(props);
1003     json->PutExtAttr("value", props->GetContent().value_or("").c_str(), filter);
1004     Color fontColor = props->GetTextColor().value_or(Color::BLACK);
1005     json->PutExtAttr("fontColor", fontColor.ColorToString().c_str(), filter);
1006     json->PutExtAttr("font", props->InspectorGetTextFont().c_str(), filter);
1007     json->PutExtAttr("controlSize", ConvertControlSizeToString(controlSize_).c_str(), filter);
1008 }
1009 
ToJsonMenuBackgroundStyle(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1010 void SelectPattern::ToJsonMenuBackgroundStyle(
1011     std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1012 {
1013     /* no fixed attr below, just return */
1014     if (filter.IsFastFilter()) {
1015         return;
1016     }
1017     auto menu = GetMenuNode();
1018     CHECK_NULL_VOID(menu);
1019     auto menuRenderContext = menu->GetRenderContext();
1020     CHECK_NULL_VOID(menuRenderContext);
1021     json->PutExtAttr("menuBackgroundColor",
1022         menuRenderContext->GetBackgroundColor()->ColorToString().c_str(), filter);
1023     if (menuRenderContext->GetBackBlurStyle().has_value()) {
1024         BlurStyleOption blurStyleOption = menuRenderContext->GetBackBlurStyle().value();
1025         auto jsonValue = JsonUtil::Create(true);
1026         blurStyleOption.ToJsonValue(jsonValue, filter);
1027         json->PutExtAttr("menuBackgroundBlurStyle",
1028             jsonValue->GetValue("backgroundBlurStyle")->GetValue("value"), filter);
1029     } else {
1030         json->PutExtAttr("menuBackgroundBlurStyle", "", filter);
1031     }
1032 }
1033 
ToJsonDivider(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1034 void SelectPattern::ToJsonDivider(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1035 {
1036     /* no fixed attr below, just return */
1037     if (filter.IsFastFilter()) {
1038         return;
1039     }
1040     if (options_.empty()) {
1041         json->PutExtAttr("divider", "", filter);
1042     } else {
1043         auto props = options_[0]->GetPaintProperty<OptionPaintProperty>();
1044         CHECK_NULL_VOID(props);
1045         auto divider = JsonUtil::Create(true);
1046         if (props->HasDivider()) {
1047             divider->Put("strokeWidth", props->GetDividerValue().strokeWidth.ToString().c_str());
1048             divider->Put("startMargin", props->GetDividerValue().startMargin.ToString().c_str());
1049             divider->Put("endMargin", props->GetDividerValue().endMargin.ToString().c_str());
1050             divider->Put("color", props->GetDividerValue().color.ColorToString().c_str());
1051             json->PutExtAttr("divider", divider->ToString().c_str(), filter);
1052         } else {
1053             json->PutExtAttr("divider", "", filter);
1054         }
1055     }
1056 }
1057 
ToJsonOptionAlign(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1058 void SelectPattern::ToJsonOptionAlign(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1059 {
1060     /* no fixed attr below, just return */
1061     if (filter.IsFastFilter()) {
1062         return;
1063     }
1064     auto optionAlignJson = JsonUtil::Create(true);
1065     std::string alignTypeString = "MenuAlignType.Start";
1066     if (menuAlign_.alignType == MenuAlignType::START) {
1067         alignTypeString = "MenuAlignType.Start";
1068     } else if (menuAlign_.alignType == MenuAlignType::CENTER) {
1069         alignTypeString = "MenuAlignType.Center";
1070     } else if (menuAlign_.alignType == MenuAlignType::END) {
1071         alignTypeString = "MenuAlignType.End";
1072     }
1073     optionAlignJson->Put("alignType", alignTypeString.c_str());
1074 
1075     auto offsetValueJson = JsonUtil::Create(true);
1076     offsetValueJson->Put("dX", menuAlign_.offset.GetX().Value());
1077     offsetValueJson->Put("dY", menuAlign_.offset.GetY().Value());
1078     optionAlignJson->Put("offset", offsetValueJson);
1079 
1080     json->PutExtAttr("menuAlign", optionAlignJson, filter);
1081 }
1082 
InspectorGetOptions() const1083 std::string SelectPattern::InspectorGetOptions() const
1084 {
1085     auto jsonValue = JsonUtil::Create(true);
1086     auto jsonOptions = JsonUtil::CreateArray(true);
1087     for (size_t i = 0; i < options_.size(); ++i) {
1088         auto temp = JsonUtil::Create(true);
1089         auto optionPattern = options_[i]->GetPattern<OptionPattern>();
1090         temp->Put("value", optionPattern->GetText().c_str());
1091         temp->Put("icon", optionPattern->GetIcon().c_str());
1092         auto index = std::to_string(i);
1093         jsonOptions->Put(index.c_str(), temp);
1094     }
1095     jsonValue->Put("options", jsonOptions);
1096     return jsonValue->ToString();
1097 }
1098 
InspectorGetSelectedFont() const1099 std::string SelectPattern::InspectorGetSelectedFont() const
1100 {
1101     TextStyle font;
1102     if (selectedFont_.FontFamily.has_value()) {
1103         font.SetFontFamilies(selectedFont_.FontFamily.value());
1104     }
1105     if (selectedFont_.FontSize.has_value()) {
1106         font.SetFontSize(selectedFont_.FontSize.value());
1107     }
1108     if (selectedFont_.FontStyle.has_value()) {
1109         font.SetFontStyle(selectedFont_.FontStyle.value());
1110     }
1111     if (selectedFont_.FontWeight.has_value()) {
1112         font.SetFontWeight(selectedFont_.FontWeight.value());
1113     }
1114     return V2::GetTextStyleInJson(font);
1115 }
1116 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)1117 bool SelectPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
1118 {
1119     auto geometryNode = dirty->GetGeometryNode();
1120     CHECK_NULL_RETURN(geometryNode, false);
1121     SetSelectSize(geometryNode->GetFrameSize());
1122     return false;
1123 }
1124 
SetSpace(const Dimension & value)1125 void SelectPattern::SetSpace(const Dimension& value)
1126 {
1127     auto host = GetHost();
1128     CHECK_NULL_VOID(host);
1129     if (!host->GetChildren().empty()) {
1130         auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
1131         auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
1132         rowProps->UpdateSpace(value);
1133         row->MarkModifyDone();
1134         row->MarkDirtyNode();
1135     }
1136 }
1137 
SetArrowPosition(const ArrowPosition value)1138 void SelectPattern::SetArrowPosition(const ArrowPosition value)
1139 {
1140     auto host = GetHost();
1141     CHECK_NULL_VOID(host);
1142     if (!host->GetChildren().empty()) {
1143         auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
1144         auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
1145 
1146         if (value == ArrowPosition::END) {
1147             rowProps->UpdateFlexDirection(FlexDirection::ROW);
1148         } else {
1149             rowProps->UpdateFlexDirection(FlexDirection::ROW_REVERSE);
1150         }
1151         row->MarkModifyDone();
1152         row->MarkDirtyNode();
1153     }
1154 }
1155 
GetValue()1156 std::string SelectPattern::GetValue()
1157 {
1158     CHECK_NULL_RETURN(text_, "");
1159     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
1160     CHECK_NULL_RETURN(textProps, "");
1161     return textProps->GetContentValue("");
1162 }
1163 
SetMenuAlign(const MenuAlign & menuAlign)1164 void SelectPattern::SetMenuAlign(const MenuAlign& menuAlign)
1165 {
1166     menuAlign_ = menuAlign;
1167     auto menu = GetMenuNode();
1168     CHECK_NULL_VOID(menu);
1169     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
1170     CHECK_NULL_VOID(menuLayoutProps);
1171     menuLayoutProps->UpdateAlignType(menuAlign.alignType);
1172     menuLayoutProps->UpdateOffset(menuAlign.offset);
1173 }
1174 
ProvideRestoreInfo()1175 std::string SelectPattern::ProvideRestoreInfo()
1176 {
1177     auto jsonObj = JsonUtil::Create(true);
1178     jsonObj->Put("selected", selected_);
1179     jsonObj->Put("isSelected", isSelected_);
1180     return jsonObj->ToString();
1181 }
1182 
OnRestoreInfo(const std::string & restoreInfo)1183 void SelectPattern::OnRestoreInfo(const std::string& restoreInfo)
1184 {
1185     auto info = JsonUtil::ParseJsonString(restoreInfo);
1186     if (!info->IsValid() || !info->IsObject()) {
1187         return;
1188     }
1189     auto jsonIsOn = info->GetValue("selected");
1190     auto jsonIsSelect = info->GetValue("isSelected");
1191     if (jsonIsSelect->GetBool()) {
1192         SetSelected(jsonIsOn->GetInt());
1193         UpdateText(jsonIsOn->GetInt());
1194     }
1195 }
1196 
OnColorConfigurationUpdate()1197 void SelectPattern::OnColorConfigurationUpdate()
1198 {
1199     auto host = GetHost();
1200     CHECK_NULL_VOID(host);
1201     auto pipeline = host->GetContextWithCheck();
1202     CHECK_NULL_VOID(pipeline);
1203     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1204     CHECK_NULL_VOID(selectTheme);
1205 
1206     auto pattern = host->GetPattern<SelectPattern>();
1207     auto menuNode = pattern->GetMenuNode();
1208     CHECK_NULL_VOID(menuNode);
1209     auto menuPattern = menuNode->GetPattern<MenuPattern>();
1210     CHECK_NULL_VOID(menuPattern);
1211 
1212     auto renderContext = menuNode->GetRenderContext();
1213     renderContext->UpdateBackgroundColor(selectTheme->GetBackgroundColor());
1214     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && renderContext->IsUniRenderEnabled()) {
1215         renderContext->UpdateBackBlurStyle(renderContext->GetBackBlurStyle());
1216     }
1217 
1218     auto optionNode = menuPattern->GetOptions();
1219     for (auto child : optionNode) {
1220         auto optionsPattern = child->GetPattern<OptionPattern>();
1221         optionsPattern->SetFontColor(selectTheme->GetFontColor());
1222 
1223         child->MarkModifyDone();
1224         child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1225     }
1226     SetOptionBgColor(selectTheme->GetBackgroundColor());
1227     host->SetNeedCallChildrenUpdate(false);
1228 }
1229 
OnLanguageConfigurationUpdate()1230 void SelectPattern::OnLanguageConfigurationUpdate()
1231 {
1232     auto host = GetHost();
1233     CHECK_NULL_VOID(host);
1234     auto context = host->GetContextRefPtr();
1235     CHECK_NULL_VOID(context);
1236     auto taskExecutor = context->GetTaskExecutor();
1237     CHECK_NULL_VOID(taskExecutor);
1238     taskExecutor->PostTask(
1239         [weak = WeakClaim(this)]() {
1240             auto pattern = weak.Upgrade();
1241             CHECK_NULL_VOID(pattern);
1242             auto index = pattern->selected_;
1243             pattern->UpdateText(index);
1244             auto host = pattern->GetHost();
1245             CHECK_NULL_VOID(host);
1246             auto hub = host->GetEventHub<SelectEventHub>();
1247             CHECK_NULL_VOID(hub);
1248             if (index >= static_cast<int32_t>(pattern->options_.size()) || index < 0) {
1249                 return;
1250             }
1251             auto newSelected = pattern->options_[index]->GetPattern<OptionPattern>();
1252             CHECK_NULL_VOID(newSelected);
1253             auto value = newSelected->GetText();
1254             auto valueChangeEvent = hub->GetValueChangeEvent();
1255             if (valueChangeEvent) {
1256                 valueChangeEvent(value);
1257             }
1258             auto onSelect = hub->GetSelectEvent();
1259             if (onSelect) {
1260                 onSelect(index, value);
1261             }
1262         },
1263         TaskExecutor::TaskType::UI, "ArkUISelectLanguageConfigUpdate");
1264 }
1265 
GetFontSize()1266 Dimension SelectPattern::GetFontSize()
1267 {
1268     Dimension defaultRet = Dimension();
1269     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
1270     CHECK_NULL_RETURN(props, defaultRet);
1271     auto host = props->GetHost();
1272     CHECK_NULL_RETURN(host, defaultRet);
1273     auto pipeline = host->GetContextWithCheck();
1274     CHECK_NULL_RETURN(pipeline, defaultRet);
1275     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1276     CHECK_NULL_RETURN(selectTheme, defaultRet);
1277     return props->GetFontSize().value_or(selectTheme->GetFontSize());
1278 }
1279 
SetSelectDefaultTheme()1280 void SelectPattern::SetSelectDefaultTheme()
1281 {
1282     auto select = GetHost();
1283     CHECK_NULL_VOID(select);
1284     auto pipeline = select->GetContextWithCheck();
1285     CHECK_NULL_VOID(pipeline);
1286     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1287     CHECK_NULL_VOID(selectTheme);
1288 
1289     auto renderContext = select->GetRenderContext();
1290     CHECK_NULL_VOID(renderContext);
1291 
1292     if (selectDefaultBgColor_ == Color::TRANSPARENT) {
1293         renderContext->UpdateBackgroundColor(selectTheme->GetSelectDefaultBgColor());
1294     } else {
1295         renderContext->UpdateBackgroundColor(selectDefaultBgColor_);
1296     }
1297     BorderRadiusProperty border;
1298     border.SetRadius(selectTheme->GetSelectDefaultBorderRadius());
1299     renderContext->UpdateBorderRadius(border);
1300 }
1301 
SetOptionWidth(const Dimension & value)1302 void SelectPattern::SetOptionWidth(const Dimension& value)
1303 {
1304     isFitTrigger_ = false;
1305     auto menu = GetMenuNode();
1306     CHECK_NULL_VOID(menu);
1307     auto menuPattern = menu->GetPattern<MenuPattern>();
1308     CHECK_NULL_VOID(menuPattern);
1309     menuPattern->SetIsWidthModifiedBySelect(true);
1310     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
1311     CHECK_NULL_VOID(menuLayoutProps);
1312     menuLayoutProps->UpdateSelectMenuModifiedWidth(value.ConvertToPx() + OPTION_MARGIN.ConvertToPx());
1313 
1314     auto scroll = DynamicCast<FrameNode>(menu->GetFirstChild());
1315     CHECK_NULL_VOID(scroll);
1316     auto scrollPattern = scroll->GetPattern<ScrollPattern>();
1317     CHECK_NULL_VOID(scrollPattern);
1318     scrollPattern->SetIsWidthModifiedBySelect(true);
1319     auto scrollLayoutProps = scroll->GetLayoutProperty<ScrollLayoutProperty>();
1320     CHECK_NULL_VOID(scrollLayoutProps);
1321     scrollLayoutProps->UpdateScrollWidth(value.ConvertToPx() + OPTION_MARGIN.ConvertToPx());
1322 
1323     for (size_t i = 0; i < options_.size(); ++i) {
1324         auto optionWidth = value.ConvertToPx();
1325         auto optionPattern = options_[i]->GetPattern<OptionPattern>();
1326         CHECK_NULL_VOID(optionPattern);
1327         optionPattern->SetIsWidthModifiedBySelect(true);
1328         auto optionPaintProperty = options_[i]->GetPaintProperty<OptionPaintProperty>();
1329         CHECK_NULL_VOID(optionPaintProperty);
1330         optionPaintProperty->UpdateSelectModifiedWidth(optionWidth);
1331     }
1332 }
1333 
SetOptionWidthFitTrigger(bool isFitTrigger)1334 void SelectPattern::SetOptionWidthFitTrigger(bool isFitTrigger)
1335 {
1336     isFitTrigger_ = isFitTrigger;
1337 }
1338 
SetHasOptionWidth(bool hasOptionWidth)1339 void SelectPattern::SetHasOptionWidth(bool hasOptionWidth)
1340 {
1341     auto menu = GetMenuNode();
1342     CHECK_NULL_VOID(menu);
1343     auto menuPattern = menu->GetPattern<MenuPattern>();
1344     CHECK_NULL_VOID(menuPattern);
1345     menuPattern->SetHasOptionWidth(true);
1346     auto scroll = DynamicCast<FrameNode>(menu->GetFirstChild());
1347     CHECK_NULL_VOID(scroll);
1348     auto scrollPattern = scroll->GetPattern<ScrollPattern>();
1349     CHECK_NULL_VOID(scrollPattern);
1350     scrollPattern->SetHasOptionWidth(true);
1351     for (size_t i = 0; i < options_.size(); ++i) {
1352         auto optionPattern = options_[i]->GetPattern<OptionPattern>();
1353         CHECK_NULL_VOID(optionPattern);
1354         optionPattern->SetHasOptionWidth(true);
1355     }
1356 }
1357 
SetOptionHeight(const Dimension & value)1358 void SelectPattern::SetOptionHeight(const Dimension& value)
1359 {
1360     auto menuMaxHeight = value.ConvertToPx();
1361     auto menu = GetMenuNode();
1362     CHECK_NULL_VOID(menu);
1363     auto menuPattern = menu->GetPattern<MenuPattern>();
1364     CHECK_NULL_VOID(menuPattern);
1365     menuPattern->SetIsHeightModifiedBySelect(true);
1366     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
1367     CHECK_NULL_VOID(menuLayoutProps);
1368     menuLayoutProps->UpdateSelectModifiedHeight(menuMaxHeight);
1369 }
1370 
SetMenuBackgroundColor(const Color & color)1371 void SelectPattern::SetMenuBackgroundColor(const Color& color)
1372 {
1373     auto menu = GetMenuNode();
1374     CHECK_NULL_VOID(menu);
1375     auto renderContext = menu->GetRenderContext();
1376     CHECK_NULL_VOID(renderContext);
1377     renderContext->UpdateBackgroundColor(color);
1378 }
1379 
SetMenuBackgroundBlurStyle(const BlurStyleOption & blurStyle)1380 void SelectPattern::SetMenuBackgroundBlurStyle(const BlurStyleOption& blurStyle)
1381 {
1382     auto menu = GetMenuNode();
1383     CHECK_NULL_VOID(menu);
1384     auto renderContext = menu->GetRenderContext();
1385     CHECK_NULL_VOID(renderContext);
1386     renderContext->UpdateBackBlurStyle(blurStyle);
1387 }
1388 
ResetParams()1389 void SelectPattern::ResetParams()
1390 {
1391     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1392         return;
1393     }
1394     auto select = GetHost();
1395     CHECK_NULL_VOID(select);
1396     auto* pipeline = select->GetContextWithCheck();
1397     CHECK_NULL_VOID(pipeline);
1398     auto selectTheme = pipeline->GetTheme<SelectTheme>();
1399     CHECK_NULL_VOID(selectTheme);
1400     auto layoutProperty = select->GetLayoutProperty();
1401     CHECK_NULL_VOID(layoutProperty);
1402     layoutProperty->UpdateCalcMinSize(CalcSize(CalcLength(selectTheme->GetSelectMinWidth(controlSize_)),
1403         CalcLength(selectTheme->GetSelectDefaultHeight(controlSize_))));
1404     SetFontSize(selectTheme->GetFontSize(controlSize_));
1405     auto spinnerLayoutProperty = spinner_->GetLayoutProperty<TextLayoutProperty>();
1406     CHECK_NULL_VOID(spinnerLayoutProperty);
1407     spinnerLayoutProperty->UpdateFontSize(selectTheme->GetFontSize(controlSize_));
1408     auto renderContext = select->GetRenderContext();
1409     BorderRadiusProperty border;
1410     border.SetRadius(selectTheme->GetSelectDefaultBorderRadius(controlSize_));
1411     renderContext->UpdateBorderRadius(border);
1412 
1413     NG::PaddingProperty paddings;
1414     paddings.top = std::nullopt;
1415     paddings.bottom = std::nullopt;
1416     if (controlSize_ == ControlSize::SMALL) {
1417         paddings.left = NG::CalcLength(SELECT_SMALL_PADDING_VP);
1418         paddings.right = NG::CalcLength(SELECT_SMALL_PADDING_VP);
1419     } else {
1420         paddings.left = NG::CalcLength(SELECT_MARGIN_VP);
1421         paddings.right = NG::CalcLength(SELECT_MARGIN_VP);
1422     }
1423     ViewAbstract::SetPadding(paddings);
1424 }
1425 
SetControlSize(const ControlSize & controlSize)1426 void SelectPattern::SetControlSize(const ControlSize& controlSize)
1427 {
1428     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1429         return;
1430     }
1431     controlSize_ = controlSize;
1432     ResetParams();
1433 }
1434 
SetLayoutDirection(TextDirection value)1435 void SelectPattern::SetLayoutDirection(TextDirection value)
1436 {
1437     auto select = GetHost();
1438     auto menu = GetMenuNode();
1439     std::function<void (decltype(select))> updateDirectionFunc = [&](decltype(select) node) {
1440         if (!node) return;
1441         auto updateProperty = node->GetLayoutProperty();
1442         updateProperty->UpdateLayoutDirection(value);
1443         if (node->GetHostTag() == V2::SCROLL_ETS_TAG) {
1444             auto scrollPattern = AceType::DynamicCast<ScrollPattern>(node->GetPattern());
1445             if (scrollPattern) scrollPattern->TriggerModifyDone();
1446         }
1447         for (auto child : node->GetAllChildrenWithBuild()) {
1448             auto frameNode = AceType::DynamicCast<FrameNode>(child);
1449             if (!frameNode) continue;
1450             updateDirectionFunc(frameNode);
1451         }
1452     };
1453     updateDirectionFunc(select);
1454     updateDirectionFunc(menu);
1455 }
1456 
GetControlSize()1457 ControlSize SelectPattern::GetControlSize()
1458 {
1459     return controlSize_;
1460 }
1461 
SetDivider(const SelectDivider & divider)1462 void SelectPattern::SetDivider(const SelectDivider& divider)
1463 {
1464     for (auto&& option : options_) {
1465         auto props = option->GetPaintProperty<OptionPaintProperty>();
1466         CHECK_NULL_VOID(props);
1467         props->UpdateDivider(divider);
1468     }
1469 }
1470 } // namespace OHOS::Ace::NG
1471