• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/stack/stack_element.h"
17 
18 #include "bridge/declarative_frontend/view_stack_processor.h"
19 #include "core/components/bubble/bubble_element.h"
20 #include "core/components/bubble/render_bubble.h"
21 #include "core/components/common/properties/animation_option.h"
22 #include "core/components/dialog/dialog_component.h"
23 #include "core/components/dialog/dialog_element.h"
24 #include "core/components/dialog_tween/render_dialog_tween.h"
25 #include "core/components/drop_filter/drop_filter_element.h"
26 #include "core/components/page/page_element.h"
27 #include "core/components/picker/picker_base_element.h"
28 #include "core/components/popup/popup_component.h"
29 #include "core/components/popup/popup_theme.h"
30 #include "core/components/select_popup/select_popup_element.h"
31 #include "core/components/text_overlay/text_overlay_element.h"
32 #include "core/components_v2/inspector/inspector_composed_component.h"
33 #include "core/components_v2/inspector/inspector_composed_element.h"
34 #include "core/pipeline/base/composed_element.h"
35 
36 namespace OHOS::Ace {
37 
PushInstant(const RefPtr<Component> & newComponent,bool disableTouchEvent)38 void StackElement::PushInstant(const RefPtr<Component>& newComponent, bool disableTouchEvent)
39 {
40     PopupComponentInfo pushComponentInfo = { -1, "-1", Operation::DIRECT_PUSH, newComponent };
41     popupComponentInfos_.emplace_back(pushComponentInfo);
42     disableTouchEvent_ = disableTouchEvent;
43     PerformBuild();
44 }
45 
PushComponent(const RefPtr<Component> & newComponent,bool disableTouchEvent)46 void StackElement::PushComponent(const RefPtr<Component>& newComponent, bool disableTouchEvent)
47 {
48     PopupComponentInfo pushComponentInfo = { -1, "-1", Operation::DIRECT_PUSH, newComponent };
49     popupComponentInfos_.emplace_back(pushComponentInfo);
50     disableTouchEvent_ = disableTouchEvent;
51     MarkDirty();
52 }
53 
PopComponent()54 void StackElement::PopComponent()
55 {
56     PopupComponentInfo popComponentInfo = { -1, "-1", Operation::DIRECT_POP, nullptr };
57     popupComponentInfos_.emplace_back(popComponentInfo);
58     MarkDirty();
59 }
60 
PushToastComponent(const RefPtr<Component> & newComponent,int32_t toastId)61 void StackElement::PushToastComponent(const RefPtr<Component>& newComponent, int32_t toastId)
62 {
63     PopupComponentInfo pushComponentInfo = { toastId, "-1", Operation::TOAST_PUSH, newComponent };
64     popupComponentInfos_.emplace_back(pushComponentInfo);
65     MarkDirty();
66 }
67 
PopToastComponent(int32_t toastPopId)68 void StackElement::PopToastComponent(int32_t toastPopId)
69 {
70     PopupComponentInfo popComponentInfo = { toastPopId, "-1", Operation::TOAST_POP, nullptr };
71     popupComponentInfos_.emplace_back(popComponentInfo);
72     MarkDirty();
73 }
74 
PushPanel(const RefPtr<Component> & newComponent,bool disableTouchEvent)75 void StackElement::PushPanel(const RefPtr<Component>& newComponent, bool disableTouchEvent)
76 {
77     PopupComponentInfo pushComponentInfo = { -1, "-1", Operation::PANEL_PUSH, newComponent };
78     popupComponentInfos_.emplace_back(pushComponentInfo);
79     disableTouchEvent_ = disableTouchEvent;
80     MarkDirty();
81 }
82 
HasOverlayChild()83 bool StackElement::HasOverlayChild()
84 {
85     bool hasMultiChild = children_.size() > 1;
86     // avoid pop toast to protect page not empty.
87     for (const auto& child: children_) {
88         if (!std::none_of(toastStack_.begin(), toastStack_.end(),
89             [child](const ToastInfo& toast) { return toast.child == child; })) {
90             return false;
91         }
92     }
93     return hasMultiChild;
94 }
95 
PopPanel()96 void StackElement::PopPanel()
97 {
98     PopDialog();
99 }
100 
PushDialog(const RefPtr<Component> & newComponent,bool disableTouchEvent)101 bool StackElement::PushDialog(const RefPtr<Component>& newComponent, bool disableTouchEvent)
102 {
103     auto context = context_.Upgrade();
104     if (context) {
105         AccessibilityEvent stackEvent;
106         stackEvent.eventType = "ejectdismiss";
107         context->SendEventToAccessibility(stackEvent);
108     }
109     PopupComponentInfo pushComponentInfo = { -1, "-1", Operation::DIALOG_PUSH, newComponent };
110     CreateInspectorComponent(pushComponentInfo);
111 
112     popupComponentInfos_.emplace_back(pushComponentInfo);
113     disableTouchEvent_ = disableTouchEvent;
114     MarkDirty();
115     return true;
116 }
117 
PopDialog(int32_t id)118 bool StackElement::PopDialog(int32_t id)
119 {
120     LOGI("StackElement::PopDialog id is %{public}d", id);
121     auto context = context_.Upgrade();
122     if (context) {
123         AccessibilityEvent stackEvent;
124         stackEvent.eventType = "ejectdismiss";
125         context->SendEventToAccessibility(stackEvent);
126     }
127     PopupComponentInfo popComponentInfo = { id, "-1", Operation::DIALOG_POP, nullptr };
128     popupComponentInfos_.emplace_back(popComponentInfo);
129     MarkDirty();
130     return true;
131 }
132 
PopTextOverlay()133 void StackElement::PopTextOverlay()
134 {
135     PopupComponentInfo popComponentInfo = { -1, "-1", Operation::TEXT_OVERLAY_POP, nullptr };
136     popupComponentInfos_.emplace_back(popComponentInfo);
137     MarkDirty();
138 }
139 
PopPopup(const ComposeId & id)140 void StackElement::PopPopup(const ComposeId& id)
141 {
142     PopupComponentInfo popComponentInfo = { -1, id, Operation::POPUP_POP, nullptr };
143     popupComponentInfos_.emplace_back(popComponentInfo);
144     MarkDirty();
145 }
146 
PopMenu()147 void StackElement::PopMenu()
148 {
149     PopupComponentInfo popComponentInfo = { -1, "-1", Operation::MENU_POP, nullptr };
150     popupComponentInfos_.emplace_back(popComponentInfo);
151     MarkDirty();
152 }
153 
PopVideo()154 void StackElement::PopVideo()
155 {
156     PopupComponentInfo popComponentInfo = { -1, "-1", Operation::VIDEO_POP, nullptr };
157     popupComponentInfos_.emplace_back(popComponentInfo);
158     MarkDirty();
159 }
160 
PopInstant()161 void StackElement::PopInstant()
162 {
163     auto child = children_.end();
164     if (child != children_.begin()) {
165         child--;
166         UpdateChild(*child, nullptr);
167     }
168     EnableTouchEventAndRequestFocus();
169 }
170 
PerformBuild()171 void StackElement::PerformBuild()
172 {
173     // rebuild popup component
174     for (auto& info : popupComponentInfos_) {
175         PerformPopupChild(info);
176     }
177 
178     ComponentGroupElement::PerformBuild();
179     popupComponentInfos_.clear();
180 }
181 
PerformPopupChild(PopupComponentInfo & popupComponentInfo)182 void StackElement::PerformPopupChild(PopupComponentInfo& popupComponentInfo)
183 {
184     switch (popupComponentInfo.operation) {
185         case Operation::TOAST_PUSH:
186             PerformPushToast(popupComponentInfo);
187             break;
188         case Operation::DIRECT_PUSH:
189         case Operation::DIALOG_PUSH:
190         case Operation::PANEL_PUSH:
191             PerformPushChild(popupComponentInfo);
192             break;
193         case Operation::TOAST_POP:
194             PerformPopToastById(popupComponentInfo.popId);
195             break;
196         case Operation::DIALOG_POP:
197             PerformPopDialog(popupComponentInfo.popId);
198             break;
199         case Operation::TEXT_OVERLAY_POP:
200             PerformPopTextOverlay();
201             break;
202         case Operation::POPUP_POP:
203             PerformPopPopup(popupComponentInfo.id);
204             break;
205         case Operation::MENU_POP:
206             PerformPopMenu();
207             break;
208         case Operation::VIDEO_POP:
209             PerformPopVideo();
210             break;
211         case Operation::DIRECT_POP:
212             PerformDirectPop();
213             break;
214         default:
215             LOGD("Don't need pop popup component");
216     }
217 }
218 
PerformPushToast(PopupComponentInfo & popupComponentInfo)219 void StackElement::PerformPushToast(PopupComponentInfo& popupComponentInfo)
220 {
221     if (!popupComponentInfo.IsValid() || popupComponentInfo.operation != Operation::TOAST_PUSH) {
222         return;
223     }
224     PerformPopToast();
225     // store toast element
226     RefPtr<Element> toastElement = UpdateChild(nullptr, popupComponentInfo.component);
227     if (toastElement) {
228         ToastInfo toastInfo = { popupComponentInfo.popId, toastElement };
229         toastStack_.emplace_back(toastInfo);
230     }
231     popupComponentInfo.component = nullptr;
232 }
233 
PerformPushChild(PopupComponentInfo & popupComponentInfo)234 void StackElement::PerformPushChild(PopupComponentInfo& popupComponentInfo)
235 {
236     if (!popupComponentInfo.IsValid()) {
237         return;
238     }
239     // store toast element
240     if (!UpdateChild(nullptr, popupComponentInfo.component)) {
241         return;
242     }
243     for (auto child = (++children_.rbegin()); child != children_.rend(); ++child) {
244         auto renderNode = (*child)->GetRenderNode();
245         if (renderNode) {
246             renderNode->SetDisableTouchEvent(disableTouchEvent_);
247         }
248     }
249     auto renderNode = GetRenderNode();
250     if (!renderNode) {
251         return;
252     }
253     renderNode->MarkNeedLayout();
254     if (isPageElement()) {
255         if (!focusNodes_.empty() && focusNodes_.back()->IsFocusable()) {
256             focusNodes_.back()->RequestFocus();
257         }
258     }
259     popupComponentInfo.component = nullptr;
260 }
261 
PerformPopToastById(int32_t toastId)262 void StackElement::PerformPopToastById(int32_t toastId)
263 {
264     if (toastStack_.empty()) {
265         return;
266     }
267     for (auto iter = toastStack_.end() - 1; iter >= toastStack_.begin(); --iter) {
268         if (iter->toastId == toastId) {
269             UpdateChild(iter->child, nullptr);
270             toastStack_.erase(iter);
271             break;
272         }
273     }
274     EnableTouchEventAndRequestFocus();
275 }
276 
PerformPopToast()277 void StackElement::PerformPopToast()
278 {
279     LOGD("PerformPopToast");
280     if (!toastStack_.empty()) {
281         UpdateChild(toastStack_.back().child, nullptr);
282         toastStack_.pop_back();
283     }
284 }
285 
PerformPopDialog(int32_t id)286 void StackElement::PerformPopDialog(int32_t id)
287 {
288     if (id >= 0) {
289         PerformPopDialogById(id);
290         return;
291     }
292 
293     bool hasDialog = std::any_of(children_.begin(), children_.end(), [](const RefPtr<Element>& child) {
294         return AceType::InstanceOf<V2::InspectorComposedElement>(child) || AceType::InstanceOf<DialogElement>(child) ||
295                AceType::InstanceOf<PickerBaseElement>(child) || AceType::InstanceOf<DropFilterElement>(child);
296     });
297     if (!hasDialog) {
298         EnableTouchEventAndRequestFocus();
299         return;
300     }
301     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
302         if (AceType::InstanceOf<V2::InspectorComposedElement>(*iter) || AceType::InstanceOf<DialogElement>(*iter) ||
303             AceType::InstanceOf<PickerBaseElement>(*iter) || AceType::InstanceOf<DropFilterElement>(*iter)) {
304             UpdateChild(*iter, nullptr);
305             break;
306         }
307     }
308     EnableTouchEventAndRequestFocus();
309 }
310 
PerformPopDialogById(int32_t id)311 void StackElement::PerformPopDialogById(int32_t id)
312 {
313     bool hasDialog = std::any_of(children_.begin(), children_.end(), [](const RefPtr<Element>& child) {
314         return AceType::InstanceOf<V2::InspectorComposedElement>(child) || AceType::InstanceOf<DialogElement>(child);
315     });
316     if (!hasDialog) {
317         EnableTouchEventAndRequestFocus();
318         return;
319     }
320     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
321         auto dialogElement = AceType::DynamicCast<DialogElement>(*iter);
322         if (dialogElement && dialogElement->GetDialogId() == id) {
323             UpdateChild(*iter, nullptr);
324             break;
325         }
326         auto inspectorComposedElement = AceType::DynamicCast<V2::InspectorComposedElement>(*iter);
327         if (inspectorComposedElement) {
328             dialogElement = inspectorComposedElement->GetContentElement<DialogElement>(DialogElement::TypeId());
329             if (dialogElement && dialogElement->GetDialogId() == id) {
330                 UpdateChild(inspectorComposedElement, nullptr);
331                 break;
332             }
333         }
334     }
335     EnableTouchEventAndRequestFocus();
336 }
337 
PerformPopTextOverlay()338 void StackElement::PerformPopTextOverlay()
339 {
340     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
341         if (AceType::InstanceOf<TextOverlayElement>(*iter)) {
342             UpdateChild(*iter, nullptr);
343             break;
344         }
345     }
346     if (IsFocusable()) {
347         RequestFocus();
348     }
349     EnableTouchEventAndRequestFocus();
350 }
351 
PerformPopPopup(const ComposeId & id)352 void StackElement::PerformPopPopup(const ComposeId& id)
353 {
354     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
355         auto child = DynamicCast<TweenElement>(*iter);
356         if (child && child->GetId() == id) {
357             auto themeManager = GetThemeManager();
358             if (!themeManager || !themeManager->GetTheme<PopupTheme>()) {
359                 LOGE("themeManager or get theme is null!");
360                 return;
361             }
362 
363             auto context = context_.Upgrade();
364             if (context && !context->GetOnShow()) {
365                 UpdateChild(child, nullptr);
366                 break;
367             }
368 
369             auto theme = themeManager->GetTheme<PopupTheme>();
370             auto hideAlphaAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(1.0f, 0.0f, Curves::FAST_OUT_SLOW_IN);
371             TweenOption hideOption;
372             hideOption.SetDuration(theme->GetHideTime());
373             hideOption.SetOpacityAnimation(hideAlphaAnimation);
374 
375             auto animator = child->GetController();
376             animator->ClearAllListeners();
377             child->SetOption(hideOption);
378             child->ApplyOptions();
379             child->ApplyKeyframes();
380             animator->AddStopListener(
381                 [weakStack = AceType::WeakClaim(this), weakChild = AceType::WeakClaim(AceType::RawPtr(child))] {
382                     auto lastStack = weakStack.Upgrade();
383                     auto child = weakChild.Upgrade();
384                     if (lastStack && child) {
385                         lastStack->UpdateChild(child, nullptr);
386                     }
387                 });
388             animator->Play();
389             break;
390         }
391     }
392     if (IsFocusable()) {
393         RequestFocus();
394     }
395     EnableTouchEventAndRequestFocus();
396 }
397 
PerformPopMenu()398 void StackElement::PerformPopMenu()
399 {
400     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
401         if (AceType::InstanceOf<SelectPopupElement>(*iter)) {
402             UpdateChild(*iter, nullptr);
403             break;
404         }
405     }
406     if (IsFocusable()) {
407         RequestFocus();
408     }
409     EnableTouchEventAndRequestFocus();
410 }
411 
PerformPopVideo()412 void StackElement::PerformPopVideo()
413 {
414     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
415         auto element = DynamicCast<ComposedElement>(*iter);
416         if (element && StringUtils::EndWith(element->GetName(), "fullscreen")) {
417             UpdateChild(*iter, nullptr);
418             break;
419         }
420     }
421     EnableTouchEventAndRequestFocus();
422 }
423 
PerformDirectPop()424 void StackElement::PerformDirectPop()
425 {
426     auto child = children_.end();
427     while (child != children_.begin()) {
428         child--;
429         bool isNotToast = std::none_of(
430             toastStack_.begin(), toastStack_.end(), [child](const ToastInfo& toast) { return toast.child == *child; });
431         if (isNotToast) {
432             UpdateChild(*child, nullptr);
433             break;
434         }
435     }
436     EnableTouchEventAndRequestFocus();
437 }
438 
RequestNextFocus(bool vertical,bool reverse,const Rect & rect)439 bool StackElement::RequestNextFocus(bool vertical, bool reverse, const Rect& rect)
440 {
441     if (!isPageElement()) {
442         return GoToNextFocus(reverse, rect);
443     }
444     return false;
445 }
446 
OnFocus()447 void StackElement::OnFocus()
448 {
449     if (!isPageElement()) {
450         FocusGroup::OnFocus();
451         return;
452     }
453     if (focusNodes_.empty()) {
454         itLastFocusNode_ = focusNodes_.end();
455         return;
456     }
457     // Only focus on the top focusable child.
458     itLastFocusNode_ = focusNodes_.end();
459     while (itLastFocusNode_ != focusNodes_.begin()) {
460         --itLastFocusNode_;
461         (*itLastFocusNode_)->SetParentFocusable(IsParentFocusable());
462         if ((*itLastFocusNode_)->RequestFocusImmediately()) {
463             FocusNode::OnFocus();
464             break;
465         }
466     }
467 
468     if (!IsCurrentFocus()) {
469         itLastFocusNode_ = focusNodes_.end();
470     } else {
471         // lower focusable node can not be focus.
472         auto iter = itLastFocusNode_;
473         while (iter != focusNodes_.begin()) {
474             --iter;
475             (*iter)->SetParentFocusable(false);
476         }
477     }
478 }
479 
OnBlur()480 void StackElement::OnBlur()
481 {
482     FocusGroup::OnBlur();
483     if (!isPageElement()) {
484         return;
485     }
486 
487     auto iter = focusNodes_.end();
488     while (iter != focusNodes_.begin()) {
489         --iter;
490         (*iter)->SetParentFocusable(IsParentFocusable());
491     }
492 }
493 
EnableTouchEventAndRequestFocus()494 void StackElement::EnableTouchEventAndRequestFocus()
495 {
496     for (auto& child : children_) {
497         auto renderNode = child->GetRenderNode();
498         if (renderNode) {
499             renderNode->SetDisableTouchEvent(false);
500         }
501     }
502     if (IsFocusable()) {
503         RequestFocus();
504     }
505 }
506 
CreateInspectorComponent(PopupComponentInfo & componentInfo) const507 void StackElement::CreateInspectorComponent(PopupComponentInfo& componentInfo) const
508 {
509     auto dialog = AceType::DynamicCast<DialogComponent>(componentInfo.component);
510     if (!dialog) {
511         return;
512     }
513     auto inspectorTag = dialog->GetInspectorTag();
514     if (V2::InspectorComposedComponent::HasInspectorFinished(inspectorTag)) {
515         auto composedComponent = AceType::MakeRefPtr<V2::InspectorComposedComponent>(
516             V2::InspectorComposedComponent::GenerateId(), inspectorTag);
517         composedComponent->SetChild(componentInfo.component);
518         componentInfo.component = composedComponent;
519     }
520 }
521 
PopPopupIfExist() const522 bool StackElement::PopPopupIfExist() const
523 {
524     auto bubbleElement = GetBubble(GetLastChild());
525     if (!bubbleElement) {
526         return false;
527     }
528     auto renderBubble = DynamicCast<RenderBubble>(bubbleElement->GetRenderNode());
529     if (!renderBubble) {
530         return false;
531     }
532     renderBubble->PopBubble();
533     return true;
534 }
535 
GetBubble(const RefPtr<Element> & element) const536 RefPtr<BubbleElement> StackElement::GetBubble(const RefPtr<Element>& element) const
537 {
538     if (!element) {
539         return nullptr;
540     }
541 
542     auto bubble = DynamicCast<BubbleElement>(element);
543     if (bubble) {
544         return bubble;
545     }
546 
547     return GetBubble(element->GetFirstChild());
548 }
549 
PopDialogIfExist() const550 bool StackElement::PopDialogIfExist() const
551 {
552     auto dialogTweenElement = GetDialog(GetLastChild());
553     if (!dialogTweenElement) {
554         return false;
555     }
556     auto renderDialogTween = DynamicCast<RenderDialogTween>(dialogTweenElement->GetRenderNode());
557     if (!renderDialogTween) {
558         return false;
559     }
560     renderDialogTween->PopDialog();
561     return true;
562 }
563 
GetDialog(const RefPtr<Element> & element) const564 RefPtr<DialogTweenElement> StackElement::GetDialog(const RefPtr<Element>& element) const
565 {
566     if (!element) {
567         return nullptr;
568     }
569 
570     auto dialog = DynamicCast<DialogTweenElement>(element);
571     if (dialog) {
572         return dialog;
573     }
574 
575     return GetDialog(element->GetFirstChild());
576 }
577 
578 } // namespace OHOS::Ace
579