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