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/navdestination_node_base.h"
17
18 #include "base/utils/utf_helper.h"
19 #include "base/json/json_util.h"
20 #include "core/components_ng/pattern/image/image_layout_property.h"
21 #include "core/components_ng/pattern/navigation/bar_item_node.h"
22 #include "core/components_ng/pattern/navigation/nav_bar_node.h"
23 #include "core/components_ng/pattern/navigation/navdestination_content_pattern.h"
24 #include "core/components_ng/pattern/navigation/title_bar_pattern.h"
25 #include "core/components_ng/pattern/navigation/tool_bar_node.h"
26 #include "core/components_ng/pattern/navigation/tool_bar_pattern.h"
27 #include "core/components_ng/pattern/text/text_layout_property.h"
28
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr double HALF = 0.5;
32 constexpr float REMOVE_CLIP_SIZE = 100000.0f;
33 constexpr float CONTENT_OFFSET_PERCENT = 0.2f;
34 }
35
GetBarItemsString(bool isMenu) const36 std::string NavDestinationNodeBase::GetBarItemsString(bool isMenu) const
37 {
38 auto jsonValue = JsonUtil::Create(true);
39 auto parentNodeOfBarItems = isMenu ? DynamicCast<FrameNode>(GetMenu()) : DynamicCast<FrameNode>(GetToolBarNode());
40 CHECK_NULL_RETURN(parentNodeOfBarItems, "");
41 if (parentNodeOfBarItems->GetChildren().empty()) {
42 return "";
43 }
44 auto jsonOptions = JsonUtil::CreateArray(true);
45 int32_t i = 0;
46 for (auto iter = parentNodeOfBarItems->GetChildren().begin(); iter != parentNodeOfBarItems->GetChildren().end();
47 ++iter, i++) {
48 auto jsonToolBarItem = JsonUtil::CreateArray(true);
49 auto barItemNode = DynamicCast<BarItemNode>(*iter);
50 if (!barItemNode) {
51 jsonToolBarItem->Put("value", "");
52 jsonToolBarItem->Put("icon", "");
53 continue;
54 }
55 auto iconNode = DynamicCast<FrameNode>(barItemNode->GetIconNode());
56 if (iconNode) {
57 auto imageLayoutProperty = iconNode->GetLayoutProperty<ImageLayoutProperty>();
58 if (!imageLayoutProperty || !imageLayoutProperty->HasImageSourceInfo()) {
59 jsonToolBarItem->Put("icon", "");
60 } else {
61 jsonToolBarItem->Put("icon", imageLayoutProperty->GetImageSourceInfoValue().GetSrc().c_str());
62 }
63 } else {
64 jsonToolBarItem->Put("icon", "");
65 }
66 auto textNode = DynamicCast<FrameNode>(barItemNode->GetTextNode());
67 if (textNode) {
68 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
69 if (!textLayoutProperty) {
70 jsonToolBarItem->Put("value", "");
71 } else {
72 jsonToolBarItem->Put("value", UtfUtils::Str16ToStr8(textLayoutProperty->GetContentValue(u"")).c_str());
73 }
74 } else {
75 jsonToolBarItem->Put("value", "");
76 }
77 auto index_ = std::to_string(i);
78 jsonOptions->Put(index_.c_str(), jsonToolBarItem);
79 }
80 jsonValue->Put("items", jsonOptions);
81 return jsonValue->ToString();
82 }
83
IsToolBarVisible() const84 bool NavDestinationNodeBase::IsToolBarVisible() const
85 {
86 auto toolBarNode = AceType::DynamicCast<FrameNode>(GetToolBarNode());
87 CHECK_NULL_RETURN(toolBarNode, false);
88 auto layoutProperty = toolBarNode->GetLayoutProperty();
89 CHECK_NULL_RETURN(layoutProperty, false);
90 return layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::VISIBLE;
91 }
92
CustomizeExpandSafeArea()93 bool NavDestinationNodeBase::CustomizeExpandSafeArea()
94 {
95 if (isCustomExpandRunning_) {
96 return false;
97 }
98 if (!(rotateAngle_.has_value() && viewportConfig_)) {
99 return false;
100 }
101 isCustomExpandRunning_ = true;
102 ScopePageViewportConfig scopeConfig(viewportConfig_);
103 auto geometryNode = GetGeometryNode();
104 RectF backupParentAdjust;
105 auto angle = rotateAngle_.value();
106 if (geometryNode) {
107 backupParentAdjust = geometryNode->GetParentAdjust();
108 if (angle == ROTATION_90 || angle == ROTATION_180 || angle == ROTATION_270) {
109 RectF parentAdjust{ safeAreaInsets_.left_.end, safeAreaInsets_.top_.end, 0.0f, 0.0f };
110 geometryNode->SetParentAdjust(parentAdjust);
111 }
112 }
113 FrameNode::ExpandSafeArea();
114 if (geometryNode) {
115 geometryNode->SetParentAdjust(backupParentAdjust);
116 }
117 isCustomExpandRunning_ = false;
118 return true;
119 }
120
Measure(const std::optional<LayoutConstraintF> & parentConstraint)121 void NavDestinationNodeBase::Measure(const std::optional<LayoutConstraintF>& parentConstraint)
122 {
123 if (rotateAngle_.has_value() && rotateAngle_.value() != ROTATION_0 && viewportConfig_) {
124 ScopePageViewportConfig scopeConfig(viewportConfig_);
125 FrameNode::Measure(parentConstraint);
126 return;
127 }
128 FrameNode::Measure(parentConstraint);
129 }
130
Layout()131 void NavDestinationNodeBase::Layout()
132 {
133 if (rotateAngle_.has_value() && rotateAngle_.value() != ROTATION_0 && viewportConfig_) {
134 ScopePageViewportConfig scopeConfig(viewportConfig_);
135 FrameNode::Layout();
136 return;
137 }
138 FrameNode::Layout();
139 }
140
CalcHalfClipRectForTransition(const SizeF & frameSize)141 RectF NavDestinationNodeBase::CalcHalfClipRectForTransition(const SizeF& frameSize)
142 {
143 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
144 RectF rect;
145 float width = frameSize.Width();
146 float height = frameSize.Height();
147 if (angle == ROTATION_90 || angle == ROTATION_270) {
148 rect.SetSize({ REMOVE_CLIP_SIZE, height * HALF });
149 } else {
150 rect.SetSize({ width * HALF, REMOVE_CLIP_SIZE });
151 }
152
153 OffsetF offset{ 0.0f, 0.0f };
154 if (AceApplicationInfo::GetInstance().IsRightToLeft()) {
155 if (angle == ROTATION_90) {
156 offset = OffsetF{ 0.0f, height * HALF };
157 } else if (angle == ROTATION_180) {
158 offset = OffsetF{ width * HALF, 0.0f };
159 }
160 } else {
161 if (angle == ROTATION_270) {
162 offset = OffsetF{ 0.0f, height * HALF };
163 } else if (angle == ROTATION_0) {
164 offset = OffsetF{ width * HALF, 0.0f };
165 }
166 }
167 rect.SetOffset(offset);
168 return rect;
169 }
170
CalcFullClipRectForTransition(const SizeF & frameSize)171 RectF NavDestinationNodeBase::CalcFullClipRectForTransition(const SizeF& frameSize)
172 {
173 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
174 RectF rect;
175 if (angle == ROTATION_90 || angle == ROTATION_270) {
176 rect = RectF(0.0f, 0.0f, REMOVE_CLIP_SIZE, frameSize.Height());
177 } else {
178 rect = RectF(0.0f, 0.0f, frameSize.Width(), REMOVE_CLIP_SIZE);
179 }
180 return rect;
181 }
182
CalcTranslateForTransitionPushStart(const SizeF & frameSize,bool transitionIn)183 OffsetF NavDestinationNodeBase::CalcTranslateForTransitionPushStart(const SizeF& frameSize, bool transitionIn)
184 {
185 if (!transitionIn) {
186 return OffsetF{ 0.0f, 0.0f };
187 }
188 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
189 float isRTL = GetLanguageDirection();
190 float width = frameSize.Width();
191 if (angle == ROTATION_90 || angle == ROTATION_270) {
192 width = frameSize.Height();
193 }
194 auto translate = OffsetF{ width * HALF * isRTL, 0.0f };
195 return translate;
196 }
197
CalcTranslateForTransitionPushEnd(const SizeF & frameSize,bool transitionIn)198 OffsetF NavDestinationNodeBase::CalcTranslateForTransitionPushEnd(const SizeF& frameSize, bool transitionIn)
199 {
200 if (transitionIn) {
201 return OffsetF{ 0.0f, 0.0f };
202 }
203 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
204 float isRTL = GetLanguageDirection();
205 float width = frameSize.Width();
206 if (angle == ROTATION_90 || angle == ROTATION_270) {
207 width = frameSize.Height();
208 }
209 if (SystemProperties::IsSoftPageTransition()) {
210 auto translate = OffsetF{ -width * HALF * HALF * isRTL, 0.0f };
211 return translate;
212 }
213 auto translate = OffsetF{ -width * CONTENT_OFFSET_PERCENT * isRTL, 0.0f };
214 return translate;
215 }
216
CalcTranslateForTransitionPopStart(const SizeF & frameSize,bool transitionIn)217 OffsetF NavDestinationNodeBase::CalcTranslateForTransitionPopStart(const SizeF& frameSize, bool transitionIn)
218 {
219 if (!transitionIn) {
220 return OffsetF{ 0.0f, 0.0f };
221 }
222 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
223 float isRTL = GetLanguageDirection();
224 float width = frameSize.Width();
225 if (angle == ROTATION_90 || angle == ROTATION_270) {
226 width = frameSize.Height();
227 }
228 if (SystemProperties::IsSoftPageTransition()) {
229 auto translate = OffsetF{ -width * HALF * HALF * isRTL, 0.0f };
230 return translate;
231 }
232 auto translate = OffsetF{ -width * CONTENT_OFFSET_PERCENT * isRTL, 0.0f };
233 return translate;
234 }
235
CalcTranslateForTransitionPopEnd(const SizeF & frameSize,bool transitionIn)236 OffsetF NavDestinationNodeBase::CalcTranslateForTransitionPopEnd(const SizeF& frameSize, bool transitionIn)
237 {
238 if (transitionIn) {
239 return OffsetF{ 0.0f, 0.0f };
240 }
241 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
242 float isRTL = GetLanguageDirection();
243 float width = frameSize.Width();
244 if (angle == ROTATION_90 || angle == ROTATION_270) {
245 width = frameSize.Height();
246 }
247 auto translate = OffsetF{ width * HALF * isRTL, 0.0f };
248 return translate;
249 }
250
RestoreRenderContext()251 void NavDestinationNodeBase::RestoreRenderContext()
252 {
253 viewportConfig_ = nullptr;
254 rotateAngle_ = std::nullopt;
255 auto renderContext = GetRenderContext();
256 CHECK_NULL_VOID(renderContext);
257 renderContext->SetBaseTranslateInXY(OffsetF(0.0f, 0.0f));
258 renderContext->SetBaseRotateInZ(0.0f);
259 }
260
SetPageViewportConfig(const RefPtr<PageViewportConfig> & config)261 void NavDestinationNodeBase::SetPageViewportConfig(const RefPtr<PageViewportConfig>& config)
262 {
263 viewportConfig_ = config;
264 if (config) {
265 safeAreaInsets_ = config->GetSafeArea();
266 } else {
267 safeAreaInsets_ = SafeAreaInsets();
268 }
269 do {
270 auto titleNode = AceType::DynamicCast<TitleBarNode>(titleBarNode_);
271 CHECK_NULL_BREAK(titleNode);
272 auto titleBarPattern = titleNode->GetPattern<TitleBarPattern>();
273 CHECK_NULL_BREAK(titleBarPattern);
274 titleBarPattern->SetPageViewportConfig(config ? config->Clone() : nullptr);
275 } while (false);
276 do {
277 auto toolbarNode = AceType::DynamicCast<NavToolbarNode>(toolBarNode_);
278 CHECK_NULL_BREAK(toolbarNode);
279 auto toolBarPattern = toolbarNode->GetPattern<NavToolbarPattern>();
280 CHECK_NULL_BREAK(toolBarPattern);
281 toolBarPattern->SetPageViewportConfig(config ? config->Clone() : nullptr);
282 } while (false);
283 do {
284 auto contentNode = AceType::DynamicCast<FrameNode>(contentNode_);
285 CHECK_NULL_BREAK(contentNode);
286 auto contentPattern = contentNode->GetPattern<NavDestinationContentPattern>();
287 CHECK_NULL_BREAK(contentPattern);
288 contentPattern->SetPageViewportConfig(config ? config->Clone() : nullptr);
289 } while (false);
290 }
291
AdjustLayoutConstarintIfNeeded(const LayoutConstraintF & originConstraint)292 LayoutConstraintF NavDestinationNodeBase::AdjustLayoutConstarintIfNeeded(const LayoutConstraintF& originConstraint)
293 {
294 if (!rotateAngle_.has_value() || !viewportConfig_) {
295 return originConstraint;
296 }
297 auto layoutConstraint = viewportConfig_->CreateRootLayoutConstraint();
298 ApplySafeArea(safeAreaInsets_, layoutConstraint);
299 return layoutConstraint;
300 }
301
GetParentGlobalOffsetWithSafeArea(bool checkBoundary,bool checkPosition) const302 OffsetF NavDestinationNodeBase::GetParentGlobalOffsetWithSafeArea(bool checkBoundary, bool checkPosition) const
303 {
304 if (rotateAngle_.has_value()) {
305 return OffsetF(0.0f, 0.0f);
306 }
307 return LayoutWrapper::GetParentGlobalOffsetWithSafeArea(checkBoundary, checkPosition);
308 }
309
CalcContentTranslateForDialog(const SizeF & frameSize)310 TranslateOptions NavDestinationNodeBase::CalcContentTranslateForDialog(const SizeF& frameSize)
311 {
312 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
313 switch (angle) {
314 case ROTATION_90:
315 return TranslateOptions{ frameSize.Width(), 0.0f, 0.0f };
316 case ROTATION_180:
317 return TranslateOptions{ 0.0f, -frameSize.Height(), 0.0f };
318 case ROTATION_270:
319 return TranslateOptions{ -frameSize.Width(), 0.0f, 0.0f };
320 default:
321 return TranslateOptions{ 0.0f, frameSize.Height(), 0.0f };
322 }
323 }
324
AdjustRenderContextIfNeeded()325 void NavDestinationNodeBase::AdjustRenderContextIfNeeded()
326 {
327 auto renderContext = GetRenderContext();
328 CHECK_NULL_VOID(renderContext);
329 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
330 if (angle == ROTATION_0 || isRotated_) {
331 return;
332 }
333
334 auto geometryNode = GetGeometryNode();
335 CHECK_NULL_VOID(geometryNode);
336 auto frameRect = geometryNode->GetFrameRect();
337 auto frameRectWithSafeArea = geometryNode->GetFrameRect(true);
338
339 FrameNode::ExpandSafeArea();
340 frameRect = geometryNode->GetFrameRect();
341 frameRectWithSafeArea = geometryNode->GetFrameRect(true);
342 auto paintRect = renderContext->GetPaintRectWithoutTransform();
343 auto curCenter = paintRect.Center();
344 OffsetF targetCenter = curCenter;
345 if (angle == ROTATION_90 || angle == ROTATION_270) {
346 targetCenter = OffsetF(paintRect.Height() / 2.0f, paintRect.Width() / 2.0f);
347 }
348 auto offset = targetCenter - curCenter;
349 renderContext->SetBaseTranslateInXY(offset);
350 renderContext->SetBaseRotateInZ(angle);
351
352 isRotated_ = true;
353 }
354
CalcTranslateForSlideTransition(const SizeF & paintRect,bool isRight,bool isEnter,bool isEnd)355 OffsetF NavDestinationNodeBase::CalcTranslateForSlideTransition(
356 const SizeF& paintRect, bool isRight, bool isEnter, bool isEnd)
357 {
358 if ((isEnd && isEnter) || (!isEnd && !isEnter)) {
359 return OffsetF{ 0.0f, 0.0f };
360 }
361 auto angle = rotateAngle_.has_value() ? rotateAngle_.value() : ROTATION_0;
362 float width = paintRect.Width();
363 float height = paintRect.Height();
364 if (angle == ROTATION_90 || angle == ROTATION_270) {
365 std::swap(width, height);
366 }
367
368 auto translate = OffsetF{ isRight ? width : 0.0f, isRight ? 0.0f : height };
369 return translate;
370 }
371 } // namespace OHOS::Ace::NG
372