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