• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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