• 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(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
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_->AddStopListener([weak = WeakClaim(this), index]() {
133             auto refPtr = weak.Upgrade();
134             if (!refPtr) {
135                 return;
136             }
137             refPtr->InnerHideDialog(index);
138         });
139         animationController_->Play();
140     } else {
141         InnerHideDialog(index);
142     }
143 }
144 
ShowDialog(const RefPtr<StackElement> & stackElement,const Offset & leftTop,const Offset & rightBottom,bool isMenu)145 void SelectPopupComponent::ShowDialog(
146     const RefPtr<StackElement>& stackElement, const Offset& leftTop, const Offset& rightBottom, bool isMenu)
147 {
148     if (dialogShowed_) {
149         return;
150     }
151 
152     RefPtr<PositionedComponent> positioned = AceType::DynamicCast<PositionedComponent>(GetChild());
153     if (positioned) {
154         positioned->SetAutoFocus(true);
155     }
156     if (!IsTV() && isMenu) {
157         // do use center point reference for phone menu.
158         Offset center(leftTop.GetX() / 2 + rightBottom.GetX() / 2, leftTop.GetY() / 2 + rightBottom.GetY() / 2);
159         selectLeftTop_ = center;
160         selectRightBottom_ = center;
161     } else {
162         selectLeftTop_ = leftTop;
163         selectRightBottom_ = rightBottom;
164     }
165 
166     stackElement->PushComponent(AceType::Claim(this));
167     dialogShowed_ = true;
168     stackElement_ = stackElement;
169     isMenu_ = isMenu;
170 }
171 
ShowContextMenu(const Offset & offset)172 void SelectPopupComponent::ShowContextMenu(const Offset& offset)
173 {
174     LOGI("Show contextMenu, position is %{public}s", offset.ToString().c_str());
175     RefPtr<PositionedComponent> positioned = AceType::DynamicCast<PositionedComponent>(GetChild());
176     if (positioned) {
177         positioned->SetAutoFocus(true);
178     }
179     selectLeftTop_ = offset;
180     selectRightBottom_ = offset;
181     SubwindowManager::GetInstance()->ShowMenu(AceType::Claim(this));
182 }
183 
CloseContextMenu()184 void SelectPopupComponent::CloseContextMenu()
185 {
186     LOGI("Close Contextmenu.");
187     if (refreshAnimationCallback_ && animationController_) {
188         hideOption_.ClearListeners();
189         refreshAnimationCallback_(hideOption_, false);
190         animationController_->AddStopListener([]() {
191             SubwindowManager::GetInstance()->ClearMenu();
192         });
193         animationController_->Play();
194     } else {
195         SubwindowManager::GetInstance()->ClearMenu();
196     }
197 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
198     auto parentNode = node_->GetParentNode();
199     if (parentNode) {
200         parentNode->SetLeft(0);
201         parentNode->SetTop(0);
202         parentNode->SetWidth(0);
203         parentNode->SetHeight(0);
204     }
205 #endif
206     auto manager = manager_.Upgrade();
207     if (manager) {
208         for (const auto& option : options_) {
209             option->SetNode(nullptr);
210         }
211         manager->RemoveAccessibilityNodes(node_);
212         SetNode(nullptr);
213     }
214 }
215 
InitializeInnerBox(const RefPtr<ScrollComponent> & scroll)216 RefPtr<BoxComponent> SelectPopupComponent::InitializeInnerBox(const RefPtr<ScrollComponent>& scroll)
217 {
218     RefPtr<BoxComponent> innerBox = AceType::MakeRefPtr<BoxComponent>();
219     innerBox->SetDeliverMinToChild(false);
220     if (title_.empty()) {
221         innerBox->SetChild(scroll);
222     } else {
223         RefPtr<GestureListenerComponent> titleGesture = AceType::MakeRefPtr<GestureListenerComponent>();
224         EventMarker mark("-1");
225         titleGesture->SetOnClickId(mark);
226         RefPtr<BoxComponent> titleBox = AceType::MakeRefPtr<BoxComponent>();
227         titleBox->SetDeliverMinToChild(false);
228         titleBox->SetPadding(Edge(theme_->GetTitleLeftPadding().Value(), theme_->GetTitleTopPadding().Value(),
229             theme_->GetTitleRightPadding().Value(), theme_->GetTitleBottomPadding().Value(),
230             theme_->GetTitleBottomPadding().Unit()));
231         auto title = AceType::MakeRefPtr<TextComponent>(title_);
232         auto textStyle = GetTitleStyle();
233         auto isRtl = GetTextDirection() == TextDirection::RTL;
234         if (isRtl) {
235             textStyle.SetTextAlign(TextAlign::RIGHT);
236         }
237         textStyle.SetMaxLines(TITLE_TEXT_MAX_LINES);
238         textStyle.SetTextOverflow(TextOverflow::ELLIPSIS);
239         title->SetTextStyle(textStyle);
240         title->SetFocusColor(GetTitleStyle().GetTextColor());
241         titleGesture->SetChild(title);
242         titleBox->SetChild(titleGesture);
243 
244         std::list<RefPtr<Component>> childList;
245         childList.emplace_back(titleBox);
246         childList.emplace_back(scroll);
247 
248         RefPtr<ColumnComponent> outColumn =
249             AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::FLEX_START, childList);
250         innerBox->SetChild(outColumn);
251         if (isRtl) {
252             titleBox->SetAlignment(Alignment::CENTER_RIGHT);
253             outColumn->SetMainAxisAlign(FlexAlign::FLEX_END);
254             outColumn->SetCrossAxisAlign(FlexAlign::FLEX_END);
255         }
256     }
257     return innerBox;
258 }
259 
SetDefaultSelecting()260 void SelectPopupComponent::SetDefaultSelecting()
261 {
262     if (options_.empty()) {
263         return;
264     }
265 
266     bool hasSelecting = false;
267     for (const auto& option : options_) {
268         if (option->GetSelected()) {
269             hasSelecting = true;
270             break;
271         }
272     }
273 
274     if (!hasSelecting) {
275         options_[0]->SetSelected(true);
276     }
277 }
278 
Initialize(const RefPtr<AccessibilityManager> & manager)279 bool SelectPopupComponent::Initialize(const RefPtr<AccessibilityManager>& manager)
280 {
281     if (options_.size() == 0 || !manager) {
282         LOGW("select: there is no any option or accessibility manager is null.");
283         return false;
284     }
285     manager_ = manager;
286     auto id = manager->GenerateNextAccessibilityId();
287     std::list<RefPtr<Component>> children;
288     for (std::size_t index = 0; index < options_.size(); index++) {
289         options_[index]->SetIndex(index);
290         auto customizedFunc = options_[index]->GetCustomizedCallback();
291         options_[index]->SetClickedCallback(
292             [weak = WeakClaim(this), customizedFunc](std::size_t index) {
293                 if (customizedFunc) {
294                     customizedFunc();
295                 }
296                 auto refPtr = weak.Upgrade();
297                 if (!refPtr) {
298                     return;
299                 }
300                 refPtr->HandleOptionClick(index);
301             }
302         );
303         options_[index]->SetParentId(id);
304         if (options_[index]->GetVisible()) {
305             children.push_back(options_[index]);
306         }
307     }
308 
309     RefPtr<ColumnComponent> column =
310         AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::FLEX_START, children);
311     RefPtr<ScrollComponent> scroll = AceType::MakeRefPtr<ScrollComponent>(column);
312     RefPtr<BoxComponent> innerBox = InitializeInnerBox(scroll);
313     RefPtr<ClipComponent> innerClip = AceType::MakeRefPtr<ClipComponent>(innerBox);
314     innerClip->SetTopLeftRadius(Radius(ROUND_RADIUS_PHONE));
315     innerClip->SetTopRightRadius(Radius(ROUND_RADIUS_PHONE));
316     innerClip->SetBottomLeftRadius(Radius(ROUND_RADIUS_PHONE));
317     innerClip->SetBottomRightRadius(Radius(ROUND_RADIUS_PHONE));
318 
319     RefPtr<BoxComponent> box = AceType::MakeRefPtr<BoxComponent>();
320     box->SetDeliverMinToChild(false);
321     if (!IsTV()) {
322         RefPtr<Decoration> back = AceType::MakeRefPtr<Decoration>();
323         back->SetBackgroundColor(theme_->GetBackgroundColor());
324         back->SetBorderRadius(Radius(theme_->GetPopupRRectSize()));
325         back->AddShadow(ShadowConfig::DefaultShadowM);
326         box->SetBackDecoration(back);
327         box->SetPadding(Edge(IN_OUT_BOX_INTERVAL));
328     }
329     box->SetChild(innerClip);
330 
331     auto tweenId = TweenComponent::AllocTweenComponentId();
332     RefPtr<TweenComponent> tween = AceType::MakeRefPtr<TweenComponent>(tweenId, tweenId);
333     tween->SetShadow(ShadowConfig::DefaultShadowM);
334     tween->SetIsFirstFrameShow(false);
335     tween->SetAnimationOperation(AnimationOperation::PLAY);
336 
337 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
338     auto popupNode = manager->CreateAccessibilityNode("select-popup", id, GetSelectPopupId(), -1);
339     SetNode(popupNode);
340 #else
341     SetNode(manager->CreateSpecializedNode("select-popup", id, -1));
342 #endif
343     if (isFullScreen_) {
344         RefPtr<FocusCollaborationComponent> collaboration = AceType::MakeRefPtr<FocusCollaborationComponent>();
345         collaboration->InsertChild(0, box);
346         tween->SetChild(collaboration);
347         RefPtr<PositionedComponent> positioned = AceType::MakeRefPtr<PositionedComponent>(tween);
348         SetChild(positioned);
349     } else {
350         tween->SetChild(box);
351         SetChild(tween);
352     }
353     return true;
354 }
355 
HandleOptionClick(std::size_t index)356 void SelectPopupComponent::HandleOptionClick(std::size_t index)
357 {
358     HideDialog(index);
359 }
360 
HandleOptionModify(std::size_t index)361 void SelectPopupComponent::HandleOptionModify(std::size_t index)
362 {
363     if (!optionModifiedCallback_) {
364         LOGE("modify callback of select popup component is null.");
365         return;
366     }
367     RefPtr<OptionComponent> selectedOption;
368     RefPtr<OptionComponent> modifiedOption;
369     for (const auto& option : options_) {
370         if (option->GetSelected()) {
371             selectedOption = option;
372         }
373         if (option->GetIndex() == index) {
374             modifiedOption = option;
375         }
376     }
377     if (!modifiedOption) {
378         LOGE("modify option is null of select popup component.");
379         return;
380     }
381     if (!(modifiedOption == selectedOption || modifiedOption->GetIndex() == 0)) {
382         LOGE("no need modify callback of select popup component.");
383         return;
384     }
385     optionModifiedCallback_(index);
386 }
387 
AppendSelectOption(const RefPtr<OptionComponent> & selectOption)388 void SelectPopupComponent::AppendSelectOption(const RefPtr<OptionComponent>& selectOption)
389 {
390     if (selectOption) {
391         selectOption->SetIndex(options_.size());
392         options_.emplace_back(selectOption);
393         auto weak = AceType::WeakClaim(this);
394         selectOption->SetModifiedCallback([weak](std::size_t index) {
395             auto refPtr = weak.Upgrade();
396             if (refPtr) {
397                 refPtr->HandleOptionModify(index);
398             }
399         });
400     } else {
401         LOGE("select: input select option component is null.");
402     }
403 }
404 
RemoveSelectOption(const RefPtr<OptionComponent> & selectOption)405 void SelectPopupComponent::RemoveSelectOption(const RefPtr<OptionComponent>& selectOption)
406 {
407     if (selectOption) {
408         auto iter = std::remove(options_.begin(), options_.end(), selectOption);
409         if (iter != options_.end()) {
410             options_.erase(iter);
411             selectOption->SetIndex(SELECT_INVALID_INDEX);
412         }
413     } else {
414         LOGE("select: input select option component is null.");
415     }
416     for (uint32_t index = 0; index < options_.size(); ++index) {
417         options_[index]->SetIndex(index);
418     }
419 }
420 
InsertSelectOption(const RefPtr<OptionComponent> & selectOption,uint32_t index)421 void SelectPopupComponent::InsertSelectOption(const RefPtr<OptionComponent>& selectOption, uint32_t index)
422 {
423     if (!selectOption) {
424         return;
425     }
426     if (index >= options_.size()) {
427         AppendSelectOption(selectOption);
428         return;
429     }
430     options_.insert(options_.begin() + index, selectOption);
431     for (uint32_t index = 0; index < options_.size(); ++index) {
432         options_[index]->SetIndex(index);
433     }
434     auto weak = AceType::WeakClaim(this);
435     selectOption->SetModifiedCallback([weak](std::size_t index) {
436         auto refPtr = weak.Upgrade();
437         if (refPtr) {
438             refPtr->HandleOptionModify(index);
439         }
440     });
441 }
442 
443 } // namespace OHOS::Ace
444