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