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