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