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/menu/menu_item/menu_item_layout_algorithm.h"
17
18 #include "core/components_ng/pattern/menu/menu_item/menu_item_pattern.h"
19 #include "core/components_ng/pattern/security_component/security_component_layout_property.h"
20
21 namespace OHOS::Ace::NG {
22 constexpr Dimension ITEM_BOTTOM_TOP_PADDING = 8.0_vp;
23 constexpr int32_t PADDING_MULTIPLE = 2;
Measure(LayoutWrapper * layoutWrapper)24 void MenuItemLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
25 {
26 CHECK_NULL_VOID(layoutWrapper);
27 auto pipeline = PipelineBase::GetCurrentContext();
28 CHECK_NULL_VOID(pipeline);
29 auto theme = pipeline->GetTheme<SelectTheme>();
30 CHECK_NULL_VOID(theme);
31
32 horInterval_ = static_cast<float>(theme->GetMenuItemHorIntervalPadding().ConvertToPx());
33 middleSpace_ = static_cast<float>(theme->GetIconContentPadding().ConvertToPx());
34 auto props = layoutWrapper->GetLayoutProperty();
35 CHECK_NULL_VOID(props);
36
37 auto layoutConstraint = props->GetLayoutConstraint();
38 CHECK_NULL_VOID(layoutConstraint);
39
40 if (isOption_) {
41 MeasureOption(layoutWrapper, theme, props, layoutConstraint);
42 } else {
43 MeasureMenuItem(layoutWrapper, theme, props, layoutConstraint);
44 }
45 }
46
Layout(LayoutWrapper * layoutWrapper)47 void MenuItemLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
48 {
49 CHECK_NULL_VOID(layoutWrapper);
50 auto layoutProperty = layoutWrapper->GetLayoutProperty();
51 CHECK_NULL_VOID(layoutProperty);
52
53 if (isOption_) {
54 LayoutOption(layoutWrapper, layoutProperty);
55 } else {
56 LayoutMenuItem(layoutWrapper, layoutProperty);
57 }
58 }
59
CheckNeedMatchParent(LayoutWrapper * layoutWrapper,std::optional<LayoutConstraintF> & layoutConstraint)60 void MenuItemLayoutAlgorithm::CheckNeedMatchParent(LayoutWrapper* layoutWrapper,
61 std::optional<LayoutConstraintF>& layoutConstraint)
62 {
63 auto menuNode = layoutWrapper->GetHostNode();
64 auto menuItemPattern = menuNode ? menuNode->GetPattern<MenuItemPattern>() : nullptr;
65 SubMenuExpandingMode expandingMode = menuItemPattern ? menuItemPattern->GetExpandingMode()
66 : SubMenuExpandingMode::STACK;
67
68 auto isSubMenu = menuItemPattern ? menuItemPattern->IsSubMenu() : false;
69 auto isEmbedded = menuItemPattern ? menuItemPattern->IsEmbedded() : false;
70 bool matchParent = (expandingMode == SubMenuExpandingMode::STACK && isSubMenu) ||
71 (expandingMode == SubMenuExpandingMode::EMBEDDED && isEmbedded);
72 if (matchParent) {
73 auto width = layoutConstraint->maxSize.Width();
74 layoutConstraint->minSize.SetWidth(width);
75 }
76 }
77
CheckUserHeight(LayoutWrapper * layoutWrapper)78 void MenuItemLayoutAlgorithm::CheckUserHeight(LayoutWrapper* layoutWrapper)
79 {
80 auto props = layoutWrapper->GetLayoutProperty();
81 CHECK_NULL_VOID(props);
82 const auto& calcConstraint = props->GetCalcLayoutConstraint();
83 CHECK_NULL_VOID(calcConstraint);
84 auto layoutConstraint = props->GetLayoutConstraint();
85 CHECK_NULL_VOID(layoutConstraint);
86 if (calcConstraint->selfIdealSize.has_value() &&
87 calcConstraint->selfIdealSize.value().Height().has_value()) {
88 ScaleProperty scaleProperty;
89 if (layoutWrapper->GetGeometryNode() && layoutWrapper->GetGeometryNode()->GetParentLayoutConstraint()) {
90 scaleProperty = layoutWrapper->GetGeometryNode()->GetParentLayoutConstraint()->scaleProperty;
91 } else {
92 scaleProperty = layoutConstraint->scaleProperty;
93 }
94 userHeight_ =
95 ConvertToPx(calcConstraint->selfIdealSize.value().Height()->GetDimension(),
96 scaleProperty, layoutConstraint->percentReference.Height()).value_or(0.0f);
97 }
98 }
99
CalcItemHeight(float leftRowHeight,float rightRowHeight)100 float MenuItemLayoutAlgorithm::CalcItemHeight(float leftRowHeight, float rightRowHeight)
101 {
102 return std::max(leftRowHeight, rightRowHeight) + padding_.Height();
103 }
104
MeasureRightRow(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)105 std::pair<float, float> MenuItemLayoutAlgorithm::MeasureRightRow(LayoutWrapper* layoutWrapper,
106 LayoutConstraintF& childConstraint)
107 {
108 auto defaultPair = std::make_pair(0.0f, 0.0f);
109 CHECK_NULL_RETURN(layoutWrapper, defaultPair);
110 auto rightRow = layoutWrapper->GetOrCreateChildByIndex(1);
111 CHECK_NULL_RETURN(rightRow, defaultPair);
112 rightRow->Measure(childConstraint);
113 auto rightRowGeometryNode = rightRow->GetGeometryNode();
114 CHECK_NULL_RETURN(rightRowGeometryNode, defaultPair);
115 auto marginFrameSize = rightRowGeometryNode->GetMarginFrameSize();
116 return {marginFrameSize.Width(), marginFrameSize.Height()};
117 }
118
CalcContentExpandWidth(std::optional<LayoutConstraintF> & layoutConstraint,float contentWidth,float leftRowWidth,float rightRowWidth)119 void MenuItemLayoutAlgorithm::CalcContentExpandWidth(std::optional<LayoutConstraintF>& layoutConstraint,
120 float contentWidth, float leftRowWidth, float rightRowWidth)
121 {
122 needExpandContent_ = false;
123 emptyWidth_ = 0.0f;
124 if (contentWidth < minRowWidth_) {
125 emptyWidth_ = minRowWidth_ - contentWidth;
126 needExpandContent_ = true;
127 }
128
129 idealWidth_ = 0.0f;
130 if (layoutConstraint->selfIdealSize.Width().has_value()) {
131 idealWidth_ = std::max(layoutConstraint->minSize.Width(),
132 std::min(layoutConstraint->maxSize.Width(), layoutConstraint->selfIdealSize.Width().value()));
133 float newLeftRowWidth = idealWidth_ - rightRowWidth - middleSpace_;
134 newLeftRowWidth -= padding_.Width();
135 if (newLeftRowWidth > leftRowWidth) {
136 emptyWidth_ = newLeftRowWidth - leftRowWidth;
137 needExpandContent_ = true;
138 }
139 }
140 }
141
MeasureItemViews(LayoutConstraintF & childConstraint,std::optional<LayoutConstraintF> & layoutConstraint,LayoutWrapper * layoutWrapper)142 void MenuItemLayoutAlgorithm::MeasureItemViews(LayoutConstraintF& childConstraint,
143 std::optional<LayoutConstraintF>& layoutConstraint, LayoutWrapper* layoutWrapper)
144 {
145 auto leftRow = layoutWrapper->GetOrCreateChildByIndex(0);
146 CHECK_NULL_VOID(leftRow);
147 childConstraint.maxSize.SetWidth(leftRow->GetGeometryNode()->GetFrameSize().Width()
148 // Cannot cover left icon
149 ? maxRowWidth_ - middleSpace_ - static_cast<float>(iconSize_)
150 : maxRowWidth_);
151 // measure right row
152 auto [rightRowWidth, rightRowHeight] = MeasureRightRow(layoutWrapper, childConstraint);
153
154 // measure left row
155 auto maxWidth = maxRowWidth_ - rightRowWidth - middleSpace_;
156 childConstraint.maxSize.SetWidth(maxWidth);
157 MeasureRow(layoutWrapper, leftRow, childConstraint);
158 float leftRowWidth = leftRow->GetGeometryNode()->GetMarginFrameSize().Width();
159 float leftRowHeight = leftRow->GetGeometryNode()->GetMarginFrameSize().Height();
160 float contentWidth = 0.0f;
161 contentWidth = leftRowWidth + rightRowWidth + padding_.Width() + middleSpace_;
162
163 CalcContentExpandWidth(layoutConstraint, contentWidth, leftRowWidth, rightRowWidth);
164
165 auto width = std::max(minRowWidth_, contentWidth);
166 auto actualWidth = GreatNotEqual(idealWidth_, 0.0f) ? idealWidth_ : width;
167 childConstraint.minSize.SetWidth(actualWidth - padding_.Width());
168 childConstraint.maxSize.SetWidth(actualWidth - padding_.Width());
169 auto expandableHeight = MeasureExpandableHeight(childConstraint, layoutWrapper);
170
171 float itemHeight = CalcItemHeight(leftRowHeight, rightRowHeight);
172 UpdateSelfSize(layoutWrapper, actualWidth, itemHeight, expandableHeight);
173 }
174
MeasureExpandableHeight(LayoutConstraintF & childConstraint,LayoutWrapper * layoutWrapper)175 float MenuItemLayoutAlgorithm::MeasureExpandableHeight(LayoutConstraintF& childConstraint, LayoutWrapper* layoutWrapper)
176 {
177 auto expandableHeight = 0.0f;
178 auto expandableArea = layoutWrapper->GetOrCreateChildByIndex(EXPANDABLE_AREA_VIEW_INDEX);
179 if (expandableArea) {
180 expandableArea->Measure(childConstraint);
181 auto expandableAreaGeometryNode = expandableArea->GetGeometryNode();
182 CHECK_NULL_RETURN(expandableAreaGeometryNode, expandableHeight);
183 expandableHeight = std::max(expandableAreaGeometryNode->GetMarginFrameSize().Height(), 0.0f);
184 }
185 return expandableHeight;
186 }
187
MeasureRow(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & row,const LayoutConstraintF & constraint)188 void MenuItemLayoutAlgorithm::MeasureRow(LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& row,
189 const LayoutConstraintF& constraint)
190 {
191 auto itemNode = layoutWrapper->GetHostNode();
192 CHECK_NULL_VOID(itemNode);
193 auto pipeline = itemNode->GetContext();
194 CHECK_NULL_VOID(pipeline);
195
196 auto children = row->GetAllChildrenWithBuild();
197 CHECK_EQUAL_VOID(isOption_ && children.empty(), true);
198
199 float spaceWidth = constraint.maxSize.Width();
200 float rowWidth = 0.0f;
201 auto theme = pipeline->GetTheme<SelectTheme>();
202 CHECK_NULL_VOID(theme);
203 float rowHeight = 0.0f;
204 rowHeight = isOption_ ? 0.0f : (
205 Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) ?
206 theme->GetMenuChildMinHeight().ConvertToPx() : minItemHeight_
207 );
208 float iconContentPadding = 0.0f;
209 if (!isOption_) {
210 iconContentPadding = static_cast<float>(theme->GetIconContentPadding().ConvertToPx());
211 }
212
213 for (const auto& child : children) {
214 if (child != children.back()) {
215 // not content node
216 child->Measure(constraint);
217 } else {
218 // content node update constraint max width
219 auto contentConstraint = constraint;
220 contentConstraint.maxSize.SetWidth(spaceWidth);
221 child->Measure(contentConstraint);
222 }
223 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
224 spaceWidth -= childSize.Width() + iconContentPadding;
225 rowWidth += childSize.Width() + iconContentPadding;
226 rowHeight = std::max(rowHeight, childSize.Height());
227 }
228 if (!isOption_ && GreatNotEqual(rowWidth, iconContentPadding)) {
229 rowWidth -= iconContentPadding;
230 }
231 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN) && isOption_) {
232 rowHeight += (ITEM_BOTTOM_TOP_PADDING * PADDING_MULTIPLE).ConvertToPx();
233 }
234 row->GetGeometryNode()->SetFrameSize(SizeF(rowWidth, rowHeight));
235 }
236
CheckNeedExpandContent(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)237 void MenuItemLayoutAlgorithm::CheckNeedExpandContent(LayoutWrapper* layoutWrapper,
238 LayoutConstraintF& childConstraint)
239 {
240 if (needExpandContent_) {
241 auto menuItemNode = layoutWrapper->GetHostNode();
242 CHECK_NULL_VOID(menuItemNode);
243 auto pattern = menuItemNode->GetPattern<MenuItemPattern>();
244 CHECK_NULL_VOID(pattern);
245 auto contentNode = pattern->GetContentNode();
246 CHECK_NULL_VOID(contentNode);
247
248 auto leftRow = layoutWrapper->GetChildByIndex(0);
249 CHECK_NULL_VOID(leftRow);
250 auto newRowSize = leftRow->GetGeometryNode()->GetFrameSize();
251 newRowSize.SetWidth(emptyWidth_ + newRowSize.Width());
252 leftRow->GetGeometryNode()->SetFrameSize(newRowSize);
253
254 auto oldTextSize = contentNode->GetGeometryNode()->GetFrameSize();
255 float newTextWidth = emptyWidth_ + oldTextSize.Width();
256 childConstraint.minSize.SetWidth(newTextWidth);
257 childConstraint.maxSize.SetWidth(newTextWidth);
258 contentNode->Measure(childConstraint);
259 }
260 }
261
UpdateSelfSize(LayoutWrapper * layoutWrapper,float width,float itemHeight,float expandableHeight)262 void MenuItemLayoutAlgorithm::UpdateSelfSize(LayoutWrapper* layoutWrapper,
263 float width, float itemHeight, float expandableHeight)
264 {
265 auto menuNode = layoutWrapper->GetHostNode();
266 auto menuItemPattern = menuNode ? menuNode->GetPattern<MenuItemPattern>() : nullptr;
267 auto isEmbedded = menuItemPattern ? menuItemPattern->IsEmbedded() : false;
268 auto expandingMode = menuItemPattern ? menuItemPattern->GetExpandingMode() : SubMenuExpandingMode::SIDE;
269 float menuItemHeight = itemHeight;
270 if (expandingMode == SubMenuExpandingMode::EMBEDDED && !isEmbedded && GreatNotEqual(menuItemHeight, 0.0f)) {
271 auto props = layoutWrapper->GetLayoutProperty();
272 CHECK_NULL_VOID(props);
273 const auto& calcConstraint = props->GetCalcLayoutConstraint();
274 if (calcConstraint) {
275 auto minWidth = CalcLength(minRowWidth_);
276 auto height = CalcLength(menuItemHeight + expandableHeight);
277 auto maxWidth = calcConstraint->maxSize->Width();
278 calcConstraint->UpdateMaxSizeWithCheck(CalcSize(maxWidth, height));
279 calcConstraint->UpdateMinSizeWithCheck(CalcSize(minWidth, height));
280 }
281 }
282
283 auto bordersHeight = 0.0f;
284 itemHeight += GetDividerStroke(layoutWrapper);
285 auto clickableArea = layoutWrapper->GetOrCreateChildByIndex(CLICKABLE_AREA_VIEW_INDEX);
286 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
287 float height = 0.0f;
288 height = std::max(itemHeight - bordersHeight, minItemHeight_);
289 layoutWrapper->GetGeometryNode()->SetContentSize(SizeF(width, height + expandableHeight));
290 if (clickableArea) {
291 clickableArea->GetGeometryNode()->SetFrameSize(SizeF(width, height + bordersHeight));
292 }
293 } else {
294 layoutWrapper->GetGeometryNode()->SetContentSize(SizeF(width, itemHeight));
295 }
296 BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
297 }
298
GetDividerStroke(LayoutWrapper * layoutWrapper)299 float MenuItemLayoutAlgorithm::GetDividerStroke(LayoutWrapper* layoutWrapper)
300 {
301 auto menuItemNode = layoutWrapper->GetHostNode();
302 CHECK_NULL_RETURN(menuItemNode, 0.0f);
303 auto pattern = menuItemNode->GetPattern<MenuItemPattern>();
304 CHECK_NULL_RETURN(pattern, 0.0f);
305 auto topDivider = pattern->GetTopDivider();
306 if (topDivider && topDivider->GetParent()) {
307 return 0.0f;
308 }
309 return pattern->GetDividerStroke();
310 }
311
GetBordersHeight(LayoutWrapper * layoutWrapper)312 float MenuItemLayoutAlgorithm::GetBordersHeight(LayoutWrapper* layoutWrapper)
313 {
314 auto props = layoutWrapper->GetLayoutProperty();
315 CHECK_NULL_RETURN(props, 0.0f);
316 const auto& border = props->GetBorderWidthProperty();
317 CHECK_NULL_RETURN(border, 0.0f);
318 return border->topDimen.value_or(Dimension(0.0)).ConvertToPx() +
319 border->bottomDimen.value_or(Dimension(0.0)).ConvertToPx();
320 }
321
GetMenuItemVerticalPadding()322 float MenuItemLayoutAlgorithm::GetMenuItemVerticalPadding()
323 {
324 float ret = 0.0f;
325 auto pipeline = PipelineBase::GetCurrentContext();
326 CHECK_NULL_RETURN(pipeline, ret);
327 auto theme = pipeline->GetTheme<SelectTheme>();
328 CHECK_NULL_RETURN(theme, ret);
329 return theme->GetMenuItemVerticalPadding().ConvertToPx();
330 }
331
GetIdealWidth(LayoutWrapper * layoutWrapper)332 std::optional<float> MenuItemLayoutAlgorithm::GetIdealWidth(LayoutWrapper* layoutWrapper)
333 {
334 CHECK_NULL_RETURN(layoutWrapper, std::nullopt);
335 // layout property not update in layoutWrapper when measure
336 auto optionProps = layoutWrapper->GetLayoutProperty();
337 CHECK_NULL_RETURN(optionProps, std::nullopt);
338
339 const auto& layoutConstraint = optionProps->GetCalcLayoutConstraint();
340 CHECK_NULL_RETURN(layoutConstraint, std::nullopt);
341
342 const auto& minSize = layoutConstraint->minSize;
343 CHECK_NULL_RETURN(minSize, std::nullopt);
344
345 const auto& width = minSize->Width();
346 if (width->IsValid()) {
347 return width->GetDimension().ConvertToPx();
348 }
349
350 return std::nullopt;
351 }
352
UpdateIconMargin(LayoutWrapper * layoutWrapper)353 void MenuItemLayoutAlgorithm::UpdateIconMargin(LayoutWrapper* layoutWrapper)
354 {
355 auto optionNode = layoutWrapper->GetHostNode();
356 CHECK_NULL_VOID(optionNode);
357 auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
358 CHECK_NULL_VOID(optionPattern);
359 auto layoutProps = layoutWrapper->GetLayoutProperty();
360 CHECK_NULL_VOID(layoutProps);
361 auto direction = layoutProps->GetNonAutoLayoutDirection();
362 bool isRtl = direction == TextDirection::RTL;
363 const auto& selectTheme = optionPattern->GetSelectTheme();
364 CHECK_NULL_VOID(selectTheme);
365 auto calcLength = CalcLength(selectTheme->GetIconContentPadding());
366 MarginProperty margin;
367 if (isRtl) {
368 margin.left = calcLength;
369 margin.right = CalcLength();
370 } else {
371 margin.left = CalcLength();
372 margin.right = calcLength;
373 }
374 Alignment align = isRtl ? Alignment::CENTER_RIGHT : Alignment::CENTER_LEFT;
375 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
376 for (auto iconChild : child->GetAllChildrenWithBuild()) {
377 if ((iconChild->GetHostTag() == V2::IMAGE_ETS_TAG) || (iconChild->GetHostTag() == V2::SYMBOL_ETS_TAG)) {
378 auto iconProps = iconChild->GetLayoutProperty();
379 iconProps->UpdateAlignment(align);
380 iconProps->UpdateMargin(margin);
381 }
382 }
383 }
384
InitPadding(const RefPtr<LayoutProperty> & props,std::optional<LayoutConstraintF> & layoutConstraint)385 void MenuItemLayoutAlgorithm::InitPadding(const RefPtr<LayoutProperty>& props,
386 std::optional<LayoutConstraintF>& layoutConstraint)
387 {
388 padding_ = props->CreatePaddingAndBorderWithDefault(horInterval_, verInterval_, 0.0f, 0.0f);
389 }
390
UpdateIdealSize(LayoutWrapper * layoutWrapper,const RefPtr<LayoutProperty> & props,std::optional<LayoutConstraintF> & layoutConstraint)391 void MenuItemLayoutAlgorithm::UpdateIdealSize(LayoutWrapper* layoutWrapper, const RefPtr<LayoutProperty>& props,
392 std::optional<LayoutConstraintF>& layoutConstraint)
393 {
394 const auto& calcConstraint = props->GetCalcLayoutConstraint();
395 if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
396 calcConstraint->selfIdealSize.value().Width().has_value()) {
397 ScaleProperty scaleProperty;
398 if (layoutWrapper->GetGeometryNode() && layoutWrapper->GetGeometryNode()->GetParentLayoutConstraint()) {
399 scaleProperty = layoutWrapper->GetGeometryNode()->GetParentLayoutConstraint()->scaleProperty;
400 } else {
401 scaleProperty = layoutConstraint->scaleProperty;
402 }
403 layoutConstraint->selfIdealSize.SetWidth(
404 ConvertToPx(calcConstraint->selfIdealSize.value().Width()->GetDimension(), scaleProperty,
405 layoutConstraint->percentReference.Width()));
406 }
407 if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
408 calcConstraint->selfIdealSize.value().Height().has_value()) {
409 idealHeight_ = calcConstraint->selfIdealSize.value().Height()->GetDimension().ConvertToPx();
410 }
411 }
412
MeasureMenuItem(LayoutWrapper * layoutWrapper,const RefPtr<SelectTheme> & selectTheme,const RefPtr<LayoutProperty> & props,std::optional<LayoutConstraintF> & layoutConstraint)413 void MenuItemLayoutAlgorithm::MeasureMenuItem(LayoutWrapper* layoutWrapper, const RefPtr<SelectTheme>& selectTheme,
414 const RefPtr<LayoutProperty>& props, std::optional<LayoutConstraintF>& layoutConstraint)
415 {
416 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
417 verInterval_ = GetMenuItemVerticalPadding() - GetBordersHeight(layoutWrapper);
418 }
419 InitPadding(props, layoutConstraint);
420 maxRowWidth_ = layoutConstraint->maxSize.Width() - padding_.Width();
421 UpdateIdealSize(layoutWrapper, props, layoutConstraint);
422 if (layoutConstraint->selfIdealSize.Width().has_value()) {
423 maxRowWidth_ =
424 std::max(layoutConstraint->minSize.Width(),
425 std::min(layoutConstraint->maxSize.Width(), layoutConstraint->selfIdealSize.Width().value())) -
426 padding_.Width();
427 }
428 CheckNeedMatchParent(layoutWrapper, layoutConstraint);
429 minRowWidth_ = layoutConstraint->minSize.Width();
430
431 auto childConstraint = props->CreateChildConstraint();
432 minItemHeight_ = static_cast<float>(selectTheme->GetOptionMinHeight().ConvertToPx());
433 childConstraint.minSize.SetHeight(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) ?
434 selectTheme->GetMenuChildMinHeight().ConvertToPx() : minItemHeight_);
435
436 iconSize_ = selectTheme->GetIconSideLength().ConvertToPx();
437 MeasureItemViews(childConstraint, layoutConstraint, layoutWrapper);
438 MeasureClickableArea(layoutWrapper);
439 CheckNeedExpandContent(layoutWrapper, childConstraint);
440 }
441
MeasureClickableArea(LayoutWrapper * layoutWrapper)442 void MenuItemLayoutAlgorithm::MeasureClickableArea(LayoutWrapper* layoutWrapper)
443 {
444 auto clickableArea = layoutWrapper->GetOrCreateChildByIndex(CLICKABLE_AREA_VIEW_INDEX);
445 if (GreatNotEqual(idealWidth_, 0.0f)) {
446 layoutWrapper->GetGeometryNode()->SetFrameWidth(idealWidth_);
447 if (clickableArea) {
448 clickableArea->GetGeometryNode()->SetFrameWidth(idealWidth_);
449 }
450 }
451 }
452
MeasureOption(LayoutWrapper * layoutWrapper,const RefPtr<SelectTheme> & selectTheme,const RefPtr<LayoutProperty> & props,const std::optional<LayoutConstraintF> & layoutConstraint)453 void MenuItemLayoutAlgorithm::MeasureOption(LayoutWrapper* layoutWrapper, const RefPtr<SelectTheme>& selectTheme,
454 const RefPtr<LayoutProperty>& props, const std::optional<LayoutConstraintF>& layoutConstraint)
455 {
456 auto optionNode = layoutWrapper->GetHostNode();
457 CHECK_NULL_VOID(optionNode);
458 auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
459 CHECK_NULL_VOID(optionPattern);
460
461 auto idealSize = CreateIdealSize(
462 layoutConstraint.value(), Axis::HORIZONTAL, props->GetMeasureType(MeasureType::MATCH_CONTENT), true);
463 float maxChildWidth = layoutConstraint->maxSize.Width() - horInterval_ * 2.0f;
464 // measure child
465 auto childConstraint = props->CreateChildConstraint();
466 childConstraint.maxSize.SetWidth(maxChildWidth);
467 // set self size based on childNode size;
468 auto minOptionHeight = static_cast<float>(selectTheme->GetOptionMinHeight().ConvertToPx());
469 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
470 CHECK_NULL_VOID(child);
471
472 auto rowChild = child->GetOrCreateChildByIndex(0);
473 if (rowChild && (rowChild->GetHostTag() == V2::PASTE_BUTTON_ETS_TAG)) {
474 auto securityLayoutProperty = DynamicCast<SecurityComponentLayoutProperty>(rowChild->GetLayoutProperty());
475 CHECK_NULL_VOID(securityLayoutProperty);
476 securityLayoutProperty->UpdateBackgroundLeftPadding(Dimension(horInterval_));
477 }
478 UpdateIconMargin(layoutWrapper);
479 MeasureRow(layoutWrapper, child, childConstraint);
480 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
481 childSize.AddWidth(horInterval_ * 2.0f);
482 idealSize.UpdateSizeWithCheck(childSize);
483 auto idealWidth = GetIdealWidth(layoutWrapper);
484 if (idealWidth.has_value()) {
485 auto optionPaintProperty = optionNode->GetPaintProperty<MenuItemPaintProperty>();
486 if (optionPaintProperty && (optionPaintProperty->GetIdealWidthForWeb() > 0) &&
487 (idealWidth.value() < optionPaintProperty->GetIdealWidthForWeb())) {
488 idealSize.SetWidth(std::min(optionPaintProperty->GetIdealWidthForWeb(), layoutConstraint->maxSize.Width()));
489 } else {
490 idealSize.SetWidth(idealWidth.value());
491 }
492 }
493 idealSize.SetHeight(std::max(minOptionHeight, idealSize.Height()));
494 if (optionPattern->IsSelectOption() && optionPattern->GetHasOptionWidth()) {
495 idealSize.SetWidth(optionPattern->GetSelectOptionWidth());
496 }
497 if (rowChild && (rowChild->GetHostTag() == V2::PASTE_BUTTON_ETS_TAG)) {
498 float dividerWidth = static_cast<float>(selectTheme->GetDefaultDividerWidth().ConvertToPx());
499 SizeF idealSizePaste(idealSize.Width() - dividerWidth, idealSize.Height() - dividerWidth);
500 childConstraint.selfIdealSize.SetSize(idealSizePaste);
501 rowChild->Measure(childConstraint);
502 }
503 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
504 auto textAlign = static_cast<TextAlign>(selectTheme->GetOptionContentNormalAlign());
505 if (textAlign == TextAlign::CENTER) {
506 ExtendTextAndRowNode(child, idealSize, horInterval_, childConstraint);
507 }
508 }
509
CalcRowTopSpace(float rowsHeight,float itemHeight,LayoutWrapper * layoutWrapper,float leftOrRightRowHeight)510 float MenuItemLayoutAlgorithm::CalcRowTopSpace(float rowsHeight, float itemHeight,
511 LayoutWrapper* layoutWrapper, float leftOrRightRowHeight)
512 {
513 return (itemHeight - leftOrRightRowHeight + GetBordersHeight(layoutWrapper)) / 2.0f;
514 }
515
LayoutMenuItem(LayoutWrapper * layoutWrapper,const RefPtr<LayoutProperty> & props)516 void MenuItemLayoutAlgorithm::LayoutMenuItem(LayoutWrapper* layoutWrapper, const RefPtr<LayoutProperty>& props)
517 {
518 auto layoutDirection = props->GetNonAutoLayoutDirection();
519 auto leftRow = layoutWrapper->GetOrCreateChildByIndex(0);
520 auto leftRowSize = leftRow ? leftRow->GetGeometryNode()->GetFrameSize() : SizeT(0.0f, 0.0f);
521 auto rightRow = layoutWrapper->GetOrCreateChildByIndex(1);
522 auto rightRowSize = rightRow ? rightRow->GetGeometryNode()->GetFrameSize() : SizeT(0.0f, 0.0f);
523 float rowsHeight = 0.0f;
524 float itemHeight = idealHeight_ > 0.0f ? idealHeight_ :
525 std::max(leftRowSize.Height(), rightRowSize.Height()) + padding_.Height();
526
527 CHECK_NULL_VOID(leftRow);
528 float topSpace = CalcRowTopSpace(rowsHeight, itemHeight, layoutWrapper, leftRowSize.Height());
529 leftRow->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_LAYOUT);
530 leftRow->GetGeometryNode()->SetMarginFrameOffset(OffsetF(padding_.left.value_or(horInterval_), topSpace));
531 if (layoutDirection == TextDirection::RTL) {
532 leftRow->GetGeometryNode()->SetMarginFrameOffset(
533 OffsetF(layoutWrapper->GetGeometryNode()->GetFrameSize().Width() - padding_.right.value_or(horInterval_) -
534 leftRow->GetGeometryNode()->GetFrameSize().Width(),
535 topSpace));
536 }
537 leftRow->Layout();
538
539 CHECK_NULL_VOID(rightRow);
540 topSpace = CalcRowTopSpace(rowsHeight, itemHeight, layoutWrapper, rightRowSize.Height());
541 rightRow->GetGeometryNode()->SetMarginFrameOffset(
542 OffsetF(layoutWrapper->GetGeometryNode()->GetFrameSize().Width() - padding_.right.value_or(horInterval_) -
543 rightRow->GetGeometryNode()->GetFrameSize().Width(), topSpace));
544 if (layoutDirection == TextDirection::RTL) {
545 rightRow->GetGeometryNode()->SetMarginFrameOffset(OffsetF(padding_.left.value_or(horInterval_), topSpace));
546 }
547 rightRow->Layout();
548
549 auto clickableArea = layoutWrapper->GetOrCreateChildByIndex(CLICKABLE_AREA_VIEW_INDEX);
550 CHECK_NULL_VOID(clickableArea);
551 clickableArea->Layout();
552
553 auto expandableArea = layoutWrapper->GetOrCreateChildByIndex(EXPANDABLE_AREA_VIEW_INDEX);
554 CHECK_NULL_VOID(expandableArea);
555 expandableArea->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_LAYOUT);
556 expandableArea->GetGeometryNode()->SetMarginFrameOffset(
557 OffsetF(padding_.left.value_or(horInterval_),
558 itemHeight));
559 expandableArea->Layout();
560 }
561
LayoutOption(LayoutWrapper * layoutWrapper,const RefPtr<LayoutProperty> & props)562 void MenuItemLayoutAlgorithm::LayoutOption(LayoutWrapper* layoutWrapper, const RefPtr<LayoutProperty>& props)
563 {
564 auto optionNode = layoutWrapper->GetHostNode();
565 CHECK_NULL_VOID(optionNode);
566 auto optionPattern = optionNode->GetPattern<MenuItemPattern>();
567 CHECK_NULL_VOID(optionPattern);
568 auto optionSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
569 auto optionHeight = optionSize.Height();
570 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
571 child->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_LAYOUT);
572 auto rowChild = child->GetOrCreateChildByIndex(0);
573 if (rowChild && (rowChild->GetHostTag() == V2::PASTE_BUTTON_ETS_TAG)) {
574 float horInterval = 0.0f;
575 if (props->GetNonAutoLayoutDirection() == TextDirection::RTL) {
576 SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
577 horInterval = optionSize.Width() - childSize.Width();
578 }
579 child->GetGeometryNode()->SetMarginFrameOffset(
580 OffsetF(horInterval, (optionHeight - child->GetGeometryNode()->GetFrameSize().Height()) / 2.0f));
581 child->Layout();
582 return;
583 }
584 const auto& selectTheme = optionPattern->GetSelectTheme();
585 CHECK_NULL_VOID(selectTheme);
586
587 float horInterval = horInterval_;
588 auto textAlign = static_cast<TextAlign>(selectTheme->GetOptionContentNormalAlign());
589 if (textAlign != TextAlign::CENTER && props->GetNonAutoLayoutDirection() == TextDirection::RTL) {
590 SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
591 horInterval = optionSize.Width() - childSize.Width() - horInterval_;
592 }
593 child->GetGeometryNode()->SetMarginFrameOffset(
594 OffsetF(horInterval, (optionHeight - child->GetGeometryNode()->GetFrameSize().Height()) / 2.0f));
595 child->Layout();
596 }
597
ExtendTextAndRowNode(const RefPtr<LayoutWrapper> & row,const SizeF & optSize,float interval,const LayoutConstraintF & constraint)598 void MenuItemLayoutAlgorithm::ExtendTextAndRowNode(const RefPtr<LayoutWrapper>& row,
599 const SizeF& optSize, float interval, const LayoutConstraintF& constraint)
600 {
601 CHECK_NULL_VOID(row);
602 auto children = row->GetAllChildrenWithBuild();
603 if (children.empty()) {
604 return;
605 }
606 SizeF frameSize;
607 float imageNodeWidth = 0.0f;
608 for (auto textChild : children) {
609 if (!textChild) {
610 continue;
611 }
612 auto geometryNode = textChild->GetGeometryNode();
613 if (!geometryNode) {
614 continue;
615 }
616 if (textChild->GetHostTag() != V2::TEXT_ETS_TAG) {
617 imageNodeWidth += geometryNode->GetMarginFrameSize().Width();
618 continue;
619 }
620 frameSize = geometryNode->GetMarginFrameSize();
621 auto width = optSize.Width() - 2.0f * interval - imageNodeWidth;
622 auto contentConstraint = constraint;
623 contentConstraint.minSize.SetWidth(width);
624 textChild->Measure(contentConstraint);
625 }
626 auto geometryNode = row->GetGeometryNode();
627 CHECK_NULL_VOID(geometryNode);
628 frameSize = geometryNode->GetMarginFrameSize();
629 geometryNode->SetFrameSize(SizeF(optSize.Width() - 2.0f * interval, frameSize.Height()));
630 }
631 } // namespace OHOS::Ace::NG
632