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