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