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/common/container.h"
17 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
18 #include "core/components_ng/pattern/navigation/navigation_title_util.h"
19 #include "core/components_ng/pattern/navigation/navigation_transition_proxy.h"
20 #include "core/components_ng/pattern/navrouter/navdestination_group_node.h"
21 #include "core/components_ng/pattern/navrouter/navdestination_context.h"
22 #include "core/components_ng/pattern/navrouter/navdestination_layout_property.h"
23 #include "core/components_ng/pattern/navrouter/navdestination_pattern.h"
24 #include "core/components_ng/pattern/text/text_layout_property.h"
25 #include "core/components_v2/inspector/inspector_constants.h"
26 #include "core/components_ng/pattern/text/text_pattern.h"
27
28 namespace OHOS::Ace::NG {
29 constexpr double HALF = 0.5;
30 constexpr float CONTENT_OFFSET_PERCENT = 0.2f;
31 constexpr float TITLE_OFFSET_PERCENT = 0.02f;
32 constexpr float REMOVE_CLIP_SIZE = 10000.0f;
33 constexpr int32_t MAX_RENDER_GROUP_TEXT_NODE_COUNT = 50;
34 constexpr float MAX_RENDER_GROUP_TEXT_NODE_HEIGHT = 150.0f;
35
~NavDestinationGroupNode()36 NavDestinationGroupNode::~NavDestinationGroupNode()
37 {
38 if (contentNode_) {
39 contentNode_->Clean();
40 }
41 ReleaseTextNodeList();
42 }
43
GetOrCreateGroupNode(const std::string & tag,int32_t nodeId,const std::function<RefPtr<Pattern> (void)> & patternCreator)44 RefPtr<NavDestinationGroupNode> NavDestinationGroupNode::GetOrCreateGroupNode(
45 const std::string& tag, int32_t nodeId, const std::function<RefPtr<Pattern>(void)>& patternCreator)
46 {
47 auto frameNode = GetFrameNode(tag, nodeId);
48 CHECK_NULL_RETURN(!frameNode, AceType::DynamicCast<NavDestinationGroupNode>(frameNode));
49 auto pattern = patternCreator ? patternCreator() : MakeRefPtr<Pattern>();
50 auto navDestinationNode = AceType::MakeRefPtr<NavDestinationGroupNode>(tag, nodeId, pattern);
51 navDestinationNode->InitializePatternAndContext();
52 ElementRegister::GetInstance()->AddUINode(navDestinationNode);
53 return navDestinationNode;
54 }
55
AddChildToGroup(const RefPtr<UINode> & child,int32_t slot)56 void NavDestinationGroupNode::AddChildToGroup(const RefPtr<UINode>& child, int32_t slot)
57 {
58 auto pattern = AceType::DynamicCast<Pattern>(GetPattern());
59 CHECK_NULL_VOID(pattern);
60 auto contentNode = GetContentNode();
61 if (!contentNode) {
62 auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
63 ACE_LAYOUT_SCOPED_TRACE("Create[%s][self:%d]", V2::NAVDESTINATION_CONTENT_ETS_TAG, nodeId);
64 contentNode = FrameNode::GetOrCreateFrameNode(V2::NAVDESTINATION_CONTENT_ETS_TAG, nodeId,
65 []() { return AceType::MakeRefPtr<LinearLayoutPattern>(true); });
66 SetContentNode(contentNode);
67 AddChild(contentNode);
68
69 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
70 auto navdestinationContentNode = AceType::DynamicCast<FrameNode>(contentNode);
71 SafeAreaExpandOpts opts = { .type = SAFE_AREA_TYPE_SYSTEM | SAFE_AREA_TYPE_CUTOUT,
72 .edges = SAFE_AREA_EDGE_ALL };
73 navdestinationContentNode->GetLayoutProperty()->UpdateSafeAreaExpandOpts(opts);
74 }
75 }
76 contentNode->AddChild(child, slot);
77 }
78
DeleteChildFromGroup(int32_t slot)79 void NavDestinationGroupNode::DeleteChildFromGroup(int32_t slot)
80 {
81 auto navDestination = GetContentNode();
82 CHECK_NULL_VOID(navDestination);
83 navDestination->RemoveChildAtIndex(slot);
84 }
85
OnAttachToMainTree(bool recursive)86 void NavDestinationGroupNode::OnAttachToMainTree(bool recursive)
87 {
88 if (!UseOffscreenProcess()) {
89 ProcessShallowBuilder();
90 }
91 FrameNode::OnAttachToMainTree(recursive);
92 }
93
OnOffscreenProcess(bool recursive)94 void NavDestinationGroupNode::OnOffscreenProcess(bool recursive)
95 {
96 ProcessShallowBuilder();
97 FrameNode::OnOffscreenProcess(recursive);
98 }
99
ProcessShallowBuilder()100 void NavDestinationGroupNode::ProcessShallowBuilder()
101 {
102 if (isCacheNode_) {
103 return;
104 }
105
106 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "render navDestination content");
107 auto navDestinationPattern = GetPattern<NavDestinationPattern>();
108 CHECK_NULL_VOID(navDestinationPattern);
109 auto shallowBuilder = navDestinationPattern->GetShallowBuilder();
110 if (shallowBuilder && !shallowBuilder->IsExecuteDeepRenderDone()) {
111 auto eventHub = GetEventHub<NavDestinationEventHub>();
112 if (eventHub) {
113 auto ctx = navDestinationPattern->GetNavDestinationContext();
114 eventHub->FireOnReady(ctx);
115 }
116 shallowBuilder->ExecuteDeepRender();
117 GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
118 AceType::DynamicCast<FrameNode>(contentNode_)
119 ->GetLayoutProperty()
120 ->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
121 }
122 }
123
GetNavDestinationCustomNode()124 RefPtr<CustomNodeBase> NavDestinationGroupNode::GetNavDestinationCustomNode()
125 {
126 return customNode_.Upgrade();
127 }
128
SetNavDestinationMode(NavDestinationMode mode)129 void NavDestinationGroupNode::SetNavDestinationMode(NavDestinationMode mode)
130 {
131 mode_ = mode;
132 auto pattern = GetPattern<NavDestinationPattern>();
133 CHECK_NULL_VOID(pattern);
134 auto context = pattern->GetNavDestinationContext();
135 CHECK_NULL_VOID(context);
136 context->SetMode(mode);
137 }
138
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const139 void NavDestinationGroupNode::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
140 {
141 FrameNode::ToJsonValue(json, filter);
142 /* no fixed attr below, just return */
143 if (filter.IsFastFilter()) {
144 return;
145 }
146 auto titleBarNode = DynamicCast<TitleBarNode>(titleBarNode_);
147 if (titleBarNode) {
148 std::string title = NavigationTitleUtil::GetTitleString(titleBarNode, GetPrevTitleIsCustomValue(false));
149 std::string subtitle = NavigationTitleUtil::GetSubtitleString(titleBarNode);
150 json->PutExtAttr("title", title.c_str(), filter);
151 json->PutExtAttr("subtitle", subtitle.c_str(), filter);
152 }
153 json->PutExtAttr("mode", mode_ == NavDestinationMode::DIALOG
154 ? "NavDestinationMode::DIALOG"
155 : "NavDestinationMode::STANDARD", filter);
156 }
157
InitSystemTransitionPush(bool transitionIn)158 void NavDestinationGroupNode::InitSystemTransitionPush(bool transitionIn)
159 {
160 auto titleBarNode = AceType::DynamicCast<FrameNode>(GetTitleBarNode());
161 float isRTL = GetLanguageDirection();
162 if (transitionIn) {
163 SetIsOnAnimation(true);
164 SetTransitionType(PageTransitionType::ENTER_PUSH);
165 auto frameSize = GetGeometryNode()->GetFrameSize();
166 if (AceApplicationInfo::GetInstance().IsRightToLeft()) {
167 GetRenderContext()->ClipWithRRect(
168 RectF(0.0f, 0.0f, frameSize.Width() * HALF, REMOVE_CLIP_SIZE), RadiusF(EdgeF(0.0f, 0.0f)));
169 } else {
170 GetRenderContext()->ClipWithRRect(
171 RectF(frameSize.Width() * HALF, 0.0f, frameSize.Width(), REMOVE_CLIP_SIZE),
172 RadiusF(EdgeF(0.0f, 0.0f)));
173 }
174 GetRenderContext()->UpdateTranslateInXY({ frameSize.Width() * HALF * isRTL, 0.0f });
175 CHECK_NULL_VOID(titleBarNode);
176 titleBarNode->GetRenderContext()->UpdateTranslateInXY({ frameSize.Width() * HALF * isRTL, 0.0f });
177 return;
178 }
179 SetTransitionType(PageTransitionType::EXIT_PUSH);
180 SetIsOnAnimation(true);
181 GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
182 GetEventHub<EventHub>()->SetEnabledInternal(false);
183 CHECK_NULL_VOID(titleBarNode);
184 titleBarNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
185 if (NeedRemoveInPush()) {
186 GetEventHub<EventHub>()->SetEnabledInternal(false);
187 }
188 }
189
StartSystemTransitionPush(bool transitionIn)190 void NavDestinationGroupNode::StartSystemTransitionPush(bool transitionIn)
191 {
192 auto titleBarNode = AceType::DynamicCast<FrameNode>(GetTitleBarNode());
193 auto frameSize = GetGeometryNode()->GetFrameSize();
194 float isRTL = GetLanguageDirection();
195 if (transitionIn) {
196 GetRenderContext()->ClipWithRRect(
197 RectF(0.0f, 0.0f, frameSize.Width(), REMOVE_CLIP_SIZE), RadiusF(EdgeF(0.0f, 0.0f)));
198 GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
199 CHECK_NULL_VOID(titleBarNode);
200 titleBarNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
201 return;
202 }
203 GetRenderContext()->UpdateTranslateInXY(
204 { -frameSize.Width() * CONTENT_OFFSET_PERCENT * isRTL, 0.0f });
205 CHECK_NULL_VOID(titleBarNode);
206 titleBarNode->GetRenderContext()->UpdateTranslateInXY(
207 { frameSize.Width() * TITLE_OFFSET_PERCENT * isRTL, 0.0f });
208 }
209
SystemTransitionPushCallback(bool transitionIn)210 void NavDestinationGroupNode::SystemTransitionPushCallback(bool transitionIn)
211 {
212 if (transitionIn) {
213 if (GetTransitionType() != PageTransitionType::ENTER_PUSH) {
214 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "curNode has another transition");
215 return;
216 }
217 SetIsOnAnimation(false);
218 GetRenderContext()->RemoveClipWithRRect();
219 return;
220 }
221 GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
222 GetRenderContext()->SetActualForegroundColor(Color::TRANSPARENT);
223 SetIsOnAnimation(false);
224 auto navDestinationPattern = GetPattern<NavDestinationPattern>();
225 auto navigation = AceType::DynamicCast<NavigationGroupNode>(navDestinationPattern->GetNavigationNode());
226 CHECK_NULL_VOID(navigation);
227 bool isInvisible = IsNodeInvisible(navigation);
228 if (GetTransitionType() == PageTransitionType::EXIT_PUSH && isInvisible) {
229 GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
230 }
231 auto titleNode = AceType::DynamicCast<FrameNode>(GetTitleBarNode());
232 CHECK_NULL_VOID(titleNode);
233 titleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
234 }
235
InitSystemTransitionPop(bool isTransitionIn)236 void NavDestinationGroupNode::InitSystemTransitionPop(bool isTransitionIn)
237 {
238 auto frameSize = GetGeometryNode()->GetFrameSize();
239 auto titleBarNode = AceType::DynamicCast<FrameNode>(GetTitleBarNode());
240 float isRTL = GetLanguageDirection();
241 if (isTransitionIn) {
242 SetTransitionType(PageTransitionType::ENTER_POP);
243 GetRenderContext()->RemoveClipWithRRect();
244 GetRenderContext()->UpdateTranslateInXY({ -frameSize.Width() * CONTENT_OFFSET_PERCENT * isRTL, 0.0f });
245 CHECK_NULL_VOID(titleBarNode);
246 titleBarNode->GetRenderContext()->UpdateTranslateInXY(
247 { frameSize.Width() * TITLE_OFFSET_PERCENT * isRTL, 0.0f });
248 return;
249 }
250 SetIsOnAnimation(true);
251 SetTransitionType(PageTransitionType::EXIT_POP);
252 GetEventHub<EventHub>()->SetEnabledInternal(false);
253 GetRenderContext()->ClipWithRRect(RectF(0.0f, 0.0f, frameSize.Width(), REMOVE_CLIP_SIZE),
254 RadiusF(EdgeF(0.0f, 0.0f)));
255 GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
256 CHECK_NULL_VOID(titleBarNode);
257 titleBarNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
258 }
259
StartSystemTransitionPop(bool transitionIn)260 void NavDestinationGroupNode::StartSystemTransitionPop(bool transitionIn)
261 {
262 auto titleBarNode = AceType::DynamicCast<FrameNode>(GetTitleBarNode());
263 if (transitionIn) {
264 GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
265 CHECK_NULL_VOID(titleBarNode);
266 titleBarNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
267 return;
268 }
269 auto frameSize = GetGeometryNode()->GetFrameSize();
270 if (AceApplicationInfo::GetInstance().IsRightToLeft()) {
271 GetRenderContext()->ClipWithRRect(
272 RectF(0.0f, 0.0f, frameSize.Width() * HALF, REMOVE_CLIP_SIZE), RadiusF(EdgeF(0.0f, 0.0f)));
273 } else {
274 GetRenderContext()->ClipWithRRect(
275 RectF(frameSize.Width() * HALF, 0.0f, frameSize.Width(), REMOVE_CLIP_SIZE),
276 RadiusF(EdgeF(0.0f, 0.0f)));
277 }
278 float isRTL = GetLanguageDirection();
279 GetRenderContext()->UpdateTranslateInXY({ frameSize.Width() * HALF * isRTL, 0.0f });
280 CHECK_NULL_VOID(titleBarNode);
281 titleBarNode->GetRenderContext()->UpdateTranslateInXY({ frameSize.Width() * HALF * isRTL, 0.0f });
282 }
283
SystemTransitionPopCallback(bool transitionIn)284 bool NavDestinationGroupNode::SystemTransitionPopCallback(bool transitionIn)
285 {
286 if (GetTransitionType() != PageTransitionType::EXIT_POP || transitionIn) {
287 // has another transition, just return
288 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "preNavDesNode has another transition");
289 return false;
290 }
291 auto preNavDesPattern = GetPattern<NavDestinationPattern>();
292 CHECK_NULL_RETURN(preNavDesPattern, false);
293
294 // NavRouter will restore the preNavDesNode and needs to set the initial state after the animation ends.
295 auto shallowBuilder = preNavDesPattern->GetShallowBuilder();
296 if (shallowBuilder) {
297 shallowBuilder->MarkIsExecuteDeepRenderDone(false);
298 }
299 if (!IsCacheNode() && GetContentNode()) {
300 GetContentNode()->Clean();
301 }
302 SetIsOnAnimation(false);
303 GetEventHub<EventHub>()->SetEnabledInternal(true);
304 GetRenderContext()->RemoveClipWithRRect();
305 GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
306 auto preTitleNode = AceType::DynamicCast<FrameNode>(GetTitleBarNode());
307 if (preTitleNode) {
308 preTitleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
309 preTitleNode->GetRenderContext()->SetOpacity(1.0);
310 auto titleBarNode = AceType::DynamicCast<TitleBarNode>(preTitleNode);
311 CHECK_NULL_RETURN(titleBarNode, true);
312 auto preBackIcon = AceType::DynamicCast<FrameNode>(titleBarNode->GetBackButton());
313 if (preBackIcon) {
314 preBackIcon->GetRenderContext()->SetOpacity(1.0);
315 }
316 }
317 return true;
318 }
319
InitDialogTransition(bool isZeroY)320 void NavDestinationGroupNode::InitDialogTransition(bool isZeroY)
321 {
322 auto contentNode = AceType::DynamicCast<FrameNode>(GetContentNode());
323 CHECK_NULL_VOID(contentNode);
324 auto context = contentNode->GetRenderContext();
325 CHECK_NULL_VOID(context);
326 if (isZeroY) {
327 context->UpdateTransformTranslate({ 0.0f, 0.0f, 0.0f });
328 return;
329 }
330 context->UpdateTransformTranslate({ 0.0f,
331 contentNode->GetGeometryNode()->GetFrameSize().Height(), 0.0f });
332 }
333
UpdateTextNodeListAsRenderGroup(bool isPopPage,const RefPtr<NavigationTransitionProxy> & proxy)334 void NavDestinationGroupNode::UpdateTextNodeListAsRenderGroup(
335 bool isPopPage, const RefPtr<NavigationTransitionProxy>& proxy)
336 {
337 if (isPopPage) {
338 CollectTextNodeAsRenderGroup(isPopPage);
339 } else {
340 CHECK_NULL_VOID(proxy);
341 auto pipeline = PipelineContext::GetCurrentContext();
342 CHECK_NULL_VOID(pipeline);
343 pipeline->AddAfterLayoutTask([weakNavDestiniation = WeakClaim(this),
344 weakProxy = WeakPtr<NavigationTransitionProxy>(proxy)] () {
345 auto navDestination = weakNavDestiniation.Upgrade();
346 CHECK_NULL_VOID(navDestination);
347 auto proxy = weakProxy.Upgrade();
348 if (proxy && proxy->GetIsFinished()) {
349 return;
350 }
351 navDestination->CollectTextNodeAsRenderGroup(false);
352 });
353 pipeline->RequestFrame();
354 }
355 }
356
CollectTextNodeAsRenderGroup(bool isPopPage)357 void NavDestinationGroupNode::CollectTextNodeAsRenderGroup(bool isPopPage)
358 {
359 ReleaseTextNodeList();
360 std::queue<RefPtr<UINode>> childrenLoopQueue;
361 childrenLoopQueue.push(contentNode_);
362
363 // only the first 50 text nodes will be marked, avoid too much time for traversal
364 // and off-screen drawing at first few frames
365 int32_t remainTextNodeNeedRenderGroup = MAX_RENDER_GROUP_TEXT_NODE_COUNT;
366 while (!childrenLoopQueue.empty() && remainTextNodeNeedRenderGroup > 0) {
367 auto currentNode = childrenLoopQueue.front();
368 childrenLoopQueue.pop();
369 CHECK_NULL_CONTINUE(currentNode);
370 for (auto& child : currentNode->GetChildren()) {
371 if (remainTextNodeNeedRenderGroup <= 0) {
372 break;
373 }
374 CHECK_NULL_CONTINUE(child);
375 childrenLoopQueue.push(child);
376 auto frameNode = AceType::DynamicCast<FrameNode>(child);
377 if (!frameNode || (frameNode->GetTag() != V2::TEXT_ETS_TAG)) {
378 continue;
379 }
380 auto layoutProperty = frameNode->GetLayoutProperty<TextLayoutProperty>();
381 if (!layoutProperty ||
382 (layoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE)) {
383 continue;
384 }
385 auto& renderContext = frameNode->GetRenderContext();
386 if (!renderContext || renderContext->GetRenderGroupValue(false)) {
387 continue;
388 }
389 renderContext->SetMarkNodeGroup(isPopPage ||
390 (renderContext->GetPaintRectWithoutTransform().Height() < MAX_RENDER_GROUP_TEXT_NODE_HEIGHT));
391 textNodeList_.emplace_back(WeakPtr<UINode>(child));
392 --remainTextNodeNeedRenderGroup;
393 auto pattern = AceType::DynamicCast<TextPattern>(frameNode->GetPattern());
394 CHECK_NULL_CONTINUE(pattern);
395 pattern->RegisterAfterLayoutCallback([weakRenderContext = WeakPtr<RenderContext>(renderContext)]() {
396 auto renderContext = weakRenderContext.Upgrade();
397 if (renderContext && !(renderContext->GetRenderGroupValue(false))) {
398 renderContext->SetMarkNodeGroup(
399 renderContext->GetPaintRectWithoutTransform().Height() < MAX_RENDER_GROUP_TEXT_NODE_HEIGHT);
400 }
401 });
402 }
403 }
404 }
405
ReleaseTextNodeList()406 void NavDestinationGroupNode::ReleaseTextNodeList()
407 {
408 for (auto& child : textNodeList_) {
409 auto textNode = AceType::DynamicCast<FrameNode>(child.Upgrade());
410 if (!textNode) {
411 continue;
412 }
413 auto pattern = AceType::DynamicCast<TextPattern>(textNode->GetPattern());
414 if (pattern) {
415 pattern->UnRegisterAfterLayoutCallback();
416 }
417 auto renderContext = textNode->GetRenderContext();
418 if (renderContext) {
419 renderContext->SetMarkNodeGroup(renderContext->GetRenderGroupValue(false));
420 }
421 }
422 textNodeList_.clear();
423 }
424
CleanContent()425 void NavDestinationGroupNode::CleanContent()
426 {
427 auto pattern = GetPattern<NavDestinationPattern>();
428 CHECK_NULL_VOID(pattern);
429 auto shallowBuilder = pattern->GetShallowBuilder();
430 if (shallowBuilder) {
431 shallowBuilder->MarkIsExecuteDeepRenderDone(false);
432 }
433 if (GetContentNode()) {
434 GetContentNode()->Clean(false, true);
435 }
436 }
437
IsNodeInvisible(const RefPtr<FrameNode> & node)438 bool NavDestinationGroupNode::IsNodeInvisible(const RefPtr<FrameNode>& node)
439 {
440 auto navigaiton = DynamicCast<NavigationGroupNode>(node);
441 CHECK_NULL_RETURN(navigaiton, false);
442 int32_t lastStandardIndex = navigaiton->GetLastStandardIndex();
443 bool isInvisible = index_ < lastStandardIndex;
444 return isInvisible;
445 }
446 } // namespace OHOS::Ace::NG
447