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(false, true);
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 SystemProperties::IsNeedSymbol()) {
443 auto spinnerLayoutProperty = spinner_->GetLayoutProperty<TextLayoutProperty>();
444 CHECK_NULL_VOID(spinnerLayoutProperty);
445 spinnerLayoutProperty->UpdateSymbolColorList({theme->GetDisabledSpinnerSymbolColor()});
446 } else {
447 auto spinnerLayoutProperty = spinner_->GetLayoutProperty<ImageLayoutProperty>();
448 CHECK_NULL_VOID(spinnerLayoutProperty);
449
450 ImageSourceInfo imageSourceInfo = spinnerLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo());
451 auto iconTheme = pipeline->GetTheme<IconTheme>();
452 CHECK_NULL_VOID(iconTheme);
453 auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::SPINNER_DISABLE);
454 imageSourceInfo.SetSrc(iconPath);
455 if (imageSourceInfo.IsSvg()) {
456 imageSourceInfo.SetFillColor(theme->GetDisabledSpinnerColor());
457 }
458 spinnerLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
459 auto spinnerRenderProperty = spinner_->GetPaintProperty<ImageRenderProperty>();
460 CHECK_NULL_VOID(spinnerRenderProperty);
461 spinnerRenderProperty->UpdateSvgFillColor(theme->GetDisabledSpinnerColor());
462 }
463 spinner_->MarkModifyDone();
464
465 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
466 auto renderContext = host->GetRenderContext();
467 CHECK_NULL_VOID(renderContext);
468 renderContext->UpdateBackgroundColor(renderContext->GetBackgroundColor()
469 .value_or(theme->GetButtonBackgroundColor())
470 .BlendOpacity(theme->GetDisabledFontColorAlpha()));
471 }
472 }
473
SetSelected(int32_t index)474 void SelectPattern::SetSelected(int32_t index)
475 {
476 // if option is already selected, do nothing
477 if (index == selected_) {
478 return;
479 }
480 if (index >= static_cast<int32_t>(options_.size()) || index < 0) {
481 selected_ = -1;
482 ResetOptionProps();
483 return;
484 }
485 UpdateLastSelectedProps(index);
486 selected_ = index;
487 }
488
AddOptionNode(const RefPtr<FrameNode> & option)489 void SelectPattern::AddOptionNode(const RefPtr<FrameNode>& option)
490 {
491 CHECK_NULL_VOID(option);
492 options_.push_back(option);
493 }
494
BuildChild()495 void SelectPattern::BuildChild()
496 {
497 auto select = GetHost();
498 CHECK_NULL_VOID(select);
499 // get theme from SelectThemeManager
500 auto* pipeline = select->GetContextWithCheck();
501 CHECK_NULL_VOID(pipeline);
502 auto theme = pipeline->GetTheme<SelectTheme>();
503 CHECK_NULL_VOID(theme);
504
505 bool hasRowNode = HasRowNode();
506 bool hasTextNode = HasTextNode();
507 bool hasSpinnerNode = HasSpinnerNode();
508 auto rowId = GetRowId();
509 auto textId = GetTextId();
510 auto spinnerId = GetSpinnerId();
511
512 auto row = FrameNode::GetOrCreateFrameNode(
513 V2::ROW_ETS_TAG, rowId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(false); });
514 CHECK_NULL_VOID(row);
515 row->SetInternal();
516 auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
517 CHECK_NULL_VOID(rowProps);
518 rowProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
519 rowProps->UpdateCrossAxisAlign(FlexAlign::CENTER);
520 rowProps->UpdateFlexDirection(FlexDirection::ROW);
521 rowProps->UpdateSpace(theme->GetContentSpinnerPadding());
522 text_ =
523 FrameNode::GetOrCreateFrameNode(V2::TEXT_ETS_TAG, textId, []() { return AceType::MakeRefPtr<TextPattern>(); });
524 CHECK_NULL_VOID(text_);
525 text_->SetInternal();
526 auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
527 CHECK_NULL_VOID(textProps);
528 InitTextProps(textProps, theme);
529 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE) &&
530 SystemProperties::IsNeedSymbol()) {
531 spinner_ = FrameNode::GetOrCreateFrameNode(
532 V2::SYMBOL_ETS_TAG, spinnerId, []() { return AceType::MakeRefPtr<TextPattern>(); });
533 CHECK_NULL_VOID(spinner_);
534 spinner_->SetInternal();
535 InitSpinner(spinner_, theme);
536 } else {
537 spinner_ = FrameNode::GetOrCreateFrameNode(
538 V2::IMAGE_ETS_TAG, spinnerId, []() { return AceType::MakeRefPtr<ImagePattern>(); });
539 CHECK_NULL_VOID(spinner_);
540 spinner_->SetInternal();
541 auto iconTheme = pipeline->GetTheme<IconTheme>();
542 CHECK_NULL_VOID(iconTheme);
543 InitSpinner(spinner_, iconTheme, theme);
544 }
545 // mount triangle and text
546 text_->MarkModifyDone();
547 if (!hasTextNode) {
548 text_->MountToParent(row);
549 }
550 spinner_->MarkModifyDone();
551 if (!hasSpinnerNode) {
552 spinner_->MountToParent(row);
553 }
554 if (!hasRowNode) {
555 row->MountToParent(select);
556 }
557 row->MarkModifyDone();
558 row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
559
560 // set bgColor and border
561 auto renderContext = select->GetRenderContext();
562 CHECK_NULL_VOID(renderContext);
563 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
564 renderContext->UpdateBackgroundColor(theme->GetBackgroundColor());
565 } else {
566 renderContext->UpdateBackgroundColor(theme->GetButtonBackgroundColor());
567 }
568 renderContext->SetClipToFrame(true);
569 BorderRadiusProperty border;
570 border.SetRadius(theme->GetSelectBorderRadius());
571 renderContext->UpdateBorderRadius(border);
572 }
573
SetValue(const std::string & value)574 void SelectPattern::SetValue(const std::string& value)
575 {
576 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
577 CHECK_NULL_VOID(props);
578 props->UpdateContent(value);
579 auto pattern = text_->GetPattern<TextPattern>();
580 CHECK_NULL_VOID(pattern);
581 auto modifier = pattern->GetContentModifier();
582 CHECK_NULL_VOID(modifier);
583 modifier->ContentChange();
584 selectValue_ = value;
585 }
586
SetFontSize(const Dimension & value)587 void SelectPattern::SetFontSize(const Dimension& value)
588 {
589 if (value.IsNegative()) {
590 return;
591 }
592 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
593 CHECK_NULL_VOID(props);
594 props->UpdateFontSize(value);
595 }
596
SetItalicFontStyle(const Ace::FontStyle & value)597 void SelectPattern::SetItalicFontStyle(const Ace::FontStyle& value)
598 {
599 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
600 CHECK_NULL_VOID(props);
601 props->UpdateItalicFontStyle(value);
602 }
603
SetFontWeight(const FontWeight & value)604 void SelectPattern::SetFontWeight(const FontWeight& value)
605 {
606 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
607 CHECK_NULL_VOID(props);
608 props->UpdateFontWeight(value);
609 }
610
SetFontFamily(const std::vector<std::string> & value)611 void SelectPattern::SetFontFamily(const std::vector<std::string>& value)
612 {
613 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
614 CHECK_NULL_VOID(props);
615 props->UpdateFontFamily(value);
616 }
617
SetFontColor(const Color & color)618 void SelectPattern::SetFontColor(const Color& color)
619 {
620 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
621 CHECK_NULL_VOID(props);
622 props->UpdateTextColor(color);
623 auto context = text_->GetRenderContext();
624 context->UpdateForegroundColor(color);
625 context->UpdateForegroundColorFlag(false);
626 context->ResetForegroundColorStrategy();
627 }
628
SetOptionBgColor(const Color & color)629 void SelectPattern::SetOptionBgColor(const Color& color)
630 {
631 optionBgColor_ = color;
632 for (size_t i = 0; i < options_.size(); ++i) {
633 if (static_cast<int32_t>(i) == selected_ && selectedBgColor_.has_value()) {
634 continue;
635 }
636 auto pattern = options_[i]->GetPattern<OptionPattern>();
637 CHECK_NULL_VOID(pattern);
638 pattern->SetBgColor(color);
639 }
640 }
641
SetOptionFontSize(const Dimension & value)642 void SelectPattern::SetOptionFontSize(const Dimension& value)
643 {
644 optionFont_.FontSize = value;
645 for (size_t i = 0; i < options_.size(); ++i) {
646 if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontSize.has_value()) {
647 continue;
648 }
649 auto pattern = options_[i]->GetPattern<OptionPattern>();
650 CHECK_NULL_VOID(pattern);
651 pattern->SetFontSize(value);
652 }
653 }
654
SetOptionItalicFontStyle(const Ace::FontStyle & value)655 void SelectPattern::SetOptionItalicFontStyle(const Ace::FontStyle& value)
656 {
657 optionFont_.FontStyle = value;
658 for (size_t i = 0; i < options_.size(); ++i) {
659 if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontStyle.has_value()) {
660 continue;
661 }
662 auto pattern = options_[i]->GetPattern<OptionPattern>();
663 CHECK_NULL_VOID(pattern);
664 pattern->SetItalicFontStyle(value);
665 }
666 }
667
SetOptionFontWeight(const FontWeight & value)668 void SelectPattern::SetOptionFontWeight(const FontWeight& value)
669 {
670 optionFont_.FontWeight = value;
671 for (size_t i = 0; i < options_.size(); ++i) {
672 if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontWeight.has_value()) {
673 continue;
674 }
675 auto pattern = options_[i]->GetPattern<OptionPattern>();
676 CHECK_NULL_VOID(pattern);
677 pattern->SetFontWeight(value);
678 }
679 }
680
SetOptionFontFamily(const std::vector<std::string> & value)681 void SelectPattern::SetOptionFontFamily(const std::vector<std::string>& value)
682 {
683 optionFont_.FontFamily = value;
684 for (size_t i = 0; i < options_.size(); ++i) {
685 if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontFamily.has_value()) {
686 continue;
687 }
688 auto pattern = options_[i]->GetPattern<OptionPattern>();
689 CHECK_NULL_VOID(pattern);
690 pattern->SetFontFamily(value);
691 }
692 }
693
SetOptionFontColor(const Color & color)694 void SelectPattern::SetOptionFontColor(const Color& color)
695 {
696 optionFont_.FontColor = color;
697 for (size_t i = 0; i < options_.size(); ++i) {
698 if (static_cast<int32_t>(i) == selected_ && selectedFont_.FontColor.has_value()) {
699 continue;
700 }
701 auto pattern = options_[i]->GetPattern<OptionPattern>();
702 CHECK_NULL_VOID(pattern);
703 pattern->SetFontColor(color);
704 }
705 }
706
707 // set props of option node when selected
SetSelectedOptionBgColor(const Color & color)708 void SelectPattern::SetSelectedOptionBgColor(const Color& color)
709 {
710 selectedBgColor_ = color;
711 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
712 auto pattern = options_[selected_]->GetPattern<OptionPattern>();
713 CHECK_NULL_VOID(pattern);
714 pattern->SetBgColor(color);
715 }
716 }
717
SetSelectedOptionFontSize(const Dimension & value)718 void SelectPattern::SetSelectedOptionFontSize(const Dimension& value)
719 {
720 selectedFont_.FontSize = value;
721 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
722 auto pattern = options_[selected_]->GetPattern<OptionPattern>();
723 CHECK_NULL_VOID(pattern);
724 pattern->SetFontSize(value);
725 }
726 }
727
SetSelectedOptionItalicFontStyle(const Ace::FontStyle & value)728 void SelectPattern::SetSelectedOptionItalicFontStyle(const Ace::FontStyle& value)
729 {
730 selectedFont_.FontStyle = value;
731 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
732 auto pattern = options_[selected_]->GetPattern<OptionPattern>();
733 CHECK_NULL_VOID(pattern);
734 pattern->SetItalicFontStyle(value);
735 }
736 }
737
SetSelectedOptionFontWeight(const FontWeight & value)738 void SelectPattern::SetSelectedOptionFontWeight(const FontWeight& value)
739 {
740 selectedFont_.FontWeight = value;
741 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
742 auto pattern = options_[selected_]->GetPattern<OptionPattern>();
743 CHECK_NULL_VOID(pattern);
744 pattern->SetFontWeight(value);
745 }
746 }
747
SetSelectedOptionFontFamily(const std::vector<std::string> & value)748 void SelectPattern::SetSelectedOptionFontFamily(const std::vector<std::string>& value)
749 {
750 selectedFont_.FontFamily = value;
751 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
752 auto pattern = options_[selected_]->GetPattern<OptionPattern>();
753 CHECK_NULL_VOID(pattern);
754 pattern->SetFontFamily(value);
755 }
756 }
757
SetSelectedOptionFontColor(const Color & color)758 void SelectPattern::SetSelectedOptionFontColor(const Color& color)
759 {
760 selectedFont_.FontColor = color;
761 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
762 auto pattern = options_[selected_]->GetPattern<OptionPattern>();
763 CHECK_NULL_VOID(pattern);
764 pattern->SetFontColor(color);
765 }
766 }
767
GetOptions()768 const std::vector<RefPtr<FrameNode>>& SelectPattern::GetOptions()
769 {
770 return options_;
771 }
772
ResetOptionProps()773 void SelectPattern::ResetOptionProps()
774 {
775 auto host = GetHost();
776 CHECK_NULL_VOID(host);
777 auto pipeline = host->GetContextWithCheck();
778 CHECK_NULL_VOID(pipeline);
779 auto selectTheme = pipeline->GetTheme<SelectTheme>();
780 auto textTheme = pipeline->GetTheme<TextTheme>();
781 CHECK_NULL_VOID(selectTheme && textTheme);
782
783 for (const auto& option : options_) {
784 auto pattern = option->GetPattern<OptionPattern>();
785 CHECK_NULL_VOID(pattern);
786 pattern->SetSelected(false);
787 pattern->SetBgColor(optionBgColor_.value_or(selectTheme->GetBackgroundColor()));
788 pattern->SetFontSize(optionFont_.FontSize.value_or(selectTheme->GetMenuFontSize()));
789 pattern->SetItalicFontStyle(optionFont_.FontStyle.value_or(textTheme->GetTextStyle().GetFontStyle()));
790 pattern->SetFontWeight(optionFont_.FontWeight.value_or(textTheme->GetTextStyle().GetFontWeight()));
791 pattern->SetFontFamily(optionFont_.FontFamily.value_or(textTheme->GetTextStyle().GetFontFamilies()));
792 pattern->SetFontColor(optionFont_.FontColor.value_or(selectTheme->GetMenuFontColor()));
793 }
794 }
795
UpdateLastSelectedProps(int32_t index)796 void SelectPattern::UpdateLastSelectedProps(int32_t index)
797 {
798 CHECK_NULL_VOID(options_[index]);
799 auto newSelected = options_[index]->GetPattern<OptionPattern>();
800 CHECK_NULL_VOID(newSelected);
801 // set lastSelected option props back to default (unselected) values
802 if (selected_ >= 0 && selected_ < static_cast<int32_t>(options_.size())) {
803 CHECK_NULL_VOID(options_[selected_]);
804 auto lastSelected = options_[selected_]->GetPattern<OptionPattern>();
805 CHECK_NULL_VOID(lastSelected);
806
807 lastSelected->SetFontColor(newSelected->GetFontColor());
808 lastSelected->SetFontFamily(newSelected->GetFontFamily());
809 lastSelected->SetFontSize(newSelected->GetFontSize());
810 lastSelected->SetItalicFontStyle(newSelected->GetItalicFontStyle());
811 lastSelected->SetFontWeight(newSelected->GetFontWeight());
812
813 lastSelected->SetBgColor(newSelected->GetBgColor());
814 lastSelected->SetSelected(false);
815 lastSelected->UpdateNextNodeDivider(true);
816 if (selected_ != 0) {
817 auto lastSelectedNode = lastSelected->GetHost();
818 CHECK_NULL_VOID(lastSelectedNode);
819 auto lastSelectedPros = lastSelectedNode->GetPaintProperty<OptionPaintProperty>();
820 CHECK_NULL_VOID(lastSelectedPros);
821 lastSelectedPros->UpdateNeedDivider(true);
822 }
823 options_[selected_]->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
824 }
825 }
826
827 // update selected option props
UpdateSelectedProps(int32_t index)828 void SelectPattern::UpdateSelectedProps(int32_t index)
829 {
830 CHECK_NULL_VOID(options_[index]);
831 auto newSelected = options_[index]->GetPattern<OptionPattern>();
832 CHECK_NULL_VOID(newSelected);
833
834 // set newSelected props
835 auto host = GetHost();
836 CHECK_NULL_VOID(host);
837 auto pipeline = host->GetContextRefPtr();
838 CHECK_NULL_VOID(pipeline);
839 auto theme = pipeline->GetTheme<SelectTheme>();
840 CHECK_NULL_VOID(theme);
841 if (selectedFont_.FontColor.has_value()) {
842 newSelected->SetFontColor(selectedFont_.FontColor.value());
843 } else {
844 auto selectedColorText = theme->GetSelectedColorText();
845 newSelected->SetFontColor(selectedColorText);
846 }
847 if (selectedFont_.FontFamily.has_value()) {
848 newSelected->SetFontFamily(selectedFont_.FontFamily.value());
849 }
850 if (selectedFont_.FontSize.has_value()) {
851 newSelected->SetFontSize(selectedFont_.FontSize.value());
852 }
853 if (selectedFont_.FontStyle.has_value()) {
854 newSelected->SetItalicFontStyle(selectedFont_.FontStyle.value());
855 }
856 if (selectedFont_.FontWeight.has_value()) {
857 newSelected->SetFontWeight(selectedFont_.FontWeight.value());
858 }
859 if (selectedBgColor_.has_value()) {
860 newSelected->SetBgColor(selectedBgColor_.value());
861 } else {
862 auto selectedColor = theme->GetSelectedColor();
863 newSelected->SetBgColor(selectedColor);
864 }
865 newSelected->SetSelected(true);
866 newSelected->UpdateNextNodeDivider(false);
867 auto newSelectedNode = newSelected->GetHost();
868 CHECK_NULL_VOID(newSelectedNode);
869 auto newSelectedPros = newSelectedNode->GetPaintProperty<OptionPaintProperty>();
870 CHECK_NULL_VOID(newSelectedPros);
871 newSelectedPros->UpdateNeedDivider(false);
872 }
873
UpdateText(int32_t index)874 void SelectPattern::UpdateText(int32_t index)
875 {
876 // update text to selected option's text
877 CHECK_NULL_VOID(text_);
878 auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
879 CHECK_NULL_VOID(textProps);
880 if (index >= static_cast<int32_t>(options_.size()) || index < 0) {
881 return;
882 }
883 auto newSelected = options_[index]->GetPattern<OptionPattern>();
884 CHECK_NULL_VOID(newSelected);
885 textProps->UpdateContent(newSelected->GetText());
886 text_->MarkModifyDone();
887 auto host = GetHost();
888 CHECK_NULL_VOID(host);
889 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
890 selectValue_ = newSelected->GetText();
891 }
892
InitTextProps(const RefPtr<TextLayoutProperty> & textProps,const RefPtr<SelectTheme> & theme)893 void SelectPattern::InitTextProps(const RefPtr<TextLayoutProperty>& textProps, const RefPtr<SelectTheme>& theme)
894 {
895 textProps->UpdateFontSize(theme->GetFontSize());
896 textProps->UpdateFontWeight(FontWeight::MEDIUM);
897 textProps->UpdateTextColor(theme->GetFontColor());
898 textProps->UpdateTextDecoration(theme->GetTextDecoration());
899 textProps->UpdateTextOverflow(TextOverflow::ELLIPSIS);
900 textProps->UpdateMaxLines(SELECT_ITSELF_TEXT_LINES);
901 }
902
InitSpinner(const RefPtr<FrameNode> & spinner,const RefPtr<IconTheme> & iconTheme,const RefPtr<SelectTheme> & selectTheme)903 void SelectPattern::InitSpinner(
904 const RefPtr<FrameNode>& spinner, const RefPtr<IconTheme>& iconTheme, const RefPtr<SelectTheme>& selectTheme)
905 {
906 ImageSourceInfo imageSourceInfo;
907 auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::SPINNER);
908 imageSourceInfo.SetSrc(iconPath);
909 imageSourceInfo.SetFillColor(selectTheme->GetSpinnerColor());
910
911 auto spinnerLayoutProperty = spinner->GetLayoutProperty<ImageLayoutProperty>();
912 CHECK_NULL_VOID(spinnerLayoutProperty);
913 spinnerLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
914 CalcSize idealSize = { CalcLength(selectTheme->GetSpinnerWidth()), CalcLength(selectTheme->GetSpinnerHeight()) };
915 MeasureProperty layoutConstraint;
916 layoutConstraint.selfIdealSize = idealSize;
917 spinnerLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
918 auto spinnerRenderProperty = spinner->GetPaintProperty<ImageRenderProperty>();
919 CHECK_NULL_VOID(spinnerRenderProperty);
920 spinnerRenderProperty->UpdateSvgFillColor(selectTheme->GetSpinnerColor());
921 }
922
InitSpinner(const RefPtr<FrameNode> & spinner,const RefPtr<SelectTheme> & selectTheme)923 void SelectPattern::InitSpinner(
924 const RefPtr<FrameNode>& spinner, const RefPtr<SelectTheme>& selectTheme)
925 {
926 auto spinnerLayoutProperty = spinner->GetLayoutProperty<TextLayoutProperty>();
927 CHECK_NULL_VOID(spinnerLayoutProperty);
928 uint32_t symbolId = selectTheme->GetSpinnerSource();
929 spinnerLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo{symbolId});
930 spinnerLayoutProperty->UpdateSymbolColorList({selectTheme->GetSpinnerSymbolColor()});
931 spinnerLayoutProperty->UpdateFontSize(selectTheme->GetFontSize());
932 }
933
934 // XTS inspector code
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const935 void SelectPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
936 {
937 /* no fixed attr below, just return */
938 if (filter.IsFastFilter()) {
939 ToJsonArrowAndText(json, filter);
940 ToJsonOptionAlign(json, filter);
941 ToJsonMenuBackgroundStyle(json, filter);
942 return;
943 }
944 json->PutExtAttr("options", InspectorGetOptions().c_str(), filter);
945 json->PutExtAttr("selected", std::to_string(selected_).c_str(), filter);
946 ToJsonArrowAndText(json, filter);
947 json->PutExtAttr("selectedOptionBgColor", selectedBgColor_->ColorToString().c_str(), filter);
948 json->PutExtAttr("selectedOptionFont", InspectorGetSelectedFont().c_str(), filter);
949 json->PutExtAttr("selectedOptionFontColor",
950 selectedFont_.FontColor.value_or(Color::BLACK).ColorToString().c_str(), filter);
951
952 if (options_.empty()) {
953 json->PutExtAttr("optionBgColor", "", filter);
954 json->PutExtAttr("optionFont", "", filter);
955 json->PutExtAttr("optionFontColor", "", filter);
956 } else {
957 auto optionPattern = options_[0]->GetPattern<OptionPattern>();
958 CHECK_NULL_VOID(optionPattern);
959 json->PutExtAttr("optionBgColor", optionPattern->GetBgColor().ColorToString().c_str(), filter);
960 json->PutExtAttr("optionFont", optionPattern->InspectorGetFont().c_str(), filter);
961 json->PutExtAttr("optionFontColor", optionPattern->GetFontColor().ColorToString().c_str(), filter);
962 }
963 ToJsonOptionAlign(json, filter);
964 for (size_t i = 0; i < options_.size(); ++i) {
965 auto optionPaintProperty = options_[i]->GetPaintProperty<OptionPaintProperty>();
966 CHECK_NULL_VOID(optionPaintProperty);
967 std::string optionWidth = std::to_string(optionPaintProperty->GetSelectModifiedWidthValue(0.0f));
968 json->PutExtAttr("optionWidth", optionWidth.c_str(), filter);
969 }
970
971 auto menu = GetMenuNode();
972 CHECK_NULL_VOID(menu);
973 auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
974 CHECK_NULL_VOID(menuLayoutProps);
975 std::string optionHeight = std::to_string(menuLayoutProps->GetSelectModifiedHeightValue(0.0f));
976 json->PutExtAttr("optionHeight", optionHeight.c_str(), filter);
977 ToJsonMenuBackgroundStyle(json, filter);
978 ToJsonDivider(json, filter);
979 }
980
ToJsonArrowAndText(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const981 void SelectPattern::ToJsonArrowAndText(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
982 {
983 /* no fixed attr below, just return */
984 if (filter.IsFastFilter()) {
985 return;
986 }
987 auto host = GetHost();
988 CHECK_NULL_VOID(host);
989 if (!host->GetChildren().empty()) {
990 auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
991 CHECK_NULL_VOID(row);
992 auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
993 CHECK_NULL_VOID(rowProps);
994 json->PutExtAttr("space", rowProps->GetSpace()->ToString().c_str(), filter);
995
996 if (rowProps->GetFlexDirection().value() == FlexDirection::ROW) {
997 json->PutExtAttr("arrowPosition", "ArrowPosition.END", filter);
998 } else {
999 json->PutExtAttr("arrowPosition", "ArrowPosition.START", filter);
1000 }
1001 }
1002
1003 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
1004 CHECK_NULL_VOID(props);
1005 json->PutExtAttr("value", props->GetContent().value_or("").c_str(), filter);
1006 Color fontColor = props->GetTextColor().value_or(Color::BLACK);
1007 json->PutExtAttr("fontColor", fontColor.ColorToString().c_str(), filter);
1008 json->PutExtAttr("font", props->InspectorGetTextFont().c_str(), filter);
1009 json->PutExtAttr("controlSize", ConvertControlSizeToString(controlSize_).c_str(), filter);
1010 }
1011
ToJsonMenuBackgroundStyle(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1012 void SelectPattern::ToJsonMenuBackgroundStyle(
1013 std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1014 {
1015 /* no fixed attr below, just return */
1016 if (filter.IsFastFilter()) {
1017 return;
1018 }
1019 auto menu = GetMenuNode();
1020 CHECK_NULL_VOID(menu);
1021 auto menuRenderContext = menu->GetRenderContext();
1022 CHECK_NULL_VOID(menuRenderContext);
1023 json->PutExtAttr("menuBackgroundColor",
1024 menuRenderContext->GetBackgroundColor()->ColorToString().c_str(), filter);
1025 if (menuRenderContext->GetBackBlurStyle().has_value()) {
1026 BlurStyleOption blurStyleOption = menuRenderContext->GetBackBlurStyle().value();
1027 auto jsonValue = JsonUtil::Create(true);
1028 blurStyleOption.ToJsonValue(jsonValue, filter);
1029 json->PutExtAttr("menuBackgroundBlurStyle",
1030 jsonValue->GetValue("backgroundBlurStyle")->GetValue("value"), filter);
1031 } else {
1032 json->PutExtAttr("menuBackgroundBlurStyle", "", filter);
1033 }
1034 }
1035
ToJsonDivider(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1036 void SelectPattern::ToJsonDivider(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1037 {
1038 /* no fixed attr below, just return */
1039 if (filter.IsFastFilter()) {
1040 return;
1041 }
1042 if (options_.empty()) {
1043 json->PutExtAttr("divider", "", filter);
1044 } else {
1045 auto props = options_[0]->GetPaintProperty<OptionPaintProperty>();
1046 CHECK_NULL_VOID(props);
1047 auto divider = JsonUtil::Create(true);
1048 if (props->HasDivider()) {
1049 divider->Put("strokeWidth", props->GetDividerValue().strokeWidth.ToString().c_str());
1050 divider->Put("startMargin", props->GetDividerValue().startMargin.ToString().c_str());
1051 divider->Put("endMargin", props->GetDividerValue().endMargin.ToString().c_str());
1052 divider->Put("color", props->GetDividerValue().color.ColorToString().c_str());
1053 json->PutExtAttr("divider", divider->ToString().c_str(), filter);
1054 } else {
1055 json->PutExtAttr("divider", "", filter);
1056 }
1057 }
1058 }
1059
ToJsonOptionAlign(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1060 void SelectPattern::ToJsonOptionAlign(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1061 {
1062 /* no fixed attr below, just return */
1063 if (filter.IsFastFilter()) {
1064 return;
1065 }
1066 auto optionAlignJson = JsonUtil::Create(true);
1067 std::string alignTypeString = "MenuAlignType.Start";
1068 if (menuAlign_.alignType == MenuAlignType::START) {
1069 alignTypeString = "MenuAlignType.Start";
1070 } else if (menuAlign_.alignType == MenuAlignType::CENTER) {
1071 alignTypeString = "MenuAlignType.Center";
1072 } else if (menuAlign_.alignType == MenuAlignType::END) {
1073 alignTypeString = "MenuAlignType.End";
1074 }
1075 optionAlignJson->Put("alignType", alignTypeString.c_str());
1076
1077 auto offsetValueJson = JsonUtil::Create(true);
1078 offsetValueJson->Put("dX", menuAlign_.offset.GetX().Value());
1079 offsetValueJson->Put("dY", menuAlign_.offset.GetY().Value());
1080 optionAlignJson->Put("offset", offsetValueJson);
1081
1082 json->PutExtAttr("menuAlign", optionAlignJson, filter);
1083 }
1084
InspectorGetOptions() const1085 std::string SelectPattern::InspectorGetOptions() const
1086 {
1087 auto jsonValue = JsonUtil::Create(true);
1088 auto jsonOptions = JsonUtil::CreateArray(true);
1089 for (size_t i = 0; i < options_.size(); ++i) {
1090 auto temp = JsonUtil::Create(true);
1091 auto optionPattern = options_[i]->GetPattern<OptionPattern>();
1092 temp->Put("value", optionPattern->GetText().c_str());
1093 temp->Put("icon", optionPattern->GetIcon().c_str());
1094 auto index = std::to_string(i);
1095 jsonOptions->Put(index.c_str(), temp);
1096 }
1097 jsonValue->Put("options", jsonOptions);
1098 return jsonValue->ToString();
1099 }
1100
InspectorGetSelectedFont() const1101 std::string SelectPattern::InspectorGetSelectedFont() const
1102 {
1103 TextStyle font;
1104 if (selectedFont_.FontFamily.has_value()) {
1105 font.SetFontFamilies(selectedFont_.FontFamily.value());
1106 }
1107 if (selectedFont_.FontSize.has_value()) {
1108 font.SetFontSize(selectedFont_.FontSize.value());
1109 }
1110 if (selectedFont_.FontStyle.has_value()) {
1111 font.SetFontStyle(selectedFont_.FontStyle.value());
1112 }
1113 if (selectedFont_.FontWeight.has_value()) {
1114 font.SetFontWeight(selectedFont_.FontWeight.value());
1115 }
1116 return V2::GetTextStyleInJson(font);
1117 }
1118
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)1119 bool SelectPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
1120 {
1121 auto geometryNode = dirty->GetGeometryNode();
1122 CHECK_NULL_RETURN(geometryNode, false);
1123 SetSelectSize(geometryNode->GetFrameSize());
1124 return false;
1125 }
1126
SetSpace(const Dimension & value)1127 void SelectPattern::SetSpace(const Dimension& value)
1128 {
1129 auto host = GetHost();
1130 CHECK_NULL_VOID(host);
1131 if (!host->GetChildren().empty()) {
1132 auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
1133 auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
1134 rowProps->UpdateSpace(value);
1135 row->MarkModifyDone();
1136 row->MarkDirtyNode();
1137 }
1138 }
1139
SetArrowPosition(const ArrowPosition value)1140 void SelectPattern::SetArrowPosition(const ArrowPosition value)
1141 {
1142 auto host = GetHost();
1143 CHECK_NULL_VOID(host);
1144 if (!host->GetChildren().empty()) {
1145 auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
1146 auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
1147
1148 if (value == ArrowPosition::END) {
1149 rowProps->UpdateFlexDirection(FlexDirection::ROW);
1150 } else {
1151 rowProps->UpdateFlexDirection(FlexDirection::ROW_REVERSE);
1152 }
1153 row->MarkModifyDone();
1154 row->MarkDirtyNode();
1155 }
1156 }
1157
GetValue()1158 std::string SelectPattern::GetValue()
1159 {
1160 CHECK_NULL_RETURN(text_, "");
1161 auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
1162 CHECK_NULL_RETURN(textProps, "");
1163 return textProps->GetContentValue("");
1164 }
1165
SetMenuAlign(const MenuAlign & menuAlign)1166 void SelectPattern::SetMenuAlign(const MenuAlign& menuAlign)
1167 {
1168 menuAlign_ = menuAlign;
1169 auto menu = GetMenuNode();
1170 CHECK_NULL_VOID(menu);
1171 auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
1172 CHECK_NULL_VOID(menuLayoutProps);
1173 menuLayoutProps->UpdateAlignType(menuAlign.alignType);
1174 menuLayoutProps->UpdateOffset(menuAlign.offset);
1175 }
1176
ProvideRestoreInfo()1177 std::string SelectPattern::ProvideRestoreInfo()
1178 {
1179 auto jsonObj = JsonUtil::Create(true);
1180 jsonObj->Put("selected", selected_);
1181 jsonObj->Put("isSelected", isSelected_);
1182 return jsonObj->ToString();
1183 }
1184
OnRestoreInfo(const std::string & restoreInfo)1185 void SelectPattern::OnRestoreInfo(const std::string& restoreInfo)
1186 {
1187 auto info = JsonUtil::ParseJsonString(restoreInfo);
1188 if (!info->IsValid() || !info->IsObject()) {
1189 return;
1190 }
1191 auto jsonIsOn = info->GetValue("selected");
1192 auto jsonIsSelect = info->GetValue("isSelected");
1193 if (jsonIsSelect->GetBool()) {
1194 SetSelected(jsonIsOn->GetInt());
1195 UpdateText(jsonIsOn->GetInt());
1196 }
1197 }
1198
OnColorConfigurationUpdate()1199 void SelectPattern::OnColorConfigurationUpdate()
1200 {
1201 auto host = GetHost();
1202 CHECK_NULL_VOID(host);
1203 auto pipeline = host->GetContextWithCheck();
1204 CHECK_NULL_VOID(pipeline);
1205 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1206 CHECK_NULL_VOID(selectTheme);
1207
1208 auto pattern = host->GetPattern<SelectPattern>();
1209 auto menuNode = pattern->GetMenuNode();
1210 CHECK_NULL_VOID(menuNode);
1211 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1212 CHECK_NULL_VOID(menuPattern);
1213
1214 auto renderContext = menuNode->GetRenderContext();
1215 renderContext->UpdateBackgroundColor(selectTheme->GetBackgroundColor());
1216 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && renderContext->IsUniRenderEnabled()) {
1217 renderContext->UpdateBackBlurStyle(renderContext->GetBackBlurStyle());
1218 }
1219
1220 auto optionNode = menuPattern->GetOptions();
1221 for (auto child : optionNode) {
1222 auto optionsPattern = child->GetPattern<OptionPattern>();
1223 optionsPattern->SetFontColor(selectTheme->GetFontColor());
1224
1225 child->MarkModifyDone();
1226 child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1227 }
1228 SetOptionBgColor(selectTheme->GetBackgroundColor());
1229 host->SetNeedCallChildrenUpdate(false);
1230 }
1231
OnLanguageConfigurationUpdate()1232 void SelectPattern::OnLanguageConfigurationUpdate()
1233 {
1234 auto host = GetHost();
1235 CHECK_NULL_VOID(host);
1236 auto context = host->GetContextRefPtr();
1237 CHECK_NULL_VOID(context);
1238 auto taskExecutor = context->GetTaskExecutor();
1239 CHECK_NULL_VOID(taskExecutor);
1240 taskExecutor->PostTask(
1241 [weak = WeakClaim(this)]() {
1242 auto pattern = weak.Upgrade();
1243 CHECK_NULL_VOID(pattern);
1244 auto index = pattern->selected_;
1245 pattern->UpdateText(index);
1246 auto host = pattern->GetHost();
1247 CHECK_NULL_VOID(host);
1248 auto hub = host->GetEventHub<SelectEventHub>();
1249 CHECK_NULL_VOID(hub);
1250 if (index >= static_cast<int32_t>(pattern->options_.size()) || index < 0) {
1251 return;
1252 }
1253 auto newSelected = pattern->options_[index]->GetPattern<OptionPattern>();
1254 CHECK_NULL_VOID(newSelected);
1255 auto value = newSelected->GetText();
1256 auto valueChangeEvent = hub->GetValueChangeEvent();
1257 if (valueChangeEvent) {
1258 valueChangeEvent(value);
1259 }
1260 auto onSelect = hub->GetSelectEvent();
1261 if (onSelect) {
1262 onSelect(index, value);
1263 }
1264 },
1265 TaskExecutor::TaskType::UI, "ArkUISelectLanguageConfigUpdate");
1266 }
1267
GetFontSize()1268 Dimension SelectPattern::GetFontSize()
1269 {
1270 Dimension defaultRet = Dimension();
1271 auto props = text_->GetLayoutProperty<TextLayoutProperty>();
1272 CHECK_NULL_RETURN(props, defaultRet);
1273 auto host = props->GetHost();
1274 CHECK_NULL_RETURN(host, defaultRet);
1275 auto pipeline = host->GetContextWithCheck();
1276 CHECK_NULL_RETURN(pipeline, defaultRet);
1277 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1278 CHECK_NULL_RETURN(selectTheme, defaultRet);
1279 return props->GetFontSize().value_or(selectTheme->GetFontSize());
1280 }
1281
SetSelectDefaultTheme()1282 void SelectPattern::SetSelectDefaultTheme()
1283 {
1284 auto select = GetHost();
1285 CHECK_NULL_VOID(select);
1286 auto pipeline = select->GetContextWithCheck();
1287 CHECK_NULL_VOID(pipeline);
1288 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1289 CHECK_NULL_VOID(selectTheme);
1290
1291 auto renderContext = select->GetRenderContext();
1292 CHECK_NULL_VOID(renderContext);
1293
1294 if (selectDefaultBgColor_ == Color::TRANSPARENT) {
1295 renderContext->UpdateBackgroundColor(selectTheme->GetSelectDefaultBgColor());
1296 } else {
1297 renderContext->UpdateBackgroundColor(selectDefaultBgColor_);
1298 }
1299 BorderRadiusProperty border;
1300 border.SetRadius(selectTheme->GetSelectDefaultBorderRadius());
1301 renderContext->UpdateBorderRadius(border);
1302 }
1303
SetOptionWidth(const Dimension & value)1304 void SelectPattern::SetOptionWidth(const Dimension& value)
1305 {
1306 isFitTrigger_ = false;
1307 auto menu = GetMenuNode();
1308 CHECK_NULL_VOID(menu);
1309 auto menuPattern = menu->GetPattern<MenuPattern>();
1310 CHECK_NULL_VOID(menuPattern);
1311 menuPattern->SetIsWidthModifiedBySelect(true);
1312 auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
1313 CHECK_NULL_VOID(menuLayoutProps);
1314 menuLayoutProps->UpdateSelectMenuModifiedWidth(value.ConvertToPx() + OPTION_MARGIN.ConvertToPx());
1315
1316 auto scroll = DynamicCast<FrameNode>(menu->GetFirstChild());
1317 CHECK_NULL_VOID(scroll);
1318 auto scrollPattern = scroll->GetPattern<ScrollPattern>();
1319 CHECK_NULL_VOID(scrollPattern);
1320 scrollPattern->SetIsWidthModifiedBySelect(true);
1321 auto scrollLayoutProps = scroll->GetLayoutProperty<ScrollLayoutProperty>();
1322 CHECK_NULL_VOID(scrollLayoutProps);
1323 scrollLayoutProps->UpdateScrollWidth(value.ConvertToPx() + OPTION_MARGIN.ConvertToPx());
1324
1325 for (size_t i = 0; i < options_.size(); ++i) {
1326 auto optionWidth = value.ConvertToPx();
1327 auto optionPattern = options_[i]->GetPattern<OptionPattern>();
1328 CHECK_NULL_VOID(optionPattern);
1329 optionPattern->SetIsWidthModifiedBySelect(true);
1330 auto optionPaintProperty = options_[i]->GetPaintProperty<OptionPaintProperty>();
1331 CHECK_NULL_VOID(optionPaintProperty);
1332 optionPaintProperty->UpdateSelectModifiedWidth(optionWidth);
1333 }
1334 }
1335
SetOptionWidthFitTrigger(bool isFitTrigger)1336 void SelectPattern::SetOptionWidthFitTrigger(bool isFitTrigger)
1337 {
1338 isFitTrigger_ = isFitTrigger;
1339 }
1340
SetHasOptionWidth(bool hasOptionWidth)1341 void SelectPattern::SetHasOptionWidth(bool hasOptionWidth)
1342 {
1343 auto menu = GetMenuNode();
1344 CHECK_NULL_VOID(menu);
1345 auto menuPattern = menu->GetPattern<MenuPattern>();
1346 CHECK_NULL_VOID(menuPattern);
1347 menuPattern->SetHasOptionWidth(true);
1348 auto scroll = DynamicCast<FrameNode>(menu->GetFirstChild());
1349 CHECK_NULL_VOID(scroll);
1350 auto scrollPattern = scroll->GetPattern<ScrollPattern>();
1351 CHECK_NULL_VOID(scrollPattern);
1352 scrollPattern->SetHasOptionWidth(true);
1353 for (size_t i = 0; i < options_.size(); ++i) {
1354 auto optionPattern = options_[i]->GetPattern<OptionPattern>();
1355 CHECK_NULL_VOID(optionPattern);
1356 optionPattern->SetHasOptionWidth(true);
1357 }
1358 }
1359
SetOptionHeight(const Dimension & value)1360 void SelectPattern::SetOptionHeight(const Dimension& value)
1361 {
1362 auto menuMaxHeight = value.ConvertToPx();
1363 auto menu = GetMenuNode();
1364 CHECK_NULL_VOID(menu);
1365 auto menuPattern = menu->GetPattern<MenuPattern>();
1366 CHECK_NULL_VOID(menuPattern);
1367 menuPattern->SetIsHeightModifiedBySelect(true);
1368 auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
1369 CHECK_NULL_VOID(menuLayoutProps);
1370 menuLayoutProps->UpdateSelectModifiedHeight(menuMaxHeight);
1371 }
1372
SetMenuBackgroundColor(const Color & color)1373 void SelectPattern::SetMenuBackgroundColor(const Color& color)
1374 {
1375 auto menu = GetMenuNode();
1376 CHECK_NULL_VOID(menu);
1377 auto renderContext = menu->GetRenderContext();
1378 CHECK_NULL_VOID(renderContext);
1379 renderContext->UpdateBackgroundColor(color);
1380 }
1381
SetMenuBackgroundBlurStyle(const BlurStyleOption & blurStyle)1382 void SelectPattern::SetMenuBackgroundBlurStyle(const BlurStyleOption& blurStyle)
1383 {
1384 auto menu = GetMenuNode();
1385 CHECK_NULL_VOID(menu);
1386 auto renderContext = menu->GetRenderContext();
1387 CHECK_NULL_VOID(renderContext);
1388 renderContext->UpdateBackBlurStyle(blurStyle);
1389 }
1390
ResetParams()1391 void SelectPattern::ResetParams()
1392 {
1393 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1394 return;
1395 }
1396 auto select = GetHost();
1397 CHECK_NULL_VOID(select);
1398 auto* pipeline = select->GetContextWithCheck();
1399 CHECK_NULL_VOID(pipeline);
1400 auto selectTheme = pipeline->GetTheme<SelectTheme>();
1401 CHECK_NULL_VOID(selectTheme);
1402 auto layoutProperty = select->GetLayoutProperty();
1403 CHECK_NULL_VOID(layoutProperty);
1404 layoutProperty->UpdateCalcMinSize(CalcSize(CalcLength(selectTheme->GetSelectMinWidth(controlSize_)),
1405 CalcLength(selectTheme->GetSelectDefaultHeight(controlSize_))));
1406 SetFontSize(selectTheme->GetFontSize(controlSize_));
1407 auto spinnerLayoutProperty = spinner_->GetLayoutProperty<TextLayoutProperty>();
1408 CHECK_NULL_VOID(spinnerLayoutProperty);
1409 spinnerLayoutProperty->UpdateFontSize(selectTheme->GetFontSize(controlSize_));
1410 auto renderContext = select->GetRenderContext();
1411 BorderRadiusProperty border;
1412 border.SetRadius(selectTheme->GetSelectDefaultBorderRadius(controlSize_));
1413 renderContext->UpdateBorderRadius(border);
1414
1415 NG::PaddingProperty paddings;
1416 paddings.top = std::nullopt;
1417 paddings.bottom = std::nullopt;
1418 if (controlSize_ == ControlSize::SMALL) {
1419 paddings.left = NG::CalcLength(SELECT_SMALL_PADDING_VP);
1420 paddings.right = NG::CalcLength(SELECT_SMALL_PADDING_VP);
1421 } else {
1422 paddings.left = NG::CalcLength(SELECT_MARGIN_VP);
1423 paddings.right = NG::CalcLength(SELECT_MARGIN_VP);
1424 }
1425 ViewAbstract::SetPadding(paddings);
1426 }
1427
SetControlSize(const ControlSize & controlSize)1428 void SelectPattern::SetControlSize(const ControlSize& controlSize)
1429 {
1430 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1431 return;
1432 }
1433 controlSize_ = controlSize;
1434 ResetParams();
1435 }
1436
SetLayoutDirection(TextDirection value)1437 void SelectPattern::SetLayoutDirection(TextDirection value)
1438 {
1439 auto select = GetHost();
1440 auto menu = GetMenuNode();
1441 std::function<void (decltype(select))> updateDirectionFunc = [&](decltype(select) node) {
1442 if (!node) return;
1443 auto updateProperty = node->GetLayoutProperty();
1444 updateProperty->UpdateLayoutDirection(value);
1445 if (node->GetHostTag() == V2::SCROLL_ETS_TAG) {
1446 auto scrollPattern = AceType::DynamicCast<ScrollPattern>(node->GetPattern());
1447 if (scrollPattern) scrollPattern->TriggerModifyDone();
1448 }
1449 for (auto child : node->GetAllChildrenWithBuild()) {
1450 auto frameNode = AceType::DynamicCast<FrameNode>(child);
1451 if (!frameNode) continue;
1452 updateDirectionFunc(frameNode);
1453 }
1454 };
1455 updateDirectionFunc(select);
1456 updateDirectionFunc(menu);
1457 }
1458
GetControlSize()1459 ControlSize SelectPattern::GetControlSize()
1460 {
1461 return controlSize_;
1462 }
1463
SetDivider(const SelectDivider & divider)1464 void SelectPattern::SetDivider(const SelectDivider& divider)
1465 {
1466 for (auto&& option : options_) {
1467 auto props = option->GetPaintProperty<OptionPaintProperty>();
1468 CHECK_NULL_VOID(props);
1469 props->UpdateDivider(divider);
1470 }
1471 }
1472 } // namespace OHOS::Ace::NG
1473