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