1 /*
2 * Copyright (c) 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_layout_algorithm.h"
17
18 #include "core/components_ng/pattern/flex/flex_layout_property.h"
19 #include "core/components_ng/pattern/select/select_pattern.h"
20 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
21 #include "core/components_ng/pattern/menu/menu_theme.h"
22
23 namespace OHOS::Ace::NG {
24 namespace {
25 constexpr float MIN_SPACE = 8.0f;
26 constexpr float MIN_CHAR_VAL = 2.0f;
27 constexpr float SMALL_MIN_CHAR_VAL = 1.0f;
28 } // namespace
Measure(LayoutWrapper * layoutWrapper)29 void SelectLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
30 {
31 NeedAgingUpdateParams(layoutWrapper);
32 auto layoutProps = layoutWrapper->GetLayoutProperty();
33 CHECK_NULL_VOID(layoutProps);
34 auto childConstraint = layoutProps->CreateChildConstraint();
35 // Measure child row to get row height and width.
36 auto rowWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
37 CHECK_NULL_VOID(rowWrapper);
38 auto rowProps = DynamicCast<FlexLayoutProperty>(rowWrapper->GetLayoutProperty());
39 CHECK_NULL_VOID(rowProps);
40 auto space = static_cast<float>(rowProps->GetSpaceValue(Dimension()).ConvertToPx());
41 auto minSpace = Dimension(MIN_SPACE, DimensionUnit::VP);
42 if (space < minSpace.ConvertToPx()) {
43 space = minSpace.ConvertToPx();
44 rowProps->UpdateSpace(minSpace);
45 }
46 auto host = layoutProps->GetHost();
47 CHECK_NULL_VOID(host);
48 auto pipeline = host->GetContext();
49 CHECK_NULL_VOID(pipeline);
50 auto theme = pipeline->GetTheme<SelectTheme>();
51 CHECK_NULL_VOID(theme);
52 UpdateMargin(layoutWrapper, theme);
53 auto spinnerSize = MeasureAndGetSize(rowWrapper->GetOrCreateChildByIndex(1), childConstraint);
54 childConstraint.maxSize.MinusWidth(spinnerSize.Width() + space);
55 auto textWrapper = rowWrapper->GetOrCreateChildByIndex(0);
56 CHECK_NULL_VOID(textWrapper);
57 std::optional<float> maxWidth = std::nullopt;
58 if (childConstraint.parentIdealSize.Width().has_value()) {
59 // Make the spinner icon layout at the right end
60 maxWidth = childConstraint.parentIdealSize.Width().value() - spinnerSize.Width() - space;
61 }
62 auto textSize = MeasureSelectText(textWrapper, childConstraint, maxWidth);
63
64 auto rowGeometry = rowWrapper->GetGeometryNode();
65 CHECK_NULL_VOID(rowGeometry);
66
67 auto rowWidth = textSize.Width() + space + spinnerSize.Width();
68 auto rowHeight = std::max(textSize.Height(), spinnerSize.Height());
69 rowGeometry->SetFrameSize(SizeF(rowWidth, rowHeight));
70 rowWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_LAYOUT);
71 float defaultHeight = MeasureAndGetDefaultHeight(layoutProps, theme);
72 layoutWrapper->GetGeometryNode()->SetContentSize(
73 SizeF(rowWidth, rowHeight > defaultHeight ? rowHeight : defaultHeight));
74
75 // Measure same as box, base on the child row.
76 BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
77 }
78
MeasureSelectText(RefPtr<LayoutWrapper> textWrapper,const LayoutConstraintF & childConstraint,std::optional<float> maxWidth)79 SizeF SelectLayoutAlgorithm::MeasureSelectText(
80 RefPtr<LayoutWrapper> textWrapper, const LayoutConstraintF& childConstraint, std::optional<float> maxWidth)
81 {
82 auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
83 CHECK_NULL_RETURN(textLayoutProperty, SizeF());
84 auto textLayoutConstraint = textLayoutProperty->CreateContentConstraint();
85 auto contentValue = textLayoutProperty->GetContentValue(u"");
86
87 SizeF textSize;
88 if (!contentValue.empty()) {
89 textSize = MeasureAndGetSize(textWrapper, childConstraint);
90 }
91 if (maxWidth.has_value()) {
92 // Make the spinner icon layout at the right end
93 textSize.SetWidth(maxWidth.value());
94 }
95 auto fontSize = textLayoutProperty->GetFontSizeValue(Dimension()).ConvertToPx();
96 bool isTextMin = false;
97 MeasureAndGetTextSize(fontSize, textSize, isTextMin);
98 if (contentValue.empty()) {
99 auto textGeometry = textWrapper->GetGeometryNode();
100 CHECK_NULL_RETURN(textGeometry, SizeF());
101 auto textMargin = textGeometry->GetMarginFrameSize() - textGeometry->GetFrameSize();
102 textGeometry->SetFrameSize(textSize - textMargin);
103 } else if (isTextMin || childConstraint.parentIdealSize.Width().has_value()) {
104 textLayoutProperty->UpdateMarginSelfIdealSize(textSize);
105 textLayoutConstraint.selfIdealSize = OptionalSize<float>(textSize.Width(), textSize.Height());
106 textLayoutConstraint.maxSize.SetSizeT(textSize);
107 textWrapper->Measure(textLayoutConstraint);
108 }
109 return textSize;
110 }
111
MeasureAndGetTextSize(double fontSize,SizeF & textSize,bool & isTextMin)112 void SelectLayoutAlgorithm::MeasureAndGetTextSize(double fontSize, SizeF& textSize, bool& isTextMin)
113 {
114 float minCharVal =
115 Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? MIN_CHAR_VAL : SMALL_MIN_CHAR_VAL;
116 if (textSize.Width() < fontSize * minCharVal) {
117 textSize.SetWidth(fontSize * minCharVal);
118 isTextMin = true;
119 }
120 }
121
MeasureAndGetDefaultHeight(RefPtr<LayoutProperty> layoutProps,RefPtr<SelectTheme> theme)122 float SelectLayoutAlgorithm::MeasureAndGetDefaultHeight(RefPtr<LayoutProperty> layoutProps, RefPtr<SelectTheme> theme)
123 {
124 float defaultHeight = 0.0f;
125 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
126 auto host = layoutProps->GetHost();
127 auto selectPattern = host->GetPattern<SelectPattern>();
128 defaultHeight =
129 static_cast<float>(theme->GetSelectDefaultHeight(selectPattern->GetControlSize()).ConvertToPx());
130 } else {
131 defaultHeight = static_cast<float>(theme->GetSelectDefaultHeight().ConvertToPx());
132 }
133 return defaultHeight;
134 }
135
MeasureAndGetSize(const RefPtr<LayoutWrapper> & childLayoutWrapper,const LayoutConstraintF & constraint)136 SizeF SelectLayoutAlgorithm::MeasureAndGetSize(
137 const RefPtr<LayoutWrapper>& childLayoutWrapper, const LayoutConstraintF& constraint)
138 {
139 CHECK_NULL_RETURN(childLayoutWrapper, SizeF());
140 childLayoutWrapper->Measure(constraint);
141 auto geometry = childLayoutWrapper->GetGeometryNode();
142 CHECK_NULL_RETURN(geometry, SizeF());
143 return geometry->GetMarginFrameSize();
144 }
145
NeedAgingUpdateParams(LayoutWrapper * layoutWrapper)146 void SelectLayoutAlgorithm::NeedAgingUpdateParams(LayoutWrapper* layoutWrapper)
147 {
148 auto host = layoutWrapper->GetHostNode();
149 CHECK_NULL_VOID(host);
150 auto context = host->GetContext();
151 CHECK_NULL_VOID(context);
152 if (fontScale_ == context->GetFontScale()) {
153 return;
154 }
155 fontScale_ = context->GetFontScale();
156 auto menuTheme = context->GetTheme<NG::MenuTheme>();
157 CHECK_NULL_VOID(menuTheme);
158 auto pattern = host->GetPattern<SelectPattern>();
159 CHECK_NULL_VOID(pattern);
160 auto options = pattern->GetOptions();
161 if (NearEqual(fontScale_, menuTheme->GetBigFontSizeScale()) ||
162 NearEqual(fontScale_, menuTheme->GetLargeFontSizeScale()) ||
163 NearEqual(fontScale_, menuTheme->GetMaxFontSizeScale())) {
164 UpdateOptionsMaxLines(options, menuTheme->GetTextMaxLines());
165 } else {
166 UpdateOptionsMaxLines(options, 1);
167 }
168 }
169
UpdateOptionsMaxLines(const std::vector<RefPtr<FrameNode>> & options,int32_t maxLines)170 void SelectLayoutAlgorithm::UpdateOptionsMaxLines(const std::vector<RefPtr<FrameNode>>& options, int32_t maxLines)
171 {
172 for (auto child :options) {
173 auto optionPattern = child->GetPattern<MenuItemPattern>();
174 CHECK_NULL_VOID(optionPattern);
175 auto textNode = AceType::DynamicCast<FrameNode>(optionPattern->GetTextNode());
176 CHECK_NULL_VOID(textNode);
177 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
178 CHECK_NULL_VOID(textLayoutProperty);
179 textLayoutProperty->UpdateMaxLines(maxLines);
180 }
181 }
182
UpdateMargin(LayoutWrapper * layoutWrapper,RefPtr<SelectTheme> theme)183 void SelectLayoutAlgorithm::UpdateMargin(LayoutWrapper* layoutWrapper, RefPtr<SelectTheme> theme)
184 {
185 auto rowWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
186 CHECK_NULL_VOID(rowWrapper);
187 auto spinner = rowWrapper->GetOrCreateChildByIndex(1);
188 auto spinnerLayoutProperty = spinner->GetLayoutProperty();
189 auto layoutProps = layoutWrapper->GetLayoutProperty();
190 auto rowProps = DynamicCast<FlexLayoutProperty>(rowWrapper->GetLayoutProperty());
191 CHECK_NULL_VOID(rowProps);
192 auto arrowStart = rowProps->GetFlexDirection() == FlexDirection::ROW_REVERSE;
193 CHECK_NULL_VOID(layoutProps);
194 auto isRtl = layoutProps->GetNonAutoLayoutDirection() == TextDirection::RTL;
195 MarginProperty spinnerMargin;
196 MarginProperty TextMargin;
197 if (arrowStart ^ isRtl) {
198 spinnerMargin.left = CalcLength(theme->GetContentMargin());
199 spinnerMargin.right = CalcLength();
200 TextMargin.left = CalcLength();
201 TextMargin.right = CalcLength(theme->GetContentMargin());
202 } else {
203 spinnerMargin.left = CalcLength();
204 spinnerMargin.right = CalcLength(theme->GetContentMargin());
205 TextMargin.left = CalcLength(theme->GetContentMargin());
206 TextMargin.right = CalcLength();
207 }
208 spinnerLayoutProperty->UpdateMargin(spinnerMargin);
209 auto textWrapper = rowWrapper->GetOrCreateChildByIndex(0);
210 CHECK_NULL_VOID(textWrapper);
211 auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
212 CHECK_NULL_VOID(textLayoutProperty);
213 textLayoutProperty->UpdateMargin(TextMargin);
214 }
215 } // namespace OHOS::Ace::NG
216