1 /*
2 * Copyright (c) 2024 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_util.h"
17
18 #include "core/components/common/layout/grid_system_manager.h"
19 #include "core/components_ng/pattern/navigation/navdestination_node_base.h"
20 #include "core/components_ng/pattern/navigation/navdestination_pattern_base.h"
21 #include "core/components_ng/pattern/navigation/nav_bar_pattern.h"
22 #include "core/components_ng/pattern/navigation/title_bar_pattern.h"
23 #include "core/components_ng/pattern/navigation/tool_bar_node.h"
24 #include "core/components_ng/pattern/navigation/navigation_title_util.h"
25
26 namespace OHOS::Ace::NG {
CheckWhetherNeedToHideToolbar(const RefPtr<NavDestinationNodeBase> & nodeBase,const SizeF & navigationSize)27 bool NavigationLayoutUtil::CheckWhetherNeedToHideToolbar(
28 const RefPtr<NavDestinationNodeBase>& nodeBase, const SizeF& navigationSize)
29 {
30 // if current menu or toolBar is custom, no need to hide.
31 if (nodeBase->GetPrevMenuIsCustomValue(false) || nodeBase->GetPrevToolBarIsCustom().value_or(false)) {
32 return false;
33 }
34
35 auto toolbarNode = AceType::DynamicCast<NavToolbarNode>(nodeBase->GetToolBarNode());
36 CHECK_NULL_RETURN(toolbarNode, false);
37
38 if (!EnableToolBarAdaptation(nodeBase)) {
39 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "The toolbar adaptation has been closed");
40 return false;
41 }
42
43 if (!toolbarNode->HasValidContent()) {
44 return true;
45 }
46
47 auto theme = NavigationGetTheme();
48 CHECK_NULL_RETURN(theme, false);
49 auto rotationLimitCount = theme->GetToolbarRotationLimitGridCount();
50
51 RefPtr<GridColumnInfo> columnInfo;
52 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::NAVIGATION_TOOLBAR);
53 CHECK_NULL_RETURN(columnInfo, false);
54 auto columnInfoParent = columnInfo->GetParent();
55 CHECK_NULL_RETURN(columnInfoParent, false);
56 columnInfoParent->BuildColumnWidth();
57
58 auto currentColumns = columnInfoParent->GetColumns();
59 float gridWidth = static_cast<float>(columnInfo->GetWidth(rotationLimitCount));
60 float gutterWidth = columnInfoParent->GetGutterWidth().ConvertToPx();
61 float hideLimitWidth = gridWidth + gutterWidth * 2;
62 if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
63 if (currentColumns >= static_cast<int32_t>(rotationLimitCount) &&
64 GreatOrEqual(navigationSize.Width(), gridWidth)) {
65 return true;
66 }
67 } else if (SystemProperties::GetDeviceType() == DeviceType::TABLET) {
68 if (currentColumns > static_cast<int32_t>(rotationLimitCount) &&
69 GreatNotEqual(navigationSize.Width(), hideLimitWidth)) {
70 return true;
71 }
72 }
73 return false;
74 }
75
EnableToolBarAdaptation(const RefPtr<NavDestinationNodeBase> & nodeBase)76 bool NavigationLayoutUtil::EnableToolBarAdaptation(const RefPtr<NavDestinationNodeBase>& nodeBase)
77 {
78 auto navigationNode = AceType::DynamicCast<NavigationGroupNode>(nodeBase->GetNavigationNode());
79 if (navigationNode) {
80 auto navigatonLayoutProperty = navigationNode->GetLayoutProperty<NavigationLayoutProperty>();
81 if (navigatonLayoutProperty) {
82 return navigatonLayoutProperty->GetEnableToolBarAdaptationValue(true);
83 }
84 }
85 return true;
86 }
87
UpdateTitleBarMenuNode(const RefPtr<NavDestinationNodeBase> & nodeBase,const SizeF & navigationSize)88 void NavigationLayoutUtil::UpdateTitleBarMenuNode(
89 const RefPtr<NavDestinationNodeBase>& nodeBase, const SizeF& navigationSize)
90 {
91 auto pattern = nodeBase->GetPattern<NavDestinationPatternBase>();
92 CHECK_NULL_VOID(pattern);
93 if (nodeBase->GetPrevMenuIsCustomValue(false)) {
94 return;
95 }
96
97 auto titleBarNode = AceType::DynamicCast<TitleBarNode>(nodeBase->GetTitleBarNode());
98 CHECK_NULL_VOID(titleBarNode);
99 auto toolBarNode = AceType::DynamicCast<FrameNode>(nodeBase->GetToolBarNode());
100 CHECK_NULL_VOID(toolBarNode);
101 auto toolBarLayoutProperty = toolBarNode->GetLayoutProperty<LayoutProperty>();
102 CHECK_NULL_VOID(toolBarLayoutProperty);
103 auto toolBarDivider = AceType::DynamicCast<FrameNode>(nodeBase->GetToolBarDividerNode());
104 RefPtr<LayoutProperty> toolBarDividerProperty = nullptr;
105 if (toolBarDivider) {
106 toolBarDividerProperty = toolBarDivider->GetLayoutProperty();
107 }
108 auto isHideToolbar = pattern->GetToolbarHideStatus();
109 auto preMenuNode = titleBarNode->GetMenu();
110 bool needHide = CheckWhetherNeedToHideToolbar(nodeBase, navigationSize);
111 bool isNeedLandscapeMenu = needHide && !isHideToolbar;
112 pattern->SetIsNeedHideToolBarForNavWidth(needHide);
113 if (isNeedLandscapeMenu) {
114 toolBarLayoutProperty->UpdateVisibility(VisibleType::GONE);
115 if (toolBarDividerProperty) {
116 toolBarDividerProperty->UpdateVisibility(VisibleType::GONE);
117 }
118 } else if (!isHideToolbar) {
119 toolBarLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
120 if (toolBarDividerProperty) {
121 toolBarDividerProperty->UpdateVisibility(VisibleType::VISIBLE);
122 }
123 }
124 RefPtr<UINode> newMenuNode = isNeedLandscapeMenu ? nodeBase->GetLandscapeMenu() : nodeBase->GetMenu();
125 if (preMenuNode == newMenuNode) {
126 return;
127 }
128 auto nodeBasePattern = nodeBase->GetPattern<NavDestinationPatternBase>();
129 CHECK_NULL_VOID(nodeBasePattern);
130 // Mark need update safeAreaPadding when need hide safe-area-padding-mode-toolBar by landscape menu
131 if (nodeBasePattern->GetToolBarStyle().value_or(BarStyle::STANDARD) == BarStyle::SAFE_AREA_PADDING) {
132 nodeBasePattern->UpdateSafeAreaPaddingChanged(true);
133 }
134 titleBarNode->RemoveChild(preMenuNode);
135 titleBarNode->SetMenu(newMenuNode);
136 titleBarNode->AddChild(newMenuNode);
137 }
138
MeasureToolBar(LayoutWrapper * layoutWrapper,const RefPtr<NavDestinationNodeBase> & nodeBase,const RefPtr<NavDestinationLayoutPropertyBase> & layoutPropertyBase,const SizeF & navigationSize)139 float NavigationLayoutUtil::MeasureToolBar(LayoutWrapper* layoutWrapper, const RefPtr<NavDestinationNodeBase>& nodeBase,
140 const RefPtr<NavDestinationLayoutPropertyBase>& layoutPropertyBase, const SizeF& navigationSize)
141 {
142 auto navDestinationPatternBase = nodeBase->GetPattern<NavDestinationPatternBase>();
143 CHECK_NULL_RETURN(navDestinationPatternBase, 0.0f);
144 auto toolBarNode = nodeBase->GetToolBarNode();
145 CHECK_NULL_RETURN(toolBarNode, 0.0f);
146 auto index = nodeBase->GetChildIndexById(toolBarNode->GetId());
147 auto toolBarWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
148 CHECK_NULL_RETURN(toolBarWrapper, 0.0f);
149 auto constraint = layoutPropertyBase->CreateChildConstraint();
150
151 /**
152 * In the follow scenarios, we need to set the toolBar size to zero.
153 * 1. ToolBar has no child.
154 * 2. ToolBar is hidden and no toolBar animation is running.
155 * 3. ToolBar should be moved to the menu position in the Title.
156 */
157 auto translateState = layoutPropertyBase->GetToolBarTranslateStateValue(BarTranslateState::NONE);
158 if ((translateState == BarTranslateState::NONE &&
159 layoutPropertyBase->GetHideToolBar().value_or(false)) || toolBarNode->GetChildren().empty() ||
160 CheckWhetherNeedToHideToolbar(nodeBase, navigationSize)) {
161 constraint.selfIdealSize = OptionalSizeF(0.0f, 0.0f);
162 toolBarWrapper->Measure(constraint);
163 return 0.0f;
164 }
165
166 auto theme = NavigationGetTheme();
167 CHECK_NULL_RETURN(theme, 0.0f);
168 auto toolbarHeight = theme->GetToolbarHeigth();
169 constraint.selfIdealSize = OptionalSizeF(navigationSize.Width(), static_cast<float>(toolbarHeight.ConvertToPx()));
170 toolBarWrapper->Measure(constraint);
171 auto toolbarHeightAfterMeasure = toolBarWrapper->GetGeometryNode()->GetFrameSize().Height();
172 return static_cast<float>(toolbarHeightAfterMeasure);
173 }
174
MeasureToolBarDivider(LayoutWrapper * layoutWrapper,const RefPtr<NavDestinationNodeBase> & nodeBase,const RefPtr<NavDestinationLayoutPropertyBase> & layoutPropertyBase,const SizeF & navigationSize,float toolBarHeight)175 float NavigationLayoutUtil::MeasureToolBarDivider(
176 LayoutWrapper* layoutWrapper, const RefPtr<NavDestinationNodeBase>& nodeBase,
177 const RefPtr<NavDestinationLayoutPropertyBase>& layoutPropertyBase,
178 const SizeF& navigationSize, float toolBarHeight)
179 {
180 if (nodeBase->GetPrevToolBarIsCustom().value_or(false) || !nodeBase->IsUseToolbarConfiguration()) {
181 return 0.0f;
182 }
183
184 auto navDestinationPatternBase = nodeBase->GetPattern<NavDestinationPatternBase>();
185 CHECK_NULL_RETURN(navDestinationPatternBase, 0.0f);
186 auto toolBarDividerNode = AceType::DynamicCast<FrameNode>(nodeBase->GetToolBarDividerNode());
187 CHECK_NULL_RETURN(toolBarDividerNode, 0.0f);
188 auto dividerIndex = nodeBase->GetChildIndexById(toolBarDividerNode->GetId());
189 auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(dividerIndex);
190 CHECK_NULL_RETURN(dividerWrapper, 0.0f);
191 auto constraint = layoutPropertyBase->CreateChildConstraint();
192
193 /**
194 * In the follow scenarios, we need to set the toolBarDivider size to zero.
195 * 1. ToolBar has zero size.
196 * 2. ToolBar is hidden and no toolBar animation is running.
197 */
198 auto translateState = layoutPropertyBase->GetToolBarTranslateStateValue(BarTranslateState::NONE);
199 if ((translateState == BarTranslateState::NONE &&
200 layoutPropertyBase->GetHideToolBar().value_or(false)) || NearEqual(toolBarHeight, 0.0f)) {
201 constraint.selfIdealSize = OptionalSizeF(0.0f, 0.0f);
202 dividerWrapper->Measure(constraint);
203 return 0.0f;
204 }
205 auto theme = NavigationGetTheme();
206 CHECK_NULL_RETURN(theme, 0.0f);
207 constraint.selfIdealSize =
208 OptionalSizeF(navigationSize.Width(), static_cast<float>(theme->GetToolBarDividerWidth().ConvertToPx()));
209 dividerWrapper->Measure(constraint);
210 return static_cast<float>(theme->GetToolBarDividerWidth().ConvertToPx());
211 }
212
LayoutToolBar(LayoutWrapper * layoutWrapper,const RefPtr<NavDestinationNodeBase> & nodeBase,const RefPtr<NavDestinationLayoutPropertyBase> & layoutPropertyBase,bool isNeedToCreatePaddingAndBorder)213 float NavigationLayoutUtil::LayoutToolBar(LayoutWrapper* layoutWrapper, const RefPtr<NavDestinationNodeBase>& nodeBase,
214 const RefPtr<NavDestinationLayoutPropertyBase>& layoutPropertyBase, bool isNeedToCreatePaddingAndBorder)
215 {
216 /**
217 * When all the following conditions are met, we consider the boolBar height to be 0:
218 * 1. ToolBar should hide.
219 * 2. No toolBar animation is running or toolBar was translate out of navigation area.
220 */
221 auto translateState = layoutPropertyBase->GetToolBarTranslateStateValue(BarTranslateState::NONE);
222 if (translateState != BarTranslateState::TRANSLATE_ZERO && layoutPropertyBase->GetHideToolBar().value_or(false)) {
223 return 0.0f;
224 }
225 auto toolBarNode = nodeBase->GetToolBarNode();
226 CHECK_NULL_RETURN(toolBarNode, 0.0f);
227 auto index = nodeBase->GetChildIndexById(toolBarNode->GetId());
228 auto toolBarWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
229 CHECK_NULL_RETURN(toolBarWrapper, 0.0f);
230 auto geometryNode = toolBarWrapper->GetGeometryNode();
231 float toolbarHeight = geometryNode->GetFrameSize().Height();
232 if (NearZero(toolbarHeight)) {
233 return 0.0f;
234 }
235 auto toolBarOffsetX = geometryNode->GetFrameOffset().GetX();
236 auto toolBarOffsetY = layoutWrapper->GetGeometryNode()->GetFrameSize().Height() - toolbarHeight;
237 if (isNeedToCreatePaddingAndBorder) {
238 const auto& padding = layoutPropertyBase->CreatePaddingAndBorder();
239 toolBarOffsetX = padding.left.value_or(0.0f);
240 toolBarOffsetY -= padding.bottom.value_or(0.0f);
241 }
242 auto toolBarOffset = OffsetF(static_cast<float>(toolBarOffsetX), static_cast<float>(toolBarOffsetY));
243 geometryNode->SetMarginFrameOffset(toolBarOffset);
244 toolBarWrapper->Layout();
245 return toolbarHeight;
246 }
247
LayoutToolBarDivider(LayoutWrapper * layoutWrapper,const RefPtr<NavDestinationNodeBase> & nodeBase,const RefPtr<NavDestinationLayoutPropertyBase> & layoutPropertyBase,float toolbarHeight,bool isNeedToCreatePaddingAndBorder)248 void NavigationLayoutUtil::LayoutToolBarDivider(
249 LayoutWrapper* layoutWrapper, const RefPtr<NavDestinationNodeBase>& nodeBase,
250 const RefPtr<NavDestinationLayoutPropertyBase>& layoutPropertyBase, float toolbarHeight,
251 bool isNeedToCreatePaddingAndBorder)
252 {
253 /**
254 * In the follow scenarios, we should not layout the toolBarDivider:
255 * 1. ToolBar has zero size.
256 * 2. Developer use the deprecated `toolBar` attr.
257 * 3. Custom toolbar was used.
258 * 4. Hide toolbar, no toolBar animation is running or toolBar was translate out of navigation area.
259 */
260 auto translateState = layoutPropertyBase->GetToolBarTranslateStateValue(BarTranslateState::NONE);
261 if ((translateState != BarTranslateState::TRANSLATE_ZERO && layoutPropertyBase->GetHideToolBar().value_or(false)) ||
262 nodeBase->GetPrevToolBarIsCustom().value_or(false) ||
263 !nodeBase->IsUseToolbarConfiguration() || NearZero(toolbarHeight)) {
264 return;
265 }
266 auto dividerNode = nodeBase->GetToolBarDividerNode();
267 CHECK_NULL_VOID(dividerNode);
268 auto dividerIndex = nodeBase->GetChildIndexById(dividerNode->GetId());
269 auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(dividerIndex);
270 CHECK_NULL_VOID(dividerWrapper);
271 auto dividerGeometryNode = dividerWrapper->GetGeometryNode();
272
273 auto theme = NavigationGetTheme();
274 CHECK_NULL_VOID(theme);
275 auto dividerOffsetX = dividerGeometryNode->GetFrameOffset().GetX();
276 auto dividerOffsetY = layoutWrapper->GetGeometryNode()->GetFrameSize().Height() - toolbarHeight -
277 theme->GetToolBarDividerWidth().ConvertToPx();
278 if (isNeedToCreatePaddingAndBorder) {
279 const auto& padding = layoutPropertyBase->CreatePaddingAndBorder();
280 dividerOffsetX = padding.left.value_or(0.0f);
281 dividerOffsetY -= padding.bottom.value_or(0.0f);
282 }
283 auto toolBarDividerOffset = OffsetF(static_cast<float>(dividerOffsetX), static_cast<float>(dividerOffsetY));
284 dividerGeometryNode->SetFrameOffset(toolBarDividerOffset);
285 dividerWrapper->Layout();
286 }
287
UpdateContentSafeAreaPadding(const RefPtr<NavDestinationNodeBase> & nodeBase,float titleBarHeight)288 void NavigationLayoutUtil::UpdateContentSafeAreaPadding(
289 const RefPtr<NavDestinationNodeBase>& nodeBase, float titleBarHeight)
290 {
291 CHECK_NULL_VOID(nodeBase);
292 auto patternBase = nodeBase->GetPattern<NavDestinationPatternBase>();
293 CHECK_NULL_VOID(patternBase);
294 if (!patternBase->IsSafeAreaPaddingChanged()) {
295 return;
296 }
297 patternBase->UpdateSafeAreaPaddingChanged(false);
298 auto contentNode = AceType::DynamicCast<FrameNode>(nodeBase->GetContentNode());
299 CHECK_NULL_VOID(contentNode);
300 auto contentLayoutProperty = contentNode->GetLayoutProperty();
301 CHECK_NULL_VOID(contentLayoutProperty);
302
303 Dimension paddingTop = 0.0_vp;
304 if (patternBase->GetTitleBarStyle().value_or(BarStyle::STANDARD) == BarStyle::SAFE_AREA_PADDING &&
305 !NavigationTitleUtil::IsTitleBarHasOffsetY(AceType::DynamicCast<FrameNode>(nodeBase->GetTitleBarNode()))) {
306 paddingTop = Dimension(titleBarHeight);
307 }
308 Dimension paddingBottom = 0.0_vp;
309 auto toolBarNode = AceType::DynamicCast<FrameNode>(nodeBase->GetToolBarNode());
310 if (patternBase->GetToolBarStyle().value_or(BarStyle::STANDARD) == BarStyle::SAFE_AREA_PADDING &&
311 toolBarNode && toolBarNode->IsVisible()) {
312 auto theme = NavigationGetTheme();
313 if (theme) {
314 paddingBottom = theme->GetHeight();
315 }
316 }
317 PaddingProperty paddingProperty;
318 paddingProperty.left = CalcLength(0.0_vp);
319 paddingProperty.right = CalcLength(0.0_vp);
320 paddingProperty.top = CalcLength(paddingTop);
321 paddingProperty.bottom = CalcLength(paddingBottom);
322
323 contentLayoutProperty->UpdateSafeAreaPadding(paddingProperty);
324 }
325 } // namespace OHOS::Ace::NG
326