• 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/multi_menu_layout_algorithm.h"
17 
18 #include "base/subwindow/subwindow_manager.h"
19 #include "core/components/common/layout/grid_system_manager.h"
20 #include "core/components_ng/pattern/menu/menu_item/menu_item_layout_property.h"
21 #include "core/components_ng/pattern/menu/menu_pattern.h"
22 #include "core/components_ng/pattern/menu/menu_theme.h"
23 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
24 #include "core/components_ng/pattern/select_overlay/select_overlay_node.h"
25 #include "core/components_ng/property/measure_utils.h"
26 
27 namespace OHOS::Ace::NG {
28 namespace {
29 struct SelectOverlayRightClickMenuLayoutHelper {
AdjustLayoutConstraintsOHOS::Ace::NG::__anon960aadfe0111::SelectOverlayRightClickMenuLayoutHelper30     static void AdjustLayoutConstraints(
31         LayoutConstraintF& constrainMinWidth, const RefPtr<LayoutWrapper>& child, LayoutWrapper* layoutWrapper)
32     {
33         auto host = layoutWrapper->GetHostNode();
34         CHECK_NULL_VOID(host);
35         auto scrollNode = host->GetParentFrameNode();
36         CHECK_NULL_VOID(scrollNode);
37         auto outterMenuNode = scrollNode->GetParentFrameNode();
38         CHECK_NULL_VOID(outterMenuNode);
39         auto outterMenuPattern = outterMenuNode->GetPattern<MenuPattern>();
40         CHECK_NULL_VOID(outterMenuPattern);
41         auto menuWrapperNode = outterMenuNode->GetParentFrameNode();
42         CHECK_NULL_VOID(menuWrapperNode);
43         auto childLayoutProperty = child->GetLayoutProperty();
44         CHECK_NULL_VOID(childLayoutProperty);
45         if (menuWrapperNode->GetInspectorIdValue("") != SelectOverlayRrightClickMenuWrapper ||
46             !outterMenuPattern->IsSelectOverlayRightClickMenu() ||
47             child->GetHostTag() != V2::RELATIVE_CONTAINER_ETS_TAG) {
48             return;
49         }
50         childLayoutProperty->UpdateUserDefinedIdealSize(
51             { CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(0.0, DimensionUnit::AUTO) });
52         constrainMinWidth.percentReference.SetWidth(constrainMinWidth.selfIdealSize.Width().value_or(0.0f));
53         auto row = child->GetChildByIndex(0);
54         CHECK_NULL_VOID(row);
55         auto layoutProperty = row->GetLayoutProperty();
56         CHECK_NULL_VOID(layoutProperty);
57         layoutProperty->UpdateUserDefinedIdealSize(
58             { CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(1.0, DimensionUnit::AUTO) });
59         auto pasteButtonRow = child->GetChildByIndex(1);
60         CHECK_NULL_VOID(pasteButtonRow);
61         auto pasteButton = pasteButtonRow->GetChildByIndex(0);
62         CHECK_NULL_VOID(pasteButton);
63         auto pasteButtonLayoutProperty = pasteButton->GetLayoutProperty();
64         CHECK_NULL_VOID(pasteButtonLayoutProperty);
65         pasteButtonLayoutProperty->UpdateUserDefinedIdealSize(
66             { CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(1.0, DimensionUnit::PERCENT) });
67     }
68 
AdjustLayoutConstraintsAutoWidthOHOS::Ace::NG::__anon960aadfe0111::SelectOverlayRightClickMenuLayoutHelper69     static void AdjustLayoutConstraintsAutoWidth(const RefPtr<LayoutWrapper>& child, LayoutWrapper* layoutWrapper)
70     {
71         auto host = layoutWrapper->GetHostNode();
72         CHECK_NULL_VOID(host);
73         auto scrollNode = host->GetParentFrameNode();
74         CHECK_NULL_VOID(scrollNode);
75         auto outterMenuNode = scrollNode->GetParentFrameNode();
76         CHECK_NULL_VOID(outterMenuNode);
77         auto outterMenuPattern = outterMenuNode->GetPattern<MenuPattern>();
78         CHECK_NULL_VOID(outterMenuPattern);
79         auto menuWrapperNode = outterMenuNode->GetParentFrameNode();
80         CHECK_NULL_VOID(menuWrapperNode);
81         auto childLayoutProperty = child->GetLayoutProperty();
82         CHECK_NULL_VOID(childLayoutProperty);
83         if (menuWrapperNode->GetInspectorIdValue("") != SelectOverlayRrightClickMenuWrapper ||
84             !outterMenuPattern->IsSelectOverlayRightClickMenu() ||
85             child->GetHostTag() != V2::RELATIVE_CONTAINER_ETS_TAG) {
86             return;
87         }
88         childLayoutProperty->UpdateUserDefinedIdealSize(
89             { CalcLength(0.0, DimensionUnit::AUTO), CalcLength(0.0, DimensionUnit::AUTO) });
90         auto row = child->GetChildByIndex(0);
91         CHECK_NULL_VOID(row);
92         auto layoutProperty = row->GetLayoutProperty();
93         CHECK_NULL_VOID(layoutProperty);
94         layoutProperty->UpdateUserDefinedIdealSize(
95             { CalcLength(0.0, DimensionUnit::AUTO), CalcLength(0.0, DimensionUnit::AUTO) });
96         auto pasteButtonRow = child->GetChildByIndex(1);
97         CHECK_NULL_VOID(pasteButtonRow);
98         auto pasteButton = pasteButtonRow->GetChildByIndex(0);
99         CHECK_NULL_VOID(pasteButton);
100         auto pasteButtonLayoutProperty = pasteButton->GetLayoutProperty();
101         CHECK_NULL_VOID(pasteButtonLayoutProperty);
102         pasteButtonLayoutProperty->UpdateUserDefinedIdealSize(
103             { CalcLength(0.0, DimensionUnit::AUTO), CalcLength(0.0, DimensionUnit::AUTO) });
104     }
105 };
106 } // namespace
107 
UpdateColumnWidth(const RefPtr<FrameNode> & frameNode,const RefPtr<GridColumnInfo> & columnInfo)108 bool UpdateColumnWidth(const RefPtr<FrameNode>& frameNode, const RefPtr<GridColumnInfo>& columnInfo)
109 {
110     CHECK_NULL_RETURN(frameNode && columnInfo, false);
111     auto pipeline = frameNode->GetContext();
112     CHECK_NULL_RETURN(pipeline, false);
113     if (Positive(pipeline->GetRootWidth())) {
114         return false;
115     }
116 
117     auto currentId = Container::CurrentId();
118     CHECK_NULL_RETURN(currentId >= MIN_SUBCONTAINER_ID, false);
119     auto subwindowManager = SubwindowManager::GetInstance();
120     CHECK_NULL_RETURN(subwindowManager, false);
121     auto subwindow = subwindowManager->GetSubwindowByType(pipeline->GetInstanceId(), SubwindowType::TYPE_MENU);
122     CHECK_NULL_RETURN(subwindow, false);
123     auto width = subwindow->GetRect().Width();
124     CHECK_NULL_RETURN(Positive(width), false);
125     auto parent = columnInfo->GetParent();
126     CHECK_NULL_RETURN(parent, false);
127     parent->BuildColumnWidth(width);
128     return true;
129 }
130 
RemoveParentRestrictionsForFixIdeal(const LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)131 void MultiMenuLayoutAlgorithm::RemoveParentRestrictionsForFixIdeal(
132     const LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint)
133 {
134     auto host = layoutWrapper->GetHostNode();
135     CHECK_NULL_VOID(host);
136     auto menuPattern = host->GetPattern<MenuPattern>();
137     CHECK_NULL_VOID(menuPattern);
138     auto layoutProperty = layoutWrapper->GetLayoutProperty();
139     CHECK_NULL_VOID(layoutProperty);
140     auto layoutPolicyProperty = layoutProperty->GetLayoutPolicyProperty();
141     if (menuPattern->IsEnabledContentForFixIdeal() && layoutPolicyProperty.has_value()) {
142         auto& layoutPolicy = layoutPolicyProperty.value();
143         if (layoutPolicy.IsWidthFix()) {
144             childConstraint.maxSize.SetWidth(std::numeric_limits<float>::infinity());
145         }
146         if (layoutPolicy.IsHeightFix()) {
147             childConstraint.maxSize.SetHeight(std::numeric_limits<float>::infinity());
148         }
149     }
150 }
151 
Measure(LayoutWrapper * layoutWrapper)152 void MultiMenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
153 {
154     CHECK_NULL_VOID(layoutWrapper);
155     auto layoutProperty = layoutWrapper->GetLayoutProperty();
156     CHECK_NULL_VOID(layoutProperty);
157     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
158     CHECK_NULL_VOID(layoutConstraint);
159     auto childConstraint = layoutProperty->CreateChildConstraint();
160     childConstraint.maxSize.SetWidth(layoutConstraint->maxSize.Width());
161     RemoveParentRestrictionsForFixIdeal(layoutWrapper, childConstraint);
162     // constraint max size minus padding
163     const auto& padding = layoutProperty->CreatePaddingAndBorder();
164     auto node = layoutWrapper->GetHostNode();
165     CHECK_NULL_VOID(node);
166     auto pattern = node->GetPattern<MenuPattern>();
167     CHECK_NULL_VOID(pattern);
168     if (!pattern->IsEmbedded()) {
169         MinusPaddingToSize(padding, childConstraint.maxSize);
170     } else {
171         const auto& safeAreaPadding = layoutProperty->GetOrCreateSafeAreaPadding();
172         MinusPaddingToSize(safeAreaPadding, childConstraint.maxSize);
173         UpdateEmbeddedPercentReference(layoutWrapper, childConstraint, layoutConstraint);
174     }
175     if (layoutConstraint->selfIdealSize.Width().has_value()) {
176         // when Menu is set self ideal width, make children node adaptively fill up.
177         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
178             if (LessNotEqual(layoutConstraint->selfIdealSize.Width().value(), MIN_MENU_WIDTH.ConvertToPx())) {
179                 RefPtr<GridColumnInfo> columnInfo;
180                 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
181                 CHECK_NULL_VOID(columnInfo);
182                 auto columnParent = columnInfo->GetParent();
183                 CHECK_NULL_VOID(columnParent);
184                 columnParent->BuildColumnWidth();
185                 auto minWidth = static_cast<float>(columnInfo->GetWidth(MENU_MIN_GRID_COUNTS));
186                 layoutConstraint->selfIdealSize.SetWidth(minWidth);
187 
188                 UpdateMenuDefaultConstraintByDevice(pattern, childConstraint, padding.Width(), layoutConstraint, true);
189             }
190         }
191         auto idealWidth =
192             std::max(layoutConstraint->minSize.Width(),
193                 std::min(layoutConstraint->maxSize.Width(), layoutConstraint->selfIdealSize.Width().value())) -
194             padding.Width();
195         childConstraint.selfIdealSize.SetWidth(idealWidth);
196     } else {
197         // constraint min width base on grid column
198         auto columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
199         CHECK_NULL_VOID(columnInfo);
200         auto columnParent = columnInfo->GetParent();
201         CHECK_NULL_VOID(columnParent);
202         if (!UpdateSelectOverlayMenuMinWidth(pattern, columnInfo) && !UpdateColumnWidth(node, columnInfo)) {
203             columnParent->BuildColumnWidth();
204         }
205         auto minWidth = static_cast<float>(columnInfo->GetWidth()) - padding.Width();
206         childConstraint.minSize.SetWidth(minWidth);
207 
208         UpdateMenuDefaultConstraintByDevice(pattern, childConstraint, padding.Width(), layoutConstraint, false);
209     }
210     // Calculate max width of menu items
211     UpdateConstraintBaseOnMenuItems(layoutWrapper, childConstraint);
212     UpdateSelfSize(layoutWrapper, childConstraint, layoutConstraint);
213 }
214 
UpdateMenuDefaultConstraintByDevice(const RefPtr<MenuPattern> & pattern,LayoutConstraintF & childConstraint,float paddingWidth,std::optional<LayoutConstraintF> & layoutConstraint,bool idealSizeHasVal)215 void MultiMenuLayoutAlgorithm::UpdateMenuDefaultConstraintByDevice(const RefPtr<MenuPattern>& pattern,
216     LayoutConstraintF& childConstraint, float paddingWidth, std::optional<LayoutConstraintF>& layoutConstraint,
217     bool idealSizeHasVal)
218 {
219     CHECK_NULL_VOID(pattern);
220 
221     // only 2in1 device has restrictions on the menu width in API13
222     CHECK_NULL_VOID(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN));
223     auto host = pattern->GetHost();
224     CHECK_NULL_VOID(host);
225     auto pipeline = host->GetContext();
226     CHECK_NULL_VOID(pipeline);
227     auto theme = pipeline->GetTheme<SelectTheme>();
228     CHECK_NULL_VOID(theme);
229     auto expandDisplay = theme->GetExpandDisplay();
230     CHECK_NULL_VOID(expandDisplay);
231 
232     auto mainMenuPattern = pattern->GetMainMenuPattern();
233     CHECK_NULL_VOID(mainMenuPattern);
234     if (!mainMenuPattern->IsContextMenu() && !mainMenuPattern->IsMenu() &&
235         !mainMenuPattern->IsSelectOverlayRightClickMenu()) {
236         return;
237     }
238 
239     if (idealSizeHasVal) {
240         layoutConstraint->selfIdealSize.SetWidth(theme->GetMenuDefaultWidth().ConvertToPx());
241     } else {
242         childConstraint.minSize.SetWidth(theme->GetMenuDefaultWidth().ConvertToPx() - paddingWidth);
243     }
244 }
245 
UpdateChildPositionWidthIgnoreLayoutSafeArea(const RefPtr<LayoutWrapper> & childLayoutWrapper,OffsetF & translate,bool isEmbed,OffsetF & embedCorrect)246 void MultiMenuLayoutAlgorithm::UpdateChildPositionWidthIgnoreLayoutSafeArea(
247     const RefPtr<LayoutWrapper>& childLayoutWrapper, OffsetF& translate, bool isEmbed, OffsetF& embedCorrect)
248 {
249     CHECK_NULL_VOID(childLayoutWrapper);
250     auto childNode = childLayoutWrapper->GetHostNode();
251     CHECK_NULL_VOID(childNode);
252     auto property = childNode->GetLayoutProperty();
253     CHECK_NULL_VOID(property);
254     if (!property->IsIgnoreOptsValid()) {
255         return;
256     };
257     auto saeCorrect = translate;
258     IgnoreLayoutSafeAreaOpts& opts = *(property->GetIgnoreLayoutSafeAreaOpts());
259     auto sae = childNode->GetAccumulatedSafeAreaExpand(false, opts);
260     auto offsetX = sae.left.value_or(0.0f);
261     auto offsetY = sae.top.value_or(0.0f);
262     if (isEmbed) {
263         if (sae.left.has_value())
264             offsetX += embedCorrect.GetX();
265         if (sae.top.has_value())
266             offsetY += embedCorrect.GetY();
267     }
268     OffsetF saeTrans = OffsetF(offsetX, offsetY);
269     saeCorrect -= saeTrans;
270     childLayoutWrapper->GetGeometryNode()->SetMarginFrameOffset(saeCorrect);
271     translate.AddY(-offsetY);
272 }
273 
Layout(LayoutWrapper * layoutWrapper)274 void MultiMenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
275 {
276     CHECK_NULL_VOID(layoutWrapper);
277     BoxLayoutAlgorithm::PerformLayout(layoutWrapper);
278 
279     auto node = layoutWrapper->GetHostNode();
280     CHECK_NULL_VOID(node);
281     auto pipeline = node->GetContext();
282     CHECK_NULL_VOID(pipeline);
283     auto theme = pipeline->GetTheme<SelectTheme>();
284     CHECK_NULL_VOID(theme);
285     auto layoutProperty = layoutWrapper->GetLayoutProperty();
286     CHECK_NULL_VOID(layoutProperty);
287     auto pattern = node->GetPattern<MenuPattern>();
288     CHECK_NULL_VOID(pattern);
289     OffsetF translate(0.0f, 0.0f);
290     const auto& padding = layoutProperty->CreatePaddingAndBorder();
291     auto outPadding = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
292     if (!pattern->IsEmbedded()) {
293         translate.AddX(padding.left.value_or(outPadding));
294         translate.AddY(padding.top.value_or(outPadding));
295     }
296     // translate each option by the height of previous options
297     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
298         child->GetGeometryNode()->SetMarginFrameOffset(translate);
299         child->Layout();
300         translate.AddY(child->GetGeometryNode()->GetMarginFrameSize().Height());
301     }
302 }
303 
UpdateEmbeddedPercentReference(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint,std::optional<LayoutConstraintF> & layoutConstraint)304 void MultiMenuLayoutAlgorithm::UpdateEmbeddedPercentReference(LayoutWrapper* layoutWrapper,
305     LayoutConstraintF& childConstraint, std::optional<LayoutConstraintF>& layoutConstraint)
306 {
307     // Set percent reference for embedded menu the same as for parent menu item
308     auto node = layoutWrapper->GetHostNode();
309     CHECK_NULL_VOID(node);
310     auto parentNode = AceType::DynamicCast<FrameNode>(node->GetParent());
311     CHECK_NULL_VOID(parentNode);
312     auto parentProperty = parentNode->GetLayoutProperty<MenuItemLayoutProperty>();
313     CHECK_NULL_VOID(parentProperty);
314     auto parentConstraint = parentProperty->GetLayoutConstraint();
315     CHECK_NULL_VOID(parentConstraint);
316     auto parentPercentReference = parentConstraint->percentReference.Height();
317     layoutConstraint->percentReference.SetHeight(parentPercentReference);
318 
319     auto props = layoutWrapper->GetLayoutProperty();
320     CHECK_NULL_VOID(props);
321     const auto& calcConstraint = props->GetCalcLayoutConstraint();
322     if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
323         calcConstraint->selfIdealSize.value().Height().has_value()) {
324         ScaleProperty scaleProperty;
325         if (layoutWrapper->GetGeometryNode() && layoutWrapper->GetGeometryNode()->GetParentLayoutConstraint()) {
326             scaleProperty = layoutWrapper->GetGeometryNode()->GetParentLayoutConstraint()->scaleProperty;
327         } else {
328             scaleProperty = layoutConstraint->scaleProperty;
329         }
330         userHeight_ = ConvertToPx(calcConstraint->selfIdealSize.value().Height()->GetDimension(),
331             scaleProperty, layoutConstraint->percentReference.Height()).value_or(0.0f);
332     }
333 
334     // Update embedded menu items percent reference dependent of the submenu constraint
335     if (GreatNotEqual(userHeight_, 0.0f)) {
336         childConstraint.percentReference.SetHeight(userHeight_);
337     } else {
338         childConstraint.percentReference.SetHeight(parentPercentReference);
339     }
340 }
341 
MarkChildForDelayedMeasurement(LayoutWrapper * layoutWrapper)342 void MultiMenuLayoutAlgorithm::MarkChildForDelayedMeasurement(LayoutWrapper* layoutWrapper)
343 {
344     CHECK_NULL_VOID(layoutWrapper);
345     layoutPolicyChildren_.clear();
346     for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
347         auto childLayoutProperty = child->GetLayoutProperty();
348         CHECK_NULL_CONTINUE(childLayoutProperty);
349         auto layoutPolicy = childLayoutProperty->GetLayoutPolicyProperty();
350         if (layoutPolicy.has_value()) {
351             auto widthLayoutPolicy = layoutPolicy.value().widthLayoutPolicy_;
352             auto heightLayoutPolicy = layoutPolicy.value().heightLayoutPolicy_;
353             if (widthLayoutPolicy.value_or(LayoutCalPolicy::NO_MATCH) != LayoutCalPolicy::NO_MATCH ||
354                 heightLayoutPolicy.value_or(LayoutCalPolicy::NO_MATCH) != LayoutCalPolicy::NO_MATCH) {
355                 layoutPolicyChildren_.emplace_back(child);
356             }
357         }
358     }
359 }
360 
MeasureAdaptiveLayoutChildren(LayoutWrapper * layoutWrapper,const LayoutConstraintF & childConstraint)361 void MultiMenuLayoutAlgorithm::MeasureAdaptiveLayoutChildren(
362     LayoutWrapper* layoutWrapper, const LayoutConstraintF& childConstraint)
363 {
364     MarkChildForDelayedMeasurement(layoutWrapper);
365     auto host = layoutWrapper->GetHostNode();
366     CHECK_NULL_VOID(host);
367     auto pattern = host->GetPattern<MenuPattern>();
368     CHECK_NULL_VOID(pattern);
369     auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
370     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
371     auto layoutProperty = layoutWrapper->GetLayoutProperty();
372     CHECK_NULL_VOID(layoutProperty);
373     auto safeAreaPadding = layoutProperty->GetOrCreateSafeAreaPadding();
374     if (!pattern->IsEmbedded()) {
375         MinusPaddingToNonNegativeSize(padding, frameSize);
376     } else {
377         MinusPaddingToNonNegativeSize(safeAreaPadding, frameSize);
378     }
379 
380     for (const auto& child : layoutPolicyChildren_) {
381         auto childNode = child->GetHostNode();
382         auto resetLayoutConstraint = ResetLayoutConstraintMinWidth(child, childConstraint);
383         SelectOverlayRightClickMenuLayoutHelper::AdjustLayoutConstraints(resetLayoutConstraint, child, layoutWrapper);
384         if (pattern->IsEmbedded() && (resetLayoutConstraint.minSize.Width() > resetLayoutConstraint.maxSize.Width())) {
385             resetLayoutConstraint.minSize.SetWidth(resetLayoutConstraint.maxSize.Width());
386         }
387         resetLayoutConstraint.parentIdealSize.SetSize(frameSize);
388         if (childNode && childNode->GetLayoutProperty() && childNode->GetLayoutProperty()->IsExpandConstraintNeeded()) {
389             child->GetGeometryNode()->SetParentLayoutConstraint(resetLayoutConstraint);
390         }
391     }
392 }
393 
UpdateSelfSize(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint,std::optional<LayoutConstraintF> & layoutConstraint)394 void MultiMenuLayoutAlgorithm::UpdateSelfSize(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint,
395     std::optional<LayoutConstraintF>& layoutConstraint)
396 {
397     auto node = layoutWrapper->GetHostNode();
398     CHECK_NULL_VOID(node);
399     auto pattern = node->GetPattern<MenuPattern>();
400     CHECK_NULL_VOID(pattern);
401 
402     float contentHeight = 0.0f;
403     float contentWidth = childConstraint.selfIdealSize.Width().value_or(0.0f);
404     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
405         if (!child) {
406             TAG_LOGW(AceLogTag::ACE_MENU, "child is null in MultiMenu");
407             continue;
408         }
409         auto resetLayoutConstraint = ResetLayoutConstraintMinWidth(child, childConstraint);
410         SelectOverlayRightClickMenuLayoutHelper::AdjustLayoutConstraints(
411             resetLayoutConstraint, child, layoutWrapper);
412         if (pattern->IsEmbedded() && (resetLayoutConstraint.minSize.Width() > resetLayoutConstraint.maxSize.Width())) {
413             resetLayoutConstraint.minSize.SetWidth(resetLayoutConstraint.maxSize.Width());
414         }
415         child->Measure(resetLayoutConstraint);
416         auto childGeometryNode = child->GetGeometryNode();
417         CHECK_NULL_VOID(childGeometryNode);
418         auto childHeight = childGeometryNode->GetMarginFrameSize().Height();
419         if (pattern->IsEmbedded()) {
420             childHeight = std::max(childHeight, childGeometryNode->GetContentSize().Height());
421         }
422         contentHeight += childHeight;
423     }
424     layoutWrapper->GetGeometryNode()->SetContentSize(SizeF(contentWidth, contentHeight));
425     BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
426 
427     // Stack or Embedded submenu must follow parent width
428     if (pattern->IsStackSubmenu() || pattern->IsEmbedded()) {
429         auto idealSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
430         auto width = layoutConstraint->maxSize.Width();
431         idealSize.SetWidth(width);
432         if (pattern->IsEmbedded()) {
433             auto idealHeight = GreatNotEqual(userHeight_, 0.0f) ? userHeight_ : contentHeight;
434             idealSize.SetHeight(idealHeight);
435         }
436         layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
437     } else if (layoutConstraint->selfIdealSize.Width().has_value()) {
438         auto idealWidth = std::max(layoutConstraint->minSize.Width(),
439             std::min(layoutConstraint->maxSize.Width(), layoutConstraint->selfIdealSize.Width().value()));
440         auto idealSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
441         idealSize.SetWidth(idealWidth);
442         layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
443     }
444     if (pattern->IsEnableChildrenMatchParent()) {
445         MeasureAdaptiveLayoutChildren(layoutWrapper, childConstraint);
446     }
447 }
448 
UpdateConstraintBaseOnMenuItems(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)449 void MultiMenuLayoutAlgorithm::UpdateConstraintBaseOnMenuItems(
450     LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
451 {
452     // multiMenu children are menuItem or menuItemGroup, constrain width is same as the menu
453     auto maxChildrenWidth = GetChildrenMaxWidth(layoutWrapper, constraint);
454     constraint.selfIdealSize.SetWidth(maxChildrenWidth);
455 }
456 
GetChildrenMaxWidth(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)457 float MultiMenuLayoutAlgorithm::GetChildrenMaxWidth(
458     LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
459 {
460     float maxWidth = 0.0f;
461     auto node = layoutWrapper->GetHostNode();
462     CHECK_NULL_RETURN(node, maxWidth);
463     auto pattern = node->GetPattern<MenuPattern>();
464     CHECK_NULL_RETURN(pattern, maxWidth);
465     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
466         auto childConstraint = ResetLayoutConstraintMinWidth(child, layoutConstraint);
467         if (pattern->IsEmbedded() && (childConstraint.minSize.Width() > childConstraint.maxSize.Width())) {
468             childConstraint.minSize.SetWidth(childConstraint.maxSize.Width());
469         }
470         SelectOverlayRightClickMenuLayoutHelper::AdjustLayoutConstraintsAutoWidth(child, layoutWrapper);
471         auto childLayoutProperty = child->GetLayoutProperty();
472         if (childLayoutProperty) {
473             auto layoutPolicy = childLayoutProperty->GetLayoutPolicyProperty();
474             if (layoutPolicy->IsMatch()) {
475                 continue;
476             }
477         }
478         child->Measure(childConstraint);
479         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
480         maxWidth = std::max(maxWidth, childSize.Width());
481     }
482     return maxWidth;
483 }
484 
ResetLayoutConstraintMinWidth(const RefPtr<LayoutWrapper> & child,const LayoutConstraintF & layoutConstraint)485 LayoutConstraintF MultiMenuLayoutAlgorithm::ResetLayoutConstraintMinWidth(
486     const RefPtr<LayoutWrapper>& child, const LayoutConstraintF& layoutConstraint)
487 {
488     auto childLayoutProps = child->GetLayoutProperty();
489     CHECK_NULL_RETURN(childLayoutProps, layoutConstraint);
490     auto childConstraint = layoutConstraint;
491     const auto& calcConstraint = childLayoutProps->GetCalcLayoutConstraint();
492     if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
493         calcConstraint->selfIdealSize.value().Width().has_value()) {
494         childConstraint.minSize.Reset();
495     }
496     return childConstraint;
497 }
498 
UpdateSelectOverlayMenuMinWidth(const RefPtr<MenuPattern> & pattern,const RefPtr<GridColumnInfo> & columnInfo)499 bool MultiMenuLayoutAlgorithm::UpdateSelectOverlayMenuMinWidth(
500     const RefPtr<MenuPattern>& pattern, const RefPtr<GridColumnInfo>& columnInfo)
501 {
502     CHECK_NULL_RETURN(pattern, false);
503     auto mainMenuPattern = pattern->GetMainMenuPattern();
504     CHECK_NULL_RETURN(mainMenuPattern, false);
505     CHECK_NULL_RETURN(mainMenuPattern->IsSelectOverlayRightClickMenu(), false);
506     auto menuWrapper = pattern->GetMenuWrapper();
507     CHECK_NULL_RETURN(menuWrapper, false);
508     auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
509     CHECK_NULL_RETURN(menuWrapperPattern, false);
510     CHECK_NULL_RETURN(menuWrapperPattern->GetIsSelectOverlaySubWindowWrapper(), false);
511     auto mainWindowContainerId = menuWrapperPattern->GetContainerId();
512     auto container = Container::GetContainer(mainWindowContainerId);
513     CHECK_NULL_RETURN(container, false);
514     auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
515     CHECK_NULL_RETURN(pipelineContext, false);
516     auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
517     auto mainWindowWidth = displayWindowRect.Width();
518     if (Positive(mainWindowWidth)) {
519         auto parent = columnInfo->GetParent();
520         CHECK_NULL_RETURN(parent, false);
521         parent->BuildColumnWidth(mainWindowWidth);
522         TAG_LOGD(
523             AceLogTag::ACE_MENU, "Update select overlay right click menu min width with main window width constraint.");
524         return true;
525     }
526     return false;
527 }
528 } // namespace OHOS::Ace::NG
529