1 /*
2 * Copyright (c) 2023 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/pattern/folder_stack/folder_stack_layout_algorithm.h"
17
18 #include "base/memory/ace_type.h"
19 #include "base/subwindow/subwindow_manager.h"
20 #include "base/utils/utils.h"
21 #include "core/common/container.h"
22 #include "core/common/display_info.h"
23 #include "core/components/common/layout/constants.h"
24 #include "core/components_ng/layout/layout_wrapper.h"
25 #include "core/components_ng/pattern/folder_stack/control_parts_stack_node.h"
26 #include "core/components_ng/pattern/folder_stack/folder_stack_group_node.h"
27 #include "core/components_ng/pattern/folder_stack/folder_stack_layout_property.h"
28 #include "core/components_ng/pattern/folder_stack/folder_stack_pattern.h"
29 #include "core/components_ng/pattern/folder_stack/hover_stack_node.h"
30 #include "core/components_ng/pattern/stack/stack_layout_algorithm.h"
31 #include "core/components_ng/pattern/stack/stack_layout_property.h"
32 #include "core/components_ng/syntax/if_else_model.h"
33 #include "core/pipeline/pipeline_base.h"
34 #include "core/pipeline_ng/pipeline_context.h"
35
36 namespace OHOS::Ace::NG {
37 namespace {
38 constexpr float OFFSET_VALUE = 1.0f;
39 constexpr float OFFSET_DIVISOR = 2.0f;
40 } // namespace
41
42 FolderStackLayoutAlgorithm::FolderStackLayoutAlgorithm() = default;
43
Layout(LayoutWrapper * layoutWrapper)44 void FolderStackLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
45 {
46 auto folderStackLayoutProperty =
47 AceType::DynamicCast<FolderStackLayoutProperty>(layoutWrapper->GetLayoutProperty());
48 CHECK_NULL_VOID(folderStackLayoutProperty);
49 auto hostNode = AceType::DynamicCast<FolderStackGroupNode>(layoutWrapper->GetHostNode());
50 CHECK_NULL_VOID(hostNode);
51 auto align = Alignment::CENTER;
52 if (folderStackLayoutProperty->GetPositionProperty()) {
53 align = folderStackLayoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER);
54 }
55 if (!isIntoFolderStack_) {
56 auto childLayoutProperty = AceType::DynamicCast<StackLayoutProperty>(layoutWrapper->GetLayoutProperty());
57 if (childLayoutProperty->GetPositionProperty()) {
58 childLayoutProperty->GetPositionProperty()->UpdateAlignment(align);
59 }
60 StackLayoutAlgorithm::Layout(layoutWrapper);
61 return;
62 }
63 LayoutHoverStack(layoutWrapper, hostNode, folderStackLayoutProperty);
64 LayoutControlPartsStack(layoutWrapper, hostNode, folderStackLayoutProperty);
65 }
66
LayoutHoverStack(LayoutWrapper * layoutWrapper,const RefPtr<FolderStackGroupNode> & hostNode,const RefPtr<FolderStackLayoutProperty> & folderStackLayoutProperty)67 void FolderStackLayoutAlgorithm::LayoutHoverStack(LayoutWrapper* layoutWrapper,
68 const RefPtr<FolderStackGroupNode>& hostNode, const RefPtr<FolderStackLayoutProperty>& folderStackLayoutProperty)
69 {
70 auto folderStackGeometryNode = layoutWrapper->GetGeometryNode();
71 auto size = folderStackGeometryNode->GetFrameSize();
72 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
73 auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetLayoutDirection();
74 if (layoutDirection == TextDirection::AUTO) {
75 layoutDirection = AceApplicationInfo::GetInstance().IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR;
76 }
77 MinusPaddingToSize(padding, size);
78 auto left = padding.left.value_or(0);
79 auto top = padding.top.value_or(0);
80 auto paddingOffset = OffsetF(left, top);
81 auto align = Alignment::CENTER;
82 if (folderStackLayoutProperty->GetPositionProperty()) {
83 align = folderStackLayoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER);
84 }
85 auto hoverNode = hostNode->GetHoverNode();
86 auto index = hostNode->GetChildIndexById(hoverNode->GetId());
87 auto hoverStackWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
88 auto geometryNode = hoverStackWrapper->GetGeometryNode();
89 auto hoverStackOffset = OffsetT<float>(0.0f, 0.0f);
90 geometryNode->SetMarginFrameOffset(hoverStackOffset);
91 auto hoverSize = geometryNode->GetFrameSize();
92 for (auto&& child : hoverStackWrapper->GetAllChildrenWithBuild()) {
93 auto translate =
94 CalculateStackAlignment(hoverSize, child->GetGeometryNode()->GetMarginFrameSize(), align) + paddingOffset;
95 if (layoutDirection == TextDirection::RTL) {
96 translate.SetX(
97 hoverSize.Width() - translate.GetX() - child->GetGeometryNode()->GetMarginFrameSize().Width());
98 }
99 child->GetGeometryNode()->SetMarginFrameOffset(translate);
100 }
101 hoverStackWrapper->Layout();
102 }
103
LayoutControlPartsStack(LayoutWrapper * layoutWrapper,const RefPtr<FolderStackGroupNode> & hostNode,const RefPtr<FolderStackLayoutProperty> & folderStackLayoutProperty)104 void FolderStackLayoutAlgorithm::LayoutControlPartsStack(LayoutWrapper* layoutWrapper,
105 const RefPtr<FolderStackGroupNode>& hostNode, const RefPtr<FolderStackLayoutProperty>& folderStackLayoutProperty)
106 {
107 auto folderStackGeometryNode = layoutWrapper->GetGeometryNode();
108 auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetLayoutDirection();
109 if (layoutDirection == TextDirection::AUTO) {
110 layoutDirection = AceApplicationInfo::GetInstance().IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR;
111 }
112
113 auto align = Alignment::CENTER;
114 if (folderStackLayoutProperty->GetPositionProperty()) {
115 align = folderStackLayoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER);
116 }
117 auto controlPartsStackNode = hostNode->GetControlPartsStackNode();
118 auto index = hostNode->GetChildIndexById(controlPartsStackNode->GetId());
119 auto controlPartsStackWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
120 auto geometryNode = controlPartsStackWrapper->GetGeometryNode();
121 auto controlPartsStackRect = GetControlPartsStackRect();
122 geometryNode->SetMarginFrameOffset(controlPartsStackRect);
123 controlPartsStackWrapper->Layout();
124 }
125
Measure(LayoutWrapper * layoutWrapper)126 void FolderStackLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
127 {
128 CHECK_NULL_VOID(layoutWrapper);
129 auto hostNode = AceType::DynamicCast<FolderStackGroupNode>(layoutWrapper->GetHostNode());
130 CHECK_NULL_VOID(hostNode);
131 auto pattern = layoutWrapper->GetHostNode()->GetPattern<FolderStackPattern>();
132 CHECK_NULL_VOID(pattern);
133 const auto& layoutProperty = DynamicCast<FolderStackLayoutProperty>(layoutWrapper->GetLayoutProperty());
134 CHECK_NULL_VOID(layoutProperty);
135 const auto& layoutConstraint = layoutProperty->GetLayoutConstraint();
136 CHECK_NULL_VOID(layoutConstraint);
137 auto geometryNode = layoutWrapper->GetGeometryNode();
138 auto size = CreateIdealSizeByPercentRef(layoutConstraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT)
139 .ConvertToSizeT();
140 layoutWrapper->GetGeometryNode()->SetFrameSize(size);
141 isIntoFolderStack_ = IsIntoFolderStack(size, layoutProperty, layoutWrapper);
142 AdjustNodeTree(hostNode);
143 OnHoverStatusChange(layoutWrapper);
144 if (!isIntoFolderStack_) {
145 MeasureByStack(hostNode, layoutWrapper);
146 pattern->SetNeedCallBack(false);
147 return;
148 }
149 if (!pattern->GetNeedCallBack()) {
150 pattern->SetNeedCallBack(true);
151 auto displayInfo = pattern->GetDisplayInfo();
152 if (displayInfo) {
153 FolderEventInfo event(displayInfo->GetFoldStatus());
154 auto eventHub = layoutWrapper->GetHostNode()->GetEventHub<FolderStackEventHub>();
155 if (eventHub) {
156 eventHub->OnFolderStateChange(event);
157 }
158 }
159 }
160 RangeCalculation(hostNode, layoutProperty, size);
161 MeasureHoverStack(layoutWrapper, hostNode, layoutProperty, size);
162 MeasureControlPartsStack(layoutWrapper, hostNode, layoutProperty, size);
163 }
164
MeasureHoverStack(LayoutWrapper * layoutWrapper,const RefPtr<FolderStackGroupNode> & hostNode,const RefPtr<FolderStackLayoutProperty> & foldStackLayoutProperty,const SizeF & size)165 void FolderStackLayoutAlgorithm::MeasureHoverStack(LayoutWrapper* layoutWrapper,
166 const RefPtr<FolderStackGroupNode>& hostNode, const RefPtr<FolderStackLayoutProperty>& foldStackLayoutProperty,
167 const SizeF& size)
168 {
169 auto hoverNode = hostNode->GetHoverNode();
170 CHECK_NULL_VOID(hoverNode);
171 auto index = hostNode->GetChildIndexById(hoverNode->GetId());
172 auto hoverWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
173 CHECK_NULL_VOID(hoverWrapper);
174 auto constraint = foldStackLayoutProperty->CreateChildConstraint();
175 constraint.selfIdealSize = OptionalSizeF(size.Width(), preHoverStackHeight_);
176 hoverWrapper->Measure(constraint);
177 }
178
MeasureControlPartsStack(LayoutWrapper * layoutWrapper,const RefPtr<FolderStackGroupNode> & hostNode,const RefPtr<FolderStackLayoutProperty> & foldStackLayoutProperty,const SizeF & size)179 void FolderStackLayoutAlgorithm::MeasureControlPartsStack(LayoutWrapper* layoutWrapper,
180 const RefPtr<FolderStackGroupNode>& hostNode, const RefPtr<FolderStackLayoutProperty>& foldStackLayoutProperty,
181 const SizeF& size)
182 {
183 auto controlPartsStackNode = hostNode->GetControlPartsStackNode();
184 CHECK_NULL_VOID(controlPartsStackNode);
185 auto index = hostNode->GetChildIndexById(controlPartsStackNode->GetId());
186 auto controlPartsWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
187 CHECK_NULL_VOID(controlPartsWrapper);
188 auto constraint = foldStackLayoutProperty->CreateChildConstraint();
189 constraint.selfIdealSize = OptionalSizeF(size.Width(), preControlPartsStackHeight_);
190 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
191 PaddingProperty controlPartsPadding;
192 controlPartsPadding.left = CalcLength(padding.left.value_or(0));
193 controlPartsPadding.right = CalcLength(padding.right.value_or(0));
194 controlPartsPadding.top = CalcLength(padding.top.value_or(0));
195 controlPartsPadding.bottom = CalcLength(padding.bottom.value_or(0));
196 controlPartsWrapper->GetLayoutProperty()->UpdatePadding(controlPartsPadding);
197 controlPartsWrapper->Measure(constraint);
198 }
199
RangeCalculation(const RefPtr<FolderStackGroupNode> & hostNode,const RefPtr<FolderStackLayoutProperty> & folderStackLayoutProperty,const SizeF & size)200 void FolderStackLayoutAlgorithm::RangeCalculation(const RefPtr<FolderStackGroupNode>& hostNode,
201 const RefPtr<FolderStackLayoutProperty>& folderStackLayoutProperty, const SizeF& size)
202 {
203 int32_t creaseY = 0;
204 int32_t creaseHeight = 0;
205 const auto& constraint = folderStackLayoutProperty->GetLayoutConstraint();
206 CHECK_NULL_VOID(constraint);
207 auto pipeline = PipelineContext::GetCurrentContext();
208 CHECK_NULL_VOID(pipeline);
209 auto safeArea = pipeline->GetSafeArea();
210 int32_t length = static_cast<int32_t>(safeArea.top_.Length());
211 auto container = Container::Current();
212 CHECK_NULL_VOID(container);
213 auto displayInfo = container->GetDisplayInfo();
214 CHECK_NULL_VOID(displayInfo);
215 auto foldCreaseRects = displayInfo->GetCurrentFoldCreaseRegion();
216 if (!foldCreaseRects.empty()) {
217 auto foldCrease = foldCreaseRects.front();
218 creaseY = static_cast<int32_t>(foldCrease.Bottom() - foldCrease.Height());
219 creaseHeight = static_cast<int32_t>(foldCrease.Height());
220 }
221 preHoverStackHeight_ = static_cast<float>(creaseY - length);
222 preControlPartsStackHeight_ = static_cast<float>(size.Height() - creaseHeight - preHoverStackHeight_);
223 controlPartsStackRect_ = OffsetF(0.0f, creaseY - length + creaseHeight);
224 }
225
IsFullWindow(SizeF & frameSize,const RefPtr<FolderStackLayoutProperty> & foldStackLayoutProperty,LayoutWrapper * layoutWrapper)226 bool FolderStackLayoutAlgorithm::IsFullWindow(
227 SizeF& frameSize, const RefPtr<FolderStackLayoutProperty>& foldStackLayoutProperty, LayoutWrapper* layoutWrapper)
228 {
229 auto host = layoutWrapper->GetHostNode();
230 CHECK_NULL_RETURN(host, false);
231 auto parent = AceType::DynamicCast<FrameNode>(host->GetParent());
232 CHECK_NULL_RETURN(parent, false);
233 auto padding = parent->GetLayoutProperty()->CreatePaddingAndBorder();
234 auto pipeline = PipelineContext::GetCurrentContext();
235 CHECK_NULL_RETURN(pipeline, false);
236 auto windowManager = pipeline->GetWindowManager();
237 auto safeArea = pipeline->GetSafeArea();
238 CHECK_NULL_RETURN(windowManager, false);
239 auto windowMode = windowManager->GetWindowMode();
240 auto realWidth = frameSize.Width() + padding.Width();
241 auto realHeight = frameSize.Height() + padding.Height();
242 if (!NearEqual(realWidth, pipeline->GetRootWidth() - safeArea.left_.Length() - safeArea.right_.Length()) ||
243 !NearEqual(realHeight, pipeline->GetRootHeight() - safeArea.top_.Length() - safeArea.bottom_.Length()) ||
244 windowMode != WindowMode::WINDOW_MODE_FULLSCREEN) {
245 return false;
246 }
247 return true;
248 }
249
AdjustNodeTree(const RefPtr<FolderStackGroupNode> & hostNode)250 void FolderStackLayoutAlgorithm::AdjustNodeTree(const RefPtr<FolderStackGroupNode>& hostNode)
251 {
252 auto hoverNode = hostNode->GetHoverNode();
253 auto controlPartsStackNode = hostNode->GetControlPartsStackNode();
254 auto isChangeItemId = hostNode->GetIsChangeItemId();
255 if (isChangeItemId) {
256 hostNode->SetIsChangeItemId(false);
257 } else if ((!isIntoFolderStack_ && hoverNode->GetChildren().size() == 0) ||
258 (isIntoFolderStack_ && hoverNode->GetChildren().size() > 0)) {
259 return;
260 }
261
262 for (auto& hover : hoverNode->GetChildren()) {
263 hoverNode->RemoveChild(hover);
264 }
265 for (auto& controlPart : controlPartsStackNode->GetChildren()) {
266 controlPartsStackNode->RemoveChild(controlPart);
267 }
268 if (!isIntoFolderStack_) {
269 for (auto& childNode : hostNode->GetChildNode()) {
270 controlPartsStackNode->AddChild(childNode);
271 }
272 } else {
273 auto itemId = hostNode->GetItemId();
274 for (auto& childNode : hostNode->GetChildNode()) {
275 if (std::count(itemId.begin(), itemId.end(), childNode->GetInspectorId())) {
276 hoverNode->AddChild(childNode);
277 } else {
278 controlPartsStackNode->AddChild(childNode);
279 }
280 }
281 }
282 }
283
CalculateStackAlignment(const NG::SizeF & parentSize,const NG::SizeF & childSize,const Alignment & alignment)284 NG::OffsetF FolderStackLayoutAlgorithm::CalculateStackAlignment(
285 const NG::SizeF& parentSize, const NG::SizeF& childSize, const Alignment& alignment)
286 {
287 NG::OffsetF offset;
288 offset.SetX((OFFSET_VALUE + alignment.GetHorizontal()) * (parentSize.Width() - childSize.Width()) / OFFSET_DIVISOR);
289 offset.SetY((OFFSET_VALUE + alignment.GetVertical()) * (parentSize.Height() - childSize.Height()) / OFFSET_DIVISOR);
290 return offset;
291 }
292
IsIntoFolderStack(SizeF & frameSize,const RefPtr<FolderStackLayoutProperty> & foldStackLayoutProperty,LayoutWrapper * layoutWrapper)293 bool FolderStackLayoutAlgorithm::IsIntoFolderStack(
294 SizeF& frameSize, const RefPtr<FolderStackLayoutProperty>& foldStackLayoutProperty, LayoutWrapper* layoutWrapper)
295 {
296 auto pattern = layoutWrapper->GetHostNode()->GetPattern<FolderStackPattern>();
297 CHECK_NULL_RETURN(pattern, false);
298 auto displayInfo = pattern->GetDisplayInfo();
299 if (!displayInfo) {
300 auto container = Container::Current();
301 CHECK_NULL_RETURN(container, false);
302 displayInfo = container->GetDisplayInfo();
303 }
304 CHECK_NULL_RETURN(displayInfo, false);
305 bool isFullWindow = IsFullWindow(frameSize, foldStackLayoutProperty, layoutWrapper);
306 bool isFoldable = displayInfo->GetIsFoldable();
307 auto foldStatus = displayInfo->GetFoldStatus();
308 auto rotation = displayInfo->GetRotation();
309 auto isLandscape = rotation == Rotation::ROTATION_90 || rotation == Rotation::ROTATION_270;
310 TAG_LOGI(AceLogTag::ACE_FOLDER_STACK,
311 "folderStack state isFullWindow:%{public}d, isFoldable:%{public}d, "
312 "foldStatus:%{public}d, isLandscape:%{public}d",
313 isFullWindow, isFoldable, foldStatus, isLandscape);
314 return isFullWindow && isFoldable && foldStatus == FoldStatus::HALF_FOLD && isLandscape;
315 }
316
OnHoverStatusChange(LayoutWrapper * layoutWrapper)317 void FolderStackLayoutAlgorithm::OnHoverStatusChange(LayoutWrapper* layoutWrapper)
318 {
319 auto pattern = layoutWrapper->GetHostNode()->GetPattern<FolderStackPattern>();
320 CHECK_NULL_VOID(pattern);
321 if (isIntoFolderStack_ == pattern->IsInHoverMode()) {
322 return;
323 }
324 auto eventHub = layoutWrapper->GetHostNode()->GetEventHub<FolderStackEventHub>();
325 auto pipeline = PipelineContext::GetCurrentContext();
326 CHECK_NULL_VOID(pipeline);
327 auto windowManager = pipeline->GetWindowManager();
328 CHECK_NULL_VOID(windowManager);
329 auto windowMode = windowManager->GetWindowMode();
330 auto displayInfo = pattern->GetDisplayInfo();
331 FolderEventInfo hoverChangeEvent(
332 displayInfo->GetFoldStatus(), isIntoFolderStack_, displayInfo->GetRotation(), windowMode);
333 if (eventHub) {
334 eventHub->OnHoverStatusChange(std::move(hoverChangeEvent));
335 TAG_LOGI(AceLogTag::ACE_FOLDER_STACK,
336 "hoverStatus change callback FoldStatus:%{public}d, isHoverMode:%{public}d, "
337 "appRotation:%{public}d, windowMode:%{public}d",
338 displayInfo->GetFoldStatus(), isIntoFolderStack_, displayInfo->GetRotation(), windowMode);
339 }
340 }
341
MeasureByStack(const RefPtr<FolderStackGroupNode> & hostNode,LayoutWrapper * layoutWrapper)342 void FolderStackLayoutAlgorithm::MeasureByStack(
343 const RefPtr<FolderStackGroupNode>& hostNode, LayoutWrapper* layoutWrapper)
344 {
345 PaddingProperty padding { CalcLength(0.0f), CalcLength(0.0f), CalcLength(0.0f), CalcLength(0.0f) };
346 auto controlPartsStackNode = hostNode->GetControlPartsStackNode();
347 CHECK_NULL_VOID(controlPartsStackNode);
348 auto index = hostNode->GetChildIndexById(controlPartsStackNode->GetId());
349 auto controlPartsWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
350 CHECK_NULL_VOID(controlPartsWrapper);
351 controlPartsWrapper->GetLayoutProperty()->UpdatePadding(padding);
352 StackLayoutAlgorithm::Measure(layoutWrapper);
353 auto hoverNode = hostNode->GetHoverNode();
354 CHECK_NULL_VOID(hoverNode);
355 auto hoverIndex = hostNode->GetChildIndexById(hoverNode->GetId());
356 auto hoverStackWrapper = layoutWrapper->GetOrCreateChildByIndex(hoverIndex);
357 CHECK_NULL_VOID(hoverStackWrapper);
358 auto geometryNode = hoverStackWrapper->GetGeometryNode();
359 geometryNode->SetFrameSize(controlPartsWrapper->GetGeometryNode()->GetFrameSize());
360 }
361 } // namespace OHOS::Ace::NG
362