• 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/select_popup/select_popup_component.h"
17 
18 #include "base/subwindow/subwindow_manager.h"
19 #include "base/utils/string_utils.h"
20 #include "core/components/clip/clip_component.h"
21 #include "core/components/common/properties/shadow_config.h"
22 #include "core/components/focus_collaboration/focus_collaboration_component.h"
23 #include "core/components/gesture_listener/gesture_listener_component.h"
24 #include "core/components/menu/menu_component.h"
25 #include "core/components/positioned/positioned_component.h"
26 #include "core/components/select_popup/render_select_popup.h"
27 #include "core/components/select_popup/select_popup_element.h"
28 #include "core/components/tween/tween_component.h"
29 
30 namespace OHOS::Ace {
31 namespace {
32 
33 constexpr uint32_t TITLE_TEXT_MAX_LINES = 1;
34 const Dimension ROUND_RADIUS_PHONE = 12.0_vp;
35 const Dimension IN_OUT_BOX_INTERVAL = 4.0_vp;
36 
37 } // namespace
38 
SelectPopupComponent()39 SelectPopupComponent::SelectPopupComponent() : SoleChildComponent() {}
40 
InitTheme(const RefPtr<ThemeManager> & themeManager)41 void SelectPopupComponent::InitTheme(const RefPtr<ThemeManager>& themeManager)
42 {
43     if (!themeManager) {
44         return;
45     }
46     auto selectTheme = themeManager->GetTheme<SelectTheme>();
47     if (!selectTheme) {
48         return;
49     }
50     theme_ = selectTheme->clone();
51 }
52 
CreateRenderNode()53 RefPtr<RenderNode> SelectPopupComponent::CreateRenderNode()
54 {
55     return RenderSelectPopup::Create();
56 }
57 
CreateElement()58 RefPtr<Element> SelectPopupComponent::CreateElement()
59 {
60     return AceType::MakeRefPtr<SelectPopupElement>();
61 }
62 
GetSelectOption(std::size_t index) const63 RefPtr<OptionComponent> SelectPopupComponent::GetSelectOption(std::size_t index) const
64 {
65     if (index >= options_.size()) {
66         LOGE("select: input index is too large.");
67         return nullptr;
68     }
69 
70     return options_[index];
71 }
72 
InnerHideDialog(uint32_t index)73 void SelectPopupComponent::InnerHideDialog(uint32_t index)
74 {
75     if (!dialogShowed_) {
76         return;
77     }
78 
79     if (!stackElement_) {
80         LOGE("stored stack element is null.");
81         return;
82     }
83 
84     stackElement_->PopMenu();
85     dialogShowed_ = false;
86     stackElement_ = nullptr;
87 
88     if (index == SELECT_INVALID_INDEX) {
89         if (popupCanceledCallback_) {
90             popupCanceledCallback_();
91         } else {
92             LOGW("popup cancel callback is null.");
93         }
94     } else {
95         if (optionClickedCallback_) {
96             optionClickedCallback_(index);
97         } else {
98             LOGW("option clicked callback is null.");
99         }
100     }
101 
102 #if defined(PREVIEW)
103     if (node_) {
104         auto parentNode = node_->GetParentNode();
105         if (parentNode) {
106             parentNode->SetLeft(0);
107             parentNode->SetTop(0);
108             parentNode->SetWidth(0);
109             parentNode->SetHeight(0);
110         }
111     }
112 #endif
113     auto manager = manager_.Upgrade();
114     if (manager) {
115         for (const auto& option : options_) {
116             option->SetNode(nullptr);
117         }
118         manager->RemoveAccessibilityNodes(node_);
119         SetNode(nullptr);
120     }
121 }
122 
HideDialog(uint32_t index)123 void SelectPopupComponent::HideDialog(uint32_t index)
124 {
125     if (!dialogShowed_) {
126         return;
127     }
128 
129     if (refreshAnimationCallback_ && animationController_) {
130         hideOption_.ClearListeners();
131         refreshAnimationCallback_(hideOption_, false);
132         animationController_->ClearStopListeners();
133         animationController_->AddStopListener([weak = WeakClaim(this), index]() {
134             auto refPtr = weak.Upgrade();
135             if (!refPtr) {
136                 return;
137             }
138             refPtr->InnerHideDialog(index);
139         });
140         animationController_->Play();
141     } else {
142         InnerHideDialog(index);
143     }
144 }
145 
ShowDialog(const RefPtr<StackElement> & stackElement,const Offset & leftTop,const Offset & rightBottom,bool isMenu)146 void SelectPopupComponent::ShowDialog(
147     const RefPtr<StackElement>& stackElement, const Offset& leftTop, const Offset& rightBottom, bool isMenu)
148 {
149     if (dialogShowed_) {
150         return;
151     }
152 
153     RefPtr<PositionedComponent> positioned = AceType::DynamicCast<PositionedComponent>(GetChild());
154     if (positioned) {
155         positioned->SetAutoFocus(true);
156     }
157     if (!IsTV() && isMenu) {
158         // do use center point reference for phone menu.
159         Offset center(leftTop.GetX() / 2 + rightBottom.GetX() / 2, leftTop.GetY() / 2 + rightBottom.GetY() / 2);
160         selectLeftTop_ = center;
161         selectRightBottom_ = center;
162     } else {
163         selectLeftTop_ = leftTop;
164         selectRightBottom_ = rightBottom;
165     }
166 
167     stackElement->PushComponent(AceType::Claim(this));
168     dialogShowed_ = true;
169     stackElement_ = stackElement;
170     isMenu_ = isMenu;
171 }
172 
ShowContextMenu(const Offset & offset)173 void SelectPopupComponent::ShowContextMenu(const Offset& offset)
174 {
175     LOGI("Show contextMenu, position is %{public}s", offset.ToString().c_str());
176     RefPtr<PositionedComponent> positioned = AceType::DynamicCast<PositionedComponent>(GetChild());
177     if (positioned) {
178         positioned->SetAutoFocus(true);
179     }
180     selectLeftTop_ = offset;
181     selectRightBottom_ = offset;
182     SubwindowManager::GetInstance()->ShowMenu(AceType::Claim(this));
183 }
184 
CloseContextMenu()185 void SelectPopupComponent::CloseContextMenu()
186 {
187     LOGI("Close Contextmenu.");
188     if (refreshAnimationCallback_ && animationController_) {
189         hideOption_.ClearListeners();
190         refreshAnimationCallback_(hideOption_, false);
191         animationController_->ClearStopListeners();
192         animationController_->AddStopListener([]() {
193             SubwindowManager::GetInstance()->ClearMenu();
194         });
195         animationController_->Play();
196     } else {
197         SubwindowManager::GetInstance()->ClearMenu();
198     }
199 #if defined(PREVIEW)
200     auto parentNode = node_->GetParentNode();
201     if (parentNode) {
202         parentNode->SetLeft(0);
203         parentNode->SetTop(0);
204         parentNode->SetWidth(0);
205         parentNode->SetHeight(0);
206     }
207 #endif
208     auto manager = manager_.Upgrade();
209     if (manager) {
210         for (const auto& option : options_) {
211             option->SetNode(nullptr);
212         }
213         manager->RemoveAccessibilityNodes(node_);
214         SetNode(nullptr);
215     }
216 }
217 
InitializeInnerBox(const RefPtr<ScrollComponent> & scroll)218 RefPtr<BoxComponent> SelectPopupComponent::InitializeInnerBox(const RefPtr<ScrollComponent>& scroll)
219 {
220     RefPtr<BoxComponent> innerBox = AceType::MakeRefPtr<BoxComponent>();
221     innerBox->SetDeliverMinToChild(false);
222     if (title_.empty()) {
223         innerBox->SetChild(scroll);
224     } else {
225         RefPtr<GestureListenerComponent> titleGesture = AceType::MakeRefPtr<GestureListenerComponent>();
226         EventMarker mark("-1");
227         titleGesture->SetOnClickId(mark);
228         RefPtr<BoxComponent> titleBox = AceType::MakeRefPtr<BoxComponent>();
229         titleBox->SetDeliverMinToChild(false);
230         titleBox->SetPadding(Edge(theme_->GetTitleLeftPadding().Value(), theme_->GetTitleTopPadding().Value(),
231             theme_->GetTitleRightPadding().Value(), theme_->GetTitleBottomPadding().Value(),
232             theme_->GetTitleBottomPadding().Unit()));
233         auto title = AceType::MakeRefPtr<TextComponent>(title_);
234         auto textStyle = GetTitleStyle();
235         auto isRtl = GetTextDirection() == TextDirection::RTL;
236         if (isRtl) {
237             textStyle.SetTextAlign(TextAlign::RIGHT);
238         }
239         textStyle.SetMaxLines(TITLE_TEXT_MAX_LINES);
240         textStyle.SetTextOverflow(TextOverflow::ELLIPSIS);
241         title->SetTextStyle(textStyle);
242         title->SetFocusColor(GetTitleStyle().GetTextColor());
243         titleGesture->SetChild(title);
244         titleBox->SetChild(titleGesture);
245 
246         std::list<RefPtr<Component>> childList;
247         childList.emplace_back(titleBox);
248         childList.emplace_back(scroll);
249 
250         RefPtr<ColumnComponent> outColumn =
251             AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::FLEX_START, childList);
252         innerBox->SetChild(outColumn);
253         if (isRtl) {
254             titleBox->SetAlignment(Alignment::CENTER_RIGHT);
255             outColumn->SetMainAxisAlign(FlexAlign::FLEX_END);
256             outColumn->SetCrossAxisAlign(FlexAlign::FLEX_END);
257         }
258     }
259     return innerBox;
260 }
261 
SetDefaultSelecting()262 void SelectPopupComponent::SetDefaultSelecting()
263 {
264     if (options_.empty()) {
265         return;
266     }
267 
268     bool hasSelecting = false;
269     for (const auto& option : options_) {
270         if (option->GetSelected()) {
271             hasSelecting = true;
272             break;
273         }
274     }
275 
276     if (!hasSelecting) {
277         options_[0]->SetSelected(true);
278     }
279 }
280 
Initialize(const RefPtr<AccessibilityManager> & manager)281 bool SelectPopupComponent::Initialize(const RefPtr<AccessibilityManager>& manager)
282 {
283     if (options_.size() == 0 || !manager) {
284         LOGW("select: there is no any option or accessibility manager is null.");
285         return false;
286     }
287     manager_ = manager;
288     auto id = manager->GenerateNextAccessibilityId();
289     std::list<RefPtr<Component>> children;
290     for (std::size_t index = 0; index < options_.size(); index++) {
291         options_[index]->SetIndex(index);
292         auto customizedFunc = options_[index]->GetCustomizedCallback();
293         options_[index]->SetClickedCallback(
294             [weak = WeakClaim(this), customizedFunc](std::size_t index) {
295                 if (customizedFunc) {
296                     customizedFunc();
297                 }
298                 auto refPtr = weak.Upgrade();
299                 if (!refPtr) {
300                     return;
301                 }
302                 refPtr->HandleOptionClick(index);
303             }
304         );
305         options_[index]->SetParentId(id);
306         if (options_[index]->GetVisible()) {
307             children.push_back(options_[index]);
308         }
309     }
310 
311     RefPtr<ColumnComponent> column =
312         AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::FLEX_START, children);
313     RefPtr<ScrollComponent> scroll = AceType::MakeRefPtr<ScrollComponent>(column);
314     RefPtr<BoxComponent> innerBox = InitializeInnerBox(scroll);
315     RefPtr<ClipComponent> innerClip = AceType::MakeRefPtr<ClipComponent>(innerBox);
316     innerClip->SetTopLeftRadius(Radius(ROUND_RADIUS_PHONE));
317     innerClip->SetTopRightRadius(Radius(ROUND_RADIUS_PHONE));
318     innerClip->SetBottomLeftRadius(Radius(ROUND_RADIUS_PHONE));
319     innerClip->SetBottomRightRadius(Radius(ROUND_RADIUS_PHONE));
320     RefPtr<BoxComponent> box = AceType::MakeRefPtr<BoxComponent>();
321     box->SetEnableDebugBoundary(true);
322     box->SetDeliverMinToChild(false);
323     if (!IsTV()) {
324         RefPtr<Decoration> back = AceType::MakeRefPtr<Decoration>();
325         back->SetBackgroundColor(theme_->GetBackgroundColor());
326         back->SetBorderRadius(Radius(theme_->GetPopupRRectSize()));
327         back->AddShadow(ShadowConfig::DefaultShadowM);
328         box->SetBackDecoration(back);
329         box->SetPadding(isCustomMenu_ ? Edge() : Edge(IN_OUT_BOX_INTERVAL));
330     }
331     box->SetChild(innerBox);
332 
333     auto tweenId = TweenComponent::AllocTweenComponentId();
334     RefPtr<TweenComponent> tween = AceType::MakeRefPtr<TweenComponent>(tweenId, tweenId);
335     tween->SetShadow(ShadowConfig::DefaultShadowM);
336     tween->SetIsFirstFrameShow(false);
337     tween->SetAnimationOperation(AnimationOperation::PLAY);
338 
339 #if defined(PREVIEW)
340     auto popupNode = manager->CreateAccessibilityNode("select-popup", id, GetSelectPopupId(), -1);
341     SetNode(popupNode);
342 #else
343     SetNode(manager->CreateSpecializedNode("select-popup", id, -1));
344 #endif
345     if (isFullScreen_) {
346         RefPtr<FocusCollaborationComponent> collaboration = AceType::MakeRefPtr<FocusCollaborationComponent>();
347         collaboration->InsertChild(0, box);
348         tween->SetChild(collaboration);
349         RefPtr<PositionedComponent> positioned = AceType::MakeRefPtr<PositionedComponent>(tween);
350         SetChild(positioned);
351     } else {
352         tween->SetChild(box);
353         SetChild(tween);
354     }
355     return true;
356 }
357 
HandleOptionClick(std::size_t index)358 void SelectPopupComponent::HandleOptionClick(std::size_t index)
359 {
360     HideDialog(index);
361 }
362 
HandleOptionModify(std::size_t index)363 void SelectPopupComponent::HandleOptionModify(std::size_t index)
364 {
365     if (!optionModifiedCallback_) {
366         LOGE("modify callback of select popup component is null.");
367         return;
368     }
369     RefPtr<OptionComponent> selectedOption;
370     RefPtr<OptionComponent> modifiedOption;
371     for (const auto& option : options_) {
372         if (option->GetSelected()) {
373             selectedOption = option;
374         }
375         if (option->GetIndex() == index) {
376             modifiedOption = option;
377         }
378     }
379     if (!modifiedOption) {
380         LOGE("modify option is null of select popup component.");
381         return;
382     }
383     if (!(modifiedOption == selectedOption || modifiedOption->GetIndex() == 0)) {
384         LOGE("no need modify callback of select popup component.");
385         return;
386     }
387     optionModifiedCallback_(index);
388 }
389 
AppendSelectOption(const RefPtr<OptionComponent> & selectOption)390 void SelectPopupComponent::AppendSelectOption(const RefPtr<OptionComponent>& selectOption)
391 {
392     if (selectOption) {
393         selectOption->SetIndex(options_.size());
394         options_.emplace_back(selectOption);
395         auto weak = AceType::WeakClaim(this);
396         selectOption->SetModifiedCallback([weak](std::size_t index) {
397             auto refPtr = weak.Upgrade();
398             if (refPtr) {
399                 refPtr->HandleOptionModify(index);
400             }
401         });
402     } else {
403         LOGE("select: input select option component is null.");
404     }
405 }
406 
RemoveSelectOption(const RefPtr<OptionComponent> & selectOption)407 void SelectPopupComponent::RemoveSelectOption(const RefPtr<OptionComponent>& selectOption)
408 {
409     if (selectOption) {
410         auto iter = std::remove(options_.begin(), options_.end(), selectOption);
411         if (iter != options_.end()) {
412             options_.erase(iter);
413             selectOption->SetIndex(SELECT_INVALID_INDEX);
414         }
415     } else {
416         LOGE("select: input select option component is null.");
417     }
418     for (uint32_t index = 0; index < options_.size(); ++index) {
419         options_[index]->SetIndex(index);
420     }
421 }
422 
InsertSelectOption(const RefPtr<OptionComponent> & selectOption,uint32_t index)423 void SelectPopupComponent::InsertSelectOption(const RefPtr<OptionComponent>& selectOption, uint32_t index)
424 {
425     if (!selectOption) {
426         return;
427     }
428     if (index >= options_.size()) {
429         AppendSelectOption(selectOption);
430         return;
431     }
432     options_.insert(options_.begin() + index, selectOption);
433     for (uint32_t index = 0; index < options_.size(); ++index) {
434         options_[index]->SetIndex(index);
435     }
436     auto weak = AceType::WeakClaim(this);
437     selectOption->SetModifiedCallback([weak](std::size_t index) {
438         auto refPtr = weak.Upgrade();
439         if (refPtr) {
440             refPtr->HandleOptionModify(index);
441         }
442     });
443 }
444 
445 } // namespace OHOS::Ace
446