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