• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/button/button_layout_algorithm.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/button/button_theme.h"
20 #include "core/components_ng/base/frame_node.h"
21 #include "core/components_ng/layout/layout_wrapper.h"
22 #include "core/components_ng/pattern/button/button_layout_property.h"
23 #include "core/components_ng/pattern/text/text_layout_property.h"
24 #include "core/components_ng/property/measure_utils.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26 
27 namespace OHOS::Ace::NG {
28 
Measure(LayoutWrapper * layoutWrapper)29 void ButtonLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
30 {
31     auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
32     HandleChildLayoutConstraint(layoutWrapper, layoutConstraint);
33     auto buttonLayoutProperty = DynamicCast<ButtonLayoutProperty>(layoutWrapper->GetLayoutProperty());
34     CHECK_NULL_VOID(buttonLayoutProperty);
35     if (buttonLayoutProperty->HasLabel()) {
36         // If the button has label, according to whether the font size is set to do the corresponding expansion button,
37         // font reduction, truncation and other operations.
38         HandleAdaptiveText(layoutWrapper, layoutConstraint);
39     } else {
40         // If the button has not label, measure the child directly.
41         for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
42             child->Measure(layoutConstraint);
43         }
44     }
45     PerformMeasureSelf(layoutWrapper);
46 }
47 
HandleChildLayoutConstraint(LayoutWrapper * layoutWrapper,LayoutConstraintF & layoutConstraint)48 void ButtonLayoutAlgorithm::HandleChildLayoutConstraint(
49     LayoutWrapper* layoutWrapper, LayoutConstraintF& layoutConstraint)
50 {
51     auto buttonLayoutProperty = DynamicCast<ButtonLayoutProperty>(layoutWrapper->GetLayoutProperty());
52     CHECK_NULL_VOID(buttonLayoutProperty);
53     if (!buttonLayoutProperty->HasLabel()) {
54         return;
55     }
56     if (buttonLayoutProperty->GetType().value_or(ButtonType::CAPSULE) == ButtonType::CIRCLE) {
57         layoutConstraint.maxSize = HandleLabelCircleButtonConstraint(layoutWrapper).value_or(SizeF());
58         return;
59     }
60     const auto& selfLayoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
61     // If height is not set, apply the default height.
62     if (selfLayoutConstraint && !selfLayoutConstraint->selfIdealSize.Height().has_value()) {
63         auto buttonTheme = PipelineBase::GetCurrentContext()->GetTheme<ButtonTheme>();
64         CHECK_NULL_VOID(buttonTheme);
65         auto defaultHeight = static_cast<float>(buttonTheme->GetHeight().ConvertToPx());
66         auto maxHeight = selfLayoutConstraint->maxSize.Height();
67         layoutConstraint.maxSize.SetHeight(maxHeight > defaultHeight ? defaultHeight : maxHeight);
68     }
69 }
70 
71 // If the ButtonType is CIRCLE, then omit text by the smaller edge.
HandleLabelCircleButtonConstraint(LayoutWrapper * layoutWrapper)72 std::optional<SizeF> ButtonLayoutAlgorithm::HandleLabelCircleButtonConstraint(LayoutWrapper* layoutWrapper)
73 {
74     SizeF constraintSize;
75     auto buttonLayoutProperty = DynamicCast<ButtonLayoutProperty>(layoutWrapper->GetLayoutProperty());
76     CHECK_NULL_RETURN(buttonLayoutProperty, constraintSize);
77     const auto& selfLayoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
78     auto buttonTheme = PipelineBase::GetCurrentContext()->GetTheme<ButtonTheme>();
79     CHECK_NULL_RETURN(buttonTheme, constraintSize);
80     const auto& padding = buttonLayoutProperty->CreatePaddingAndBorder();
81     auto defaultHeight = static_cast<float>(buttonTheme->GetHeight().ConvertToPx());
82     float minLength = 0.0f;
83     if (selfLayoutConstraint->selfIdealSize.IsNull()) {
84         // Width and height are not set.
85         minLength = defaultHeight;
86     } else if (selfLayoutConstraint->selfIdealSize.Width().has_value() &&
87                !selfLayoutConstraint->selfIdealSize.Height().has_value()) {
88         // Only width is set.
89         minLength = selfLayoutConstraint->selfIdealSize.Width().value();
90     } else if (selfLayoutConstraint->selfIdealSize.Height().has_value() &&
91                !selfLayoutConstraint->selfIdealSize.Width().has_value()) {
92         // Only height is set.
93         minLength = selfLayoutConstraint->selfIdealSize.Height().value();
94     } else {
95         // Both width and height are set.
96         auto buttonWidth = selfLayoutConstraint->selfIdealSize.Width().value();
97         auto buttonHeight = selfLayoutConstraint->selfIdealSize.Height().value();
98         minLength = std::min(buttonWidth, buttonHeight);
99     }
100     if (buttonLayoutProperty->HasBorderRadius() && selfLayoutConstraint->selfIdealSize.IsNull()) {
101         auto radius =
102             static_cast<float>(GetFirstValidRadius(buttonLayoutProperty->GetBorderRadius().value()).ConvertToPx());
103         minLength = 2 * radius;
104     }
105     constraintSize.SetSizeT(SizeF(minLength, minLength));
106     MinusPaddingToSize(padding, constraintSize);
107     return ConstrainSize(constraintSize, selfLayoutConstraint->minSize, selfLayoutConstraint->maxSize);
108 }
109 
HandleAdaptiveText(LayoutWrapper * layoutWrapper,LayoutConstraintF & layoutConstraint)110 void ButtonLayoutAlgorithm::HandleAdaptiveText(LayoutWrapper* layoutWrapper, LayoutConstraintF& layoutConstraint)
111 {
112     auto buttonLayoutProperty = DynamicCast<ButtonLayoutProperty>(layoutWrapper->GetLayoutProperty());
113     CHECK_NULL_VOID(buttonLayoutProperty);
114     auto buttonTheme = PipelineBase::GetCurrentContext()->GetTheme<ButtonTheme>();
115     CHECK_NULL_VOID(buttonTheme);
116     auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
117     CHECK_NULL_VOID(childWrapper);
118     auto childConstraint = layoutWrapper->GetLayoutProperty()->GetContentLayoutConstraint();
119     childWrapper->Measure(childConstraint);
120     auto textSize = childWrapper->GetGeometryNode()->GetContentSize();
121     if (buttonLayoutProperty->HasFontSize()) {
122         // Fonsize is set. When the font height is larger than the button height, make the button fit the font
123         // height.
124         if (GreatOrEqual(textSize.Height(), layoutConstraint.maxSize.Height())) {
125             layoutConstraint.maxSize.SetHeight(textSize.Height());
126         }
127     } else {
128         // Fonsize is not set. When the font width is greater than the button width, dynamically change the font
129         // size to no less than 9sp.
130         auto textLayoutProperty = DynamicCast<TextLayoutProperty>(childWrapper->GetLayoutProperty());
131         textLayoutProperty->UpdateAdaptMaxFontSize(
132             buttonLayoutProperty->GetMaxFontSize().value_or(buttonTheme->GetMaxFontSize()));
133         textLayoutProperty->UpdateAdaptMinFontSize(
134             buttonLayoutProperty->GetMinFontSize().value_or(buttonTheme->GetMinFontSize()));
135     }
136     childWrapper->Measure(layoutConstraint);
137     childSize_ = childWrapper->GetGeometryNode()->GetContentSize();
138 }
139 
HandleBorderRadius(LayoutWrapper * layoutWrapper)140 void ButtonLayoutAlgorithm::HandleBorderRadius(LayoutWrapper* layoutWrapper)
141 {
142     auto host = layoutWrapper->GetHostNode();
143     CHECK_NULL_VOID(host);
144     auto buttonLayoutProperty = DynamicCast<ButtonLayoutProperty>(layoutWrapper->GetLayoutProperty());
145     CHECK_NULL_VOID(buttonLayoutProperty);
146     auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
147     auto renderContext = host->GetRenderContext();
148     if (buttonLayoutProperty->GetType().value_or(ButtonType::CAPSULE) == ButtonType::CIRCLE) {
149         auto minSize = std::min(frameSize.Height(), frameSize.Width());
150         auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
151         if (buttonLayoutProperty->HasBorderRadius() && layoutConstraint.parentIdealSize.IsNull()) {
152             auto borderRadius = buttonLayoutProperty->GetBorderRadius().value_or(NG::BorderRadiusProperty());
153             minSize = static_cast<float>(GetFirstValidRadius(borderRadius).ConvertToPx() * 2);
154         }
155         renderContext->UpdateBorderRadius(BorderRadiusProperty(Dimension(minSize / 2)));
156         MeasureCircleButton(layoutWrapper);
157     } else if (buttonLayoutProperty->GetType().value_or(ButtonType::CAPSULE) == ButtonType::CAPSULE) {
158         renderContext->UpdateBorderRadius(BorderRadiusProperty(Dimension(frameSize.Height() / 2)));
159     } else {
160         auto normalRadius =
161             buttonLayoutProperty->GetBorderRadiusValue(BorderRadiusProperty({ 0.0_vp, 0.0_vp, 0.0_vp, 0.0_vp }));
162         renderContext->UpdateBorderRadius(normalRadius);
163     }
164 }
165 
166 // Called to perform measure current render node.
PerformMeasureSelf(LayoutWrapper * layoutWrapper)167 void ButtonLayoutAlgorithm::PerformMeasureSelf(LayoutWrapper* layoutWrapper)
168 {
169     auto buttonLayoutProperty = DynamicCast<ButtonLayoutProperty>(layoutWrapper->GetLayoutProperty());
170     CHECK_NULL_VOID(buttonLayoutProperty);
171     BoxLayoutAlgorithm::PerformMeasureSelf(layoutWrapper);
172     if (buttonLayoutProperty->HasLabel()) {
173         auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
174         auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
175         const auto& selfLayoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
176         auto padding = buttonLayoutProperty->CreatePaddingAndBorder();
177         auto topPadding = padding.top.value_or(0.0);
178         auto bottomPadding = padding.bottom.value_or(0.0);
179         if (buttonLayoutProperty->GetType().value_or(ButtonType::CAPSULE) == ButtonType::CIRCLE) {
180             HandleLabelCircleButtonFrameSize(layoutConstraint, frameSize);
181         } else {
182             if (selfLayoutConstraint && !selfLayoutConstraint->selfIdealSize.Height().has_value()) {
183                 auto buttonTheme = PipelineBase::GetCurrentContext()->GetTheme<ButtonTheme>();
184                 CHECK_NULL_VOID(buttonTheme);
185                 auto defaultHeight = static_cast<float>(buttonTheme->GetHeight().ConvertToPx());
186                 auto layoutContraint = buttonLayoutProperty->GetLayoutConstraint();
187                 CHECK_NULL_VOID(layoutContraint);
188                 auto maxHeight = layoutContraint->maxSize.Height();
189                 auto actualHeight = static_cast<float>(childSize_.Height() + topPadding + bottomPadding);
190                 actualHeight = std::min(actualHeight, maxHeight);
191                 frameSize.SetHeight(maxHeight > defaultHeight ? std::max(defaultHeight, actualHeight) : maxHeight);
192             }
193         }
194         // Determine if the button needs to fit the font size.
195         if (buttonLayoutProperty->HasFontSize()) {
196             if (GreatOrEqual(childSize_.Height() + topPadding + bottomPadding, frameSize.Height())) {
197                 frameSize = SizeF(frameSize.Width(), childSize_.Height() + topPadding + bottomPadding);
198             }
199         }
200         layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize);
201     }
202     HandleBorderRadius(layoutWrapper);
203 }
204 
HandleLabelCircleButtonFrameSize(const LayoutConstraintF & layoutConstraint,SizeF & frameSize)205 void ButtonLayoutAlgorithm::HandleLabelCircleButtonFrameSize(
206     const LayoutConstraintF& layoutConstraint, SizeF& frameSize)
207 {
208     auto pipeline = PipelineBase::GetCurrentContext();
209     CHECK_NULL_VOID(pipeline);
210     auto buttonTheme = pipeline->GetTheme<ButtonTheme>();
211     CHECK_NULL_VOID(buttonTheme);
212     auto defaultHeight = buttonTheme->GetHeight().ConvertToPx();
213     float minLength = 0.0f;
214     if (layoutConstraint.parentIdealSize.IsNull()) {
215         minLength = static_cast<float>(defaultHeight);
216     } else if (layoutConstraint.parentIdealSize.Width().has_value() &&
217                !layoutConstraint.parentIdealSize.Height().has_value()) {
218         minLength = frameSize.Width();
219     } else if (layoutConstraint.parentIdealSize.Height().has_value() &&
220                !layoutConstraint.parentIdealSize.Width().has_value()) {
221         minLength = frameSize.Height();
222     } else {
223         minLength = std::min(frameSize.Width(), frameSize.Height());
224     }
225     frameSize.SetWidth(minLength);
226     frameSize.SetHeight(minLength);
227 }
228 
MeasureCircleButton(LayoutWrapper * layoutWrapper)229 void ButtonLayoutAlgorithm::MeasureCircleButton(LayoutWrapper* layoutWrapper)
230 {
231     auto frameNode = layoutWrapper->GetHostNode();
232     CHECK_NULL_VOID(frameNode);
233     const auto& radius = frameNode->GetRenderContext()->GetBorderRadius();
234     SizeF frameSize = { -1, -1 };
235     if (radius.has_value()) {
236         auto radiusTopMax = std::max(radius->radiusTopLeft, radius->radiusTopRight);
237         auto radiusBottomMax = std::max(radius->radiusBottomLeft, radius->radiusBottomRight);
238         auto radiusMax = std::max(radiusTopMax, radiusBottomMax);
239         auto rrectRadius = radiusMax.value_or(0.0_vp).ConvertToPx();
240         frameSize.SetSizeT(SizeF { static_cast<float>(rrectRadius * 2), static_cast<float>(rrectRadius * 2) });
241     }
242     frameSize.UpdateIllegalSizeWithCheck(SizeF { 0.0f, 0.0f });
243     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize);
244 }
245 
GetFirstValidRadius(const BorderRadiusProperty & borderRadius)246 Dimension ButtonLayoutAlgorithm::GetFirstValidRadius(const BorderRadiusProperty& borderRadius)
247 {
248     if (borderRadius.radiusTopLeft.has_value()) {
249         return borderRadius.radiusTopLeft.value();
250     }
251     if (borderRadius.radiusTopRight.has_value()) {
252         return borderRadius.radiusTopRight.value();
253     }
254     if (borderRadius.radiusBottomLeft.has_value()) {
255         return borderRadius.radiusBottomLeft.value();
256     }
257     if (borderRadius.radiusBottomRight.has_value()) {
258         return borderRadius.radiusBottomRight.value();
259     }
260     return 0.0_vp;
261 }
262 } // namespace OHOS::Ace::NG
263