1 /*
2 * Copyright (c) 2022 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/option/option_layout_algorithm.h"
17
18 #include <optional>
19 #include <string>
20
21 #include "base/utils/utils.h"
22 #include "core/components/select/select_theme.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/pattern/option/option_paint_property.h"
25 #include "core/components_ng/pattern/option/option_pattern.h"
26 #include "core/pipeline/pipeline_base.h"
27 #include "core/components_ng/pattern/security_component/security_component_layout_property.h"
28 #include "core/components_ng/pattern/image/image_pattern.h"
29
30 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * layoutWrapper)31 void OptionLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
32 {
33 CHECK_NULL_VOID(layoutWrapper);
34 auto optionNode = layoutWrapper->GetHostNode();
35 CHECK_NULL_VOID(optionNode);
36 auto optionPattern = optionNode->GetPattern<OptionPattern>();
37 CHECK_NULL_VOID(optionPattern);
38 const auto& selectTheme = optionPattern->GetSelectTheme();
39 CHECK_NULL_VOID(selectTheme);
40 horInterval_ = static_cast<float>(selectTheme->GetMenuIconPadding().ConvertToPx()) -
41 static_cast<float>(selectTheme->GetOutPadding().ConvertToPx());
42 auto props = layoutWrapper->GetLayoutProperty();
43 CHECK_NULL_VOID(props);
44 auto layoutConstraint = props->GetLayoutConstraint();
45 CHECK_NULL_VOID(layoutConstraint);
46 auto idealSize = CreateIdealSize(
47 layoutConstraint.value(), Axis::HORIZONTAL, props->GetMeasureType(MeasureType::MATCH_CONTENT), true);
48 float maxChildWidth = layoutConstraint->maxSize.Width() - horInterval_ * 2.0f;
49
50 // measure child
51 auto childConstraint = props->CreateChildConstraint();
52 childConstraint.maxSize.SetWidth(maxChildWidth);
53 // set self size based on childNode size;
54 auto minOptionHeight = static_cast<float>(selectTheme->GetOptionMinHeight().ConvertToPx());
55 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
56 CHECK_NULL_VOID(child);
57 UpdateIconMargin(layoutWrapper);
58 MeasureRow(child, childConstraint);
59
60 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
61 childSize.AddWidth(horInterval_ * 2.0f);
62 idealSize.UpdateSizeWithCheck(childSize);
63
64 auto idealWidth = GetIdealWidth(layoutWrapper);
65 if (idealWidth.has_value()) {
66 auto optionPaintProperty = optionNode->GetPaintProperty<OptionPaintProperty>();
67 if (optionPaintProperty && (optionPaintProperty->GetIdealWidthForWeb() > 0) &&
68 (idealWidth.value() < optionPaintProperty->GetIdealWidthForWeb())) {
69 idealSize.SetWidth(std::min(optionPaintProperty->GetIdealWidthForWeb(), layoutConstraint->maxSize.Width()));
70 } else {
71 idealSize.SetWidth(idealWidth.value());
72 }
73 }
74
75 idealSize.SetHeight(std::max(minOptionHeight, idealSize.Height()));
76
77 if (optionPattern->IsSelectOption() && optionPattern->GetHasOptionWidth()) {
78 auto selectOptionWidth = optionPattern->GetSelectOptionWidth();
79 idealSize.SetWidth(selectOptionWidth);
80 }
81 auto rowChild = child->GetOrCreateChildByIndex(0);
82 if (rowChild && (rowChild->GetHostTag() == V2::PASTE_BUTTON_ETS_TAG)) {
83 float dividerWidth = static_cast<float>(selectTheme->GetDefaultDividerWidth().ConvertToPx());
84 SizeF idealSizePaste(idealSize.Width() - dividerWidth, idealSize.Height() - dividerWidth);
85 childConstraint.selfIdealSize.SetSize(idealSizePaste);
86 auto securityLayoutProperty = DynamicCast<SecurityComponentLayoutProperty>(rowChild->GetLayoutProperty());
87 CHECK_NULL_VOID(securityLayoutProperty);
88 securityLayoutProperty->UpdateBackgroundLeftPadding(Dimension(horInterval_));
89 rowChild->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
90 rowChild->Measure(childConstraint);
91 }
92 LOGD("option frame size set to %{public}s", idealSize.ToString().c_str());
93 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
94 }
95
Layout(LayoutWrapper * layoutWrapper)96 void OptionLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
97 {
98 CHECK_NULL_VOID(layoutWrapper);
99 auto layoutProps = layoutWrapper->GetLayoutProperty();
100 CHECK_NULL_VOID(layoutProps);
101 auto optionSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
102 auto optionHeight = optionSize.Height();
103 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
104 child->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_LAYOUT);
105 auto rowChild = child->GetOrCreateChildByIndex(0);
106 if (rowChild && (rowChild->GetHostTag() == V2::PASTE_BUTTON_ETS_TAG)) {
107 float horInterval = 0.0f;
108 if (layoutProps->GetNonAutoLayoutDirection() == TextDirection::RTL) {
109 SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
110 horInterval = optionSize.Width() - childSize.Width();
111 }
112 child->GetGeometryNode()->SetMarginFrameOffset(
113 OffsetF(horInterval, (optionHeight - child->GetGeometryNode()->GetFrameSize().Height()) / 2.0f));
114 child->Layout();
115 return;
116 }
117
118 float horInterval = horInterval_;
119 if (layoutProps->GetNonAutoLayoutDirection() == TextDirection::RTL) {
120 SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
121 horInterval = optionSize.Width() - childSize.Width() - horInterval_;
122 }
123 child->GetGeometryNode()->SetMarginFrameOffset(
124 OffsetF(horInterval, (optionHeight - child->GetGeometryNode()->GetFrameSize().Height()) / 2.0f));
125 child->Layout();
126 }
127
GetIdealWidth(LayoutWrapper * layoutWrapper)128 std::optional<float> OptionLayoutAlgorithm::GetIdealWidth(LayoutWrapper* layoutWrapper)
129 {
130 CHECK_NULL_RETURN(layoutWrapper, std::nullopt);
131 // layout property not update in layoutWrapper when measure
132 auto optionProps = layoutWrapper->GetLayoutProperty();
133 CHECK_NULL_RETURN(optionProps, std::nullopt);
134 CHECK_NULL_RETURN(optionProps->GetCalcLayoutConstraint(), std::nullopt);
135 CHECK_NULL_RETURN(optionProps->GetCalcLayoutConstraint()->minSize, std::nullopt);
136 if (optionProps->GetCalcLayoutConstraint()->minSize->Width()->IsValid()) {
137 auto idealWidth = optionProps->GetCalcLayoutConstraint()->minSize->Width()->GetDimension().ConvertToPx();
138 return idealWidth;
139 }
140 return std::nullopt;
141 }
142
MeasureRow(const RefPtr<LayoutWrapper> & row,const LayoutConstraintF & constraint)143 void OptionLayoutAlgorithm::MeasureRow(const RefPtr<LayoutWrapper>& row, const LayoutConstraintF& constraint)
144 {
145 auto children = row->GetAllChildrenWithBuild();
146 CHECK_NULL_VOID(!children.empty());
147
148 float spaceWidth = constraint.maxSize.Width();
149 float rowWidth = 0.0f;
150 float roWHeight = 0.0f;
151 for (const auto& child : children) {
152 if (child != children.back()) {
153 // not content node
154 child->Measure(constraint);
155 } else {
156 // content node update constraint max width
157 auto contentConstraint = constraint;
158 contentConstraint.maxSize.SetWidth(spaceWidth);
159 child->Measure(contentConstraint);
160 }
161 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
162 spaceWidth -= childSize.Width();
163 rowWidth += childSize.Width();
164 roWHeight = std::max(roWHeight, childSize.Height());
165 }
166 row->GetGeometryNode()->SetFrameSize(SizeF(rowWidth, roWHeight));
167 }
168
UpdateIconMargin(LayoutWrapper * layoutWrapper)169 void OptionLayoutAlgorithm::UpdateIconMargin(LayoutWrapper* layoutWrapper)
170 {
171 auto optionNode = layoutWrapper->GetHostNode();
172 CHECK_NULL_VOID(optionNode);
173 auto optionPattern = optionNode->GetPattern<OptionPattern>();
174 CHECK_NULL_VOID(optionPattern);
175 auto layoutProps = layoutWrapper->GetLayoutProperty();
176 CHECK_NULL_VOID(layoutProps);
177 auto direction = layoutProps->GetNonAutoLayoutDirection();
178 bool isRtl = direction == TextDirection::RTL;
179 const auto& selectTheme = optionPattern->GetSelectTheme();
180 CHECK_NULL_VOID(selectTheme);
181 auto calcLength = CalcLength(selectTheme->GetIconContentPadding());
182 MarginProperty margin;
183 if (isRtl) {
184 margin.left = calcLength;
185 margin.right = CalcLength();
186 } else {
187 margin.left = CalcLength();
188 margin.right = calcLength;
189 }
190 Alignment align = isRtl ? Alignment::CENTER_RIGHT : Alignment::CENTER_LEFT;
191 auto child = layoutWrapper->GetOrCreateChildByIndex(0);
192 for (auto iconChild : child->GetAllChildrenWithBuild()) {
193 if ((iconChild->GetHostTag() == V2::IMAGE_ETS_TAG) || (iconChild->GetHostTag() == V2::SYMBOL_ETS_TAG)) {
194 auto iconProps = iconChild->GetLayoutProperty();
195 iconProps->UpdateAlignment(align);
196 iconProps->UpdateMargin(margin);
197 }
198 }
199 }
200 } // namespace OHOS::Ace::NG
201