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/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/utils/utils.h"
21 #include "core/components/common/layout/grid_system_manager.h"
22 #include "core/components_ng/layout/box_layout_algorithm.h"
23 #include "core/components_ng/property/measure_utils.h"
24
25 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * layoutWrapper)26 void MultiMenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
27 {
28 CHECK_NULL_VOID(layoutWrapper);
29 auto layoutProperty = layoutWrapper->GetLayoutProperty();
30 CHECK_NULL_VOID(layoutProperty);
31 auto layoutConstraint = layoutProperty->GetLayoutConstraint();
32 CHECK_NULL_VOID(layoutConstraint);
33 auto childConstraint = layoutProperty->CreateChildConstraint();
34 childConstraint.maxSize.SetWidth(layoutConstraint->maxSize.Width());
35 // constraint max size minus padding
36 const auto& padding = layoutProperty->CreatePaddingAndBorder();
37 MinusPaddingToSize(padding, childConstraint.maxSize);
38 if (layoutConstraint->selfIdealSize.Width().has_value()) {
39 // when Menu is set self ideal width, make children node adaptively fill up.
40 auto idealWidth =
41 std::max(layoutConstraint->minSize.Width(),
42 std::min(layoutConstraint->maxSize.Width(), layoutConstraint->selfIdealSize.Width().value())) -
43 padding.Width();
44 childConstraint.selfIdealSize.SetWidth(idealWidth);
45 } else {
46 // constraint min width base on grid column
47 auto columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
48 CHECK_NULL_VOID(columnInfo);
49 CHECK_NULL_VOID(columnInfo->GetParent());
50 columnInfo->GetParent()->BuildColumnWidth();
51 auto minWidth = static_cast<float>(columnInfo->GetWidth()) - padding.Width();
52 childConstraint.minSize.SetWidth(minWidth);
53 }
54 // Calculate max width of menu items
55 UpdateConstraintBaseOnMenuItems(layoutWrapper, childConstraint);
56
57 float contentHeight = 0.0f;
58 float contentWidth = childConstraint.selfIdealSize.Width().value();
59 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
60 child->Measure(ResetLayoutConstraintMinWidth(child, childConstraint));
61 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
62 LOGD("child finish measure, child %{public}s size = %{public}s", child->GetHostTag().c_str(),
63 child->GetGeometryNode()->GetMarginFrameSize().ToString().c_str());
64 contentHeight += childSize.Height();
65 }
66 layoutWrapper->GetGeometryNode()->SetContentSize(SizeF(contentWidth, contentHeight));
67 BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
68
69 if (layoutConstraint->selfIdealSize.Width().has_value()) {
70 auto idealWidth = std::max(layoutConstraint->minSize.Width(),
71 std::min(layoutConstraint->maxSize.Width(), layoutConstraint->selfIdealSize.Width().value()));
72 auto idealSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
73 idealSize.SetWidth(idealWidth);
74 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
75 }
76 }
77
Layout(LayoutWrapper * layoutWrapper)78 void MultiMenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
79 {
80 CHECK_NULL_VOID(layoutWrapper);
81 BoxLayoutAlgorithm::PerformLayout(layoutWrapper);
82
83 auto pipeline = PipelineBase::GetCurrentContext();
84 CHECK_NULL_VOID(pipeline);
85 auto theme = pipeline->GetTheme<SelectTheme>();
86 CHECK_NULL_VOID(theme);
87 auto layoutProperty = layoutWrapper->GetLayoutProperty();
88 CHECK_NULL_VOID(layoutProperty);
89 // translate each option by the height of previous options
90 auto outPadding = static_cast<float>(theme->GetOutPadding().ConvertToPx());
91 const auto& padding = layoutProperty->CreatePaddingAndBorder();
92 OffsetF translate(padding.left.value_or(outPadding), padding.top.value_or(outPadding));
93 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
94 child->GetGeometryNode()->SetMarginFrameOffset(translate);
95 child->Layout();
96 translate.AddY(child->GetGeometryNode()->GetMarginFrameSize().Height());
97 }
98 }
99
UpdateConstraintBaseOnMenuItems(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)100 void MultiMenuLayoutAlgorithm::UpdateConstraintBaseOnMenuItems(
101 LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
102 {
103 // multiMenu children are menuItem or menuItemGroup, constrain width is same as the menu
104 auto maxChildrenWidth = GetChildrenMaxWidth(layoutWrapper, constraint);
105 constraint.selfIdealSize.SetWidth(maxChildrenWidth);
106 }
107
GetChildrenMaxWidth(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)108 float MultiMenuLayoutAlgorithm::GetChildrenMaxWidth(
109 LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
110 {
111 float maxWidth = 0.0f;
112 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
113 auto childConstraint = ResetLayoutConstraintMinWidth(child, layoutConstraint);
114 child->Measure(childConstraint);
115 auto childSize = child->GetGeometryNode()->GetFrameSize();
116 maxWidth = std::max(maxWidth, childSize.Width());
117 }
118 return maxWidth;
119 }
120
ResetLayoutConstraintMinWidth(const RefPtr<LayoutWrapper> & child,const LayoutConstraintF & layoutConstraint)121 LayoutConstraintF MultiMenuLayoutAlgorithm::ResetLayoutConstraintMinWidth(
122 const RefPtr<LayoutWrapper>& child, const LayoutConstraintF& layoutConstraint)
123 {
124 auto childLayoutProps = child->GetLayoutProperty();
125 CHECK_NULL_RETURN(childLayoutProps, layoutConstraint);
126 auto childConstraint = layoutConstraint;
127 const auto& calcConstraint = childLayoutProps->GetCalcLayoutConstraint();
128 if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
129 calcConstraint->selfIdealSize.value().Width().has_value()) {
130 childConstraint.minSize.Reset();
131 }
132 return childConstraint;
133 }
134 } // namespace OHOS::Ace::NG
135