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