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