• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/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/utils.h"
25 #include "core/animation/curves.h"
26 #include "core/components/common/properties/color.h"
27 #include "core/components/common/properties/text_style.h"
28 #include "core/components/select/select_theme.h"
29 #include "core/components/theme/icon_theme.h"
30 #include "core/components_ng/base/frame_node.h"
31 #include "core/components_ng/pattern/image/image_pattern.h"
32 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
33 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
34 #include "core/components_ng/pattern/menu/menu_pattern.h"
35 #include "core/components_ng/pattern/option/option_pattern.h"
36 #include "core/components_ng/pattern/select/select_event_hub.h"
37 #include "core/components_ng/pattern/text/text_layout_property.h"
38 #include "core/components_ng/pattern/text/text_pattern.h"
39 #include "core/components_ng/property/border_property.h"
40 #include "core/components_ng/property/measure_property.h"
41 #include "core/components_ng/property/property.h"
42 #include "core/components_v2/inspector/inspector_constants.h"
43 #include "core/components_v2/inspector/utils.h"
44 #include "core/gestures/gesture_info.h"
45 #include "core/pipeline/pipeline_base.h"
46 
47 namespace OHOS::Ace::NG {
48 
49 namespace {
50 
51 constexpr uint32_t SELECT_ITSELF_TEXT_LINES = 1;
52 
53 } // namespace
54 
OnAttachToFrameNode()55 void SelectPattern::OnAttachToFrameNode()
56 {
57     RegisterOnKeyEvent();
58     RegisterOnClick();
59     RegisterOnPress();
60     RegisterOnHover();
61 }
62 
OnModifyDone()63 void SelectPattern::OnModifyDone()
64 {
65     Pattern::OnModifyDone();
66     CreateSelectedCallback();
67 
68     auto host = GetHost();
69     CHECK_NULL_VOID(host);
70     auto eventHub = host->GetEventHub<SelectEventHub>();
71     CHECK_NULL_VOID(eventHub);
72     if (!eventHub->IsEnabled()) {
73         SetDisabledStyle();
74     }
75 }
76 
ShowSelectMenu()77 void SelectPattern::ShowSelectMenu()
78 {
79     CHECK_NULL_VOID(!options_.empty());
80     if (menuWrapper_) {
81         LOGI("start executing click callback %d", menuWrapper_->GetId());
82     }
83     auto context = PipelineContext::GetCurrentContext();
84     CHECK_NULL_VOID(context);
85     auto overlayManager = context->GetOverlayManager();
86     CHECK_NULL_VOID(overlayManager);
87 
88     auto menu = GetMenuNode();
89     CHECK_NULL_VOID(menu);
90     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
91     CHECK_NULL_VOID(menuLayoutProps);
92     menuLayoutProps->UpdateTargetSize(selectSize_);
93     auto offset = GetHost()->GetPaintRectOffset();
94     offset.AddY(selectSize_.Height());
95     LOGD("select offset %{public}s size %{public}s", offset.ToString().c_str(), selectSize_.ToString().c_str());
96     overlayManager->ShowMenu(GetHost()->GetId(), offset, menuWrapper_);
97 }
98 
99 // add click event to show menu
RegisterOnClick()100 void SelectPattern::RegisterOnClick()
101 {
102     auto host = GetHost();
103     CHECK_NULL_VOID(host);
104 
105     GestureEventFunc callback = [weak = WeakClaim(this)](GestureEvent& /* info */) mutable {
106         auto pattern = weak.Upgrade();
107         CHECK_NULL_VOID_NOLOG(pattern);
108 
109         auto selected = pattern->GetSelected();
110         if (selected > -1 && selected < pattern->GetOptions().size()) {
111             pattern->UpdateSelectedProps(selected);
112         }
113         pattern->ShowSelectMenu();
114     };
115     auto gestureHub = host->GetOrCreateGestureEventHub();
116     if (!gestureHub->GetTouchable()) {
117         return;
118     }
119     gestureHub->BindMenu(std::move(callback));
120 }
121 
PlayBgColorAnimation(bool isHoverChange)122 void SelectPattern::PlayBgColorAnimation(bool isHoverChange)
123 {
124     auto pipeline = PipelineBase::GetCurrentContext();
125     CHECK_NULL_VOID(pipeline);
126     auto selectTheme = pipeline->GetTheme<SelectTheme>();
127     CHECK_NULL_VOID(selectTheme);
128 
129     AnimationOption option = AnimationOption();
130     if (isHoverChange) {
131         option.SetDuration(selectTheme->GetHoverAnimationDuration());
132         option.SetCurve(Curves::FRICTION);
133     } else {
134         option.SetDuration(selectTheme->GetPressAnimationDuration());
135         option.SetCurve(Curves::SHARP);
136     }
137 
138     AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
139         auto pattern = weak.Upgrade();
140         CHECK_NULL_VOID_NOLOG(pattern);
141         auto host = pattern->GetHost();
142         CHECK_NULL_VOID_NOLOG(host);
143         auto renderContext = host->GetRenderContext();
144         CHECK_NULL_VOID_NOLOG(renderContext);
145         renderContext->BlendBgColor(pattern->GetBgBlendColor());
146     });
147 }
148 
149 // change background color when hovered
RegisterOnHover()150 void SelectPattern::RegisterOnHover()
151 {
152     auto host = GetHost();
153     CHECK_NULL_VOID(host);
154     auto inputHub = host->GetOrCreateInputEventHub();
155     CHECK_NULL_VOID(inputHub);
156     auto mouseCallback = [weak = WeakClaim(this)](bool isHover) {
157         auto pattern = weak.Upgrade();
158         CHECK_NULL_VOID(pattern);
159         pattern->SetIsHover(isHover);
160         auto pipeline = PipelineBase::GetCurrentContext();
161         CHECK_NULL_VOID(pipeline);
162         auto theme = pipeline->GetTheme<SelectTheme>();
163         CHECK_NULL_VOID(theme);
164         // update hover status, repaint background color
165         if (isHover) {
166             pattern->SetBgBlendColor(theme->GetHoverColor());
167         } else {
168             pattern->SetBgBlendColor(Color::TRANSPARENT);
169         }
170         pattern->PlayBgColorAnimation();
171     };
172     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseCallback));
173     inputHub->AddOnHoverEvent(mouseEvent);
174 }
175 
176 // change background color when pressed
RegisterOnPress()177 void SelectPattern::RegisterOnPress()
178 {
179     auto host = GetHost();
180     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
181         auto pattern = weak.Upgrade();
182         auto host = pattern->GetHost();
183         auto theme = host->GetContext()->GetTheme<SelectTheme>();
184         CHECK_NULL_VOID(pattern);
185         auto touchType = info.GetTouches().front().GetTouchType();
186         const auto& renderContext = host->GetRenderContext();
187         CHECK_NULL_VOID(renderContext);
188         // update press status, repaint background color
189         if (touchType == TouchType::DOWN) {
190             LOGD("triggers option press");
191             pattern->SetBgBlendColor(theme->GetClickedColor());
192             pattern->PlayBgColorAnimation(false);
193         }
194         if (touchType == TouchType::UP) {
195             if (pattern->IsHover()) {
196                 pattern->SetBgBlendColor(theme->GetHoverColor());
197             } else {
198                 pattern->SetBgBlendColor(Color::TRANSPARENT);
199             }
200             pattern->PlayBgColorAnimation(false);
201         }
202     };
203     auto touchEvent = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
204     auto gestureHub = host->GetOrCreateGestureEventHub();
205     gestureHub->AddTouchEvent(touchEvent);
206 }
207 
CreateSelectedCallback()208 void SelectPattern::CreateSelectedCallback()
209 {
210     auto host = GetHost();
211     CHECK_NULL_VOID(host);
212     auto callback = [weak = WeakClaim(RawPtr(host))](int32_t index) {
213         auto host = weak.Upgrade();
214         CHECK_NULL_VOID(host);
215         auto pattern = host->GetPattern<SelectPattern>();
216         CHECK_NULL_VOID(pattern);
217         pattern->SetSelected(index);
218         pattern->UpdateText(index);
219         pattern->isSelected_ = true;
220         auto hub = host->GetEventHub<SelectEventHub>();
221         CHECK_NULL_VOID(hub);
222         // execute change event callback
223         auto selectChangeEvent = hub->GetSelectChangeEvent();
224         if (selectChangeEvent) {
225             selectChangeEvent(index);
226         }
227         auto valueChangeEvent = hub->GetValueChangeEvent();
228         if (valueChangeEvent) {
229             auto newSelected = pattern->options_[index]->GetPattern<OptionPattern>();
230             CHECK_NULL_VOID(newSelected);
231             valueChangeEvent(newSelected->GetText());
232         }
233         auto onSelect = hub->GetSelectEvent();
234         // execute onSelect callback
235         if (onSelect) {
236             auto newSelected = pattern->options_[index]->GetPattern<OptionPattern>();
237             CHECK_NULL_VOID(newSelected);
238             onSelect(index, newSelected->GetText());
239         }
240     };
241     for (auto&& option : options_) {
242         auto hub = option->GetEventHub<OptionEventHub>();
243         // no std::move, need to set multiple options
244         hub->SetOnSelect(callback);
245         option->MarkModifyDone();
246     }
247 }
248 
RegisterOnKeyEvent()249 void SelectPattern::RegisterOnKeyEvent()
250 {
251     auto host = GetHost();
252     CHECK_NULL_VOID(host);
253     auto focusHub = host->GetOrCreateFocusHub();
254     CHECK_NULL_VOID(focusHub);
255     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
256         auto pattern = wp.Upgrade();
257         CHECK_NULL_RETURN_NOLOG(pattern, false);
258         return pattern->OnKeyEvent(event);
259     };
260     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
261 }
262 
OnKeyEvent(const KeyEvent & event)263 bool SelectPattern::OnKeyEvent(const KeyEvent& event)
264 {
265     if (event.action != KeyAction::DOWN) {
266         return false;
267     }
268     if (event.code == KeyCode::KEY_ENTER) {
269         auto host = GetHost();
270         CHECK_NULL_RETURN(host, false);
271         auto focusHub = host->GetOrCreateFocusHub();
272         CHECK_NULL_RETURN(focusHub, false);
273         focusHub->OnClick(event);
274         return true;
275     }
276     return false;
277 }
278 
SetDisabledStyle()279 void SelectPattern::SetDisabledStyle()
280 {
281     auto pipeline = PipelineBase::GetCurrentContext();
282     CHECK_NULL_VOID(pipeline);
283     auto theme = pipeline->GetTheme<SelectTheme>();
284     CHECK_NULL_VOID(theme);
285 
286     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
287     CHECK_NULL_VOID(textProps);
288     textProps->UpdateTextColor(theme->GetDisabledFontColor());
289     text_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
290 
291     auto spinnerLayoutProperty = spinner_->GetLayoutProperty<ImageLayoutProperty>();
292     CHECK_NULL_VOID(spinnerLayoutProperty);
293 
294     ImageSourceInfo imageSourceInfo = spinnerLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo());
295     auto iconTheme = pipeline->GetTheme<IconTheme>();
296     CHECK_NULL_VOID(iconTheme);
297     auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::SPINNER_DISABLE);
298     imageSourceInfo.SetSrc(iconPath);
299     if (imageSourceInfo.IsSvg()) {
300         imageSourceInfo.SetFillColor(theme->GetDisabledSpinnerColor());
301     }
302     spinnerLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
303     auto spinnerRenderProperty = spinner_->GetPaintProperty<ImageRenderProperty>();
304     CHECK_NULL_VOID(spinnerRenderProperty);
305     spinnerRenderProperty->UpdateSvgFillColor(theme->GetDisabledSpinnerColor());
306     spinner_->MarkModifyDone();
307 }
308 
SetSelected(int32_t index)309 void SelectPattern::SetSelected(int32_t index)
310 {
311     // if option is already selected, do nothing
312     if (index == selected_) {
313         return;
314     }
315     if (index >= options_.size() || index < 0) {
316         LOGW("newly selected index invalid");
317         selected_ = -1;
318         ResetOptionProps();
319         return;
320     }
321     UpdateLastSelectedProps(index);
322     selected_ = index;
323 }
324 
AddOptionNode(const RefPtr<FrameNode> & option)325 void SelectPattern::AddOptionNode(const RefPtr<FrameNode>& option)
326 {
327     CHECK_NULL_VOID(option);
328     options_.push_back(option);
329 }
330 
BuildChild()331 void SelectPattern::BuildChild()
332 {
333     // get theme from SelectThemeManager
334     auto pipeline = PipelineBase::GetCurrentContext();
335     CHECK_NULL_VOID(pipeline);
336     auto theme = pipeline->GetTheme<SelectTheme>();
337 
338     auto select = GetHost();
339 
340     bool hasRowNode = HasRowNode();
341     bool hasTextNode = HasTextNode();
342     bool hasSpinnerNode = HasSpinnerNode();
343     auto rowId = GetRowId();
344     auto textId = GetTextId();
345     auto spinnerId = GetSpinnerId();
346 
347     auto row = FrameNode::GetOrCreateFrameNode(
348         V2::ROW_ETS_TAG, rowId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(false); });
349     CHECK_NULL_VOID(row);
350     row->SetInternal();
351     auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
352     CHECK_NULL_VOID(rowProps);
353     rowProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
354     rowProps->UpdateCrossAxisAlign(FlexAlign::CENTER);
355     rowProps->UpdateFlexDirection(FlexDirection::ROW);
356     rowProps->UpdateSpace(theme->GetContentSpinnerPadding());
357     text_ =
358         FrameNode::GetOrCreateFrameNode(V2::TEXT_ETS_TAG, textId, []() { return AceType::MakeRefPtr<TextPattern>(); });
359     CHECK_NULL_VOID(text_);
360     text_->SetInternal();
361     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
362     CHECK_NULL_VOID(textProps);
363     InitTextProps(textProps, theme);
364 
365     spinner_ = FrameNode::GetOrCreateFrameNode(
366         V2::IMAGE_ETS_TAG, spinnerId, []() { return AceType::MakeRefPtr<ImagePattern>(); });
367     CHECK_NULL_VOID(spinner_);
368     spinner_->SetInternal();
369     auto iconTheme = pipeline->GetTheme<IconTheme>();
370     CHECK_NULL_VOID(iconTheme);
371     InitSpinner(spinner_, iconTheme, theme);
372 
373     // mount triangle and text
374     text_->MarkModifyDone();
375     if (!hasTextNode) {
376         text_->MountToParent(row);
377     }
378     if (!hasSpinnerNode) {
379         spinner_->MountToParent(row);
380     }
381     spinner_->MarkModifyDone();
382     if (!hasRowNode) {
383         row->MountToParent(select);
384     }
385     row->MarkModifyDone();
386     row->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
387 
388     // set bgColor and border
389     auto renderContext = select->GetRenderContext();
390     CHECK_NULL_VOID(renderContext);
391     renderContext->UpdateBackgroundColor(theme->GetBackgroundColor());
392     renderContext->SetClipToFrame(true);
393     BorderRadiusProperty border;
394     border.SetRadius(theme->GetSelectBorderRadius());
395     renderContext->UpdateBorderRadius(border);
396 }
397 
SetValue(const std::string & value)398 void SelectPattern::SetValue(const std::string& value)
399 {
400     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
401     CHECK_NULL_VOID(props);
402     props->UpdateContent(value);
403 }
404 
SetFontSize(const Dimension & value)405 void SelectPattern::SetFontSize(const Dimension& value)
406 {
407     if (value.IsNegative()) {
408         return;
409     }
410     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
411     CHECK_NULL_VOID(props);
412     props->UpdateFontSize(value);
413 }
414 
SetItalicFontStyle(const Ace::FontStyle & value)415 void SelectPattern::SetItalicFontStyle(const Ace::FontStyle& value)
416 {
417     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
418     CHECK_NULL_VOID(props);
419     props->UpdateItalicFontStyle(value);
420 }
421 
SetFontWeight(const FontWeight & value)422 void SelectPattern::SetFontWeight(const FontWeight& value)
423 {
424     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
425     CHECK_NULL_VOID(props);
426     props->UpdateFontWeight(value);
427 }
428 
SetFontFamily(const std::vector<std::string> & value)429 void SelectPattern::SetFontFamily(const std::vector<std::string>& value)
430 {
431     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
432     CHECK_NULL_VOID(props);
433     props->UpdateFontFamily(value);
434 }
435 
SetFontColor(const Color & color)436 void SelectPattern::SetFontColor(const Color& color)
437 {
438     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
439     CHECK_NULL_VOID(props);
440     props->UpdateTextColor(color);
441     auto context = text_->GetRenderContext();
442     context->UpdateForegroundColor(color);
443     context->UpdateForegroundColorFlag(false);
444     context->ResetForegroundColorStrategy();
445 }
446 
SetOptionBgColor(const Color & color)447 void SelectPattern::SetOptionBgColor(const Color& color)
448 {
449     optionBgColor_ = color;
450     for (size_t i = 0; i < options_.size(); ++i) {
451         if (i == selected_ && selectedBgColor_.has_value()) {
452             continue;
453         }
454         auto pattern = options_[i]->GetPattern<OptionPattern>();
455         CHECK_NULL_VOID(pattern);
456         pattern->SetBgColor(color);
457     }
458 }
459 
SetOptionFontSize(const Dimension & value)460 void SelectPattern::SetOptionFontSize(const Dimension& value)
461 {
462     optionFont_.FontSize = value;
463     for (size_t i = 0; i < options_.size(); ++i) {
464         if (i == selected_ && selectedFont_.FontSize.has_value()) {
465             continue;
466         }
467         auto pattern = options_[i]->GetPattern<OptionPattern>();
468         CHECK_NULL_VOID(pattern);
469         pattern->SetFontSize(value);
470     }
471 }
472 
SetOptionItalicFontStyle(const Ace::FontStyle & value)473 void SelectPattern::SetOptionItalicFontStyle(const Ace::FontStyle& value)
474 {
475     optionFont_.FontStyle = value;
476     for (size_t i = 0; i < options_.size(); ++i) {
477         if (i == selected_ && selectedFont_.FontStyle.has_value()) {
478             continue;
479         }
480         auto pattern = options_[i]->GetPattern<OptionPattern>();
481         CHECK_NULL_VOID(pattern);
482         pattern->SetItalicFontStyle(value);
483     }
484 }
485 
SetOptionFontWeight(const FontWeight & value)486 void SelectPattern::SetOptionFontWeight(const FontWeight& value)
487 {
488     optionFont_.FontWeight = value;
489     for (size_t i = 0; i < options_.size(); ++i) {
490         if (i == selected_ && selectedFont_.FontWeight.has_value()) {
491             continue;
492         }
493         auto pattern = options_[i]->GetPattern<OptionPattern>();
494         CHECK_NULL_VOID(pattern);
495         pattern->SetFontWeight(value);
496     }
497 }
498 
SetOptionFontFamily(const std::vector<std::string> & value)499 void SelectPattern::SetOptionFontFamily(const std::vector<std::string>& value)
500 {
501     optionFont_.FontFamily = value;
502     for (size_t i = 0; i < options_.size(); ++i) {
503         if (i == selected_ && selectedFont_.FontFamily.has_value()) {
504             continue;
505         }
506         auto pattern = options_[i]->GetPattern<OptionPattern>();
507         CHECK_NULL_VOID(pattern);
508         pattern->SetFontFamily(value);
509     }
510 }
511 
SetOptionFontColor(const Color & color)512 void SelectPattern::SetOptionFontColor(const Color& color)
513 {
514     optionFont_.FontColor = color;
515     for (size_t i = 0; i < options_.size(); ++i) {
516         if (i == selected_ && selectedFont_.FontColor.has_value()) {
517             continue;
518         }
519         auto pattern = options_[i]->GetPattern<OptionPattern>();
520         CHECK_NULL_VOID(pattern);
521         pattern->SetFontColor(color);
522     }
523 }
524 
525 // set props of option node when selected
SetSelectedOptionBgColor(const Color & color)526 void SelectPattern::SetSelectedOptionBgColor(const Color& color)
527 {
528     selectedBgColor_ = color;
529     if (selected_ >= 0 && selected_ < options_.size()) {
530         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
531         CHECK_NULL_VOID(pattern);
532         pattern->SetBgColor(color);
533     }
534 }
535 
SetSelectedOptionFontSize(const Dimension & value)536 void SelectPattern::SetSelectedOptionFontSize(const Dimension& value)
537 {
538     selectedFont_.FontSize = value;
539     if (selected_ >= 0 && selected_ < options_.size()) {
540         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
541         CHECK_NULL_VOID(pattern);
542         pattern->SetFontSize(value);
543     }
544 }
545 
SetSelectedOptionItalicFontStyle(const Ace::FontStyle & value)546 void SelectPattern::SetSelectedOptionItalicFontStyle(const Ace::FontStyle& value)
547 {
548     selectedFont_.FontStyle = value;
549     if (selected_ >= 0 && selected_ < options_.size()) {
550         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
551         CHECK_NULL_VOID(pattern);
552         pattern->SetItalicFontStyle(value);
553     }
554 }
555 
SetSelectedOptionFontWeight(const FontWeight & value)556 void SelectPattern::SetSelectedOptionFontWeight(const FontWeight& value)
557 {
558     selectedFont_.FontWeight = value;
559     if (selected_ >= 0 && selected_ < options_.size()) {
560         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
561         CHECK_NULL_VOID(pattern);
562         pattern->SetFontWeight(value);
563     }
564 }
565 
SetSelectedOptionFontFamily(const std::vector<std::string> & value)566 void SelectPattern::SetSelectedOptionFontFamily(const std::vector<std::string>& value)
567 {
568     selectedFont_.FontFamily = value;
569     if (selected_ >= 0 && selected_ < options_.size()) {
570         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
571         CHECK_NULL_VOID(pattern);
572         pattern->SetFontFamily(value);
573     }
574 }
575 
SetSelectedOptionFontColor(const Color & color)576 void SelectPattern::SetSelectedOptionFontColor(const Color& color)
577 {
578     selectedFont_.FontColor = color;
579     if (selected_ >= 0 && selected_ < options_.size()) {
580         auto pattern = options_[selected_]->GetPattern<OptionPattern>();
581         CHECK_NULL_VOID(pattern);
582         pattern->SetFontColor(color);
583     }
584 }
585 
GetOptions()586 const std::vector<RefPtr<FrameNode>>& SelectPattern::GetOptions()
587 {
588     return options_;
589 }
590 
ResetOptionProps()591 void SelectPattern::ResetOptionProps()
592 {
593     auto pipeline = PipelineBase::GetCurrentContext();
594     CHECK_NULL_VOID(pipeline);
595     auto selectTheme = pipeline->GetTheme<SelectTheme>();
596     auto textTheme = pipeline->GetTheme<TextTheme>();
597     CHECK_NULL_VOID(selectTheme && textTheme);
598 
599     for (const auto& option : options_) {
600         auto pattern = option->GetPattern<OptionPattern>();
601         CHECK_NULL_VOID(pattern);
602         pattern->SetBgColor(optionBgColor_.value_or(selectTheme->GetBackgroundColor()));
603         pattern->SetFontSize(optionFont_.FontSize.value_or(selectTheme->GetMenuFontSize()));
604         pattern->SetItalicFontStyle(optionFont_.FontStyle.value_or(textTheme->GetTextStyle().GetFontStyle()));
605         pattern->SetFontWeight(optionFont_.FontWeight.value_or(textTheme->GetTextStyle().GetFontWeight()));
606         pattern->SetFontFamily(optionFont_.FontFamily.value_or(textTheme->GetTextStyle().GetFontFamilies()));
607         pattern->SetFontColor(optionFont_.FontColor.value_or(selectTheme->GetMenuFontColor()));
608     }
609 }
610 
UpdateLastSelectedProps(int32_t index)611 void SelectPattern::UpdateLastSelectedProps(int32_t index)
612 {
613     CHECK_NULL_VOID(options_[index]);
614     auto newSelected = options_[index]->GetPattern<OptionPattern>();
615     CHECK_NULL_VOID(newSelected);
616     // set lastSelected option props back to default (unselected) values
617     if (selected_ >= 0 && selected_ < options_.size()) {
618         CHECK_NULL_VOID(options_[selected_]);
619         auto lastSelected = options_[selected_]->GetPattern<OptionPattern>();
620         CHECK_NULL_VOID(lastSelected);
621 
622         lastSelected->SetFontColor(newSelected->GetFontColor());
623         lastSelected->SetFontFamily(newSelected->GetFontFamily());
624         lastSelected->SetFontSize(newSelected->GetFontSize());
625         lastSelected->SetItalicFontStyle(newSelected->GetItalicFontStyle());
626         lastSelected->SetFontWeight(newSelected->GetFontWeight());
627 
628         lastSelected->SetBgColor(newSelected->GetBgColor());
629         lastSelected->UpdateNextNodeDivider(true);
630         if (selected_ != 0) {
631             auto lastSelectedNode = lastSelected->GetHost();
632             CHECK_NULL_VOID(lastSelectedNode);
633             auto lastSelectedPros = lastSelectedNode->GetPaintProperty<OptionPaintProperty>();
634             CHECK_NULL_VOID(lastSelectedPros);
635             lastSelectedPros->UpdateNeedDivider(true);
636         }
637         options_[selected_]->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
638     }
639 }
640 
641 // update selected option props
UpdateSelectedProps(int32_t index)642 void SelectPattern::UpdateSelectedProps(int32_t index)
643 {
644     CHECK_NULL_VOID(options_[index]);
645     auto newSelected = options_[index]->GetPattern<OptionPattern>();
646     CHECK_NULL_VOID(newSelected);
647 
648     // set newSelected props
649     auto host = GetHost();
650     CHECK_NULL_VOID(host);
651     auto pipeline = host->GetContext();
652     CHECK_NULL_VOID(pipeline);
653     auto theme = pipeline->GetTheme<SelectTheme>();
654     CHECK_NULL_VOID(theme);
655     if (selectedFont_.FontColor.has_value()) {
656         newSelected->SetFontColor(selectedFont_.FontColor.value());
657     } else {
658         auto selectedColorText = theme->GetSelectedColorText();
659         newSelected->SetFontColor(selectedColorText);
660     }
661     if (selectedFont_.FontFamily.has_value()) {
662         newSelected->SetFontFamily(selectedFont_.FontFamily.value());
663     }
664     if (selectedFont_.FontSize.has_value()) {
665         newSelected->SetFontSize(selectedFont_.FontSize.value());
666     }
667     if (selectedFont_.FontStyle.has_value()) {
668         newSelected->SetItalicFontStyle(selectedFont_.FontStyle.value());
669     }
670     if (selectedFont_.FontWeight.has_value()) {
671         newSelected->SetFontWeight(selectedFont_.FontWeight.value());
672     }
673     if (selectedBgColor_.has_value()) {
674         newSelected->SetBgColor(selectedBgColor_.value());
675     } else {
676         auto selectedColor = theme->GetSelectedColor();
677         newSelected->SetBgColor(selectedColor);
678     }
679     newSelected->UpdateNextNodeDivider(false);
680     auto newSelectedNode = newSelected->GetHost();
681     CHECK_NULL_VOID(newSelectedNode);
682     auto newSelectedPros = newSelectedNode->GetPaintProperty<OptionPaintProperty>();
683     CHECK_NULL_VOID(newSelectedPros);
684     newSelectedPros->UpdateNeedDivider(false);
685 }
686 
UpdateText(int32_t index)687 void SelectPattern::UpdateText(int32_t index)
688 {
689     // update text to selected option's text
690     CHECK_NULL_VOID(text_);
691     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
692     CHECK_NULL_VOID(textProps);
693     if (index >= options_.size() || index < 0) {
694         return;
695     }
696     auto newSelected = options_[index]->GetPattern<OptionPattern>();
697     CHECK_NULL_VOID(newSelected);
698     textProps->UpdateContent(newSelected->GetText());
699     text_->MarkModifyDone();
700     LOGD("new text = %s", newSelected->GetText().c_str());
701     auto host = GetHost();
702     CHECK_NULL_VOID(host);
703     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
704 }
705 
InitTextProps(const RefPtr<TextLayoutProperty> & textProps,const RefPtr<SelectTheme> & theme)706 void SelectPattern::InitTextProps(const RefPtr<TextLayoutProperty>& textProps, const RefPtr<SelectTheme>& theme)
707 {
708     textProps->UpdateFontSize(theme->GetFontSize());
709     textProps->UpdateFontWeight(FontWeight::MEDIUM);
710     textProps->UpdateTextColor(theme->GetFontColor());
711     textProps->UpdateTextDecoration(theme->GetTextDecoration());
712     textProps->UpdateTextOverflow(TextOverflow::ELLIPSIS);
713     textProps->UpdateMaxLines(SELECT_ITSELF_TEXT_LINES);
714     MarginProperty margin;
715     margin.left = CalcLength(theme->GetContentMargin());
716     textProps->UpdateMargin(margin);
717 }
718 
InitSpinner(const RefPtr<FrameNode> & spinner,const RefPtr<IconTheme> & iconTheme,const RefPtr<SelectTheme> & selectTheme)719 void SelectPattern::InitSpinner(
720     const RefPtr<FrameNode>& spinner, const RefPtr<IconTheme>& iconTheme, const RefPtr<SelectTheme>& selectTheme)
721 {
722     ImageSourceInfo imageSourceInfo;
723     auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::SPINNER);
724     imageSourceInfo.SetSrc(iconPath);
725     imageSourceInfo.SetFillColor(selectTheme->GetSpinnerColor());
726 
727     auto spinnerLayoutProperty = spinner->GetLayoutProperty<ImageLayoutProperty>();
728     CHECK_NULL_VOID(spinnerLayoutProperty);
729     spinnerLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
730     CalcSize idealSize = { CalcLength(selectTheme->GetSpinnerWidth()), CalcLength(selectTheme->GetSpinnerHeight()) };
731     MeasureProperty layoutConstraint;
732     layoutConstraint.selfIdealSize = idealSize;
733     spinnerLayoutProperty->UpdateCalcLayoutProperty(layoutConstraint);
734     MarginProperty margin;
735     margin.right = CalcLength(selectTheme->GetContentMargin());
736     spinnerLayoutProperty->UpdateMargin(margin);
737 
738     auto spinnerRenderProperty = spinner->GetPaintProperty<ImageRenderProperty>();
739     CHECK_NULL_VOID(spinnerRenderProperty);
740     spinnerRenderProperty->UpdateSvgFillColor(selectTheme->GetSpinnerColor());
741 }
742 
743 // XTS inspector code
ToJsonValue(std::unique_ptr<JsonValue> & json) const744 void SelectPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
745 {
746     json->Put("options", InspectorGetOptions().c_str());
747     json->Put("selected", std::to_string(selected_).c_str());
748 
749     auto host = GetHost();
750     CHECK_NULL_VOID(host);
751     if (!host->GetChildren().empty()) {
752         auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
753         CHECK_NULL_VOID(row);
754         auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
755         CHECK_NULL_VOID(rowProps);
756         json->Put("space", rowProps->GetSpace()->ToString().c_str());
757 
758         if (rowProps->GetFlexDirection().value() == FlexDirection::ROW) {
759             json->Put("arrowPosition", "ArrowPosition.END");
760         } else {
761             json->Put("arrowPosition", "ArrowPosition.START");
762         }
763     }
764 
765     auto props = text_->GetLayoutProperty<TextLayoutProperty>();
766     CHECK_NULL_VOID(props);
767     json->Put("value", props->GetContent().value_or("").c_str());
768     Color fontColor = props->GetTextColor().value_or(Color::BLACK);
769     json->Put("fontColor", fontColor.ColorToString().c_str());
770     json->Put("font", props->InspectorGetTextFont().c_str());
771 
772     json->Put("selectedOptionBgColor", selectedBgColor_->ColorToString().c_str());
773     json->Put("selectedOptionFont", InspectorGetSelectedFont().c_str());
774     json->Put("selectedOptionFontColor", selectedFont_.FontColor.value_or(Color::BLACK).ColorToString().c_str());
775 
776     if (options_.empty()) {
777         json->Put("optionBgColor", "");
778         json->Put("optionFont", "");
779         json->Put("optionFontColor", "");
780     } else {
781         auto optionPattern = options_[0]->GetPattern<OptionPattern>();
782         CHECK_NULL_VOID(optionPattern);
783         json->Put("optionBgColor", optionPattern->GetBgColor().ColorToString().c_str());
784         json->Put("optionFont", optionPattern->InspectorGetFont().c_str());
785         json->Put("optionFontColor", optionPattern->GetFontColor().ColorToString().c_str());
786     }
787     ToJsonOptionAlign(json);
788 }
789 
ToJsonOptionAlign(std::unique_ptr<JsonValue> & json) const790 void SelectPattern::ToJsonOptionAlign(std::unique_ptr<JsonValue>& json) const
791 {
792     auto optionAlignJson = JsonUtil::Create(true);
793     std::string alignTypeString = "MenuAlignType.Start";
794     if (menuAlign_.alignType == MenuAlignType::START) {
795         alignTypeString = "MenuAlignType.Start";
796     } else if (menuAlign_.alignType == MenuAlignType::CENTER) {
797         alignTypeString = "MenuAlignType.Center";
798     } else if (menuAlign_.alignType == MenuAlignType::END) {
799         alignTypeString = "MenuAlignType.End";
800     }
801     optionAlignJson->Put("alignType", alignTypeString.c_str());
802 
803     auto offsetValueJson = JsonUtil::Create(true);
804     offsetValueJson->Put("dX", menuAlign_.offset.GetX().Value());
805     offsetValueJson->Put("dY", menuAlign_.offset.GetY().Value());
806     optionAlignJson->Put("offset", offsetValueJson);
807 
808     json->Put("menuAlign", optionAlignJson);
809 }
810 
InspectorGetOptions() const811 std::string SelectPattern::InspectorGetOptions() const
812 {
813     auto jsonValue = JsonUtil::Create(true);
814     auto jsonOptions = JsonUtil::CreateArray(true);
815     for (size_t i = 0; i < options_.size(); ++i) {
816         auto temp = JsonUtil::Create(true);
817         auto optionPattern = options_[i]->GetPattern<OptionPattern>();
818         temp->Put("value", optionPattern->GetText().c_str());
819         temp->Put("icon", optionPattern->GetIcon().c_str());
820         auto index = std::to_string(i);
821         jsonOptions->Put(index.c_str(), temp);
822     }
823     jsonValue->Put("options", jsonOptions);
824     return jsonValue->ToString();
825 }
826 
InspectorGetSelectedFont() const827 std::string SelectPattern::InspectorGetSelectedFont() const
828 {
829     TextStyle font;
830     if (selectedFont_.FontFamily.has_value()) {
831         font.SetFontFamilies(selectedFont_.FontFamily.value());
832     }
833     if (selectedFont_.FontSize.has_value()) {
834         font.SetFontSize(selectedFont_.FontSize.value());
835     }
836     if (selectedFont_.FontStyle.has_value()) {
837         font.SetFontStyle(selectedFont_.FontStyle.value());
838     }
839     if (selectedFont_.FontWeight.has_value()) {
840         font.SetFontWeight(selectedFont_.FontWeight.value());
841     }
842     return V2::GetTextStyleInJson(font);
843 }
844 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)845 bool SelectPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
846 {
847     auto geometryNode = dirty->GetGeometryNode();
848     CHECK_NULL_RETURN(geometryNode, false);
849     SetSelectSize(geometryNode->GetFrameSize());
850     if (isColorConfigurationUpdate_ && GetSelected() >= 0) {
851         auto props = text_->GetLayoutProperty<TextLayoutProperty>();
852         CHECK_NULL_RETURN(props, false);
853         props->UpdateContent(options_[GetSelected()]->GetPattern<OptionPattern>()->GetText());
854         isColorConfigurationUpdate_ = false;
855     }
856     return false;
857 }
858 
SetSpace(const Dimension & value)859 void SelectPattern::SetSpace(const Dimension& value)
860 {
861     auto host = GetHost();
862     CHECK_NULL_VOID(host);
863     if (!host->GetChildren().empty()) {
864         auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
865         auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
866         rowProps->UpdateSpace(value);
867         row->MarkModifyDone();
868         row->MarkDirtyNode();
869     }
870 }
871 
SetArrowPosition(const ArrowPosition value)872 void SelectPattern::SetArrowPosition(const ArrowPosition value)
873 {
874     auto host = GetHost();
875     CHECK_NULL_VOID(host);
876     if (!host->GetChildren().empty()) {
877         auto row = FrameNode::GetFrameNode(host->GetFirstChild()->GetTag(), host->GetFirstChild()->GetId());
878         auto rowProps = row->GetLayoutProperty<FlexLayoutProperty>();
879 
880         if (value == ArrowPosition::END) {
881             rowProps->UpdateFlexDirection(FlexDirection::ROW);
882         } else {
883             rowProps->UpdateFlexDirection(FlexDirection::ROW_REVERSE);
884         }
885         row->MarkModifyDone();
886         row->MarkDirtyNode();
887     }
888 }
889 
GetValue()890 std::string SelectPattern::GetValue()
891 {
892     CHECK_NULL_RETURN(text_, "");
893     auto textProps = text_->GetLayoutProperty<TextLayoutProperty>();
894     CHECK_NULL_RETURN(textProps, "");
895     return textProps->GetContentValue("");
896 }
897 
SetMenuAlign(const MenuAlign & menuAlign)898 void SelectPattern::SetMenuAlign(const MenuAlign& menuAlign)
899 {
900     menuAlign_ = menuAlign;
901     auto menu = GetMenuNode();
902     CHECK_NULL_VOID(menu);
903     auto menuLayoutProps = menu->GetLayoutProperty<MenuLayoutProperty>();
904     CHECK_NULL_VOID(menuLayoutProps);
905     menuLayoutProps->UpdateAlignType(menuAlign.alignType);
906     menuLayoutProps->UpdateOffset(menuAlign.offset);
907 }
908 
ProvideRestoreInfo()909 std::string SelectPattern::ProvideRestoreInfo()
910 {
911     auto jsonObj = JsonUtil::Create(true);
912     jsonObj->Put("selected", selected_);
913     jsonObj->Put("isSelected", isSelected_);
914     return jsonObj->ToString();
915 }
916 
OnRestoreInfo(const std::string & restoreInfo)917 void SelectPattern::OnRestoreInfo(const std::string& restoreInfo)
918 {
919     auto info = JsonUtil::ParseJsonString(restoreInfo);
920     if (!info->IsValid() || !info->IsObject()) {
921         return;
922     }
923     auto jsonIsOn = info->GetValue("selected");
924     auto jsonIsSelect = info->GetValue("isSelected");
925     if (jsonIsSelect->GetBool()) {
926         SetSelected(jsonIsOn->GetInt());
927         UpdateText(jsonIsOn->GetInt());
928     }
929 }
930 
OnColorConfigurationUpdate()931 void SelectPattern::OnColorConfigurationUpdate()
932 {
933     isColorConfigurationUpdate_ = true;
934     auto host = GetHost();
935     auto pipeline = PipelineBase::GetCurrentContext();
936     CHECK_NULL_VOID(pipeline);
937     auto selectTheme = pipeline->GetTheme<SelectTheme>();
938     CHECK_NULL_VOID(selectTheme);
939 
940     auto pattern = host->GetPattern<SelectPattern>();
941     auto menuNode = pattern->GetMenuNode();
942     CHECK_NULL_VOID(menuNode);
943     auto menuPattern = menuNode->GetPattern<MenuPattern>();
944     CHECK_NULL_VOID(menuPattern);
945 
946     auto renderContext = menuNode->GetRenderContext();
947     renderContext->UpdateBackgroundColor(selectTheme->GetBackgroundColor());
948 
949     auto optionNode = menuPattern->GetOptions();
950     for (auto child : optionNode) {
951         auto optionsPattern = child->GetPattern<OptionPattern>();
952         optionsPattern->SetFontColor(selectTheme->GetFontColor());
953 
954         child->MarkModifyDone();
955         child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
956     }
957     SetOptionBgColor(selectTheme->GetBackgroundColor());
958     host->SetNeedCallChildrenUpdate(false);
959 }
960 } // namespace OHOS::Ace::NG
961