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