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