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