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/layout/box_layout_algorithm.h"
17 
18 #include <optional>
19 
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/geometry/size.h"
23 #include "base/utils/utils.h"
24 #include "core/common/window.h"
25 #include "core/components_ng/base/frame_node.h"
26 #include "core/components_ng/layout/layout_wrapper.h"
27 #include "core/components_ng/property/measure_property.h"
28 #include "core/components_ng/property/measure_utils.h"
29 #include "core/components_ng/property/property.h"
30 #include "core/pipeline/pipeline_base.h"
31 
32 namespace OHOS::Ace::NG {
33 
Measure(LayoutWrapper * layoutWrapper)34 void BoxLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
35 {
36     auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
37     for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
38         child->Measure(layoutConstraint);
39     }
40     PerformMeasureSelf(layoutWrapper);
41 }
42 
Layout(LayoutWrapper * layoutWrapper)43 void BoxLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
44 {
45     PerformLayout(layoutWrapper);
46     for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
47         child->Layout();
48     }
49 }
50 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)51 std::optional<SizeF> BoxLayoutAlgorithm::MeasureContent(
52     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
53 {
54     auto host = layoutWrapper->GetHostNode();
55     CHECK_NULL_RETURN(host, std::nullopt);
56     if (!host->IsAtomicNode()) {
57         return std::nullopt;
58     }
59     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
60     auto measureType = layoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT);
61     OptionalSizeF contentSize;
62     do {
63         // Use idea size first if it is valid.
64         contentSize.UpdateSizeWithCheck(contentConstraint.selfIdealSize);
65         if (contentSize.IsValid()) {
66             break;
67         }
68 
69         if (measureType == MeasureType::MATCH_PARENT) {
70             contentSize.UpdateIllegalSizeWithCheck(contentConstraint.parentIdealSize);
71             // use max is parent ideal size is invalid.
72             contentSize.UpdateIllegalSizeWithCheck(contentConstraint.percentReference);
73             break;
74         }
75 
76         // wrap content case use min size default.
77         contentSize.UpdateIllegalSizeWithCheck(contentConstraint.minSize);
78     } while (false);
79     return contentSize.ConvertToSizeT();
80 }
81 
PerformMeasureSelfWithChildList(LayoutWrapper * layoutWrapper,const std::list<RefPtr<LayoutWrapper>> & childList)82 void BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(
83     LayoutWrapper* layoutWrapper, const std::list<RefPtr<LayoutWrapper>>& childList)
84 {
85     const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
86     const auto& minSize = layoutConstraint->minSize;
87     const auto& maxSize = layoutConstraint->maxSize;
88     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
89     auto measureType = layoutWrapper->GetLayoutProperty()->GetMeasureType();
90     OptionalSizeF frameSize;
91     auto version10OrLarger =
92         PipelineBase::GetCurrentContext() && PipelineBase::GetCurrentContext()->GetMinPlatformVersion() > 9;
93     do {
94         // Use idea size first if it is valid.
95         frameSize.UpdateSizeWithCheck(layoutConstraint->selfIdealSize);
96         if (frameSize.IsValid()) {
97             break;
98         }
99 
100         if (measureType == MeasureType::MATCH_PARENT) {
101             frameSize.UpdateIllegalSizeWithCheck(layoutConstraint->parentIdealSize);
102             if (frameSize.IsValid()) {
103                 frameSize.Constrain(minSize, maxSize, version10OrLarger);
104                 break;
105             }
106         }
107 
108         const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
109         if (content) {
110             // use content size.
111             auto contentSize = content->GetRect().GetSize();
112             AddPaddingToSize(padding, contentSize);
113             frameSize.UpdateIllegalSizeWithCheck(contentSize);
114         } else {
115             // use the max child size.
116             auto childFrame = SizeF();
117             float maxWidth = 0.0f;
118             float maxHeight = 0.0f;
119             for (const auto& child : childList) {
120                 if (!child) {
121                     continue;
122                 }
123                 auto layoutProperty = child->GetLayoutProperty();
124                 if (layoutProperty && layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
125                     continue;
126                 }
127                 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
128                 if (maxWidth < childSize.Width()) {
129                     maxWidth = childSize.Width();
130                 }
131                 if (maxHeight < childSize.Height()) {
132                     maxHeight = childSize.Height();
133                 }
134                 childFrame.SetSizeT(SizeF { maxWidth, maxHeight });
135             }
136             AddPaddingToSize(padding, childFrame);
137             frameSize.UpdateIllegalSizeWithCheck(childFrame);
138         }
139         if (layoutConstraint->selfIdealSize.Width()) {
140             frameSize.ConstrainFloat(minSize, maxSize, false, version10OrLarger);
141         } else if (layoutConstraint->selfIdealSize.Height()) {
142             frameSize.ConstrainFloat(minSize, maxSize, true, version10OrLarger);
143         } else {
144             frameSize.Constrain(minSize, maxSize, version10OrLarger);
145         }
146         frameSize.UpdateIllegalSizeWithCheck(SizeF { 0.0f, 0.0f });
147     } while (false);
148     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize.ConvertToSizeT());
149 }
150 
151 // Called to perform measure current render node.
PerformMeasureSelf(LayoutWrapper * layoutWrapper)152 void BoxLayoutAlgorithm::PerformMeasureSelf(LayoutWrapper* layoutWrapper)
153 {
154     PerformMeasureSelfWithChildList(layoutWrapper, layoutWrapper->GetAllChildrenWithBuild());
155 }
156 
157 // Called to perform layout render node and child.
PerformLayout(LayoutWrapper * layoutWrapper)158 void BoxLayoutAlgorithm::PerformLayout(LayoutWrapper* layoutWrapper)
159 {
160     // update child position.
161     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
162     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
163     MinusPaddingToSize(padding, size);
164     auto left = padding.left.value_or(0);
165     auto top = padding.top.value_or(0);
166     auto paddingOffset = OffsetF(left, top);
167     auto align = Alignment::CENTER;
168     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
169         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
170     }
171     // Update child position.
172     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
173         SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
174         auto translate = Alignment::GetAlignPosition(size, childSize, align) + paddingOffset;
175         child->GetGeometryNode()->SetMarginFrameOffset(translate);
176     }
177     // Update content position.
178     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
179     if (content) {
180         auto translate = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
181         content->SetOffset(translate);
182     }
183 }
184 } // namespace OHOS::Ace::NG
185