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