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