1 /*
2  * Copyright (c) 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_ng/pattern/navigation/navigation_layout_algorithm.h"
17 
18 #include <cmath>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/log/ace_trace.h"
24 #include "base/memory/ace_type.h"
25 #include "base/utils/utils.h"
26 #include "core/common/container.h"
27 #include "core/components/common/layout/constants.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/layout/layout_algorithm.h"
30 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
31 #include "core/components_ng/pattern/navigation/nav_bar_layout_property.h"
32 #include "core/components_ng/pattern/navigation/nav_bar_node.h"
33 #include "core/components_ng/pattern/navigation/navigation_declaration.h"
34 #include "core/components_ng/pattern/navigation/navigation_group_node.h"
35 #include "core/components_ng/pattern/navigation/navigation_layout_property.h"
36 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
37 #include "core/components_ng/pattern/navrouter/navdestination_group_node.h"
38 #include "core/components_ng/property/calc_length.h"
39 #include "core/components_ng/property/layout_constraint.h"
40 #include "core/components_ng/property/measure_property.h"
41 #include "core/components_ng/property/measure_utils.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43 
44 namespace OHOS::Ace::NG {
45 
46 constexpr static float HALF = 0.5f;
47 constexpr static int32_t PLATFORM_VERSION_TEN = 10;
48 constexpr Dimension WINDOW_WIDTH = 520.0_vp;
49 
50 namespace {
51 constexpr NavigationMode INITIAL_MODE = NavigationMode::AUTO;
52 constexpr int32_t MODE_SWITCH_ANIMATION_DURATION = 500; // ms
53 const RefPtr<CubicCurve> MODE_SWITCH_CURVE = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.2f, 0.1f, 1.0f);
54 constexpr Dimension DIVIDER_DRAG_BAR_WIDTH = 12.0_vp;
55 constexpr Dimension DIVIDER_DRAG_BAR_HEIGHT = 48.0_vp;
56 constexpr Dimension DRAG_BAR_ITEM_WIDTH = 2.0_vp;
57 constexpr Dimension DRAG_BAR_ITEM_HEIGHT = 24.0_vp;
58 
MeasureDivider(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & dividerSize)59 void MeasureDivider(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
60     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, const SizeF& dividerSize)
61 {
62     auto dividerNode = hostNode->GetDividerNode();
63     CHECK_NULL_VOID(dividerNode);
64     auto index = hostNode->GetChildIndexById(dividerNode->GetId());
65     auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
66     CHECK_NULL_VOID(dividerWrapper);
67     auto constraint = navigationLayoutProperty->CreateChildConstraint();
68     constraint.selfIdealSize = OptionalSizeF(dividerSize.Width(), dividerSize.Height());
69     dividerWrapper->Measure(constraint);
70 }
71 
MeasureDragBar(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & dividerSize)72 void MeasureDragBar(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
73     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, const SizeF& dividerSize)
74 {
75     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
76     CHECK_NULL_VOID(navigationPattern);
77     auto dragNode = hostNode->GetDragBarNode();
78     CHECK_NULL_VOID(dragNode);
79     auto index = hostNode->GetChildIndexById(dragNode->GetId());
80     auto dargWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
81     CHECK_NULL_VOID(dargWrapper);
82     auto dragBarItem = AceType::DynamicCast<FrameNode>(dragNode->GetChildAtIndex(0));
83     CHECK_NULL_VOID(dragBarItem);
84     auto dragBarItemLayoutProperty = dragBarItem->GetLayoutProperty();
85     CHECK_NULL_VOID(dragBarItemLayoutProperty);
86     auto constraint = navigationLayoutProperty->CreateChildConstraint();
87     if (NearZero(dividerSize.Width()) || !navigationPattern->GetEnableDragBar()) {
88         constraint.selfIdealSize = OptionalSizeF(0.0f, 0.0f);
89         dragBarItemLayoutProperty->UpdateUserDefinedIdealSize(
90             CalcSize(CalcLength(0.0f), CalcLength(0.0f)));
91     } else {
92         constraint.selfIdealSize = OptionalSizeF(static_cast<float>(DIVIDER_DRAG_BAR_WIDTH.ConvertToPx()),
93             static_cast<float>(DIVIDER_DRAG_BAR_HEIGHT.ConvertToPx()));
94         dragBarItemLayoutProperty->UpdateUserDefinedIdealSize(
95             CalcSize(CalcLength(DRAG_BAR_ITEM_WIDTH), CalcLength(DRAG_BAR_ITEM_HEIGHT)));
96     }
97     dargWrapper->Measure(constraint);
98 }
99 
LayoutDragBar(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,float navBarWidth,const NavBarPosition & position)100 void LayoutDragBar(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
101     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, float navBarWidth, const NavBarPosition& position)
102 {
103     auto dargNode = hostNode->GetDragBarNode();
104     CHECK_NULL_VOID(dargNode);
105     auto index = hostNode->GetChildIndexById(dargNode->GetId());
106     auto dargWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
107     CHECK_NULL_VOID(dargWrapper);
108     auto geometryNode = dargWrapper->GetGeometryNode();
109     CHECK_NULL_VOID(geometryNode);
110     auto navigationGeometryNode = layoutWrapper->GetGeometryNode();
111     CHECK_NULL_VOID(navigationGeometryNode);
112     auto navigationWidth = navigationGeometryNode->GetFrameSize().Width();
113     auto navigationHeight = navigationGeometryNode->GetFrameSize().Height();
114     auto offsetX = navBarWidth - geometryNode->GetFrameSize().Width() * HALF;
115     auto offsetY = navigationHeight * HALF - geometryNode->GetFrameSize().Height() * HALF;
116     OffsetT<float> dragOffset = OffsetT<float>(offsetX, offsetY);
117     bool isNavBarInRight = (position == NavBarPosition::END && !AceApplicationInfo::GetInstance().IsRightToLeft()) ||
118         (position == NavBarPosition::START && AceApplicationInfo::GetInstance().IsRightToLeft());
119     if (isNavBarInRight) {
120         dragOffset.SetX(navigationWidth - navBarWidth - geometryNode->GetFrameSize().Width() * HALF);
121     }
122     const auto& padding = navigationLayoutProperty->CreatePaddingAndBorder();
123     dragOffset.AddX(padding.left.value_or(0.0f));
124     dragOffset.AddY(padding.top.value_or(0.0f));
125     geometryNode->SetMarginFrameOffset(dragOffset);
126     dargWrapper->Layout();
127 }
128 
LayoutNavBar(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const NavBarPosition & position,OffsetF & returnNavBarOffset)129 float LayoutNavBar(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
130     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, const NavBarPosition& position,
131     OffsetF& returnNavBarOffset)
132 {
133     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
134     bool isZeroNavbarWidth = false;
135     if (navigationLayoutProperty->GetHideNavBar().value_or(false) &&
136         navigationPattern->GetNavigationMode() == NavigationMode::SPLIT) {
137         isZeroNavbarWidth = true;
138     }
139     auto contentNode = hostNode->GetContentNode();
140     CHECK_NULL_RETURN(contentNode, 0.0f);
141     auto navBarNode = hostNode->GetNavBarNode();
142     CHECK_NULL_RETURN(navBarNode, 0.0f);
143     auto index = hostNode->GetChildIndexById(navBarNode->GetId());
144     auto navBarWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
145     CHECK_NULL_RETURN(navBarWrapper, 0.0f);
146     auto geometryNode = navBarWrapper->GetGeometryNode();
147     auto navigationGeometryNode = layoutWrapper->GetGeometryNode();
148     auto navBarOffset = OffsetT<float>(0.0f, 0.0f);
149     bool isNavBarInRight = (position == NavBarPosition::END && !AceApplicationInfo::GetInstance().IsRightToLeft()) ||
150         (position == NavBarPosition::START && AceApplicationInfo::GetInstance().IsRightToLeft());
151     if (isNavBarInRight) {
152         navBarOffset.SetX(navigationGeometryNode->GetFrameSize().Width() - geometryNode->GetFrameSize().Width());
153     }
154     const auto& padding = navigationLayoutProperty->CreatePaddingAndBorder();
155     navBarOffset.AddX(padding.left.value_or(0.0f));
156     navBarOffset.AddY(padding.top.value_or(0.0f));
157     geometryNode->SetMarginFrameOffset(navBarOffset);
158     navBarWrapper->Layout();
159     returnNavBarOffset = navBarOffset;
160     return isZeroNavbarWidth ? 0.0f : geometryNode->GetFrameSize().Width();
161 }
162 
LayoutDivider(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,float navBarWidth,const NavBarPosition & position)163 float LayoutDivider(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
164     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, float navBarWidth, const NavBarPosition& position)
165 {
166     auto dividerNode = hostNode->GetDividerNode();
167     CHECK_NULL_RETURN(dividerNode, 0.0f);
168     auto index = hostNode->GetChildIndexById(dividerNode->GetId());
169     auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
170     CHECK_NULL_RETURN(dividerWrapper, 0.0f);
171     auto geometryNode = dividerWrapper->GetGeometryNode();
172     auto navigationGeometryNode = layoutWrapper->GetGeometryNode();
173     auto dividerOffsetX = navBarWidth;
174     if (position == NavBarPosition::END) {
175         dividerOffsetX =
176             navigationGeometryNode->GetFrameSize().Width() - geometryNode->GetFrameSize().Width() - dividerOffsetX;
177     }
178     if (AceApplicationInfo::GetInstance().IsRightToLeft()) {
179         dividerOffsetX =
180             navigationGeometryNode->GetFrameSize().Width() - geometryNode->GetFrameSize().Width() - dividerOffsetX;
181     }
182     OffsetT<float> dividerOffset = OffsetT<float>(dividerOffsetX, 0.0f);
183     const auto& padding = navigationLayoutProperty->CreatePaddingAndBorder();
184     dividerOffset.AddX(padding.left.value_or(0));
185     dividerOffset.AddY(padding.top.value_or(0));
186     geometryNode->SetMarginFrameOffset(dividerOffset);
187     dividerWrapper->Layout();
188     return geometryNode->GetFrameSize().Width();
189 }
190 
LayoutContent(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,float navBarWidth,float dividerWidth,const NavBarPosition & position)191 void LayoutContent(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
192     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, float navBarWidth, float dividerWidth,
193     const NavBarPosition& position)
194 {
195     auto contentNode = hostNode->GetContentNode();
196     CHECK_NULL_VOID(contentNode);
197     auto index = hostNode->GetChildIndexById(contentNode->GetId());
198     auto contentWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
199     CHECK_NULL_VOID(contentWrapper);
200     auto geometryNode = contentWrapper->GetGeometryNode();
201     CHECK_NULL_VOID(geometryNode);
202 
203     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
204     auto contentChildSize = contentNode->GetChildren().size();
205 
206     // cases that content layouts from start
207     // 1. displaying content pages in STACK mode
208     // 2. placing navBar at the end
209     // 3. hiding navBar in SPLIT mode
210     auto contentOffset = OffsetT<float>(0.0f, 0.0f);
211     if ((contentChildSize != 0 && navigationPattern->GetNavigationMode() == NavigationMode::STACK) ||
212         position == NavBarPosition::END ||
213         (navigationLayoutProperty->GetHideNavBar().value_or(false) &&
214             navigationPattern->GetNavigationMode() == NavigationMode::SPLIT)) {
215         if (AceApplicationInfo::GetInstance().IsRightToLeft() &&
216             navigationPattern->GetNavigationMode() == NavigationMode::SPLIT) {
217             contentOffset = OffsetT<float>(navBarWidth + dividerWidth, 0.0f);
218         }
219         const auto& padding = navigationLayoutProperty->CreatePaddingAndBorder();
220         contentOffset.AddX(padding.left.value_or(0));
221         contentOffset.AddY(padding.top.value_or(0));
222         geometryNode->SetMarginFrameOffset(contentOffset);
223         contentWrapper->Layout();
224         return;
225     }
226     if (!AceApplicationInfo::GetInstance().IsRightToLeft()) {
227         contentOffset = OffsetT<float>(navBarWidth + dividerWidth, 0.0f);
228     }
229     const auto& padding = navigationLayoutProperty->CreatePaddingAndBorder();
230     contentOffset.AddX(padding.left.value_or(0));
231     contentOffset.AddY(padding.top.value_or(0));
232     geometryNode->SetMarginFrameOffset(contentOffset);
233     contentWrapper->Layout();
234 }
235 
FitScrollFullWindow(SizeF & frameSize)236 void FitScrollFullWindow(SizeF& frameSize)
237 {
238     auto pipeline = PipelineContext::GetCurrentContext();
239     CHECK_NULL_VOID(pipeline);
240     if (frameSize.Width() == Infinity<float>()) {
241         frameSize.SetWidth(pipeline->GetRootWidth());
242     }
243     if (frameSize.Height() == Infinity<float>()) {
244         frameSize.SetHeight(pipeline->GetRootHeight());
245     }
246 }
247 
SwitchModeWithAnimation(const RefPtr<NavigationGroupNode> & hostNode)248 void SwitchModeWithAnimation(const RefPtr<NavigationGroupNode>& hostNode)
249 {
250     CHECK_NULL_VOID(hostNode);
251     hostNode->SetDoingModeSwitchAnimationFlag(true);
252     hostNode->SetNeedSetInvisible(false);
253     AnimationOption option;
254     option.SetCurve(MODE_SWITCH_CURVE);
255     option.SetFillMode(FillMode::FORWARDS);
256     option.SetDuration(MODE_SWITCH_ANIMATION_DURATION);
257     option.SetOnFinishEvent([weakHost = WeakPtr<NavigationGroupNode>(hostNode)]() {
258         auto hostNode = weakHost.Upgrade();
259         CHECK_NULL_VOID(hostNode);
260         hostNode->ReduceModeSwitchAnimationCnt();
261         if (hostNode->GetModeSwitchAnimationCnt() == 0) {
262             auto dividerNode = AceType::DynamicCast<FrameNode>(hostNode->GetDividerNode());
263             CHECK_NULL_VOID(dividerNode);
264             auto layoutProperty = dividerNode->GetLayoutProperty();
265             CHECK_NULL_VOID(layoutProperty);
266             layoutProperty->UpdateVisibility(VisibleType::VISIBLE);
267             auto pattern = hostNode->GetPattern<NavigationPattern>();
268             CHECK_NULL_VOID(pattern);
269             auto lastStandardIndex = hostNode->GetLastStandardIndex();
270             auto navigationLayoutProperty = hostNode->GetLayoutProperty<NavigationLayoutProperty>();
271             CHECK_NULL_VOID(navigationLayoutProperty);
272             bool navbarIsHidden = (pattern->GetNavigationMode() == NavigationMode::STACK && lastStandardIndex >= 0) ||
273                                   navigationLayoutProperty->GetHideNavBar().value_or(false);
274             if (navbarIsHidden) {
275                 hostNode->SetNeedSetInvisible(true);
276             } else {
277                 hostNode->SetNeedSetInvisible(false);
278             }
279             hostNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
280         }
281     });
282     AnimationUtils::Animate(option, [weakHost = WeakPtr<NavigationGroupNode>(hostNode)]() {
283         auto hostNode = weakHost.Upgrade();
284         CHECK_NULL_VOID(hostNode);
285         auto dividerNode = AceType::DynamicCast<FrameNode>(hostNode->GetDividerNode());
286         CHECK_NULL_VOID(dividerNode);
287         auto layoutProperty = dividerNode->GetLayoutProperty();
288         CHECK_NULL_VOID(layoutProperty);
289         layoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
290         hostNode->IncreaseModeSwitchAnimationCnt();
291         hostNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
292         hostNode->GetContext()->FlushUITasks();
293         hostNode->SetDoingModeSwitchAnimationFlag(false);
294     }, option.GetOnFinishEvent());
295 }
296 
297 } // namespace
298 
IsAutoHeight(const RefPtr<LayoutProperty> & layoutProperty)299 bool NavigationLayoutAlgorithm::IsAutoHeight(const RefPtr<LayoutProperty>& layoutProperty)
300 {
301     CHECK_NULL_RETURN(layoutProperty, false);
302     auto& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
303     if (!calcLayoutConstraint || !calcLayoutConstraint->selfIdealSize.has_value() ||
304         !calcLayoutConstraint->selfIdealSize->Height().has_value() ||
305         (calcLayoutConstraint->selfIdealSize->Height().value().ToString().find("auto") == std::string::npos)) {
306         return false;
307     }
308     return true;
309 }
310 
RangeCalculation(const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty)311 void NavigationLayoutAlgorithm::RangeCalculation(
312     const RefPtr<NavigationGroupNode>& hostNode, const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty)
313 {
314     const auto& constraint = navigationLayoutProperty->GetLayoutConstraint();
315     CHECK_NULL_VOID(constraint);
316     auto parentSize = CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT);
317     auto frameSize = parentSize.ConvertToSizeT();
318     float frameSizeWidth = frameSize.Width();
319     Dimension defaultValue = Dimension(-1.0);
320     auto pipeline = PipelineContext::GetCurrentContext();
321     CHECK_NULL_VOID(pipeline);
322 
323     minContentWidthValue_ = navigationLayoutProperty->GetMinContentWidthValue(defaultValue);
324     if (minContentWidthValue_ == defaultValue) {
325         userSetMinContentFlag_ = false;
326         minContentWidthValue_ = DEFAULT_MIN_CONTENT_WIDTH;
327     } else {
328         userSetMinContentFlag_ = true;
329     }
330     minNavBarWidthValue_ = navigationLayoutProperty->GetMinNavBarWidthValue(DEFAULT_MIN_NAV_BAR_WIDTH);
331     auto userSetMaxNavBarWidthValue = navigationLayoutProperty->GetMaxNavBarWidthValue(defaultValue);
332 
333     float minNavBarWidth =
334         std::min(static_cast<float>(minNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f))),
335             frameSizeWidth);
336     float maxNavBarWidth = 0.0f;
337     if (userSetMaxNavBarWidthValue == defaultValue) {
338         userSetNavBarRangeFlag_ = false;
339         maxNavBarWidth = std::min(
340             static_cast<float>(DEFAULT_MAX_NAV_BAR_WIDTH.ConvertToPx()), frameSizeWidth * MAX_NAV_BAR_WIDTH_SCALE);
341     } else {
342         userSetNavBarRangeFlag_ = true;
343         maxNavBarWidth =
344             static_cast<float>(userSetMaxNavBarWidthValue.ConvertToPxWithSize(parentSize.Width().value_or(0.0f)));
345     }
346     maxNavBarWidthValue_ = Dimension(Dimension(std::max(maxNavBarWidth, minNavBarWidth)).ConvertToVp(),
347         DimensionUnit::VP);
348     auto currentPlatformVersion = pipeline->GetMinPlatformVersion();
349     if (currentPlatformVersion >= PLATFORM_VERSION_TEN) {
350         auto minNavBarWidth = minNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
351         auto maxNavBarWidth = maxNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
352         realNavBarWidth_ = std::max(realNavBarWidth_, static_cast<float>(minNavBarWidth));
353         realNavBarWidth_ = std::min(realNavBarWidth_, static_cast<float>(maxNavBarWidth));
354     }
355     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
356     CHECK_NULL_VOID(navigationPattern);
357     navigationPattern->SetMinNavBarWidthValue(minNavBarWidthValue_);
358     navigationPattern->SetMaxNavBarWidthValue(maxNavBarWidthValue_);
359     navigationPattern->SetMinContentWidthValue(minContentWidthValue_);
360     navigationPattern->SetUserSetNavBarRangeFlag(userSetNavBarRangeFlag_);
361     navigationPattern->SetUserSetMinContentFlag(userSetMinContentFlag_);
362 }
363 
GetRange(const RefPtr<NavigationGroupNode> & hostNode)364 void NavigationLayoutAlgorithm::GetRange(const RefPtr<NavigationGroupNode>& hostNode)
365 {
366     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
367     CHECK_NULL_VOID(navigationPattern);
368     minNavBarWidthValue_ = navigationPattern->GetMinNavBarWidthValue();
369     maxNavBarWidthValue_ = navigationPattern->GetMaxNavBarWidthValue();
370     minContentWidthValue_ = navigationPattern->GetMinContentWidthValue();
371     userSetNavBarRangeFlag_ = navigationPattern->GetUserSetNavBarRangeFlag();
372     userSetMinContentFlag_ = navigationPattern->GetUserSetMinContentFlag();
373     userSetNavBarWidthFlag_ = navigationPattern->GetUserSetNavBarWidthFlag();
374 }
375 
CalculateNavigationWidth(const RefPtr<NavigationGroupNode> & hostNode)376 float NavigationLayoutAlgorithm::CalculateNavigationWidth(const RefPtr<NavigationGroupNode>& hostNode)
377 {
378     auto navigationLayoutProperty = AceType::DynamicCast<NavigationLayoutProperty>(hostNode->GetLayoutProperty());
379     auto pipeline = hostNode->GetContext();
380     CHECK_NULL_RETURN(pipeline, 0.0f);
381     auto currentPlatformVersion = pipeline->GetMinPlatformVersion();
382     auto navigationWidth = 0.0f;
383     if (currentPlatformVersion >= PLATFORM_VERSION_TEN) {
384         CHECK_NULL_RETURN(navigationLayoutProperty, navigationWidth);
385         const auto& constraint = navigationLayoutProperty->GetLayoutConstraint();
386         auto parentSize = CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT);
387         auto minNavBarWidth = minNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
388         navigationWidth = static_cast<float>(minNavBarWidth + minContentWidthValue_.ConvertToPx());
389     } else {
390         navigationWidth = static_cast<float>(WINDOW_WIDTH.ConvertToPx());
391     }
392     return navigationWidth;
393 }
394 
UpdateNavigationMode(const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & frameSize,const RefPtr<NavigationGroupNode> & hostNode)395 void NavigationLayoutAlgorithm::UpdateNavigationMode(const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty,
396     const SizeF& frameSize, const RefPtr<NavigationGroupNode>& hostNode)
397 {
398     CHECK_NULL_VOID(hostNode);
399     CHECK_NULL_VOID(navigationLayoutProperty);
400     auto usrNavigationMode = navigationLayoutProperty->GetUsrNavigationModeValue(NavigationMode::AUTO);
401     if (usrNavigationMode == NavigationMode::AUTO) {
402         if (frameSize.Width() >= CalculateNavigationWidth(hostNode)) {
403             usrNavigationMode = NavigationMode::SPLIT;
404             auto navBarNode = hostNode->GetNavBarNode();
405             if (navBarNode) {
406                 navBarNode->SetJSViewActive(true);
407             }
408         } else {
409             usrNavigationMode = NavigationMode::STACK;
410         }
411     }
412     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
413     bool modeChange = navigationPattern->GetNavigationMode() != usrNavigationMode;
414     bool isFirstTimeLayout = (navigationPattern->GetNavigationMode() == INITIAL_MODE);
415     bool doModeSwitchAnimationInAnotherTask = modeChange && !isFirstTimeLayout && !hostNode->IsOnModeSwitchAnimation();
416     if (doModeSwitchAnimationInAnotherTask) {
417         auto container = Container::Current();
418         CHECK_NULL_VOID(container);
419         if (container->IsFoldable()) {
420             // If screen-fold-state changed, no need to do mode switch animation.
421             // Only when navigation-mode changed, it is necessary to update the current screen-fold-state.
422             doModeSwitchAnimationInAnotherTask =
423                 !navigationPattern->JudgeFoldStateChangeAndUpdateState() && doModeSwitchAnimationInAnotherTask;
424         }
425     }
426     if (!doModeSwitchAnimationInAnotherTask) {
427         navigationPattern->SetNavigationMode(usrNavigationMode);
428         navigationPattern->SetNavigationModeChange(modeChange);
429     }
430 
431     auto pipeline = hostNode->GetContext();
432     CHECK_NULL_VOID(pipeline);
433     pipeline->AddAfterLayoutTask([weakNavigationPattern = WeakPtr<NavigationPattern>(navigationPattern),
434         modeChange, doModeSwitchAnimationInAnotherTask]() {
435         auto navigationPattern = weakNavigationPattern.Upgrade();
436         CHECK_NULL_VOID(navigationPattern);
437         if (doModeSwitchAnimationInAnotherTask) {
438             navigationPattern->OnNavBarStateChange(false);
439             SwitchModeWithAnimation(AceType::DynamicCast<NavigationGroupNode>(navigationPattern->GetHost()));
440         } else {
441             navigationPattern->OnNavBarStateChange(modeChange);
442             navigationPattern->OnNavigationModeChange(modeChange);
443         }
444     });
445 }
446 
SizeCalculation(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & frameSize)447 void NavigationLayoutAlgorithm::SizeCalculation(LayoutWrapper* layoutWrapper,
448     const RefPtr<NavigationGroupNode>& hostNode, const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty,
449     const SizeF& frameSize)
450 {
451     auto pipeline = PipelineContext::GetCurrentContext();
452     CHECK_NULL_VOID(pipeline);
453     auto constraint = navigationLayoutProperty->GetLayoutConstraint();
454     auto parentSize = CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT);
455     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
456     auto currentPlatformVersion = pipeline->GetMinPlatformVersion();
457     if (currentPlatformVersion >= PLATFORM_VERSION_TEN) {
458         auto minNavBarWidth = minNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
459         auto maxNavBarWidth = maxNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
460         realNavBarWidth_ = std::min(realNavBarWidth_, static_cast<float>(maxNavBarWidth));
461         realNavBarWidth_ = std::max(realNavBarWidth_, static_cast<float>(minNavBarWidth));
462     } else {
463         auto navBarWidthValue = navigationLayoutProperty->GetNavBarWidthValue(DEFAULT_NAV_BAR_WIDTH);
464         auto navBarWidth = navBarWidthValue.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
465         realNavBarWidth_ = navBarWidth;
466     }
467     navBarSize_ = frameSize;
468     contentSize_ = frameSize;
469     dividerSize_ = SizeF(0.0f, frameSize.Height());
470     if (navigationPattern->GetNavigationMode() == NavigationMode::SPLIT) {
471         SizeCalculationSplit(hostNode, navigationLayoutProperty, frameSize);
472     } else {
473         SizeCalculationStack(hostNode, navigationLayoutProperty, frameSize);
474     }
475 }
476 
SizeCalculationSplit(const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & frameSize)477 void NavigationLayoutAlgorithm::SizeCalculationSplit(const RefPtr<NavigationGroupNode>& hostNode,
478     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, const SizeF& frameSize)
479 {
480     float frameWidth = frameSize.Width();
481     auto parentSize = CreateIdealSizeByPercentRef(
482         navigationLayoutProperty->GetLayoutConstraint().value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT);
483     auto navBarWidthValue = navigationLayoutProperty->GetNavBarWidthValue(DEFAULT_NAV_BAR_WIDTH);
484     auto userSetNavBarWidth = navBarWidthValue.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
485     auto dividerWidth = static_cast<float>(DIVIDER_WIDTH.ConvertToPx());
486     auto minNavBarWidth = minNavBarWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
487     auto minContentWidth = minContentWidthValue_.ConvertToPxWithSize(parentSize.Width().value_or(0.0f));
488     realContentWidth_ = minContentWidth;
489 
490     bool isHideNavbar = navigationLayoutProperty->GetHideNavBar().value_or(false);
491     if (isHideNavbar) {
492         CHECK_NULL_VOID(hostNode);
493         auto navBarNode = AceType::DynamicCast<FrameNode>(hostNode->GetNavBarNode());
494         CHECK_NULL_VOID(navBarNode);
495         auto geometryNode = navBarNode->GetGeometryNode();
496         CHECK_NULL_VOID(geometryNode);
497         navBarSize_.SetWidth(geometryNode->GetFrameSize().Width());
498         dividerSize_.SetWidth(0.0f);
499         realNavBarWidth_ = 0.0f;
500         realContentWidth_ = frameWidth;
501     } else {
502         CheckSizeInSplit(frameWidth, userSetNavBarWidth, minNavBarWidth, minContentWidth);
503     }
504 
505     realDividerWidth_ = std::max(realDividerWidth_, 0.0f);
506     realContentWidth_ = std::max(realContentWidth_, 0.0f);
507     realNavBarWidth_ = std::min(realNavBarWidth_, frameWidth);
508     realContentWidth_ = std::min(realContentWidth_, frameWidth);
509     if (realNavBarWidth_ == 0.0f || realContentWidth_ == 0.0f) {
510         realDividerWidth_ = 0.0f;
511     } else {
512         realDividerWidth_ = dividerWidth;
513     }
514     if (!isHideNavbar) {
515         navBarSize_.SetWidth(realNavBarWidth_);
516         dividerSize_.SetWidth(realDividerWidth_);
517     }
518     contentSize_.SetWidth(realContentWidth_);
519 }
520 
CheckSizeInSplit(const float frameWidth,const float userSetNavBarWidth,const float minNavBarWidth,const float minContentWidth)521 void NavigationLayoutAlgorithm::CheckSizeInSplit(
522     const float frameWidth, const float userSetNavBarWidth, const float minNavBarWidth, const float minContentWidth)
523 {
524     auto dividerWidth = static_cast<float>(DIVIDER_WIDTH.ConvertToPx());
525 
526     if (userSetMinContentFlag_ && !userSetNavBarRangeFlag_) {
527         if (minContentWidth >= frameWidth) {
528             realContentWidth_ = frameWidth;
529             realNavBarWidth_ = 0.0f;
530         } else if (realNavBarWidth_ + dividerWidth + minContentWidth <= frameWidth) {
531             realContentWidth_ = frameWidth - realNavBarWidth_ - dividerWidth;
532         } else {
533             realContentWidth_ = minContentWidth;
534             realNavBarWidth_ = frameWidth - realContentWidth_ - dividerWidth;
535         }
536     } else if (!userSetNavBarRangeFlag_ && !userSetMinContentFlag_ && userSetNavBarWidthFlag_) {
537         realNavBarWidth_ = userSetNavBarWidth;
538         realContentWidth_ = frameWidth - realNavBarWidth_ - dividerWidth;
539     } else {
540         float remainingSpace = frameWidth - realNavBarWidth_ - dividerWidth;
541         float remainingMaxSpace = frameWidth - minNavBarWidth - dividerWidth;
542         if (remainingSpace >= minContentWidth) {
543             realContentWidth_ = remainingSpace;
544         } else if (remainingSpace < minContentWidth && remainingMaxSpace > minContentWidth &&
545                    realNavBarWidth_ > minNavBarWidth) {
546             realContentWidth_ = minContentWidth;
547             realNavBarWidth_ = frameWidth - minContentWidth - dividerWidth;
548         } else {
549             realNavBarWidth_ = minNavBarWidth;
550             realContentWidth_ = frameWidth - minNavBarWidth - dividerWidth;
551         }
552     }
553 }
554 
SizeCalculationStack(const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & frameSize)555 void NavigationLayoutAlgorithm::SizeCalculationStack(const RefPtr<NavigationGroupNode>& hostNode,
556     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, const SizeF& frameSize)
557 {
558     auto contentNode = hostNode->GetContentNode();
559     CHECK_NULL_VOID(contentNode);
560     realDividerWidth_ = 0.0f;
561     float frameWidth = frameSize.Width();
562     navBarSize_.SetWidth(frameWidth);
563     dividerSize_.SetWidth(realDividerWidth_);
564     contentSize_.SetWidth(frameWidth);
565     realContentWidth_ = frameWidth;
566 }
567 
MeasureNavBar(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & navBarSize)568 void NavigationLayoutAlgorithm::MeasureNavBar(LayoutWrapper* layoutWrapper, const RefPtr<NavigationGroupNode>& hostNode,
569     const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty, const SizeF& navBarSize)
570 {
571     auto navBarNode = hostNode->GetNavBarNode();
572     CHECK_NULL_VOID(navBarNode);
573     auto index = hostNode->GetChildIndexById(navBarNode->GetId());
574     auto navBarWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
575     CHECK_NULL_VOID(navBarWrapper);
576     auto constraint = navigationLayoutProperty->CreateChildConstraint();
577     if (IsAutoHeight(navigationLayoutProperty)) {
578         navBarWrapper->GetLayoutProperty()->UpdateUserDefinedIdealSize(
579             navigationLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize.value());
580         constraint.selfIdealSize.SetWidth(navBarSize.Width());
581     } else {
582         constraint.selfIdealSize = OptionalSizeF(navBarSize.Width(), navBarSize.Height());
583     }
584     navBarWrapper->Measure(constraint);
585     realNavBarHeight_ = navBarWrapper->GetGeometryNode()->GetFrameSize().Height();
586 }
587 
MeasureContentChild(LayoutWrapper * layoutWrapper,const RefPtr<NavigationGroupNode> & hostNode,const RefPtr<NavigationLayoutProperty> & navigationLayoutProperty,const SizeF & contentSize)588 void NavigationLayoutAlgorithm::MeasureContentChild(LayoutWrapper* layoutWrapper,
589     const RefPtr<NavigationGroupNode>& hostNode, const RefPtr<NavigationLayoutProperty>& navigationLayoutProperty,
590     const SizeF& contentSize)
591 {
592     auto contentNode = hostNode->GetContentNode();
593     CHECK_NULL_VOID(contentNode);
594     auto index = hostNode->GetChildIndexById(contentNode->GetId());
595     auto contentWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
596     CHECK_NULL_VOID(contentWrapper);
597     auto constraint = navigationLayoutProperty->CreateChildConstraint();
598     if (contentNode->GetChildren().empty()) {
599         constraint.selfIdealSize = OptionalSizeF(0.0f, 0.0f);
600     } else {
601         if (IsAutoHeight(navigationLayoutProperty)) {
602             constraint.selfIdealSize.SetWidth(contentSize.Width());
603         } else {
604             constraint.selfIdealSize = OptionalSizeF(contentSize.Width(), contentSize.Height());
605         }
606     }
607     contentWrapper->Measure(constraint);
608     realContentHeight_ = contentWrapper->GetGeometryNode()->GetFrameSize().Height();
609 }
610 
Measure(LayoutWrapper * layoutWrapper)611 void NavigationLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
612 {
613     auto hostNode = AceType::DynamicCast<NavigationGroupNode>(layoutWrapper->GetHostNode());
614     CHECK_NULL_VOID(hostNode);
615     auto navigationLayoutProperty = AceType::DynamicCast<NavigationLayoutProperty>(layoutWrapper->GetLayoutProperty());
616     CHECK_NULL_VOID(navigationLayoutProperty);
617     const auto& constraint = navigationLayoutProperty->GetLayoutConstraint();
618     CHECK_NULL_VOID(constraint);
619     auto geometryNode = layoutWrapper->GetGeometryNode();
620     auto size =
621         CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT).ConvertToSizeT();
622     FitScrollFullWindow(size);
623 
624     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
625     MinusPaddingToSize(padding, size);
626 
627     if (ifNeedInit_) {
628         RangeCalculation(hostNode, navigationLayoutProperty);
629     }
630     if (size.Width() == 0.0f) {
631         auto layoutAlgorithm = layoutWrapper->GetLayoutAlgorithm();
632         if (layoutAlgorithm) {
633             layoutAlgorithm->SetSkipLayout();
634         }
635         return;
636     }
637     GetRange(hostNode);
638     UpdateNavigationMode(navigationLayoutProperty, size, hostNode);
639     SizeCalculation(layoutWrapper, hostNode, navigationLayoutProperty, size);
640 
641     MeasureNavBar(layoutWrapper, hostNode, navigationLayoutProperty, navBarSize_);
642     MeasureContentChild(layoutWrapper, hostNode, navigationLayoutProperty, contentSize_);
643     MeasureDivider(layoutWrapper, hostNode, navigationLayoutProperty, dividerSize_);
644     MeasureDragBar(layoutWrapper, hostNode, navigationLayoutProperty, dividerSize_);
645 
646     if (IsAutoHeight(navigationLayoutProperty)) {
647         SetNavigationHeight(layoutWrapper, size);
648     }
649     size.AddWidth(padding.left.value_or(0.0f) + padding.right.value_or(0.0f));
650     size.AddHeight(padding.top.value_or(0.0f) + padding.bottom.value_or(0.0f));
651     layoutWrapper->GetGeometryNode()->SetFrameSize(size);
652 }
653 
Layout(LayoutWrapper * layoutWrapper)654 void NavigationLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
655 {
656     auto layoutAlgorithm = layoutWrapper->GetLayoutAlgorithm();
657     if (layoutAlgorithm && layoutAlgorithm->SkipLayout()) {
658         return;
659     }
660     auto hostNode = AceType::DynamicCast<NavigationGroupNode>(layoutWrapper->GetHostNode());
661     CHECK_NULL_VOID(hostNode);
662     auto navigationLayoutProperty = AceType::DynamicCast<NavigationLayoutProperty>(layoutWrapper->GetLayoutProperty());
663     CHECK_NULL_VOID(navigationLayoutProperty);
664     auto navBarPosition = navigationLayoutProperty->GetNavBarPositionValue(NavBarPosition::START);
665     OffsetF navBarOffset(0.0, 0.0);
666     float navBarWidth = LayoutNavBar(layoutWrapper, hostNode, navigationLayoutProperty, navBarPosition, navBarOffset);
667     float dividerWidth = LayoutDivider(layoutWrapper, hostNode, navigationLayoutProperty, navBarWidth, navBarPosition);
668     LayoutContent(layoutWrapper, hostNode, navigationLayoutProperty, navBarWidth, dividerWidth, navBarPosition);
669     LayoutDragBar(layoutWrapper, hostNode, navigationLayoutProperty, navBarWidth, navBarPosition);
670 
671     auto&& opts = navigationLayoutProperty->GetSafeAreaExpandOpts();
672     if (opts) {
673         auto geometryNode = hostNode->GetGeometryNode();
674         CHECK_NULL_VOID(geometryNode);
675         TAG_LOGD(AceLogTag::ACE_NAVIGATION,
676             "Navigation id is %{public}d, frameRect is %{public}s",
677             hostNode->GetId(), geometryNode->GetFrameRect().ToString().c_str());
678     }
679 }
680 
SetNavigationHeight(LayoutWrapper * layoutWrapper,SizeF & size)681 void NavigationLayoutAlgorithm::SetNavigationHeight(LayoutWrapper* layoutWrapper, SizeF& size)
682 {
683     auto hostNode = AceType::DynamicCast<NavigationGroupNode>(layoutWrapper->GetHostNode());
684     CHECK_NULL_VOID(hostNode);
685     auto navigationPattern = AceType::DynamicCast<NavigationPattern>(hostNode->GetPattern());
686     CHECK_NULL_VOID(navigationPattern);
687     auto navigationStack = navigationPattern->GetNavigationStack();
688     CHECK_NULL_VOID(navigationStack);
689     if (navigationStack->Empty()) {
690         size.SetHeight(realNavBarHeight_);
691     } else if (navigationPattern->GetNavigationMode() == NavigationMode::STACK) {
692         size.SetHeight(realContentHeight_);
693     } else if (navigationPattern->GetNavigationMode() == NavigationMode::SPLIT) {
694         float navHeight = std::max(realContentHeight_, realNavBarHeight_);
695         size.SetHeight(navHeight);
696     }
697 }
698 
699 } // namespace OHOS::Ace::NG
700