1 /*
2 * Copyright (c) 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_ng/pattern/select_overlay/select_overlay_node.h"
17
18 #include <cstdint>
19 #include <functional>
20 #include <memory>
21 #include <optional>
22 #include <securec.h>
23
24 #include "base/geometry/dimension.h"
25 #include "base/geometry/ng/offset_t.h"
26 #include "base/i18n/localization.h"
27 #include "base/utils/utils.h"
28 #include "core/animation/curves.h"
29 #include "core/components/common/layout/constants.h"
30 #include "core/components/common/properties/color.h"
31 #include "core/components/common/properties/shadow_config.h"
32 #include "core/components/custom_paint/rosen_render_custom_paint.h"
33 #include "core/components/text_overlay/text_overlay_theme.h"
34 #include "core/components_ng/base/frame_node.h"
35 #include "core/components_ng/base/view_stack_processor.h"
36 #include "core/components_ng/event/event_hub.h"
37 #include "core/components_ng/pattern/button/button_pattern.h"
38 #include "core/components_ng/pattern/image/image_pattern.h"
39 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
40 #include "core/components_ng/pattern/menu/menu_pattern.h"
41 #include "core/components_ng/pattern/menu/menu_view.h"
42 #include "core/components_ng/pattern/select_overlay/select_overlay_pattern.h"
43 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
44 #include "core/components_ng/pattern/text/text_pattern.h"
45 #include "core/components_ng/property/calc_length.h"
46 #include "core/components_ng/property/property.h"
47 #include "core/gestures/gesture_info.h"
48 #include "core/pipeline/base/element_register.h"
49 #include "core/pipeline_ng/pipeline_context.h"
50
51 namespace OHOS::Ace::NG {
52 namespace {
53 constexpr char BUTTON_COPY_ALL[] = "textoverlay.select_all";
54 constexpr char BUTTON_CUT[] = "textoverlay.cut";
55 constexpr char BUTTON_COPY[] = "textoverlay.copy";
56 constexpr char BUTTON_PASTE[] = "textoverlay.paste";
57 constexpr char BUTTON_SHARE[] = "textoverlay.share";
58 constexpr char BUTTON_TRANSLATE[] = "textoverlay.translate";
59 constexpr char BUTTON_SEARCH[] = "textoverlay.search";
60
61 constexpr int32_t OPTION_INDEX_CUT = 0;
62 constexpr int32_t OPTION_INDEX_COPY = 1;
63 constexpr int32_t OPTION_INDEX_PASTE = 2;
64 constexpr int32_t OPTION_INDEX_COPY_ALL = 3;
65 constexpr int32_t OPTION_INDEX_SHARE = 4;
66 constexpr int32_t OPTION_INDEX_TRANSLATE = 5;
67 constexpr int32_t OPTION_INDEX_SEARCH = 6;
68 constexpr int32_t ANIMATION_DURATION1 = 350;
69 constexpr int32_t ANIMATION_DURATION2 = 150;
70
71 constexpr Dimension MORE_MENU_TRANSLATE = -7.5_vp;
72 constexpr Dimension MAX_DIAMETER = 3.5_vp;
73 constexpr Dimension MIN_DIAMETER = 1.5_vp;
74 constexpr Dimension MIN_ARROWHEAD_DIAMETER = 2.0_vp;
75 constexpr Dimension ANIMATION_TEXT_OFFSET = 12.0_vp;
76
BuildButton(const std::string & data,const std::function<void ()> & callback,int32_t overlayId,float & buttonWidth,bool isSelectAll=false)77 RefPtr<FrameNode> BuildButton(const std::string& data, const std::function<void()>& callback, int32_t overlayId,
78 float& buttonWidth, bool isSelectAll = false)
79 {
80 auto button = FrameNode::GetOrCreateFrameNode("SelectMenuButton", ElementRegister::GetInstance()->MakeUniqueId(),
81 []() { return AceType::MakeRefPtr<ButtonPattern>(); });
82 auto text = FrameNode::GetOrCreateFrameNode("SelectMenuButtonText", ElementRegister::GetInstance()->MakeUniqueId(),
83 []() { return AceType::MakeRefPtr<TextPattern>(); });
84 auto textLayoutProperty = text->GetLayoutProperty<TextLayoutProperty>();
85 CHECK_NULL_RETURN(textLayoutProperty, button);
86 textLayoutProperty->UpdateContent(data);
87 text->MountToParent(button);
88 auto pipeline = PipelineContext::GetCurrentContext();
89 CHECK_NULL_RETURN(pipeline, button);
90 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
91 CHECK_NULL_RETURN(textOverlayTheme, button);
92 auto textStyle = textOverlayTheme->GetMenuButtonTextStyle();
93 textLayoutProperty->UpdateFontSize(textStyle.GetFontSize());
94 textLayoutProperty->UpdateFontWeight(textStyle.GetFontWeight());
95 textLayoutProperty->UpdateMaxLines(1);
96 if (callback) {
97 textLayoutProperty->UpdateTextColor(textStyle.GetTextColor());
98 } else {
99 textLayoutProperty->UpdateTextColor(
100 textStyle.GetTextColor().BlendOpacity(textOverlayTheme->GetAlphaDisabled()));
101 }
102 text->MarkModifyDone();
103
104 auto buttonLayoutProperty = button->GetLayoutProperty<ButtonLayoutProperty>();
105 CHECK_NULL_RETURN(buttonLayoutProperty, button);
106 const auto& padding = textOverlayTheme->GetMenuButtonPadding();
107 auto left = CalcLength(padding.Left().ConvertToPx());
108 auto right = CalcLength(padding.Right().ConvertToPx());
109 auto top = CalcLength(padding.Top().ConvertToPx());
110 auto bottom = CalcLength(padding.Bottom().ConvertToPx());
111 buttonLayoutProperty->UpdatePadding({ left, right, top, bottom });
112 MeasureContext content;
113 content.textContent = data;
114 content.fontSize = textStyle.GetFontSize();
115 auto fontweight = StringUtils::FontWeightToString(textStyle.GetFontWeight());
116 content.fontWeight = fontweight;
117 #ifdef ENABLE_ROSEN_BACKEND
118 buttonWidth = static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Width());
119 #else
120 buttonWidth = 0.0f;
121 #endif
122 // Calculate the width of default option include button padding.
123 buttonWidth = buttonWidth + padding.Left().ConvertToPx() + padding.Right().ConvertToPx();
124 buttonLayoutProperty->UpdateUserDefinedIdealSize(
125 { CalcLength(buttonWidth), CalcLength(textOverlayTheme->GetMenuButtonHeight()) });
126 buttonLayoutProperty->UpdateFlexShrink(0);
127 button->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
128
129 if (callback) {
130 button->GetOrCreateGestureEventHub()->SetUserOnClick(
131 [callback, overlayId, isSelectAll](GestureEvent& /*info*/) {
132 auto pipeline = PipelineContext::GetCurrentContext();
133 CHECK_NULL_VOID(pipeline);
134 auto overlayManager = pipeline->GetSelectOverlayManager();
135 CHECK_NULL_VOID(overlayManager);
136 auto selectOverlay = overlayManager->GetSelectOverlayNode(overlayId);
137 CHECK_NULL_VOID(selectOverlay);
138 auto isDoingAnimation = selectOverlay->GetAnimationStatus();
139 CHECK_NULL_VOID(!isDoingAnimation);
140 auto isExtensionMenu = selectOverlay->GetIsExtensionMenu();
141 CHECK_NULL_VOID(!isExtensionMenu);
142 if (callback) {
143 callback();
144 }
145 // close text overlay.
146 if (!isSelectAll) {
147 overlayManager->DestroySelectOverlay(overlayId, true);
148 }
149 });
150 } else {
151 auto buttonEventHub = button->GetEventHub<OptionEventHub>();
152 CHECK_NULL_RETURN(buttonEventHub, button);
153 buttonEventHub->SetEnabled(false);
154 }
155 button->MarkModifyDone();
156 return button;
157 }
158
BuildButton(const std::string & data,const std::function<void (std::string &)> & callback,int32_t overlayId,float & contentWidth)159 RefPtr<FrameNode> BuildButton(
160 const std::string& data, const std::function<void(std::string&)>& callback, int32_t overlayId, float& contentWidth)
161 {
162 auto button = FrameNode::GetOrCreateFrameNode("SelectMenuButton", ElementRegister::GetInstance()->MakeUniqueId(),
163 []() { return AceType::MakeRefPtr<ButtonPattern>(); });
164 auto text = FrameNode::GetOrCreateFrameNode("SelectMenuButtonText", ElementRegister::GetInstance()->MakeUniqueId(),
165 []() { return AceType::MakeRefPtr<TextPattern>(); });
166
167 // Update text property and mount to button.
168 auto textLayoutProperty = text->GetLayoutProperty<TextLayoutProperty>();
169 CHECK_NULL_RETURN(textLayoutProperty, button);
170 textLayoutProperty->UpdateContent(data);
171 text->MountToParent(button);
172 auto pipeline = PipelineContext::GetCurrentContext();
173 CHECK_NULL_RETURN(pipeline, button);
174 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
175 CHECK_NULL_RETURN(textOverlayTheme, button);
176 auto textStyle = textOverlayTheme->GetMenuButtonTextStyle();
177 textLayoutProperty->UpdateFontSize(textStyle.GetFontSize());
178 textLayoutProperty->UpdateTextColor(textStyle.GetTextColor());
179 textLayoutProperty->UpdateFontWeight(textStyle.GetFontWeight());
180 text->MarkModifyDone();
181
182 // Calculate the width of entension option include button padding.
183 MeasureContext content;
184 content.textContent = data;
185 content.fontSize = textStyle.GetFontSize();
186 auto fontweight = StringUtils::FontWeightToString(textStyle.GetFontWeight());
187 content.fontWeight = fontweight;
188 #ifdef ENABLE_ROSEN_BACKEND
189 contentWidth = static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Width());
190 #else
191 contentWidth = 0.0f;
192 #endif
193 const auto& padding = textOverlayTheme->GetMenuButtonPadding();
194 auto left = CalcLength(padding.Left().ConvertToPx());
195 auto right = CalcLength(padding.Right().ConvertToPx());
196 auto top = CalcLength(padding.Top().ConvertToPx());
197 auto bottom = CalcLength(padding.Bottom().ConvertToPx());
198 contentWidth = contentWidth + padding.Left().ConvertToPx() + padding.Right().ConvertToPx();
199
200 // Update button property.
201 auto buttonLayoutProperty = button->GetLayoutProperty<ButtonLayoutProperty>();
202 CHECK_NULL_RETURN(buttonLayoutProperty, button);
203 buttonLayoutProperty->UpdatePadding({ left, right, top, bottom });
204 buttonLayoutProperty->UpdateUserDefinedIdealSize(
205 { CalcLength(contentWidth), CalcLength(textOverlayTheme->GetMenuButtonHeight()) });
206 buttonLayoutProperty->UpdateFlexShrink(0);
207 button->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
208 button->GetOrCreateGestureEventHub()->SetUserOnClick([callback, overlayId](GestureEvent& /*info*/) {
209 auto pipeline = PipelineContext::GetCurrentContext();
210 CHECK_NULL_VOID(pipeline);
211 auto overlayManager = pipeline->GetSelectOverlayManager();
212 CHECK_NULL_VOID(overlayManager);
213
214 auto selectOverlay = overlayManager->GetSelectOverlayNode(overlayId);
215 CHECK_NULL_VOID(selectOverlay);
216 auto pattern = selectOverlay->GetPattern<SelectOverlayPattern>();
217 auto selectInfo = pattern->GetSelectInfo();
218 if (callback) {
219 callback(selectInfo);
220 }
221 // close text overlay.
222 overlayManager->DestroySelectOverlay(overlayId);
223 });
224 button->MarkModifyDone();
225 return button;
226 }
227
BuildMoreOrBackButton(int32_t overlayId,bool isMoreButton)228 RefPtr<FrameNode> BuildMoreOrBackButton(int32_t overlayId, bool isMoreButton)
229 {
230 auto button = FrameNode::GetOrCreateFrameNode("SelectMoreOrBackButton",
231 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ButtonPattern>(); });
232 auto pipeline = PipelineContext::GetCurrentContext();
233 CHECK_NULL_RETURN(pipeline, button);
234 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
235 CHECK_NULL_RETURN(textOverlayTheme, button);
236
237 // Update property.
238 auto buttonLayoutProperty = button->GetLayoutProperty<ButtonLayoutProperty>();
239 CHECK_NULL_RETURN(buttonLayoutProperty, button);
240
241 const auto& padding = textOverlayTheme->GetMenuPadding();
242
243 auto sideWidth = CalcLength(textOverlayTheme->GetMenuToolbarHeight().ConvertToPx() - padding.Top().ConvertToPx() -
244 padding.Bottom().ConvertToPx());
245 buttonLayoutProperty->UpdateUserDefinedIdealSize({ sideWidth, sideWidth });
246
247 if (!isMoreButton) {
248 auto left = CalcLength(padding.Left().ConvertToPx());
249 auto right = CalcLength(padding.Right().ConvertToPx());
250 auto top = CalcLength(padding.Top().ConvertToPx());
251 auto bottom = CalcLength(padding.Bottom().ConvertToPx());
252 buttonLayoutProperty->UpdateMargin({ left, right, top, bottom });
253 buttonLayoutProperty->UpdateVisibility(VisibleType::GONE);
254 }
255
256 button->GetRenderContext()->UpdateBackgroundColor(Color::TRANSPARENT);
257 button->GetOrCreateGestureEventHub()->SetUserOnClick([overlayId, isMore = isMoreButton](GestureEvent& /*info*/) {
258 auto pipeline = PipelineContext::GetCurrentContext();
259 CHECK_NULL_VOID(pipeline);
260 auto overlayManager = pipeline->GetSelectOverlayManager();
261 CHECK_NULL_VOID(overlayManager);
262 auto selectOverlay = overlayManager->GetSelectOverlayNode(overlayId);
263 CHECK_NULL_VOID(selectOverlay);
264 // When click button , change to extensionMenu or change to the default menu(selectMenu_).
265 selectOverlay->MoreOrBackAnimation(isMore);
266 });
267 button->MarkModifyDone();
268 return button;
269 }
270
GetPageOffset()271 OffsetF GetPageOffset()
272 {
273 auto pipeline = PipelineContext::GetCurrentContext();
274 CHECK_NULL_RETURN(pipeline, OffsetF());
275 auto stageManager = pipeline->GetStageManager();
276 CHECK_NULL_RETURN(stageManager, OffsetF());
277 auto page = stageManager->GetLastPage();
278 CHECK_NULL_RETURN(page, OffsetF());
279 return page->GetOffsetRelativeToWindow();
280 }
281
GetOptionsParams(const std::shared_ptr<SelectOverlayInfo> & info)282 std::vector<OptionParam> GetOptionsParams(const std::shared_ptr<SelectOverlayInfo>& info)
283 {
284 std::vector<OptionParam> params;
285 params.emplace_back(Localization::GetInstance()->GetEntryLetters(BUTTON_CUT), info->menuCallback.onCut);
286 params.emplace_back(Localization::GetInstance()->GetEntryLetters(BUTTON_COPY), info->menuCallback.onCopy);
287 params.emplace_back(Localization::GetInstance()->GetEntryLetters(BUTTON_PASTE), info->menuCallback.onPaste);
288 params.emplace_back(Localization::GetInstance()->GetEntryLetters(BUTTON_COPY_ALL), info->menuCallback.onSelectAll);
289 return params;
290 }
291
SetOptionDisable(const RefPtr<FrameNode> & option)292 void SetOptionDisable(const RefPtr<FrameNode>& option)
293 {
294 CHECK_NULL_VOID(option);
295 auto optionEventHub = option->GetEventHub<OptionEventHub>();
296 CHECK_NULL_VOID(optionEventHub);
297 optionEventHub->SetEnabled(false);
298 option->MarkModifyDone();
299 }
300
SetOptionsAction(const std::shared_ptr<SelectOverlayInfo> & info,const std::vector<RefPtr<FrameNode>> & options)301 void SetOptionsAction(const std::shared_ptr<SelectOverlayInfo>& info, const std::vector<RefPtr<FrameNode>>& options)
302 {
303 if (options.empty()) {
304 return;
305 }
306 if (!info->menuInfo.showCut) {
307 SetOptionDisable(options[OPTION_INDEX_CUT]);
308 }
309 if (!info->menuInfo.showCopy) {
310 SetOptionDisable(options[OPTION_INDEX_COPY]);
311 }
312 if (!info->menuInfo.showPaste) {
313 SetOptionDisable(options[OPTION_INDEX_PASTE]);
314 }
315 if (!info->menuInfo.showCopyAll) {
316 SetOptionDisable(options[OPTION_INDEX_COPY_ALL]);
317 }
318 }
319
SetOptionsAction(const std::vector<RefPtr<FrameNode>> & options)320 void SetOptionsAction(const std::vector<RefPtr<FrameNode>>& options)
321 {
322 for (const auto& option : options) {
323 SetOptionDisable(option);
324 }
325 }
326 } // namespace
327
SelectOverlayNode(const std::shared_ptr<SelectOverlayInfo> & info)328 SelectOverlayNode::SelectOverlayNode(const std::shared_ptr<SelectOverlayInfo>& info)
329 : FrameNode(V2::SELECT_OVERLAY_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
330 MakeRefPtr<SelectOverlayPattern>(info))
331 {
332 stateFuncs_[FrameNodeStatus::VISIBLE] = &SelectOverlayNode::DispatchVisibleState;
333 stateFuncs_[FrameNodeStatus::VISIBLETOGONE] = &SelectOverlayNode::DispatchVisibleToGoneState;
334 stateFuncs_[FrameNodeStatus::GONE] = &SelectOverlayNode::DispatchGoneState;
335 stateFuncs_[FrameNodeStatus::GONETOVISIBLE] = &SelectOverlayNode::DispatchGoneToVisibleState;
336 }
337
DispatchVisibleState(FrameNodeType type,FrameNodeTrigger trigger)338 void SelectOverlayNode::DispatchVisibleState(FrameNodeType type, FrameNodeTrigger trigger)
339 {
340 AnimationOption option;
341 option.SetDuration(MENU_HIDE_ANIMATION_DURATION);
342 option.SetCurve(Curves::SHARP);
343
344 switch (trigger) {
345 case FrameNodeTrigger::HIDE:
346 SetFrameNodeStatus(type, FrameNodeStatus::VISIBLETOGONE);
347 AnimationUtils::Animate(
348 option,
349 [weak = WeakClaim(this), type]() {
350 auto node = weak.Upgrade();
351 CHECK_NULL_VOID(node);
352 node->SetFrameNodeOpacity(type, 0.0);
353 },
354 [weak = WeakClaim(this), type]() {
355 auto node = weak.Upgrade();
356 CHECK_NULL_VOID(node);
357 node->ExecuteOverlayStatus(type, FrameNodeTrigger::HIDDEN);
358 });
359 break;
360 case FrameNodeTrigger::SHOW:
361 case FrameNodeTrigger::SHOWN:
362 case FrameNodeTrigger::HIDDEN:
363 default:
364 break;
365 }
366 }
367
DispatchVisibleToGoneState(FrameNodeType type,FrameNodeTrigger trigger)368 void SelectOverlayNode::DispatchVisibleToGoneState(FrameNodeType type, FrameNodeTrigger trigger)
369 {
370 AnimationOption option;
371 option.SetDuration(MENU_SHOW_ANIMATION_DURATION);
372 option.SetCurve(Curves::SHARP);
373
374 switch (trigger) {
375 case FrameNodeTrigger::SHOW:
376 SetFrameNodeStatus(type, FrameNodeStatus::GONETOVISIBLE);
377 SetFrameNodeVisibility(type, VisibleType::VISIBLE);
378 AnimationUtils::Animate(
379 option,
380 [weak = WeakClaim(this), type]() {
381 auto node = weak.Upgrade();
382 CHECK_NULL_VOID(node);
383 node->SetFrameNodeOpacity(type, 1.0);
384 },
385 [weak = WeakClaim(this), type]() {
386 auto node = weak.Upgrade();
387 CHECK_NULL_VOID(node);
388 node->ExecuteOverlayStatus(type, FrameNodeTrigger::SHOWN);
389 });
390 break;
391 case FrameNodeTrigger::HIDDEN:
392 SetFrameNodeStatus(type, FrameNodeStatus::GONE);
393 SetFrameNodeVisibility(type, VisibleType::GONE);
394 break;
395 case FrameNodeTrigger::SHOWN:
396 case FrameNodeTrigger::HIDE:
397 default:
398 break;
399 }
400 }
401
DispatchGoneState(FrameNodeType type,FrameNodeTrigger trigger)402 void SelectOverlayNode::DispatchGoneState(FrameNodeType type, FrameNodeTrigger trigger)
403 {
404 AnimationOption option;
405 option.SetDuration(MENU_SHOW_ANIMATION_DURATION);
406 option.SetCurve(Curves::SHARP);
407
408 switch (trigger) {
409 case FrameNodeTrigger::SHOW:
410 SetFrameNodeStatus(type, FrameNodeStatus::GONETOVISIBLE);
411 SetFrameNodeVisibility(type, VisibleType::VISIBLE);
412 AnimationUtils::Animate(
413 option,
414 [weak = WeakClaim(this), type]() {
415 auto node = weak.Upgrade();
416 CHECK_NULL_VOID(node);
417 node->SetFrameNodeOpacity(type, 1.0);
418 },
419 [weak = WeakClaim(this), type]() {
420 auto node = weak.Upgrade();
421 CHECK_NULL_VOID(node);
422 node->ExecuteOverlayStatus(type, FrameNodeTrigger::SHOWN);
423 });
424 break;
425 case FrameNodeTrigger::SHOWN:
426 case FrameNodeTrigger::HIDE:
427 case FrameNodeTrigger::HIDDEN:
428 default:
429 break;
430 }
431 }
432
DispatchGoneToVisibleState(FrameNodeType type,FrameNodeTrigger trigger)433 void SelectOverlayNode::DispatchGoneToVisibleState(FrameNodeType type, FrameNodeTrigger trigger)
434 {
435 AnimationOption option;
436 option.SetDuration(MENU_HIDE_ANIMATION_DURATION);
437 option.SetCurve(Curves::SHARP);
438
439 switch (trigger) {
440 case FrameNodeTrigger::SHOWN:
441 SetFrameNodeStatus(type, FrameNodeStatus::VISIBLE);
442 break;
443 case FrameNodeTrigger::HIDE:
444 SetFrameNodeStatus(type, FrameNodeStatus::VISIBLETOGONE);
445 AnimationUtils::Animate(
446 option,
447 [weak = WeakClaim(this), type]() {
448 auto node = weak.Upgrade();
449 CHECK_NULL_VOID(node);
450 node->SetFrameNodeOpacity(type, 0.0);
451 },
452 [weak = WeakClaim(this), type]() {
453 auto node = weak.Upgrade();
454 CHECK_NULL_VOID(node);
455 node->ExecuteOverlayStatus(type, FrameNodeTrigger::HIDDEN);
456 });
457 break;
458 case FrameNodeTrigger::SHOW:
459 case FrameNodeTrigger::HIDDEN:
460 break;
461 default:
462 break;
463 }
464 }
465
CreateCustomSelectMenu(const std::shared_ptr<SelectOverlayInfo> & info)466 RefPtr<FrameNode> CreateCustomSelectMenu(const std::shared_ptr<SelectOverlayInfo>& info)
467 {
468 CHECK_NULL_RETURN(info, nullptr);
469 CHECK_NULL_RETURN(info->menuInfo.menuBuilder, nullptr);
470 NG::ScopedViewStackProcessor builderViewStackProcessor;
471 info->menuInfo.menuBuilder();
472 auto customNode = NG::ViewStackProcessor::GetInstance()->Finish();
473 auto menuNode =
474 MenuView::Create(customNode, -1, "", MenuType::SELECT_OVERLAY_CUSTOM_MENU, MenuParam(), info->isUsingMouse);
475 auto eventHub = menuNode->GetEventHub<EventHub>();
476 if (eventHub && info->menuCallback.onAppear) {
477 eventHub->SetOnAppear(std::move(info->menuCallback.onAppear));
478 }
479 if (eventHub && info->menuCallback.onDisappear) {
480 eventHub->SetOnDisappear(std::move(info->menuCallback.onDisappear));
481 }
482 return menuNode;
483 }
484
CreateSelectOverlayNode(const std::shared_ptr<SelectOverlayInfo> & info)485 RefPtr<FrameNode> SelectOverlayNode::CreateSelectOverlayNode(const std::shared_ptr<SelectOverlayInfo>& info)
486 {
487 if (info->isUsingMouse) {
488 return CreateMenuNode(info);
489 }
490 auto selectOverlayNode = AceType::MakeRefPtr<SelectOverlayNode>(info);
491 selectOverlayNode->InitializePatternAndContext();
492 ElementRegister::GetInstance()->AddUINode(selectOverlayNode);
493 selectOverlayNode->CreateToolBar();
494 selectOverlayNode->UpdateToolBar(true);
495 return selectOverlayNode;
496 }
497
MoreOrBackAnimation(bool isMore)498 void SelectOverlayNode::MoreOrBackAnimation(bool isMore)
499 {
500 CHECK_NULL_VOID(!isDoingAnimation_);
501 CHECK_NULL_VOID(selectMenu_);
502 CHECK_NULL_VOID(selectMenuInner_);
503 CHECK_NULL_VOID(extensionMenu_);
504 CHECK_NULL_VOID(backButton_);
505 if (isMore && !isExtensionMenu_) {
506 MoreAnimation();
507 } else if (!isMore && isExtensionMenu_) {
508 BackAnimation();
509 }
510 }
511
MoreAnimation()512 void SelectOverlayNode::MoreAnimation()
513 {
514 auto extensionContext = extensionMenu_->GetRenderContext();
515 CHECK_NULL_VOID(extensionContext);
516 auto selectMenuInnerContext = selectMenuInner_->GetRenderContext();
517 CHECK_NULL_VOID(selectMenuInnerContext);
518
519 auto extensionProperty = extensionMenu_->GetLayoutProperty();
520 CHECK_NULL_VOID(extensionProperty);
521 auto selectProperty = selectMenu_->GetLayoutProperty();
522 CHECK_NULL_VOID(selectProperty);
523 auto selectMenuInnerProperty = selectMenuInner_->GetLayoutProperty();
524 CHECK_NULL_VOID(selectMenuInnerProperty);
525 auto backButtonProperty = backButton_->GetLayoutProperty();
526 CHECK_NULL_VOID(backButtonProperty);
527
528 auto pattern = GetPattern<SelectOverlayPattern>();
529 CHECK_NULL_VOID(pattern);
530 auto modifier = pattern->GetOverlayModifier();
531 CHECK_NULL_VOID(modifier);
532
533 auto pipeline = PipelineContext::GetCurrentContext();
534 CHECK_NULL_VOID(pipeline);
535
536 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
537 CHECK_NULL_VOID(textOverlayTheme);
538
539 isDoingAnimation_ = true;
540 isExtensionMenu_ = true;
541
542 extensionProperty->UpdateVisibility(VisibleType::VISIBLE);
543 extensionMenuStatus_ = FrameNodeStatus::VISIBLE;
544 AnimationOption extensionOption;
545 extensionOption.SetDuration(ANIMATION_DURATION2);
546 extensionOption.SetCurve(Curves::FAST_OUT_SLOW_IN);
547 auto toolbarHeight = textOverlayTheme->GetMenuToolbarHeight();
548 auto frameSize = CalcSize(CalcLength(toolbarHeight.ConvertToPx()), CalcLength(toolbarHeight.ConvertToPx()));
549
550 AnimationUtils::Animate(extensionOption, [extensionContext, selectMenuInnerContext]() {
551 extensionContext->UpdateOpacity(1.0);
552 extensionContext->UpdateTransformTranslate({ 0.0f, 0.0f, 0.0f });
553 selectMenuInnerContext->UpdateOpacity(0.0);
554 });
555 modifier->SetOtherPointRadius(MIN_DIAMETER / 2.0f);
556 modifier->SetHeadPointRadius(MIN_ARROWHEAD_DIAMETER / 2.0f);
557 modifier->SetLineEndOffset(true);
558
559 FinishCallback callback = [selectMenuInnerProperty, extensionProperty, backButtonProperty,
560 id = Container::CurrentId(), weak = WeakClaim(this)]() {
561 ContainerScope scope(id);
562 auto pipeline = PipelineBase::GetCurrentContext();
563 CHECK_NULL_VOID_NOLOG(pipeline);
564 auto taskExecutor = pipeline->GetTaskExecutor();
565 CHECK_NULL_VOID_NOLOG(taskExecutor);
566 taskExecutor->PostTask(
567 [selectMenuInnerProperty, extensionProperty, backButtonProperty, id, weak]() {
568 ContainerScope scope(id);
569 selectMenuInnerProperty->UpdateVisibility(VisibleType::GONE);
570 extensionProperty->UpdateVisibility(VisibleType::VISIBLE);
571 backButtonProperty->UpdateVisibility(VisibleType::VISIBLE);
572 auto selectOverlay = weak.Upgrade();
573 CHECK_NULL_VOID(selectOverlay);
574 selectOverlay->SetAnimationStatus(false);
575 },
576 TaskExecutor::TaskType::UI);
577 };
578 AnimationOption selectOption;
579 selectOption.SetDuration(ANIMATION_DURATION1);
580 selectOption.SetCurve(Curves::FRICTION);
581 pipeline->FlushUITasks();
582 AnimationUtils::OpenImplicitAnimation(selectOption, Curves::FRICTION, callback);
583 selectProperty->UpdateUserDefinedIdealSize(frameSize);
584 selectMenuInnerContext->UpdateTransformTranslate({ ANIMATION_TEXT_OFFSET.ConvertToPx(), 0.0f, 0.0f });
585 selectMenu_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
586 pipeline->FlushUITasks();
587 AnimationUtils::CloseImplicitAnimation();
588 }
589
BackAnimation()590 void SelectOverlayNode::BackAnimation()
591 {
592 auto selectContext = selectMenu_->GetRenderContext();
593 CHECK_NULL_VOID(selectContext);
594 auto extensionContext = extensionMenu_->GetRenderContext();
595 CHECK_NULL_VOID(extensionContext);
596 auto selectMenuInnerContext = selectMenuInner_->GetRenderContext();
597 CHECK_NULL_VOID(selectMenuInnerContext);
598
599 auto extensionProperty = extensionMenu_->GetLayoutProperty();
600 CHECK_NULL_VOID(extensionProperty);
601 auto selectProperty = selectMenu_->GetLayoutProperty();
602 CHECK_NULL_VOID(selectProperty);
603 auto selectMenuInnerProperty = selectMenuInner_->GetLayoutProperty();
604 CHECK_NULL_VOID(selectMenuInnerProperty);
605 auto backButtonProperty = backButton_->GetLayoutProperty();
606 CHECK_NULL_VOID(backButtonProperty);
607
608 auto pattern = GetPattern<SelectOverlayPattern>();
609 CHECK_NULL_VOID(pattern);
610 auto modifier = pattern->GetOverlayModifier();
611 CHECK_NULL_VOID(modifier);
612
613 auto pipeline = PipelineContext::GetCurrentContext();
614 CHECK_NULL_VOID(pipeline);
615
616 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
617 CHECK_NULL_VOID(textOverlayTheme);
618
619 isDoingAnimation_ = true;
620 isExtensionMenu_ = false;
621 auto meanuWidth = pattern->GetMenuWidth();
622
623 selectMenuInnerProperty->UpdateVisibility(VisibleType::VISIBLE);
624 AnimationOption extensionOption;
625 extensionOption.SetDuration(ANIMATION_DURATION2);
626 extensionOption.SetCurve(Curves::FAST_OUT_SLOW_IN);
627
628 AnimationUtils::Animate(extensionOption, [extensionContext, selectMenuInnerContext]() {
629 extensionContext->UpdateOpacity(0.0);
630 extensionContext->UpdateTransformTranslate({ 0.0f, MORE_MENU_TRANSLATE.ConvertToPx(), 0.0f });
631 selectMenuInnerContext->UpdateOpacity(1.0);
632 });
633
634 modifier->SetOtherPointRadius(MAX_DIAMETER / 2.0f);
635 modifier->SetHeadPointRadius(MAX_DIAMETER / 2.0f);
636 modifier->SetLineEndOffset(false);
637
638 auto toolbarHeight = textOverlayTheme->GetMenuToolbarHeight();
639 auto frameSize = CalcSize(CalcLength(meanuWidth), CalcLength(toolbarHeight.ConvertToPx()));
640
641 FinishCallback callback = [selectMenuInnerProperty, extensionProperty, backButtonProperty,
642 id = Container::CurrentId(), weak = WeakClaim(this)]() {
643 ContainerScope scope(id);
644 auto pipeline = PipelineBase::GetCurrentContext();
645 CHECK_NULL_VOID_NOLOG(pipeline);
646 auto taskExecutor = pipeline->GetTaskExecutor();
647 CHECK_NULL_VOID_NOLOG(taskExecutor);
648 taskExecutor->PostTask(
649 [selectMenuInnerProperty, extensionProperty, backButtonProperty, id, weak]() {
650 ContainerScope scope(id);
651 selectMenuInnerProperty->UpdateVisibility(VisibleType::VISIBLE);
652 extensionProperty->UpdateVisibility(VisibleType::GONE);
653 backButtonProperty->UpdateVisibility(VisibleType::GONE);
654 auto selectOverlay = weak.Upgrade();
655 CHECK_NULL_VOID(selectOverlay);
656 selectOverlay->SetAnimationStatus(false);
657 },
658 TaskExecutor::TaskType::UI);
659 };
660
661 AnimationOption selectOption;
662 selectOption.SetDuration(ANIMATION_DURATION1);
663 selectOption.SetCurve(Curves::FRICTION);
664 pipeline->FlushUITasks();
665 AnimationUtils::OpenImplicitAnimation(selectOption, Curves::FRICTION, callback);
666 selectProperty->UpdateUserDefinedIdealSize(frameSize);
667 selectMenuInnerContext->UpdateTransformTranslate({ 0.0f, 0.0f, 0.0f });
668 selectContext->UpdateOffset(OffsetT<Dimension>(0.0_px, 0.0_px));
669 selectMenu_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
670 pipeline->FlushUITasks();
671 AnimationUtils::CloseImplicitAnimation();
672 }
673
AddExtensionMenuOptions(const std::vector<MenuOptionsParam> & menuOptionItems,int32_t index)674 void SelectOverlayNode::AddExtensionMenuOptions(const std::vector<MenuOptionsParam>& menuOptionItems, int32_t index)
675 {
676 CHECK_NULL_VOID(!extensionMenu_);
677 std::vector<OptionParam> params;
678 auto id = GetId();
679
680 auto pipeline = PipelineContext::GetCurrentContext();
681 CHECK_NULL_VOID(pipeline);
682 auto iconTheme = pipeline->GetTheme<IconTheme>();
683 auto defaultOptionCallback = [overlayId = id]() {
684 auto pipeline = PipelineContext::GetCurrentContext();
685 CHECK_NULL_VOID(pipeline);
686 auto overlayManager = pipeline->GetSelectOverlayManager();
687 CHECK_NULL_VOID(overlayManager);
688 overlayManager->DestroySelectOverlay(overlayId);
689 };
690 if (!isShowInDefaultMenu_[OPTION_INDEX_CUT]) {
691 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_CUT_SVG) : "";
692 params.emplace_back(Localization::GetInstance()->GetEntryLetters(BUTTON_CUT), iconPath, defaultOptionCallback);
693 }
694 if (!isShowInDefaultMenu_[OPTION_INDEX_COPY]) {
695 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_COPY_SVG) : "";
696 params.emplace_back(Localization::GetInstance()->GetEntryLetters(BUTTON_COPY), iconPath, defaultOptionCallback);
697 }
698 if (!isShowInDefaultMenu_[OPTION_INDEX_PASTE]) {
699 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_PASTE_SVG) : "";
700 params.emplace_back(
701 Localization::GetInstance()->GetEntryLetters(BUTTON_PASTE), iconPath, defaultOptionCallback);
702 }
703 if (!isShowInDefaultMenu_[OPTION_INDEX_COPY_ALL]) {
704 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_SELECT_ALL_SVG) : "";
705 params.emplace_back(
706 Localization::GetInstance()->GetEntryLetters(BUTTON_COPY_ALL), iconPath, defaultOptionCallback);
707 }
708 if (!isShowInDefaultMenu_[OPTION_INDEX_SHARE]) {
709 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_SHARE_SVG) : "";
710 params.emplace_back(
711 Localization::GetInstance()->GetEntryLetters(BUTTON_SHARE), iconPath, defaultOptionCallback);
712 }
713 if (!isShowInDefaultMenu_[OPTION_INDEX_TRANSLATE]) {
714 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_TRANSLATE_SVG) : "";
715 params.emplace_back(
716 Localization::GetInstance()->GetEntryLetters(BUTTON_TRANSLATE), iconPath, defaultOptionCallback);
717 }
718 if (!isShowInDefaultMenu_[OPTION_INDEX_SEARCH]) {
719 auto iconPath = iconTheme ? iconTheme->GetIconPath(InternalResource::ResourceId::IC_SEARCH_SVG) : "";
720 params.emplace_back(
721 Localization::GetInstance()->GetEntryLetters(BUTTON_SEARCH), iconPath, defaultOptionCallback);
722 }
723 int32_t itemNum = 0;
724 for (auto item : menuOptionItems) {
725 if (itemNum >= index) {
726 auto callback = [overlayId = id, func = std::move(item.action)]() {
727 auto pipeline = PipelineContext::GetCurrentContext();
728 CHECK_NULL_VOID(pipeline);
729 auto overlayManager = pipeline->GetSelectOverlayManager();
730 CHECK_NULL_VOID(overlayManager);
731
732 auto selectOverlay = overlayManager->GetSelectOverlayNode(overlayId);
733 auto pattern = selectOverlay->GetPattern<SelectOverlayPattern>();
734 auto selectInfo = pattern->GetSelectInfo();
735 func(selectInfo);
736 overlayManager->DestroySelectOverlay(overlayId);
737 };
738 params.emplace_back(item.content.value_or("null"), item.icon.value_or(" "), callback);
739 }
740 itemNum++;
741 }
742 if (!params.empty()) {
743 auto menuWrapper =
744 MenuView::Create(std::move(params), -1, "ExtensionMenu", MenuType::SELECT_OVERLAY_EXTENSION_MENU);
745 CHECK_NULL_VOID(menuWrapper);
746 auto menu = DynamicCast<FrameNode>(menuWrapper->GetChildAtIndex(0));
747 CHECK_NULL_VOID(menu);
748 menuWrapper->RemoveChild(menu);
749 menuWrapper.Reset();
750
751 // set click position to menu
752 auto props = menu->GetLayoutProperty<MenuLayoutProperty>();
753 auto context = menu->GetRenderContext();
754 CHECK_NULL_VOID(props);
755 auto offsetY = 0.0f;
756 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
757 if (textOverlayTheme) {
758 offsetY = textOverlayTheme->GetMenuToolbarHeight().ConvertToPx();
759 }
760 props->UpdateMenuOffset(GetPageOffset());
761 context->UpdateBackShadow(ShadowConfig::NoneShadow);
762 auto menuPattern = menu->GetPattern<MenuPattern>();
763 CHECK_NULL_VOID(menuPattern);
764 auto options = menuPattern->GetOptions();
765 SetOptionsAction(options);
766 ElementRegister::GetInstance()->AddUINode(menu);
767 menu->MountToParent(Claim(this));
768
769 extensionMenu_ = menu;
770 auto extensionMenuContext = extensionMenu_->GetRenderContext();
771 CHECK_NULL_VOID(extensionMenuContext);
772
773 extensionMenu_->GetLayoutProperty()->UpdateVisibility(VisibleType::GONE);
774 extensionMenuStatus_ = FrameNodeStatus::GONE;
775 extensionMenuContext->UpdateOpacity(0.0);
776
777 extensionMenuContext->UpdateTransformTranslate({ 0.0f, MORE_MENU_TRANSLATE.ConvertToPx(), 0.0f });
778 extensionMenu_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
779 extensionMenu_->MarkModifyDone();
780 }
781 }
782
CreateToolBar()783 void SelectOverlayNode::CreateToolBar()
784 {
785 auto info = GetPattern<SelectOverlayPattern>()->GetSelectOverlayInfo();
786 if (info->menuInfo.menuBuilder) {
787 selectMenu_ = CreateCustomSelectMenu(info);
788 if (info->menuInfo.menuIsShow) {
789 selectMenu_->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
790 selectMenuStatus_ = FrameNodeStatus::VISIBLE;
791 } else {
792 selectMenu_->GetLayoutProperty()->UpdateVisibility(VisibleType::GONE);
793 selectMenuStatus_ = FrameNodeStatus::GONE;
794 }
795 selectMenu_->MountToParent(Claim(this));
796 selectMenu_->MarkModifyDone();
797 return;
798 }
799
800 selectMenu_ = FrameNode::GetOrCreateFrameNode("SelectMenu", ElementRegister::GetInstance()->MakeUniqueId(),
801 []() { return AceType::MakeRefPtr<LinearLayoutPattern>(false); });
802 selectMenu_->GetLayoutProperty<LinearLayoutProperty>()->UpdateMainAxisAlign(FlexAlign::FLEX_END);
803 selectMenu_->GetRenderContext()->SetClipToFrame(true);
804 selectMenu_->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_CONTENT);
805
806 // Increase the node to realize the animation effect of font transparency and offset.
807 selectMenuInner_ =
808 FrameNode::GetOrCreateFrameNode("SelectMenuInner", ElementRegister::GetInstance()->MakeUniqueId(),
809 []() { return AceType::MakeRefPtr<LinearLayoutPattern>(false); });
810 selectMenuInner_->GetLayoutProperty<LinearLayoutProperty>()->UpdateMainAxisAlign(FlexAlign::FLEX_END);
811 selectMenuInner_->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_CONTENT);
812
813 auto pipeline = PipelineContext::GetCurrentContext();
814 CHECK_NULL_VOID(pipeline);
815 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
816 CHECK_NULL_VOID(textOverlayTheme);
817 selectMenu_->GetRenderContext()->UpdateOpacity(0.0);
818 selectMenu_->GetRenderContext()->UpdateBackgroundColor(textOverlayTheme->GetMenuBackgroundColor());
819 selectMenuInner_->GetRenderContext()->UpdateOpacity(1.0);
820 selectMenuInner_->GetRenderContext()->UpdateTransformTranslate({ 0.0f, 0.0f, 0.0f });
821
822 const auto& border = textOverlayTheme->GetMenuBorder();
823 auto borderWidth = Dimension(border.Left().GetWidth().ConvertToPx());
824 selectMenu_->GetLayoutProperty()->UpdateBorderWidth({ borderWidth, borderWidth, borderWidth, borderWidth });
825 auto borderRadius = textOverlayTheme->GetMenuToolbarHeight() / 2.0f;
826 selectMenu_->GetRenderContext()->UpdateBorderRadius({ borderRadius, borderRadius, borderRadius, borderRadius });
827 auto borderColor = border.Left().GetColor();
828 selectMenu_->GetRenderContext()->UpdateBorderColor({ borderColor, borderColor, borderColor, borderColor });
829 auto borderStyle = border.Left().GetBorderStyle();
830 selectMenu_->GetRenderContext()->UpdateBorderStyle({ borderStyle, borderStyle, borderStyle, borderStyle });
831
832 const auto& padding = textOverlayTheme->GetMenuPadding();
833 auto left = CalcLength(padding.Left().ConvertToPx());
834 auto right = CalcLength(padding.Right().ConvertToPx());
835 auto top = CalcLength(padding.Top().ConvertToPx());
836 auto bottom = CalcLength(padding.Bottom().ConvertToPx());
837 selectMenuInner_->GetLayoutProperty()->UpdatePadding({ left, right, top, bottom });
838
839 selectMenuInner_->GetLayoutProperty()->UpdateUserDefinedIdealSize(
840 { std::nullopt, CalcLength(textOverlayTheme->GetMenuToolbarHeight()) });
841
842 if (info->menuInfo.menuIsShow) {
843 selectMenu_->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
844 selectMenuStatus_ = FrameNodeStatus::VISIBLE;
845 } else {
846 selectMenu_->GetLayoutProperty()->UpdateVisibility(VisibleType::GONE);
847 selectMenuStatus_ = FrameNodeStatus::GONE;
848 }
849
850 selectMenuInner_->MountToParent(selectMenu_);
851 selectMenuInner_->GetOrCreateGestureEventHub()->MarkResponseRegion(true);
852
853 selectMenu_->GetRenderContext()->UpdateBackShadow(ShadowConfig::DefaultShadowM);
854 selectMenu_->MountToParent(Claim(this));
855 selectMenu_->GetOrCreateGestureEventHub()->MarkResponseRegion(true);
856 selectMenu_->MarkModifyDone();
857 }
858
GetDefaultButtonAndMenuWidth(float & maxWidth)859 void SelectOverlayNode::GetDefaultButtonAndMenuWidth(float& maxWidth)
860 {
861 auto pipeline = PipelineContext::GetCurrentContext();
862 CHECK_NULL_VOID(pipeline);
863 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
864 CHECK_NULL_VOID(textOverlayTheme);
865 auto selectOverlayMaxWidth = textOverlayTheme->GetSelectOverlayMaxWidth().ConvertToPx();
866
867 const auto& menuPadding = textOverlayTheme->GetMenuPadding();
868
869 maxWidth = selectOverlayMaxWidth - menuPadding.Left().ConvertToPx() - menuPadding.Right().ConvertToPx() -
870 textOverlayTheme->GetMoreButtonHeight().ConvertToPx();
871 }
872
AddSystemDefaultOptions(float maxWidth,float & allocatedSize)873 bool SelectOverlayNode::AddSystemDefaultOptions(float maxWidth, float& allocatedSize)
874 {
875 auto info = GetPattern<SelectOverlayPattern>()->GetSelectOverlayInfo();
876 memset_s(isShowInDefaultMenu_, sizeof(isShowInDefaultMenu_), 0, sizeof(isShowInDefaultMenu_));
877 if (info->menuInfo.showCut) {
878 float buttonWidth = 0.0f;
879 auto button = BuildButton(
880 Localization::GetInstance()->GetEntryLetters(BUTTON_CUT), info->menuCallback.onCut, GetId(), buttonWidth);
881 if (maxWidth - allocatedSize >= buttonWidth) {
882 button->MountToParent(selectMenuInner_);
883 allocatedSize += buttonWidth;
884 isShowInDefaultMenu_[OPTION_INDEX_CUT] = true;
885 } else {
886 button.Reset();
887 }
888 } else {
889 isShowInDefaultMenu_[OPTION_INDEX_CUT] = true;
890 }
891 if (info->menuInfo.showCopy) {
892 float buttonWidth = 0.0f;
893 auto button = BuildButton(
894 Localization::GetInstance()->GetEntryLetters(BUTTON_COPY), info->menuCallback.onCopy, GetId(), buttonWidth);
895 if (maxWidth - allocatedSize >= buttonWidth) {
896 button->MountToParent(selectMenuInner_);
897 allocatedSize += buttonWidth;
898 isShowInDefaultMenu_[OPTION_INDEX_COPY] = true;
899 } else {
900 button.Reset();
901 return true;
902 }
903 } else {
904 isShowInDefaultMenu_[OPTION_INDEX_COPY] = true;
905 }
906 if (info->menuInfo.showPaste) {
907 float buttonWidth = 0.0f;
908 auto button = BuildButton(Localization::GetInstance()->GetEntryLetters(BUTTON_PASTE),
909 info->menuCallback.onPaste, GetId(), buttonWidth);
910 if (maxWidth - allocatedSize >= buttonWidth) {
911 button->MountToParent(selectMenuInner_);
912 allocatedSize += buttonWidth;
913 isShowInDefaultMenu_[OPTION_INDEX_PASTE] = true;
914 } else {
915 button.Reset();
916 return true;
917 }
918 } else {
919 isShowInDefaultMenu_[OPTION_INDEX_PASTE] = true;
920 }
921 if (info->menuInfo.showCopyAll) {
922 float buttonWidth = 0.0f;
923 auto button = BuildButton(Localization::GetInstance()->GetEntryLetters(BUTTON_COPY_ALL),
924 info->menuCallback.onSelectAll, GetId(), buttonWidth, true);
925 if (maxWidth - allocatedSize >= buttonWidth) {
926 button->MountToParent(selectMenuInner_);
927 allocatedSize += buttonWidth;
928 isShowInDefaultMenu_[OPTION_INDEX_COPY_ALL] = true;
929 } else {
930 button.Reset();
931 return true;
932 }
933 } else {
934 isShowInDefaultMenu_[OPTION_INDEX_COPY_ALL] = true;
935 }
936
937 if (info->menuInfo.showCopy) {
938 float buttonWidth = 0.0f;
939 auto buttonShare = BuildButton(
940 Localization::GetInstance()->GetEntryLetters(BUTTON_SHARE), nullptr, GetId(), buttonWidth, false);
941 if (maxWidth - allocatedSize >= buttonWidth) {
942 buttonShare->MountToParent(selectMenuInner_);
943 allocatedSize += buttonWidth;
944 isShowInDefaultMenu_[OPTION_INDEX_SHARE] = true;
945 } else {
946 buttonShare.Reset();
947 return true;
948 }
949 auto buttonTranslase = BuildButton(
950 Localization::GetInstance()->GetEntryLetters(BUTTON_TRANSLATE), nullptr, GetId(), buttonWidth, false);
951 if (maxWidth - allocatedSize >= buttonWidth) {
952 buttonTranslase->MountToParent(selectMenuInner_);
953 allocatedSize += buttonWidth;
954 isShowInDefaultMenu_[OPTION_INDEX_TRANSLATE] = true;
955 } else {
956 buttonTranslase.Reset();
957 return true;
958 }
959 auto buttonSearch = BuildButton(
960 Localization::GetInstance()->GetEntryLetters(BUTTON_SEARCH), nullptr, GetId(), buttonWidth, false);
961 if (maxWidth - allocatedSize >= buttonWidth) {
962 buttonSearch->MountToParent(selectMenuInner_);
963 allocatedSize += buttonWidth;
964 isShowInDefaultMenu_[OPTION_INDEX_SEARCH] = true;
965 } else {
966 buttonSearch.Reset();
967 return true;
968 }
969 } else {
970 isShowInDefaultMenu_[OPTION_INDEX_SHARE] = true;
971 isShowInDefaultMenu_[OPTION_INDEX_TRANSLATE] = true;
972 isShowInDefaultMenu_[OPTION_INDEX_SEARCH] = true;
973 }
974 return false;
975 }
976
UpdateToolBar(bool menuItemChanged)977 void SelectOverlayNode::UpdateToolBar(bool menuItemChanged)
978 {
979 auto info = GetPattern<SelectOverlayPattern>()->GetSelectOverlayInfo();
980 if (menuItemChanged && info->menuInfo.menuBuilder == nullptr && selectMenuInner_) {
981 selectMenuInner_->Clean();
982 selectMenuInner_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
983 if (isExtensionMenu_) {
984 MoreOrBackAnimation(false);
985 }
986 auto selectProperty = selectMenu_->GetLayoutProperty();
987 CHECK_NULL_VOID(selectProperty);
988 selectProperty->ClearUserDefinedIdealSize(true, false);
989 bool isDefaultOverMaxWidth = false;
990 float allocatedSize = 0.0f;
991 float maxWidth = 0.0f;
992 GetDefaultButtonAndMenuWidth(maxWidth);
993 isDefaultOverMaxWidth = AddSystemDefaultOptions(maxWidth, allocatedSize);
994 auto itemNum = -1;
995 auto extensionOptionStartIndex = -1;
996 if (!info->menuOptionItems.empty()) {
997 for (auto item : info->menuOptionItems) {
998 itemNum++;
999 float extensionOptionWidth = 0.0f;
1000 auto button = BuildButton(item.content.value_or("null"), item.action, GetId(), extensionOptionWidth);
1001 allocatedSize += extensionOptionWidth;
1002 if (allocatedSize > maxWidth) {
1003 button.Reset();
1004 extensionOptionStartIndex = itemNum;
1005 break;
1006 }
1007 button->MountToParent(selectMenuInner_);
1008 }
1009 }
1010 if (backButton_) {
1011 isExtensionMenu_ = false;
1012 RemoveChild(backButton_);
1013 backButton_.Reset();
1014 }
1015 if (extensionMenu_) {
1016 RemoveChild(extensionMenu_);
1017 extensionMenu_.Reset();
1018 }
1019 if (extensionOptionStartIndex != -1 || isDefaultOverMaxWidth) {
1020 auto backButton = BuildMoreOrBackButton(GetId(), true);
1021 backButton->MountToParent(selectMenuInner_);
1022 // add back button
1023 auto id = GetId();
1024 if (!backButton_) {
1025 backButton_ = BuildMoreOrBackButton(id, false);
1026 CHECK_NULL_VOID(backButton_);
1027 backButton_->GetRenderContext()->UpdateOpacity(0.0);
1028 backButton_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1029 backButton_->MountToParent(Claim(this));
1030 }
1031 }
1032 AddExtensionMenuOptions(info->menuOptionItems, extensionOptionStartIndex);
1033 }
1034 if (info->menuInfo.menuDisable) {
1035 ExecuteOverlayStatus(FrameNodeType::SELECTMENU, FrameNodeTrigger::HIDE);
1036 } else if (info->menuInfo.menuIsShow) {
1037 ExecuteOverlayStatus(FrameNodeType::SELECTMENU, FrameNodeTrigger::SHOW);
1038 } else {
1039 ExecuteOverlayStatus(FrameNodeType::SELECTMENU, FrameNodeTrigger::HIDE);
1040 }
1041 selectMenu_->MarkModifyDone();
1042 if (isExtensionMenu_ && extensionMenu_) {
1043 if (info->menuInfo.menuDisable) {
1044 ExecuteOverlayStatus(FrameNodeType::EXTENSIONMENU, FrameNodeTrigger::HIDE);
1045 if (backButton_) {
1046 ExecuteOverlayStatus(FrameNodeType::BACKBUTTON, FrameNodeTrigger::HIDE);
1047 }
1048 } else if (info->menuInfo.menuIsShow) {
1049 ExecuteOverlayStatus(FrameNodeType::EXTENSIONMENU, FrameNodeTrigger::SHOW);
1050 if (backButton_) {
1051 ExecuteOverlayStatus(FrameNodeType::BACKBUTTON, FrameNodeTrigger::SHOW);
1052 }
1053 } else {
1054 ExecuteOverlayStatus(FrameNodeType::EXTENSIONMENU, FrameNodeTrigger::HIDE);
1055 if (backButton_) {
1056 ExecuteOverlayStatus(FrameNodeType::BACKBUTTON, FrameNodeTrigger::HIDE);
1057 }
1058 }
1059 extensionMenu_->MarkModifyDone();
1060 if (backButton_) {
1061 backButton_->MarkModifyDone();
1062 }
1063 }
1064 MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1065 }
1066
CreateMenuNode(const std::shared_ptr<SelectOverlayInfo> & info)1067 RefPtr<FrameNode> SelectOverlayNode::CreateMenuNode(const std::shared_ptr<SelectOverlayInfo>& info)
1068 {
1069 RefPtr<FrameNode> menuWrapper;
1070 if (info->menuInfo.menuBuilder) {
1071 menuWrapper = CreateCustomSelectMenu(info);
1072 } else {
1073 std::vector<OptionParam> params = GetOptionsParams(info);
1074 menuWrapper = MenuView::Create(std::move(params), -1);
1075 }
1076 CHECK_NULL_RETURN(menuWrapper, nullptr);
1077 auto menu = DynamicCast<FrameNode>(menuWrapper->GetChildAtIndex(0));
1078 // set click position to menu
1079 CHECK_NULL_RETURN(menu, nullptr);
1080 auto props = menu->GetLayoutProperty<MenuLayoutProperty>();
1081 CHECK_NULL_RETURN(props, nullptr);
1082 OffsetF pageOffset;
1083 auto pipeline = PipelineContext::GetCurrentContext();
1084 CHECK_NULL_RETURN(pipeline, nullptr);
1085 auto windowManager = pipeline->GetWindowManager();
1086 auto isContainerModal = pipeline->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
1087 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
1088 if (isContainerModal) {
1089 pageOffset = GetPageOffset();
1090 }
1091 props->UpdateMenuOffset(info->rightClickOffset + pageOffset);
1092
1093 auto menuPattern = menu->GetPattern<MenuPattern>();
1094 CHECK_NULL_RETURN(menuPattern, nullptr);
1095 auto options = menuPattern->GetOptions();
1096 SetOptionsAction(info, options);
1097
1098 menu->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1099 ElementRegister::GetInstance()->AddUINode(menu);
1100
1101 auto gestureEventHub = menuWrapper->GetOrCreateGestureEventHub();
1102 if (gestureEventHub) {
1103 gestureEventHub->SetHitTestMode(HitTestMode::HTMTRANSPARENT_SELF);
1104 }
1105 return menuWrapper;
1106 }
1107
IsInSelectedOrSelectOverlayArea(const PointF & point)1108 bool SelectOverlayNode::IsInSelectedOrSelectOverlayArea(const PointF& point)
1109 {
1110 auto pattern = GetPattern<SelectOverlayPattern>();
1111 CHECK_NULL_RETURN(pattern, false);
1112
1113 std::vector<RectF> rects;
1114 auto offset = GetGeometryNode() ? GetGeometryNode()->GetFrameOffset() : OffsetF();
1115 rects.emplace_back(pattern->GetHandleRegion(true) + offset);
1116 rects.emplace_back(pattern->GetHandleRegion(false) + offset);
1117 if (selectMenu_ && selectMenu_->GetGeometryNode()) {
1118 rects.emplace_back(selectMenu_->GetGeometryNode()->GetFrameRect() + offset);
1119 }
1120 if (extensionMenu_ && extensionMenu_->GetGeometryNode()) {
1121 rects.emplace_back(extensionMenu_->GetGeometryNode()->GetFrameRect() + offset);
1122 }
1123
1124 if (pattern->IsCustomMenu()) {
1125 for (auto& child : pattern->GetHost()->GetChildren()) {
1126 auto childFrameNode = DynamicCast<FrameNode>(child);
1127 if (!childFrameNode) {
1128 continue;
1129 }
1130 rects.emplace_back(childFrameNode->GetGeometryNode()->GetFrameRect() + offset);
1131 }
1132 }
1133
1134 for (const auto& rect : rects) {
1135 if (rect.IsInRegion(point)) {
1136 LOGD("point is in select overlay rects");
1137 return true;
1138 }
1139 }
1140 return false;
1141 }
1142
SetClosedByGlobalEvent(bool closedByGlobalEvent)1143 void SelectOverlayNode::SetClosedByGlobalEvent(bool closedByGlobalEvent)
1144 {
1145 auto selectOverlayPattern = GetPattern<SelectOverlayPattern>();
1146 CHECK_NULL_VOID(selectOverlayPattern);
1147 selectOverlayPattern->SetClosedByGlobalTouchEvent(closedByGlobalEvent);
1148 }
1149
ShowSelectOverlay(bool animation)1150 void SelectOverlayNode::ShowSelectOverlay(bool animation)
1151 {
1152 auto pattern = GetPattern<SelectOverlayPattern>();
1153 CHECK_NULL_VOID(pattern);
1154
1155 if (animation) {
1156 AnimationOption option;
1157 option.SetDuration(MENU_SHOW_ANIMATION_DURATION);
1158 option.SetCurve(Curves::SHARP);
1159
1160 AnimationUtils::Animate(option, [weak = WeakClaim(this)]() {
1161 auto node = weak.Upgrade();
1162 CHECK_NULL_VOID(node);
1163 node->SetSelectMenuOpacity(1.0);
1164 node->SetExtensionMenuOpacity(1.0);
1165 node->SetBackButtonOpacity(1.0);
1166 });
1167 } else {
1168 SetSelectMenuOpacity(1.0);
1169 SetExtensionMenuOpacity(1.0);
1170 SetBackButtonOpacity(1.0);
1171 }
1172
1173 pattern->SetHasShowAnimation(animation);
1174 }
1175
HideSelectOverlay(const std::function<void ()> & callback)1176 void SelectOverlayNode::HideSelectOverlay(const std::function<void()>& callback)
1177 {
1178 AnimationOption handleOption;
1179 handleOption.SetDuration(HANDLE_ANIMATION_DURATION);
1180 handleOption.SetCurve(Curves::SHARP);
1181
1182 AnimationUtils::Animate(handleOption, [weak = WeakClaim(this)]() {
1183 auto node = weak.Upgrade();
1184 CHECK_NULL_VOID(node);
1185 auto pattern = node->GetPattern<SelectOverlayPattern>();
1186 CHECK_NULL_VOID(pattern);
1187 auto contentModifier = pattern->GetContentModifier();
1188 CHECK_NULL_VOID(contentModifier);
1189 contentModifier->SetHandleOpacity(0.0);
1190 });
1191
1192 AnimationOption overlayOption;
1193 overlayOption.SetDuration(MENU_HIDE_ANIMATION_DURATION);
1194 overlayOption.SetCurve(Curves::SHARP);
1195
1196 AnimationUtils::Animate(
1197 overlayOption,
1198 [weak = WeakClaim(this)]() {
1199 auto node = weak.Upgrade();
1200 CHECK_NULL_VOID(node);
1201 node->SetSelectMenuOpacity(0.0);
1202 node->SetExtensionMenuOpacity(0.0);
1203 node->SetBackButtonOpacity(0.0);
1204 auto pattern = node->GetPattern<SelectOverlayPattern>();
1205 CHECK_NULL_VOID(pattern);
1206 auto overlayModifier = pattern->GetOverlayModifier();
1207 CHECK_NULL_VOID(overlayModifier);
1208 overlayModifier->SetCirclesAndBackArrowOpacity(0.0);
1209 },
1210 callback);
1211 }
1212
ExecuteOverlayStatus(FrameNodeType type,FrameNodeTrigger trigger)1213 void SelectOverlayNode::ExecuteOverlayStatus(FrameNodeType type, FrameNodeTrigger trigger)
1214 {
1215 FrameNodeStatus status = FrameNodeStatus::VISIBLE;
1216 switch (type) {
1217 case FrameNodeType::SELECTMENU:
1218 status = selectMenuStatus_;
1219 break;
1220 case FrameNodeType::EXTENSIONMENU:
1221 status = extensionMenuStatus_;
1222 break;
1223 case FrameNodeType::BACKBUTTON:
1224 status = backButtonStatus_;
1225 break;
1226 default:
1227 break;
1228 }
1229
1230 auto stateFuncIter = stateFuncs_.find(status);
1231 if (stateFuncIter != stateFuncs_.end()) {
1232 auto stateFunc = stateFuncIter->second;
1233 CHECK_NULL_VOID(stateFunc);
1234 (this->*stateFunc)(type, trigger);
1235 }
1236 }
1237
SetFrameNodeStatus(FrameNodeType type,FrameNodeStatus status)1238 void SelectOverlayNode::SetFrameNodeStatus(FrameNodeType type, FrameNodeStatus status)
1239 {
1240 switch (type) {
1241 case FrameNodeType::SELECTMENU:
1242 selectMenuStatus_ = status;
1243 break;
1244 case FrameNodeType::EXTENSIONMENU:
1245 extensionMenuStatus_ = status;
1246 break;
1247 case FrameNodeType::BACKBUTTON:
1248 backButtonStatus_ = status;
1249 break;
1250 default:
1251 break;
1252 }
1253 }
1254
SetFrameNodeVisibility(FrameNodeType type,VisibleType visibleType)1255 void SelectOverlayNode::SetFrameNodeVisibility(FrameNodeType type, VisibleType visibleType)
1256 {
1257 switch (type) {
1258 case FrameNodeType::SELECTMENU:
1259 selectMenu_->GetLayoutProperty()->UpdateVisibility(visibleType);
1260 break;
1261 case FrameNodeType::EXTENSIONMENU:
1262 extensionMenu_->GetLayoutProperty()->UpdateVisibility(visibleType);
1263 break;
1264 case FrameNodeType::BACKBUTTON:
1265 backButton_->GetLayoutProperty()->UpdateVisibility(visibleType);
1266 break;
1267 default:
1268 break;
1269 }
1270 }
1271
SetFrameNodeOpacity(FrameNodeType type,float opacity)1272 void SelectOverlayNode::SetFrameNodeOpacity(FrameNodeType type, float opacity)
1273 {
1274 switch (type) {
1275 case FrameNodeType::SELECTMENU:
1276 SetSelectMenuOpacity(opacity);
1277 break;
1278 case FrameNodeType::EXTENSIONMENU:
1279 SetExtensionMenuOpacity(opacity);
1280 break;
1281 case FrameNodeType::BACKBUTTON:
1282 SetBackButtonOpacity(opacity);
1283 break;
1284 default:
1285 break;
1286 }
1287 }
1288
SetSelectMenuOpacity(float value)1289 void SelectOverlayNode::SetSelectMenuOpacity(float value)
1290 {
1291 CHECK_NULL_VOID(selectMenu_);
1292 CHECK_NULL_VOID(selectMenu_->GetRenderContext());
1293 selectMenu_->GetRenderContext()->UpdateOpacity(value);
1294 }
1295
SetExtensionMenuOpacity(float value)1296 void SelectOverlayNode::SetExtensionMenuOpacity(float value)
1297 {
1298 CHECK_NULL_VOID(extensionMenu_);
1299 CHECK_NULL_VOID(extensionMenu_->GetRenderContext());
1300 extensionMenu_->GetRenderContext()->UpdateOpacity(value);
1301 }
1302
SetBackButtonOpacity(float value)1303 void SelectOverlayNode::SetBackButtonOpacity(float value)
1304 {
1305 CHECK_NULL_VOID(backButton_);
1306 CHECK_NULL_VOID(backButton_->GetRenderContext());
1307 backButton_->GetRenderContext()->UpdateOpacity(value);
1308 }
1309
1310 } // namespace OHOS::Ace::NG
1311