• 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             break;
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     if (!toastStack_.empty()) {
280         UpdateChild(toastStack_.back().child, nullptr);
281         toastStack_.pop_back();
282     }
283 }
284 
PerformPopDialog(int32_t id)285 void StackElement::PerformPopDialog(int32_t id)
286 {
287     if (id >= 0) {
288         PerformPopDialogById(id);
289         return;
290     }
291 
292     bool hasDialog = std::any_of(children_.begin(), children_.end(), [](const RefPtr<Element>& child) {
293         return AceType::InstanceOf<V2::InspectorComposedElement>(child) || AceType::InstanceOf<DialogElement>(child) ||
294                AceType::InstanceOf<PickerBaseElement>(child) || AceType::InstanceOf<DropFilterElement>(child);
295     });
296     if (!hasDialog) {
297         EnableTouchEventAndRequestFocus();
298         return;
299     }
300     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
301         if (AceType::InstanceOf<V2::InspectorComposedElement>(*iter) || AceType::InstanceOf<DialogElement>(*iter) ||
302             AceType::InstanceOf<PickerBaseElement>(*iter) || AceType::InstanceOf<DropFilterElement>(*iter)) {
303             UpdateChild(*iter, nullptr);
304             break;
305         }
306     }
307     EnableTouchEventAndRequestFocus();
308 }
309 
PerformPopDialogById(int32_t id)310 void StackElement::PerformPopDialogById(int32_t id)
311 {
312     bool hasDialog = std::any_of(children_.begin(), children_.end(), [](const RefPtr<Element>& child) {
313         return AceType::InstanceOf<V2::InspectorComposedElement>(child) || AceType::InstanceOf<DialogElement>(child);
314     });
315     if (!hasDialog) {
316         EnableTouchEventAndRequestFocus();
317         return;
318     }
319     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
320         auto dialogElement = AceType::DynamicCast<DialogElement>(*iter);
321         if (dialogElement && dialogElement->GetDialogId() == id) {
322             UpdateChild(*iter, nullptr);
323             break;
324         }
325         auto inspectorComposedElement = AceType::DynamicCast<V2::InspectorComposedElement>(*iter);
326         if (inspectorComposedElement) {
327             dialogElement = inspectorComposedElement->GetContentElement<DialogElement>(DialogElement::TypeId());
328             if (dialogElement && dialogElement->GetDialogId() == id) {
329                 UpdateChild(inspectorComposedElement, nullptr);
330                 break;
331             }
332         }
333     }
334     EnableTouchEventAndRequestFocus();
335 }
336 
PerformPopTextOverlay()337 void StackElement::PerformPopTextOverlay()
338 {
339     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
340         if (AceType::InstanceOf<TextOverlayElement>(*iter)) {
341             UpdateChild(*iter, nullptr);
342             break;
343         }
344     }
345     if (IsFocusable()) {
346         RequestFocus();
347     }
348     EnableTouchEventAndRequestFocus();
349 }
350 
PerformPopPopup(const ComposeId & id)351 void StackElement::PerformPopPopup(const ComposeId& id)
352 {
353     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
354         auto child = DynamicCast<TweenElement>(*iter);
355         if (child && child->GetId() == id) {
356             auto themeManager = GetThemeManager();
357             if (!themeManager || !themeManager->GetTheme<PopupTheme>()) {
358                 LOGE("themeManager or get theme is null!");
359                 return;
360             }
361 
362             auto context = context_.Upgrade();
363             if (context && !context->GetOnShow()) {
364                 UpdateChild(child, nullptr);
365                 break;
366             }
367 
368             auto theme = themeManager->GetTheme<PopupTheme>();
369             auto hideAlphaAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(1.0f, 0.0f, Curves::FAST_OUT_SLOW_IN);
370             TweenOption hideOption;
371             hideOption.SetDuration(theme->GetHideTime());
372             hideOption.SetOpacityAnimation(hideAlphaAnimation);
373 
374             auto animator = child->GetController();
375             animator->ClearAllListeners();
376             child->SetOption(hideOption);
377             child->ApplyOptions();
378             child->ApplyKeyframes();
379             animator->AddStopListener(
380                 [weakStack = AceType::WeakClaim(this), weakChild = AceType::WeakClaim(AceType::RawPtr(child))] {
381                     auto lastStack = weakStack.Upgrade();
382                     auto child = weakChild.Upgrade();
383                     if (lastStack && child) {
384                         lastStack->UpdateChild(child, nullptr);
385                     }
386                 });
387             animator->Play();
388             break;
389         }
390     }
391     if (IsFocusable()) {
392         RequestFocus();
393     }
394     EnableTouchEventAndRequestFocus();
395 }
396 
PerformPopMenu()397 void StackElement::PerformPopMenu()
398 {
399     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
400         if (AceType::InstanceOf<SelectPopupElement>(*iter)) {
401             UpdateChild(*iter, nullptr);
402             break;
403         }
404     }
405     if (IsFocusable()) {
406         RequestFocus();
407     }
408     EnableTouchEventAndRequestFocus();
409 }
410 
PerformPopVideo()411 void StackElement::PerformPopVideo()
412 {
413     for (auto iter = children_.rbegin(); iter != children_.rend(); ++iter) {
414         auto element = DynamicCast<ComposedElement>(*iter);
415         if (element && StringUtils::EndWith(element->GetName(), "fullscreen")) {
416             UpdateChild(*iter, nullptr);
417             break;
418         }
419     }
420     EnableTouchEventAndRequestFocus();
421 }
422 
PerformDirectPop()423 void StackElement::PerformDirectPop()
424 {
425     auto child = children_.end();
426     while (child != children_.begin()) {
427         child--;
428         bool isNotToast = std::none_of(
429             toastStack_.begin(), toastStack_.end(), [child](const ToastInfo& toast) { return toast.child == *child; });
430         if (isNotToast) {
431             UpdateChild(*child, nullptr);
432             break;
433         }
434     }
435     EnableTouchEventAndRequestFocus();
436 }
437 
RequestNextFocus(bool vertical,bool reverse,const Rect & rect)438 bool StackElement::RequestNextFocus(bool vertical, bool reverse, const Rect& rect)
439 {
440     if (!isPageElement()) {
441         return GoToNextFocus(reverse, rect);
442     }
443     return false;
444 }
445 
OnFocus()446 void StackElement::OnFocus()
447 {
448     if (!isPageElement()) {
449         FocusGroup::OnFocus();
450         return;
451     }
452     if (focusNodes_.empty()) {
453         itLastFocusNode_ = focusNodes_.end();
454         return;
455     }
456     // Only focus on the top focusable child.
457     itLastFocusNode_ = focusNodes_.end();
458     while (itLastFocusNode_ != focusNodes_.begin()) {
459         --itLastFocusNode_;
460         (*itLastFocusNode_)->SetParentFocusable(IsParentFocusable());
461         if ((*itLastFocusNode_)->RequestFocusImmediately()) {
462             FocusNode::OnFocus();
463             break;
464         }
465     }
466 
467     if (!IsCurrentFocus()) {
468         itLastFocusNode_ = focusNodes_.end();
469     } else {
470         // lower focusable node can not be focus.
471         auto iter = itLastFocusNode_;
472         while (iter != focusNodes_.begin()) {
473             --iter;
474             (*iter)->SetParentFocusable(false);
475         }
476     }
477 }
478 
OnBlur()479 void StackElement::OnBlur()
480 {
481     FocusGroup::OnBlur();
482     if (!isPageElement()) {
483         return;
484     }
485 
486     auto iter = focusNodes_.end();
487     while (iter != focusNodes_.begin()) {
488         --iter;
489         (*iter)->SetParentFocusable(IsParentFocusable());
490     }
491 }
492 
EnableTouchEventAndRequestFocus()493 void StackElement::EnableTouchEventAndRequestFocus()
494 {
495     for (auto& child : children_) {
496         auto renderNode = child->GetRenderNode();
497         if (renderNode) {
498             renderNode->SetDisableTouchEvent(false);
499         }
500     }
501     if (IsFocusable()) {
502         RequestFocus();
503     }
504 }
505 
CreateInspectorComponent(PopupComponentInfo & componentInfo) const506 void StackElement::CreateInspectorComponent(PopupComponentInfo& componentInfo) const
507 {
508     auto dialog = AceType::DynamicCast<DialogComponent>(componentInfo.component);
509     if (!dialog) {
510         return;
511     }
512     auto inspectorTag = dialog->GetInspectorTag();
513     if (V2::InspectorComposedComponent::HasInspectorFinished(inspectorTag)) {
514         auto composedComponent = AceType::MakeRefPtr<V2::InspectorComposedComponent>(
515             V2::InspectorComposedComponent::GenerateId(), inspectorTag);
516         composedComponent->SetChild(componentInfo.component);
517         componentInfo.component = composedComponent;
518     }
519 }
520 
PopPopupIfExist() const521 bool StackElement::PopPopupIfExist() const
522 {
523     auto bubbleElement = GetBubble(GetLastChild());
524     if (!bubbleElement) {
525         return false;
526     }
527     auto renderBubble = DynamicCast<RenderBubble>(bubbleElement->GetRenderNode());
528     if (!renderBubble) {
529         return false;
530     }
531     renderBubble->PopBubble();
532     return true;
533 }
534 
GetBubble(const RefPtr<Element> & element) const535 RefPtr<BubbleElement> StackElement::GetBubble(const RefPtr<Element>& element) const
536 {
537     if (!element) {
538         return nullptr;
539     }
540 
541     auto bubble = DynamicCast<BubbleElement>(element);
542     if (bubble) {
543         return bubble;
544     }
545 
546     return GetBubble(element->GetFirstChild());
547 }
548 
PopDialogIfExist() const549 bool StackElement::PopDialogIfExist() const
550 {
551     auto dialogTweenElement = GetDialog(GetLastChild());
552     if (!dialogTweenElement) {
553         return false;
554     }
555     auto renderDialogTween = DynamicCast<RenderDialogTween>(dialogTweenElement->GetRenderNode());
556     if (!renderDialogTween) {
557         return false;
558     }
559     renderDialogTween->PopDialog();
560     return true;
561 }
562 
GetDialog(const RefPtr<Element> & element) const563 RefPtr<DialogTweenElement> StackElement::GetDialog(const RefPtr<Element>& element) const
564 {
565     if (!element) {
566         return nullptr;
567     }
568 
569     auto dialog = DynamicCast<DialogTweenElement>(element);
570     if (dialog) {
571         return dialog;
572     }
573 
574     return GetDialog(element->GetFirstChild());
575 }
576 
577 } // namespace OHOS::Ace
578