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