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