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