• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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