• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/stack/render_stack.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/positioned/render_positioned.h"
20 #include "core/components/stack/stack_component.h"
21 #include "core/pipeline/base/position_layout_utils.h"
22 
23 namespace OHOS::Ace {
24 
Update(const RefPtr<Component> & component)25 void RenderStack::Update(const RefPtr<Component>& component)
26 {
27     const auto stack = AceType::DynamicCast<StackComponent>(component);
28     if (!stack) {
29         return;
30     }
31     align_ = stack->GetAlignment();
32     fit_ = stack->GetStackFit();
33     overflow_ = stack->GetOverflow();
34     mainStackSize_ = stack->GetMainStackSize();
35     SetTextDirection(stack->GetTextDirection());
36     MarkNeedLayout();
37 }
38 
PerformLayout()39 void RenderStack::PerformLayout()
40 {
41     Size maxSize = GetLayoutParam().GetMaxSize();
42     bool hasNonPositionedItem = false;
43     if (GetChildren().empty()) {
44         SetLayoutSize(maxSize);
45         return;
46     }
47     LayoutParam innerLayout;
48     // layout children
49     RefPtr<RenderNode> firstChild;
50     std::list<RefPtr<RenderNode>> percentChild;
51     for (const auto& item : GetChildren()) {
52         if (item->GetIsPercentSize()) {
53             percentChild.emplace_back(item);
54         }
55         auto positionedItem = AceType::DynamicCast<RenderPositioned>(item);
56         if (!positionedItem) {
57             hasNonPositionedItem = true;
58             innerLayout = MakeNonPositionedInnerLayoutParam(firstChild);
59             item->Layout(innerLayout);
60         } else {
61             innerLayout = MakePositionedInnerLayoutParam(positionedItem, firstChild);
62             positionedItem->Layout(innerLayout);
63         }
64         if (!firstChild) {
65             firstChild = item;
66         }
67     }
68     // determine the stack size
69     DetermineStackSize(hasNonPositionedItem);
70 
71     auto context = context_.Upgrade();
72     if (context && context->GetIsDeclarative()) {
73         auto layoutParam = GetLayoutParam();
74         layoutParam.SetMaxSize(GetLayoutSize());
75         SetLayoutParam(layoutParam);
76     }
77 
78     // secondnary layout for percentchild
79     for (const auto& item : percentChild) {
80         innerLayout.SetMaxSize(GetLayoutSize());
81         item->Layout(innerLayout);
82     }
83 
84     SetChildrenStatus();
85     // place children
86     for (const auto& item : GetChildren()) {
87         auto positionedItem = AceType::DynamicCast<RenderPositioned>(item);
88         if (!positionedItem) {
89             if (item->GetPositionType() == PositionType::PTABSOLUTE) {
90                 auto itemOffset = PositionLayoutUtils::GetAbsoluteOffset(Claim(this), item);
91                 item->SetAbsolutePosition(itemOffset);
92                 continue;
93             }
94             item->SetPosition(GetNonPositionedChildOffset(item->GetLayoutSize()));
95             continue;
96         }
97         Offset offset = GetPositionedChildOffset(positionedItem);
98         if (offset.GetX() < 0.0 || offset.GetY() < 0.0 ||
99             offset.GetX() + positionedItem->GetLayoutSize().Width() > GetLayoutSize().Width() ||
100             offset.GetY() + positionedItem->GetLayoutSize().Height() > GetLayoutSize().Height()) {
101             isChildOverflow_ = true;
102         }
103         positionedItem->SetPosition(offset);
104     }
105 }
106 
DetermineStackSize(bool hasNonPositioned)107 void RenderStack::DetermineStackSize(bool hasNonPositioned)
108 {
109     Size maxSize = GetLayoutParam().GetMaxSize();
110     if (maxSize.IsWidthInfinite()) {
111         maxSize.SetWidth(viewPort_.Width());
112     }
113     if (maxSize.IsHeightInfinite()) {
114         maxSize.SetHeight(viewPort_.Height());
115     }
116 
117     if (mainStackSize_ == MainStackSize::MAX && !maxSize.IsInfinite()) {
118         SetLayoutSize(maxSize);
119         return;
120     }
121     double width = GetLayoutParam().GetMinSize().Width();
122     double height = GetLayoutParam().GetMinSize().Height();
123     double maxX = 0.0;
124     double maxY = 0.0;
125     double lastChildHeight = height;
126     for (const auto& item : GetChildren()) {
127         if (item->GetIsPercentSize()) {
128             continue;
129         }
130         double constrainedWidth = std::clamp(item->GetLayoutSize().Width(), GetLayoutParam().GetMinSize().Width(),
131             GetLayoutParam().GetMaxSize().Width());
132         double constrainedHeight = std::clamp(item->GetLayoutSize().Height(), GetLayoutParam().GetMinSize().Height(),
133             GetLayoutParam().GetMaxSize().Height());
134         width = std::max(width, constrainedWidth);
135         height = std::max(height, constrainedHeight);
136         lastChildHeight = constrainedHeight;
137         maxX = std::max(maxX, item->GetLayoutSize().Width() + NormalizePercentToPx(item->GetLeft(), false));
138         maxY = std::max(maxY, item->GetLayoutSize().Height() + NormalizePercentToPx(item->GetTop(), true));
139     }
140     for (const auto& item : GetChildren()) {
141         if (item->GetIsPercentSize()) {
142             if (maxX == 0 || maxY == 0) {
143                 double constrainedWidth = std::clamp(item->GetLayoutSize().Width(),
144                     GetLayoutParam().GetMinSize().Width(), GetLayoutParam().GetMaxSize().Width());
145                 double constrainedHeight = std::clamp(item->GetLayoutSize().Height(),
146                     GetLayoutParam().GetMinSize().Height(), GetLayoutParam().GetMaxSize().Height());
147                 width = std::max(width, constrainedWidth);
148                 height = std::max(height, constrainedHeight);
149                 lastChildHeight = constrainedHeight;
150                 maxX = std::max(maxX, item->GetLayoutSize().Width() + NormalizePercentToPx(item->GetLeft(), false));
151                 maxY = std::max(maxY, item->GetLayoutSize().Height() + NormalizePercentToPx(item->GetTop(), true));
152             }
153         }
154     }
155     if (mainStackSize_ == MainStackSize::NORMAL && !hasNonPositioned && !maxSize.IsInfinite()) {
156         SetLayoutSize(maxSize);
157         return;
158     }
159     // Usually used in SemiModal for determining current height.
160     if (mainStackSize_ == MainStackSize::LAST_CHILD_HEIGHT) {
161         SetLayoutSize(Size(maxSize.Width(), lastChildHeight));
162         return;
163     }
164     if (mainStackSize_ == MainStackSize::MATCH_CHILDREN) {
165         SetLayoutSize(GetLayoutParam().Constrain(Size(maxX, maxY)));
166         return;
167     }
168     if (mainStackSize_ == MainStackSize::MAX_X) {
169         auto maxSizeX = maxSize.Width();
170         SetLayoutSize(Size(maxSizeX, maxY));
171         return;
172     }
173     if (mainStackSize_ == MainStackSize::MAX_Y) {
174         auto maxSizeY = maxSize.Height();
175         SetLayoutSize(Size(maxX, maxSizeY));
176         return;
177     }
178     SetLayoutSize(Size(width, height));
179 }
180 
MakeNonPositionedInnerLayoutParam(const RefPtr<RenderNode> & firstChild) const181 LayoutParam RenderStack::MakeNonPositionedInnerLayoutParam(const RefPtr<RenderNode>& firstChild) const
182 {
183     LayoutParam innerLayout;
184     switch (fit_) {
185         case StackFit::STRETCH:
186             innerLayout.SetFixedSize(GetLayoutParam().GetMaxSize());
187             break;
188         case StackFit::KEEP:
189             innerLayout.SetMaxSize(GetLayoutParam().GetMaxSize());
190             break;
191         case StackFit::INHERIT:
192             innerLayout = GetLayoutParam();
193             break;
194         case StackFit::FIRST_CHILD:
195             innerLayout = GetLayoutParam();
196             if (firstChild) {
197                 innerLayout.SetMaxSize(firstChild->GetLayoutSize());
198             }
199             break;
200         default:
201             innerLayout.SetMaxSize(GetLayoutParam().GetMaxSize());
202             break;
203     }
204     return innerLayout;
205 }
206 
MakePositionedInnerLayoutParam(const RefPtr<RenderPositioned> & item,const RefPtr<RenderNode> & firstChild) const207 LayoutParam RenderStack::MakePositionedInnerLayoutParam(
208     const RefPtr<RenderPositioned>& item, const RefPtr<RenderNode>& firstChild) const
209 {
210     LayoutParam innerLayout;
211     double width = std::clamp(item->GetWidth(), innerLayout.GetMinSize().Width(), innerLayout.GetMaxSize().Width());
212     double height = std::clamp(item->GetHeight(), innerLayout.GetMinSize().Height(), innerLayout.GetMaxSize().Height());
213     if (!NearZero(width) && !NearZero(height)) {
214         innerLayout.SetFixedSize(Size(width, height));
215     } else if (!NearZero(width)) {
216         innerLayout.SetMinSize(Size(width, innerLayout.GetMinSize().Height()));
217         innerLayout.SetMaxSize(Size(width, innerLayout.GetMaxSize().Height()));
218     } else if (!NearZero(height)) {
219         innerLayout.SetMinSize(Size(innerLayout.GetMinSize().Width(), height));
220         innerLayout.SetMaxSize(Size(innerLayout.GetMaxSize().Width(), height));
221     } else {
222         innerLayout = MakeNonPositionedInnerLayoutParam(firstChild);
223     }
224     return innerLayout;
225 }
226 
GetNonPositionedChildOffset(const Size & childSize)227 Offset RenderStack::GetNonPositionedChildOffset(const Size& childSize)
228 {
229     Offset offset(0.0f, 0.0f);
230     double coefficients = 1.0f;
231     Size size = GetLayoutSize();
232 
233     if (IsRightToLeft()) {
234         coefficients = -1.0f;
235     }
236 
237     if (GreatOrEqual(size.Width(), childSize.Width())) {
238         offset.SetX((1.0 + coefficients * align_.GetHorizontal()) * (size.Width() - childSize.Width()) / 2.0);
239     }
240     if (GreatOrEqual(size.Height(), childSize.Height())) {
241         offset.SetY((1.0 + align_.GetVertical()) * (size.Height() - childSize.Height()) / 2.0);
242     }
243 
244     // child larger than the parent
245     if (GreatOrEqual(childSize.Width(), size.Width())) {
246         offset.SetX(-(1.0 + coefficients * align_.GetHorizontal()) * (childSize.Width() - size.Width()) / 2.0);
247     }
248     if (GreatOrEqual(childSize.Height(), size.Height())) {
249         offset.SetY(-(1.0 + align_.GetVertical()) * (childSize.Height() - size.Height()) / 2.0);
250     }
251     return offset;
252 }
253 
GetPositionedChildOffset(const RefPtr<RenderPositioned> & item)254 Offset RenderStack::GetPositionedChildOffset(const RefPtr<RenderPositioned>& item)
255 {
256     double deltaX = 0.0;
257     if (item->HasLeft()) {
258         deltaX = NormalizePercentToPx(item->GetLeft(), false);
259     } else if (item->HasRight()) {
260         deltaX =
261             GetLayoutSize().Width() - NormalizePercentToPx(item->GetRight(), false) - item->GetLayoutSize().Width();
262     } else {
263         deltaX = GetNonPositionedChildOffset(item->GetLayoutSize()).GetX();
264     }
265     double deltaY = 0.0;
266     if (item->HasTop()) {
267         deltaY = NormalizePercentToPx(item->GetTop(), true);
268     } else if (item->HasBottom()) {
269         deltaY =
270             GetLayoutSize().Height() - NormalizePercentToPx(item->GetBottom(), true) - item->GetLayoutSize().Height();
271     } else {
272         deltaY = GetNonPositionedChildOffset(item->GetLayoutSize()).GetY();
273     }
274     return Offset(deltaX, deltaY);
275 }
276 
OnAttachContext()277 void RenderStack::OnAttachContext()
278 {
279     RenderNode::OnAttachContext();
280     auto context = context_.Upgrade();
281     if (context && context->GetIsDeclarative()) {
282         SetExclusiveEventForChild(true);
283     }
284 }
285 
286 } // namespace OHOS::Ace
287