• 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 
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