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/title_bar_layout_algorithm.h"
17
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/geometry/ng/size_t.h"
20 #include "base/memory/ace_type.h"
21 #include "base/utils/measure_util.h"
22 #include "base/utils/utils.h"
23 #include "core/components/custom_paint/rosen_render_custom_paint.h"
24 #include "core/components_ng/base/frame_node.h"
25 #include "core/components_ng/pattern/navigation/nav_bar_node.h"
26 #include "core/components_ng/pattern/navigation/navigation_declaration.h"
27 #include "core/components_ng/pattern/navigation/navigation_layout_property.h"
28 #include "core/components_ng/pattern/navigation/title_bar_layout_property.h"
29 #include "core/components_ng/pattern/navigation/title_bar_node.h"
30 #include "core/components_ng/pattern/navigation/title_bar_pattern.h"
31 #include "core/components_ng/pattern/navrouter/navdestination_group_node.h"
32 #include "core/components_ng/pattern/navrouter/navdestination_layout_property.h"
33 #include "core/components_ng/pattern/text/text_layout_property.h"
34 #include "core/components_ng/property/layout_constraint.h"
35 #include "core/components_ng/property/measure_property.h"
36 #include "core/components_ng/property/measure_utils.h"
37
38 namespace OHOS::Ace::NG {
39
40 namespace {
41 constexpr int32_t MAX_MENU_ITEMS_NUM = 3;
42 constexpr int32_t MENU_OFFSET_RATIO = 9;
43 }
44
MeasureBackButton(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty)45 void TitleBarLayoutAlgorithm::MeasureBackButton(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
46 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty)
47 {
48 auto backButtonNode = titleBarNode->GetBackButton();
49 CHECK_NULL_VOID(backButtonNode);
50 auto index = titleBarNode->GetChildIndexById(backButtonNode->GetId());
51 auto backButtonWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
52 CHECK_NULL_VOID(backButtonWrapper);
53 auto constraint = titleBarLayoutProperty->CreateChildConstraint();
54 // navDestination title bar
55 if (titleBarLayoutProperty->GetTitleBarParentTypeValue(TitleBarParentType::NAVBAR) ==
56 TitleBarParentType::NAV_DESTINATION) {
57 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
58 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
59 constraint.selfIdealSize = OptionalSizeF(static_cast<float>(BACK_BUTTON_ICON_SIZE.ConvertToPx()),
60 static_cast<float>(BACK_BUTTON_ICON_SIZE.ConvertToPx()));
61 backButtonWrapper->Measure(constraint);
62 return;
63 }
64 constraint.parentIdealSize = OptionalSizeF(static_cast<float>(BACK_BUTTON_SIZE.ConvertToPx()),
65 static_cast<float>(BACK_BUTTON_SIZE.ConvertToPx()));
66 backButtonWrapper->Measure(constraint);
67 return;
68 }
69
70 // navBar title bar
71 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) != NavigationTitleMode::MINI) {
72 constraint.parentIdealSize = OptionalSizeF(0.0f, 0.0f);
73 backButtonWrapper->Measure(constraint);
74 return;
75 }
76
77 if (titleBarLayoutProperty->GetHideBackButton().value_or(false)) {
78 constraint.parentIdealSize = OptionalSizeF(0.0f, 0.0f);
79 backButtonWrapper->Measure(constraint);
80 return;
81 }
82
83 constraint.parentIdealSize = OptionalSizeF(
84 static_cast<float>(BACK_BUTTON_SIZE.ConvertToPx()), static_cast<float>(BACK_BUTTON_SIZE.ConvertToPx()));
85 backButtonWrapper->Measure(constraint);
86 }
87
GetTitleWidth(const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty,const SizeF & titleBarSize)88 float TitleBarLayoutAlgorithm::GetTitleWidth(const RefPtr<TitleBarNode>& titleBarNode,
89 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty, const SizeF& titleBarSize)
90 {
91 // navDestination title bar
92 if (titleBarLayoutProperty->GetTitleBarParentTypeValue(TitleBarParentType::NAVBAR) ==
93 TitleBarParentType::NAV_DESTINATION) {
94
95 // nav destination custom title
96 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(titleBarNode->GetParent());
97 CHECK_NULL_RETURN(navDestination, 0.0f);
98 auto isCustom = navDestination->GetPrevTitleIsCustomValue(false);
99 float occupiedWidth = 0.0f;
100 // left padding
101 if (GetNavigationBackButtonState(titleBarNode)) {
102 occupiedWidth += isCustom ? (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + BUTTON_PADDING).ConvertToPx() :
103 (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + NAV_HORIZONTAL_MARGIN_L).ConvertToPx();
104 } else {
105 occupiedWidth += isCustom ? 0.0f : maxPaddingStart_.ConvertToPx();
106 }
107 // right padding
108 occupiedWidth += isCustom ? 0.0f : maxPaddingEnd_.ConvertToPx();
109 return titleBarSize.Width() < occupiedWidth ? 0 : titleBarSize.Width() - occupiedWidth;
110 }
111 auto navBarNode = AceType::DynamicCast<NavBarNode>(titleBarNode->GetParent());
112 CHECK_NULL_RETURN(navBarNode, 0.0f);
113 float occupiedWidth = 0.0f;
114 auto isCustom = navBarNode->GetPrevTitleIsCustomValue(false);
115 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) != NavigationTitleMode::MINI) {
116 occupiedWidth = isCustom ? 0.0f : (maxPaddingStart_ + maxPaddingEnd_).ConvertToPx();
117 return titleBarSize.Width() < occupiedWidth ? 0 : titleBarSize.Width() - occupiedWidth;
118 }
119 // mini mode
120 if (titleBarLayoutProperty->GetHideBackButtonValue(false)) {
121 occupiedWidth += isCustom ? 0 : maxPaddingStart_.ConvertToPx();
122 } else {
123 occupiedWidth += (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE).ConvertToPx();
124 // custom right padding is the back button hot zone
125 occupiedWidth += isCustom ? BUTTON_PADDING.ConvertToPx() : (NAV_HORIZONTAL_MARGIN_L).ConvertToPx();
126 }
127 // compute right padding
128 if (NearZero(menuWidth_)) {
129 occupiedWidth += isCustom ? 0.0f : maxPaddingEnd_.ConvertToPx();
130 } else {
131 occupiedWidth += menuWidth_;
132 // title is custom, the title right margin is not needed
133 occupiedWidth += isCustom ? 0.0f : (NAV_HORIZONTAL_MARGIN_L).ConvertToPx();
134 auto isCustomMenu = navBarNode->GetPrevMenuIsCustomValue(false);
135 occupiedWidth += isCustomMenu ? 0.0f : (defaultPaddingStart_).ConvertToPx();
136 }
137 return titleBarSize.Width() < occupiedWidth ? 0 : titleBarSize.Width() - occupiedWidth;
138 }
139
MeasureSubtitle(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty,const SizeF & titleBarSize,float maxWidth)140 void TitleBarLayoutAlgorithm::MeasureSubtitle(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
141 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty, const SizeF& titleBarSize, float maxWidth)
142 {
143 auto subtitleNode = titleBarNode->GetSubtitle();
144 CHECK_NULL_VOID_NOLOG(subtitleNode);
145 auto index = titleBarNode->GetChildIndexById(subtitleNode->GetId());
146 auto subtitleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
147 CHECK_NULL_VOID(subtitleWrapper);
148 auto constraint = titleBarLayoutProperty->CreateChildConstraint();
149 constraint.maxSize.SetHeight(titleBarSize.Height());
150 constraint.maxSize.SetWidth(maxWidth);
151 subtitleWrapper->Measure(constraint);
152 }
153
MeasureTitle(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty,const SizeF & titleBarSize,float maxWidth)154 void TitleBarLayoutAlgorithm::MeasureTitle(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
155 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty, const SizeF& titleBarSize, float maxWidth)
156 {
157 auto titleNode = titleBarNode->GetTitle();
158 CHECK_NULL_VOID(titleNode);
159 auto index = titleBarNode->GetChildIndexById(titleNode->GetId());
160 auto titleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
161 CHECK_NULL_VOID(titleWrapper);
162 auto constraint = titleBarLayoutProperty->CreateChildConstraint();
163 constraint.maxSize.SetHeight(titleBarSize.Height());
164
165 // navDestination title bar
166 if (titleBarLayoutProperty->GetTitleBarParentTypeValue(TitleBarParentType::NAVBAR) ==
167 TitleBarParentType::NAV_DESTINATION) {
168 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(titleBarNode->GetParent());
169 CHECK_NULL_VOID(navDestination);
170 auto isCustomTitle = navDestination->GetPrevTitleIsCustomValue(false);
171 if (isCustomTitle) {
172 constraint.parentIdealSize.SetWidth(maxWidth);
173 constraint.maxSize.SetWidth(maxWidth);
174 // custom title must be single line title
175
176 auto navDestinationProperty = navDestination->GetLayoutProperty<NavDestinationLayoutProperty>();
177 auto titleHeight = navDestinationProperty->GetTitleBarHeightValue(SINGLE_LINE_TITLEBAR_HEIGHT);
178 constraint.parentIdealSize.SetHeight(titleHeight.ConvertToPx());
179 constraint.maxSize.SetHeight(titleHeight.ConvertToPx());
180 titleWrapper->Measure(constraint);
181 return;
182 }
183 constraint.maxSize.SetWidth(maxWidth);
184 if (!titleBarNode->GetSubtitle()) {
185 constraint.maxSize.SetHeight(titleBarSize.Height());
186 titleWrapper->Measure(constraint);
187 return;
188 }
189 auto subtitle = AceType::DynamicCast<FrameNode>(titleBarNode->GetSubtitle());
190 auto subtitleHeight = subtitle->GetGeometryNode()->GetFrameSize().Height();
191 constraint.maxSize.SetHeight(titleBarSize.Height() - subtitleHeight);
192 titleWrapper->Measure(constraint);
193 return;
194 }
195 // NavigationCustomTitle: Custom title + height
196 if (titleBarLayoutProperty->HasTitleHeight()) {
197 constraint.parentIdealSize.SetWidth(maxWidth);
198 constraint.maxSize.SetWidth(maxWidth);
199 constraint.parentIdealSize.SetHeight(titleBarLayoutProperty->GetTitleHeightValue().ConvertToPx());
200 constraint.maxSize.SetHeight(titleBarLayoutProperty->GetTitleHeightValue().ConvertToPx());
201 titleWrapper->Measure(constraint);
202 return;
203 }
204 // subTitle
205 auto titleMode = titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE);
206 auto subTitle = titleBarNode->GetSubtitle();
207 if (subTitle) {
208 // common title
209 auto index = titleBarNode->GetChildIndexById(subTitle->GetId());
210 auto subtitleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
211 CHECK_NULL_VOID(subtitleWrapper);
212 auto geometryNode = subtitleWrapper->GetGeometryNode();
213 // mini mode double title height is 56vp, free/full mode is 82vp
214 auto maxTitleHeight = titleMode == NavigationTitleMode::MINI ? SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx() :
215 DOUBLE_LINE_TITLEBAR_HEIGHT.ConvertToPx();
216 constraint.maxSize.SetWidth(maxWidth);
217 constraint.maxSize.SetHeight(maxTitleHeight - geometryNode->GetFrameSize().Height());
218 titleWrapper->Measure(constraint);
219 return;
220 }
221 auto navBarNode = AceType::DynamicCast<NavBarNode>(titleBarNode->GetParent());
222 CHECK_NULL_VOID(navBarNode);
223 auto isCustomTitle = navBarNode->GetPrevTitleIsCustomValue(false);
224 // single line title and mini mode
225 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) == NavigationTitleMode::MINI) {
226 if (isCustomTitle) {
227 constraint.parentIdealSize.SetWidth(maxWidth);
228 constraint.maxSize.SetWidth(maxWidth);
229 constraint.parentIdealSize.SetHeight(titleBarSize.Height());
230 constraint.maxSize.SetHeight(titleBarSize.Height());
231 } else {
232 constraint.maxSize.SetWidth(maxWidth);
233 constraint.maxSize.SetHeight(titleBarSize.Height());
234 }
235 titleWrapper->Measure(constraint);
236 return;
237 }
238 // custom builder
239 if (isCustomTitle) {
240 constraint.parentIdealSize.SetWidth(maxWidth);
241 constraint.maxSize.SetWidth(maxWidth);
242 if (PipelineContext::GetCurrentContext() && (PipelineContext::GetCurrentContext()
243 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN))) {
244 constraint.parentIdealSize.SetHeight(titleBarSize.Height());
245 } else {
246 // if has menu, max height is single line height
247 auto maxHeight = NearZero(menuWidth_) ? titleBarSize.Height() : SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx();
248 constraint.parentIdealSize.SetHeight(maxHeight);
249 constraint.maxSize.SetHeight(maxHeight);
250 }
251 titleWrapper->Measure(constraint);
252 return;
253 }
254 // resourceStr title
255 constraint.maxSize.SetWidth(maxWidth);
256 constraint.maxSize.SetHeight(SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx());
257 titleWrapper->Measure(constraint);
258 }
259
MeasureMenu(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty)260 void TitleBarLayoutAlgorithm::MeasureMenu(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
261 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty)
262 {
263 auto menuNode = titleBarNode->GetMenu();
264 CHECK_NULL_VOID_NOLOG(menuNode);
265 auto index = titleBarNode->GetChildIndexById(menuNode->GetId());
266 auto menuWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
267 CHECK_NULL_VOID(menuWrapper);
268 auto constraint = titleBarLayoutProperty->CreateChildConstraint();
269 auto navBarNode = AceType::DynamicCast<NavBarNode>(titleBarNode->GetParent());
270 CHECK_NULL_VOID(navBarNode);
271 auto isCustomMenu = navBarNode->GetPrevMenuIsCustomValue(false);
272 if (isCustomMenu) {
273 // custom title can't be higher than 56vp
274 constraint.parentIdealSize.SetHeight(SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx());
275 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) == NavigationTitleMode::MINI &&
276 !titleBarLayoutProperty->HasTitleHeight()) {
277 auto maxWidth = static_cast<float>(MENU_ITEM_SIZE.ConvertToPx()) * MAX_MENU_ITEMS_NUM +
278 defaultPaddingStart_.ConvertToPx();
279 constraint.parentIdealSize.SetWidth(maxWidth);
280 }
281 menuWrapper->Measure(constraint);
282 menuWidth_ = menuWrapper->GetGeometryNode()->GetFrameSize().Width();
283 return;
284 }
285 auto menuItemNum = static_cast<int32_t>(menuNode->GetChildren().size());
286 if (menuItemNum >= MAX_MENU_ITEMS_NUM) {
287 menuWidth_ = static_cast<float>(MENU_ITEM_SIZE.ConvertToPx()) * MAX_MENU_ITEMS_NUM;
288 } else {
289 menuWidth_ = static_cast<float>(MENU_ITEM_SIZE.ConvertToPx()) * menuItemNum;
290 }
291 constraint.selfIdealSize = OptionalSizeF(menuWidth_, static_cast<float>(menuHeight_.ConvertToPx()));
292 menuWrapper->Measure(constraint);
293 }
294
LayoutBackButton(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty)295 void TitleBarLayoutAlgorithm::LayoutBackButton(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
296 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty)
297 {
298 auto backButtonNode = titleBarNode->GetBackButton();
299 CHECK_NULL_VOID(backButtonNode);
300 auto index = titleBarNode->GetChildIndexById(backButtonNode->GetId());
301 auto backButtonWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
302 CHECK_NULL_VOID(backButtonWrapper);
303 auto geometryNode = backButtonWrapper->GetGeometryNode();
304
305 // navDestination title bar
306 if (titleBarLayoutProperty->GetTitleBarParentTypeValue(TitleBarParentType::NAVBAR) ==
307 TitleBarParentType::NAV_DESTINATION) {
308 if (!titleBarNode->GetBackButton()) {
309 return;
310 }
311 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
312 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
313 auto offsetY = ((menuHeight_ - BACK_BUTTON_ICON_SIZE) / 2).ConvertToPx();
314 geometryNode->SetMarginFrameOffset(OffsetF { maxPaddingStart_.ConvertToPx(), offsetY });
315 backButtonWrapper->Layout();
316 return;
317 }
318 auto titleHeight = titleBarLayoutProperty->GetTitleHeightValue(SINGLE_LINE_TITLEBAR_HEIGHT);
319 auto offsetY = (titleHeight - BACK_BUTTON_SIZE) / 2;
320
321 OffsetF backButtonOffset = OffsetF(static_cast<float>((maxPaddingStart_ - BUTTON_PADDING).ConvertToPx()),
322 static_cast<float>(offsetY.ConvertToPx()));
323 geometryNode->SetMarginFrameOffset(backButtonOffset);
324 backButtonWrapper->Layout();
325 return;
326 }
327
328 // navBar title bar
329 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) != NavigationTitleMode::MINI) {
330 OffsetF backButtonOffset = OffsetF(0.0f, 0.0f);
331 geometryNode->SetMarginFrameOffset(backButtonOffset);
332 backButtonWrapper->Layout();
333 return;
334 }
335
336 if (titleBarLayoutProperty->GetHideBackButton().value_or(false)) {
337 OffsetF backButtonOffset = OffsetF(0.0f, 0.0f);
338 geometryNode->SetMarginFrameOffset(backButtonOffset);
339 backButtonWrapper->Layout();
340 return;
341 }
342 auto titleHeight = titleBarLayoutProperty->GetTitleHeightValue(SINGLE_LINE_TITLEBAR_HEIGHT);
343 auto offsetY = (titleHeight - BACK_BUTTON_SIZE) / 2;
344 auto offsetX = (maxPaddingStart_ - (BACK_BUTTON_SIZE - BACK_BUTTON_ICON_SIZE) / 2).ConvertToPx();
345 OffsetF backButtonOffset = OffsetF(offsetX, static_cast<float>(offsetY.ConvertToPx()));
346 geometryNode->SetMarginFrameOffset(backButtonOffset);
347 backButtonWrapper->Layout();
348 }
349
LayoutTitle(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty,float subtitleHeight)350 void TitleBarLayoutAlgorithm::LayoutTitle(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
351 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty, float subtitleHeight)
352 {
353 auto titleNode = titleBarNode->GetTitle();
354 CHECK_NULL_VOID(titleNode);
355 auto index = titleBarNode->GetChildIndexById(titleNode->GetId());
356 auto titleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
357 CHECK_NULL_VOID(titleWrapper);
358 auto geometryNode = titleWrapper->GetGeometryNode();
359
360 auto titleHeight = geometryNode->GetFrameSize().Height();
361 float offsetY = 0.0f;
362 if (!NearZero(subtitleHeight)) {
363 offsetY = (static_cast<float>(DOUBLE_LINE_TITLEBAR_HEIGHT.ConvertToPx()) - titleHeight - subtitleHeight) / 2;
364 } else {
365 offsetY = (static_cast<float>(SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx()) - titleHeight) / 2;
366 }
367 // navDestination title bar
368 if (titleBarLayoutProperty->GetTitleBarParentTypeValue(TitleBarParentType::NAVBAR) ==
369 TitleBarParentType::NAV_DESTINATION) {
370 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(titleBarNode->GetParent());
371 CHECK_NULL_VOID(navDestination);
372 auto isCustom = navDestination->GetPrevTitleIsCustomValue(false);
373 // add sdk 9 compatible
374 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
375 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
376 if (GetNavigationBackButtonState(titleBarNode)) {
377 geometryNode->SetMarginFrameOffset(OffsetF {
378 static_cast<float>((maxPaddingStart_ + BACK_BUTTON_ICON_SIZE +
379 NAV_HORIZONTAL_MARGIN_M).ConvertToPx()), offsetY });
380 titleWrapper->Layout();
381 return;
382 }
383 geometryNode->SetMarginFrameOffset(OffsetF { maxPaddingStart_.ConvertToPx(), offsetY});
384 titleWrapper->Layout();
385 return;
386 }
387 if (GetNavigationBackButtonState(titleBarNode)) {
388 auto offsetX = isCustom ? (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + BUTTON_PADDING).ConvertToPx() :
389 (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + NAV_HORIZONTAL_MARGIN_L).ConvertToPx();
390 offsetY = isCustom ? 0.0f : offsetY;
391 geometryNode->SetMarginFrameOffset(OffsetF { offsetX, offsetY });
392 titleWrapper->Layout();
393 return;
394 }
395 auto offsetX = isCustom ? 0.0f : maxPaddingStart_.ConvertToPx();
396 offsetY = isCustom ? 0.0f : offsetY;
397 OffsetF offset = OffsetF(offsetX, offsetY);
398 geometryNode->SetMarginFrameOffset(offset);
399 titleWrapper->Layout();
400 return;
401 }
402
403 // navBar title bar
404 auto navBarNode = AceType::DynamicCast<NavBarNode>(titleBarNode->GetParent());
405 CHECK_NULL_VOID(navBarNode);
406 auto isCustom = navBarNode->GetPrevTitleIsCustomValue(false);
407 // full mode
408 if (!isCustom) {
409 auto dividerOffset = 2;
410 if (!NearZero(subtitleHeight)) {
411 offsetY = (static_cast<float>(DOUBLE_LINE_TITLEBAR_HEIGHT.ConvertToPx()) - titleHeight -
412 subtitleHeight) / dividerOffset;
413 } else {
414 offsetY = (static_cast<float>(SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx()) - titleHeight) / dividerOffset;
415 }
416 }
417 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) == NavigationTitleMode::MINI) {
418 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
419 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
420 if (titleBarLayoutProperty->GetHideBackButton().value_or(false)) {
421 geometryNode->SetMarginFrameOffset(OffsetF { maxPaddingStart_.ConvertToPx(), offsetY });
422 titleWrapper->Layout();
423 return;
424 }
425 geometryNode->SetMarginFrameOffset(OffsetF { (defaultPaddingStart_ + BACK_BUTTON_ICON_SIZE +
426 NAV_HORIZONTAL_MARGIN_L).ConvertToPx(), offsetY});
427 titleWrapper->Layout();
428 return;
429 }
430 // NavigationCustomTitle and Custom builder layout margin is (0, 0);
431 offsetY = isCustom ? 0 : offsetY;
432 if (titleBarLayoutProperty->GetHideBackButton().value_or(false)) {
433 auto offsetX = isCustom ? 0.0f : (maxPaddingStart_).ConvertToPx();
434 OffsetF titleOffset = OffsetF(offsetX, offsetY);
435 geometryNode->SetMarginFrameOffset(titleOffset);
436 titleWrapper->Layout();
437 return;
438 }
439
440 auto offsetX = isCustom ? (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + BUTTON_PADDING).ConvertToPx() :
441 (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + NAV_HORIZONTAL_MARGIN_L).ConvertToPx();
442 OffsetF offset = OffsetF(offsetX, offsetY);
443 geometryNode->SetMarginFrameOffset(offset);
444 titleWrapper->Layout();
445 return;
446 }
447
448 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) != NavigationTitleMode::FREE) {
449 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
450 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
451 geometryNode->SetMarginFrameOffset(OffsetF { maxPaddingStart_.ConvertToPx(),
452 menuHeight_.ConvertToPx() + offsetY});
453 titleWrapper->Layout();
454 return;
455 }
456 // full mode
457 if (isCustom) {
458 // custom title margin is (0.0f, menuHeight_)
459 auto customOffsetY = NearZero(menuWidth_) ? 0.0f : menuHeight_.ConvertToPx();
460 geometryNode->SetMarginFrameOffset(OffsetF { 0.0f, customOffsetY});
461 titleWrapper->Layout();
462 } else {
463 // fixed white space menuHeight
464 OffsetF titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()),
465 static_cast<float>(menuHeight_.ConvertToPx()) + offsetY);
466 geometryNode->SetMarginFrameOffset(titleOffset);
467 titleWrapper->Layout();
468 }
469 return;
470 }
471 if (isInitialTitle_) {
472 // free mode
473 if (isCustom) {
474 // customBuilder and NavigationCustomTitle offset is (0.0f, menuHeight_)
475 auto customOffsetY = NearZero(menuWidth_) ? 0.0f : menuHeight_.ConvertToPx();
476 geometryNode->SetMarginFrameOffset(OffsetF { 0.0f, customOffsetY});
477 titleWrapper->Layout();
478 return;
479 }
480 auto title = AceType::DynamicCast<FrameNode>(titleNode);
481 CHECK_NULL_VOID(title);
482
483 auto textLayoutProperty = title->GetLayoutProperty<TextLayoutProperty>();
484 if (!textLayoutProperty) {
485 // current title mode is Navigation common title
486 OffsetF titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()),
487 static_cast<float>(menuHeight_.ConvertToPx())+ offsetY);
488 geometryNode->SetMarginFrameOffset(titleOffset);
489 titleWrapper->Layout();
490 return;
491 }
492 MeasureContext context;
493 context.textContent = textLayoutProperty->GetContentValue();
494 context.fontSize = titleFontSize_;
495 #ifdef ENABLE_ROSEN_BACKEND
496 minTitleHeight_ = static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(context).Height());
497 #else
498 minTitleHeight_ = 0.0;
499 #endif
500 initialTitleOffsetY_ = static_cast<float>(menuHeight_.ConvertToPx()) + offsetY;
501 isInitialTitle_ = false;
502 auto titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()), initialTitleOffsetY_);
503 geometryNode->SetMarginFrameOffset(titleOffset);
504 titleWrapper->Layout();
505 return;
506 }
507
508 auto titlePattern = titleBarNode->GetPattern<TitleBarPattern>();
509 CHECK_NULL_VOID(titlePattern);
510 if (NearZero(titlePattern->GetTempTitleOffsetY())) {
511 initialTitleOffsetY_ = static_cast<float>(menuHeight_.ConvertToPx()) + offsetY;
512 auto titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()), initialTitleOffsetY_);
513 geometryNode->SetMarginFrameOffset(titleOffset);
514 titleWrapper->Layout();
515 return;
516 }
517 auto overDragOffset = titlePattern->GetOverDragOffset();
518 auto titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()),
519 titlePattern->GetTempTitleOffsetY() + overDragOffset / 6.0f);
520 geometryNode->SetMarginFrameOffset(titleOffset);
521 titleWrapper->Layout();
522 }
523
LayoutSubtitle(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty,float titleHeight)524 void TitleBarLayoutAlgorithm::LayoutSubtitle(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
525 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty, float titleHeight)
526 {
527 auto subtitleNode = titleBarNode->GetSubtitle();
528 CHECK_NULL_VOID_NOLOG(subtitleNode);
529 auto index = titleBarNode->GetChildIndexById(subtitleNode->GetId());
530 auto subtitleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
531 CHECK_NULL_VOID(subtitleWrapper);
532 auto geometryNode = subtitleWrapper->GetGeometryNode();
533
534 auto subtitleHeight = geometryNode->GetFrameSize().Height();
535 float offsetY = 0.0f;
536 if (!NearZero(titleHeight)) {
537 offsetY = (static_cast<float>(DOUBLE_LINE_TITLEBAR_HEIGHT.ConvertToPx()) - titleHeight - subtitleHeight) / 2 +
538 titleHeight;
539 } else {
540 offsetY = (static_cast<float>(SINGLE_LINE_TITLEBAR_HEIGHT.ConvertToPx()) - subtitleHeight) / 2;
541 }
542 // navDestination title bar
543 if (titleBarLayoutProperty->GetTitleBarParentTypeValue(TitleBarParentType::NAVBAR) ==
544 TitleBarParentType::NAV_DESTINATION) {
545 // subtitle doesn't support custom title
546 if (GetNavigationBackButtonState(titleBarNode)) {
547 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
548 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
549 geometryNode->SetMarginFrameOffset(OffsetF { (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE
550 + NAV_HORIZONTAL_MARGIN_M).ConvertToPx(), offsetY });
551 } else {
552 geometryNode->SetMarginFrameOffset(OffsetF { (maxPaddingStart_ + BACK_BUTTON_ICON_SIZE
553 + NAV_HORIZONTAL_MARGIN_L).ConvertToPx(), offsetY });
554 }
555 subtitleWrapper->Layout();
556 return;
557 }
558
559 OffsetF offset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()), offsetY);
560 geometryNode->SetMarginFrameOffset(offset);
561 subtitleWrapper->Layout();
562 return;
563 }
564
565 // navBar title bar
566 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) != NavigationTitleMode::MINI) {
567 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) == NavigationTitleMode::FREE) {
568 if (isInitialSubtitle_) {
569 initialSubtitleOffsetY_ = static_cast<float>(menuHeight_.ConvertToPx()) + offsetY;
570 isInitialSubtitle_ = false;
571 OffsetF titleOffset =
572 OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()), initialSubtitleOffsetY_);
573 geometryNode->SetMarginFrameOffset(titleOffset);
574 subtitleWrapper->Layout();
575 return;
576 }
577
578 auto titlePattern = titleBarNode->GetPattern<TitleBarPattern>();
579 CHECK_NULL_VOID(titlePattern);
580 if (NearZero(titlePattern->GetTempTitleOffsetY())) {
581 OffsetF titleOffset =
582 OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()), initialSubtitleOffsetY_);
583 geometryNode->SetMarginFrameOffset(titleOffset);
584 subtitleWrapper->Layout();
585 return;
586 }
587 auto overDragOffset = titlePattern->GetOverDragOffset();
588 OffsetF titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()),
589 titlePattern->GetTempSubTitleOffsetY() + overDragOffset / 6.0f);
590 geometryNode->SetMarginFrameOffset(titleOffset);
591 subtitleWrapper->Layout();
592 return;
593 }
594 // full mode
595 OffsetF titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()),
596 static_cast<float>(menuHeight_.ConvertToPx()) + offsetY);
597 geometryNode->SetMarginFrameOffset(titleOffset);
598 subtitleWrapper->Layout();
599 return;
600 }
601 // mini mode + hideBackButton true
602 if (titleBarLayoutProperty->GetHideBackButton().value_or(false)) {
603 OffsetF titleOffset = OffsetF(static_cast<float>(maxPaddingStart_.ConvertToPx()), offsetY);
604 geometryNode->SetMarginFrameOffset(titleOffset);
605 subtitleWrapper->Layout();
606 return;
607 }
608 Dimension occupiedWidth = Dimension(0.0f, DimensionUnit::PX);
609 // mini mode + back button
610 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
611 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
612 occupiedWidth = maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + NAV_HORIZONTAL_MARGIN_M;
613 } else {
614 occupiedWidth = maxPaddingStart_ + BACK_BUTTON_ICON_SIZE + NAV_HORIZONTAL_MARGIN_L;
615 }
616 OffsetF offset = OffsetF(static_cast<float>(occupiedWidth.ConvertToPx()), offsetY);
617 geometryNode->SetMarginFrameOffset(offset);
618 subtitleWrapper->Layout();
619 }
620
LayoutMenu(LayoutWrapper * layoutWrapper,const RefPtr<TitleBarNode> & titleBarNode,const RefPtr<TitleBarLayoutProperty> & titleBarLayoutProperty,float subtitleHeight)621 void TitleBarLayoutAlgorithm::LayoutMenu(LayoutWrapper* layoutWrapper, const RefPtr<TitleBarNode>& titleBarNode,
622 const RefPtr<TitleBarLayoutProperty>& titleBarLayoutProperty, float subtitleHeight)
623 {
624 auto menuNode = titleBarNode->GetMenu();
625 CHECK_NULL_VOID(menuNode);
626 auto index = titleBarNode->GetChildIndexById(menuNode->GetId());
627 auto menuWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
628 CHECK_NULL_VOID(menuWrapper);
629 auto geometryNode = menuWrapper->GetGeometryNode();
630 auto menuWidth = geometryNode->GetMarginFrameSize().Width();
631 auto maxWidth = geometryNode->GetParentLayoutConstraint()->maxSize.Width();
632 auto navBarNode = AceType::DynamicCast<NavBarNode>(titleBarNode->GetParent());
633 auto isCustomMenu = navBarNode->GetPrevMenuIsCustomValue(false);
634 if (titleBarLayoutProperty->GetTitleModeValue(NavigationTitleMode::FREE) == NavigationTitleMode::FREE) {
635 auto titlePattern = titleBarNode->GetPattern<TitleBarPattern>();
636 auto overDragOffset = titlePattern->GetOverDragOffset();
637 auto menuOffsetY = (SINGLE_LINE_TITLEBAR_HEIGHT - menuHeight_) / 2;
638 // custom menu width has no right padding
639 auto offsetX = isCustomMenu ? maxWidth - menuWidth :
640 (maxWidth - menuWidth - static_cast<float>(defaultPaddingStart_.ConvertToPx()));
641 if (PipelineContext::GetCurrentContext() && PipelineContext::GetCurrentContext()
642 ->GetMinPlatformVersion() < static_cast<int32_t>(PlatformVersion::VERSION_TEN)) {
643 geometryNode->SetMarginFrameOffset(OffsetF { maxWidth - menuWidth - defaultPaddingStart_.ConvertToPx(),
644 static_cast<float>(menuOffsetY.ConvertToPx()) + overDragOffset / MENU_OFFSET_RATIO });
645 menuWrapper->Layout();
646 return;
647 }
648 OffsetF menuOffset(offsetX, static_cast<float>(menuOffsetY.ConvertToPx()) + overDragOffset / MENU_OFFSET_RATIO);
649 geometryNode->SetMarginFrameOffset(menuOffset);
650 menuWrapper->Layout();
651 return;
652 }
653 if (PipelineContext::GetCurrentContext() && (PipelineContext::GetCurrentContext()->GetMinPlatformVersion() <
654 static_cast<int32_t>(PlatformVersion::VERSION_TEN))) {
655 auto totalHeight = NearZero(subtitleHeight) ? SINGLE_LINE_TITLEBAR_HEIGHT : DOUBLE_LINE_TITLEBAR_HEIGHT;
656 geometryNode->SetMarginFrameOffset(OffsetF { maxWidth - menuWidth - defaultPaddingStart_.ConvertToPx(),
657 ((totalHeight - menuHeight_).ConvertToPx()) / 2 });
658 menuWrapper->Layout();
659 return;
660 }
661 auto menuOffsetY = (SINGLE_LINE_TITLEBAR_HEIGHT - menuHeight_) / 2;
662 auto menuOffsetX = maxWidth - menuWidth;
663 // custom menu doesn't have right padding. if menu isn't custom, menu items has right padding
664 menuOffsetX = isCustomMenu ? menuOffsetX : menuOffsetX - defaultPaddingStart_.ConvertToPx();
665
666 OffsetF menuOffset(menuOffsetX, static_cast<float>(menuOffsetY.ConvertToPx()));
667 geometryNode->SetMarginFrameOffset(menuOffset);
668 menuWrapper->Layout();
669 }
670
671 // set variables from theme
InitializeTheme()672 void TitleBarLayoutAlgorithm::InitializeTheme()
673 {
674 auto theme = NavigationGetTheme();
675 CHECK_NULL_VOID(theme);
676 maxPaddingStart_ = theme->GetMaxPaddingStart();
677 maxPaddingEnd_ = theme->GetMaxPaddingEnd();
678 menuHeight_ = theme->GetHeight();
679 defaultPaddingStart_ = theme->GetDefaultPaddingStart();
680 iconSize_ = theme->GetMenuIconSize();
681 titleFontSize_ = theme->GetTitleFontSize();
682 }
683
Measure(LayoutWrapper * layoutWrapper)684 void TitleBarLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
685 {
686 auto titleBarNode = AceType::DynamicCast<TitleBarNode>(layoutWrapper->GetHostNode());
687 CHECK_NULL_VOID(titleBarNode);
688 auto layoutProperty = AceType::DynamicCast<TitleBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
689 CHECK_NULL_VOID(layoutProperty);
690 const auto& constraint = layoutProperty->GetLayoutConstraint();
691 CHECK_NULL_VOID(constraint);
692 auto size = CreateIdealSize(constraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT, true);
693 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
694 MinusPaddingToSize(padding, size);
695 InitializeTheme();
696
697 MeasureBackButton(layoutWrapper, titleBarNode, layoutProperty);
698 MeasureMenu(layoutWrapper, titleBarNode, layoutProperty);
699 auto titleMaxWidth = GetTitleWidth(titleBarNode, layoutProperty, size);
700 MeasureSubtitle(layoutWrapper, titleBarNode, layoutProperty, size, titleMaxWidth);
701 MeasureTitle(layoutWrapper, titleBarNode, layoutProperty, size, titleMaxWidth);
702 layoutWrapper->GetGeometryNode()->SetFrameSize(size);
703 }
704
Layout(LayoutWrapper * layoutWrapper)705 void TitleBarLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
706 {
707 auto titleBarNode = AceType::DynamicCast<TitleBarNode>(layoutWrapper->GetHostNode());
708 CHECK_NULL_VOID(titleBarNode);
709 auto layoutProperty = AceType::DynamicCast<TitleBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
710 CHECK_NULL_VOID(layoutProperty);
711 LayoutBackButton(layoutWrapper, titleBarNode, layoutProperty);
712
713 float subtitleHeight = 0.0f;
714 auto subtitleNode = titleBarNode->GetSubtitle();
715 if (subtitleNode) {
716 auto index = titleBarNode->GetChildIndexById(subtitleNode->GetId());
717 auto subtitleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
718 CHECK_NULL_VOID(subtitleWrapper);
719 auto geometryNode = subtitleWrapper->GetGeometryNode();
720 subtitleHeight = geometryNode->GetFrameSize().Height();
721 }
722 LayoutTitle(layoutWrapper, titleBarNode, layoutProperty, subtitleHeight);
723
724 float titleHeight = 0.0f;
725 auto titleNode = titleBarNode->GetTitle();
726 if (titleNode) {
727 auto index = titleBarNode->GetChildIndexById(titleNode->GetId());
728 auto titleWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
729 CHECK_NULL_VOID(titleWrapper);
730 auto geometryNode = titleWrapper->GetGeometryNode();
731 titleHeight = geometryNode->GetFrameSize().Height();
732 }
733 LayoutSubtitle(layoutWrapper, titleBarNode, layoutProperty, titleHeight);
734
735 LayoutMenu(layoutWrapper, titleBarNode, layoutProperty, subtitleHeight);
736 }
737
GetNavigationBackButtonState(const RefPtr<TitleBarNode> & titleBarNode)738 bool TitleBarLayoutAlgorithm::GetNavigationBackButtonState(const RefPtr<TitleBarNode>& titleBarNode)
739 {
740 auto backButton = AceType::DynamicCast<FrameNode>(titleBarNode->GetBackButton());
741 CHECK_NULL_RETURN_NOLOG(backButton, false);
742 auto layoutProperty = backButton->GetLayoutProperty();
743 CHECK_NULL_RETURN(layoutProperty, false);
744 return layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::VISIBLE;
745 }
746 } // namespace OHOS::Ace::NG
747