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