1 /*
2  * Copyright (c) 2023 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/container_modal/enhance/container_modal_view_enhance.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/i18n/localization.h"
21 #include "base/log/event_report.h"
22 #include "base/memory/ace_type.h"
23 #include "base/subwindow/subwindow_manager.h"
24 #include "base/utils/system_properties.h"
25 #include "base/utils/utils.h"
26 #include "core/common/container.h"
27 #include "core/components/common/layout/constants.h"
28 #include "core/components/common/properties/color.h"
29 #include "core/components/container_modal/container_modal_constants.h"
30 #include "core/components/theme/advanced_pattern_theme.h"
31 #include "core/components_ng/base/frame_node.h"
32 #include "core/components_ng/base/view_abstract.h"
33 #include "core/components_ng/gestures/pan_gesture.h"
34 #include "core/components_ng/gestures/tap_gesture.h"
35 #include "core/components_ng/pattern/button/button_layout_property.h"
36 #include "core/components_ng/pattern/button/button_pattern.h"
37 #include "core/components_ng/pattern/container_modal/container_modal_pattern.h"
38 #include "core/components_ng/pattern/container_modal/enhance/container_modal_pattern_enhance.h"
39 #include "core/components_ng/pattern/divider/divider_layout_property.h"
40 #include "core/components_ng/pattern/divider/divider_pattern.h"
41 #include "core/components_ng/pattern/image/image_layout_property.h"
42 #include "core/components_ng/pattern/image/image_pattern.h"
43 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
44 #include "core/components_ng/pattern/list/list_pattern.h"
45 #include "core/components_ng/pattern/menu/menu_pattern.h"
46 #include "core/components_ng/pattern/navigation/navigation_declaration.h"
47 #include "core/components_ng/pattern/patternlock/patternlock_paint_property.h"
48 #include "core/components_ng/pattern/stack/stack_pattern.h"
49 #include "core/components_ng/pattern/text/text_layout_property.h"
50 #include "core/components_ng/pattern/text/text_pattern.h"
51 #include "core/components_ng/property/calc_length.h"
52 #include "core/components_v2/inspector/inspector_constants.h"
53 #include "core/event/mouse_event.h"
54 #include "core/image/image_source_info.h"
55 #include "core/pipeline/base/element_register.h"
56 
57 namespace OHOS::Ace::NG {
58 /**
59  * The structure of container_modal enhanced is designed as follows :
60  * |--container_modal(stack)
61  *   |--column
62  *      |--container_modal_custom_title(row)
63  *          |--custom_node(js)
64  *      |--stack
65  *          |--container_modal_content(stage)
66  *              |--page
67  *          |--dialog(when show)
68  *      |--gesture_row(row)
69  *   |--container_modal_custom_floating_title(row)
70  *          |--custom_node(js)
71  *   |--container_modal_control_buttons(row)
72  *          |--[maxRecover, minimize, close](button)
73  */
74 namespace {
75 const Dimension MENU_CONTAINER_WIDTH = 232.0_vp;
76 const Dimension MENU_CONTAINER_HEIGHT = 96.0_vp;
77 const Dimension MENU_ITEM_RADIUS = 12.0_vp;
78 const Dimension MENU_ITEM_WIDTH = 232.0_vp;
79 const Dimension MENU_ITEM_HEIGHT = 48.0_vp;
80 const Dimension MENU_ITEM_LEFT_PADDING = 12.0_vp;
81 const Dimension MENU_ITEM_TEXT_WIDTH = 144.0_vp;
82 const Dimension MENU_ITEM_TEXT_HEIGHT = 22.0_vp;
83 const Dimension MENU_ITEM_TEXT_PADDING = 8.0_vp;
84 const Dimension MENU_FLOAT_X = 220.0_vp;
85 const Dimension MENU_FLOAT_Y = 31.0_vp;
86 const Dimension MENU_SAFETY_X = 8.0_vp;
87 const Dimension MENU_SAFETY_Y = 96.0_vp;
88 const int32_t MENU_ITEM_MAXLINES = 1;
89 const int32_t MENU_TASK_DELAY_TIME = 600;
90 const Color MENU_ITEM_COLOR = Color(0xffffff);
91 
92 const int32_t DOUBLE_CLICK_TO_MAXIMIZE = 1;
93 const int32_t DOUBLE_CLICK_TO_RECOVER = 2;
94 
95 const int32_t MAX_BUTTON_CLICK_TO_MAXIMIZE = 1;
96 const int32_t MAX_BUTTON_CLICK_TO_RECOVER = 2;
97 
98 const int32_t MAX_MENU_ITEM_LEFT_SPLIT = 1;
99 const int32_t MAX_MENU_ITEM_RIGHT_SPLIT = 2;
100 const int32_t MAX_MENU_ITEM_MAXIMIZE = 3;
101 
102 const int32_t MAX_MENU_DEFAULT_NOT_CHANGE = 3;
103 } // namespace
104 bool ContainerModalViewEnhance::sIsForbidMenuEvent_ = false;
105 bool ContainerModalViewEnhance::sIsMenuPending_ = false;
106 bool ContainerModalViewEnhance::enableSplit_ = true;
107 OffsetF ContainerModalViewEnhance::menuOffset_ = {};
108 CancelableCallback<void()> ContainerModalViewEnhance::sContextTimer_;
109 
Create(RefPtr<FrameNode> & content)110 RefPtr<FrameNode> ContainerModalViewEnhance::Create(RefPtr<FrameNode>& content)
111 {
112     auto containerModalNode = FrameNode::CreateFrameNode("ContainerModal",
113         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ContainerModalPatternEnhance>());
114     auto stack = FrameNode::CreateFrameNode(
115         V2::STACK_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<StackPattern>());
116     auto column = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
117         AceType::MakeRefPtr<LinearLayoutPattern>(true));
118     auto controlButtonsRow = FrameNode::CreateFrameNode(
119         V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), MakeRefPtr<LinearLayoutPattern>(false));
120 
121     column->AddChild(BuildTitle(containerModalNode));
122     stack->AddChild(content);
123     column->AddChild(stack);
124     column->AddChild(BuildGestureRow(containerModalNode));
125     auto containerPattern = containerModalNode->GetPattern<ContainerModalPatternEnhance>();
126     CHECK_NULL_RETURN(containerPattern, nullptr);
127     SetContainerModalPattern(containerPattern);
128     containerModalNode->AddChild(column);
129     containerModalNode->AddChild(BuildTitle(containerModalNode, true));
130     containerModalNode->AddChild(AddControlButtons(containerModalNode, controlButtonsRow));
131     containerPattern->Init();
132     return containerModalNode;
133 }
134 
BuildTitle(RefPtr<FrameNode> & containerNode,bool isFloatingTitle)135 RefPtr<FrameNode> ContainerModalViewEnhance::BuildTitle(RefPtr<FrameNode>& containerNode, bool isFloatingTitle)
136 {
137     LOGI("ContainerModalViewEnhance BuildTitle called");
138     auto titleRow = BuildTitleContainer(containerNode, isFloatingTitle);
139     CHECK_NULL_RETURN(titleRow, nullptr);
140     SetTapGestureEvent(containerNode, titleRow);
141     return titleRow;
142 }
143 
SetTapGestureEvent(RefPtr<FrameNode> & containerNode,RefPtr<FrameNode> & containerTitleRow)144 void ContainerModalViewEnhance::SetTapGestureEvent(
145     RefPtr<FrameNode>& containerNode, RefPtr<FrameNode>& containerTitleRow)
146 {
147     auto pipeline = PipelineContext::GetCurrentContext();
148     CHECK_NULL_VOID(pipeline);
149     auto windowManager = pipeline->GetWindowManager();
150     CHECK_NULL_VOID(windowManager);
151     auto eventHub = containerTitleRow->GetOrCreateGestureEventHub();
152     CHECK_NULL_VOID(eventHub);
153     auto tapGesture = AceType::MakeRefPtr<NG::TapGesture>(2, 1);
154     CHECK_NULL_VOID(tapGesture);
155     tapGesture->SetOnActionId([weakContainerNode = AceType::WeakClaim(AceType::RawPtr(containerNode)),
156                                   weakWindowManager = AceType::WeakClaim(AceType::RawPtr(windowManager))](
157                                   GestureEvent& info) {
158         LOGI("container window double click.");
159         auto windowManager = weakWindowManager.Upgrade();
160         CHECK_NULL_VOID(windowManager);
161         auto containerNode = weakContainerNode.Upgrade();
162         CHECK_NULL_VOID(containerNode);
163         bool isMoving = windowManager->WindowIsStartMoving();
164         if (isMoving) {
165             LOGI("window is moving, double-click is not supported.");
166             return;
167         }
168         auto windowMode = windowManager->GetWindowMode();
169         auto maximizeMode = windowManager->GetCurrentWindowMaximizeMode();
170         if (maximizeMode == MaximizeMode::MODE_AVOID_SYSTEM_BAR || windowMode == WindowMode::WINDOW_MODE_FULLSCREEN ||
171             windowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY ||
172             windowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
173             EventReport::ReportDoubleClickTitle(DOUBLE_CLICK_TO_RECOVER);
174             windowManager->WindowRecover();
175         } else if (windowMode == WindowMode::WINDOW_MODE_FLOATING) {
176             EventReport::ReportDoubleClickTitle(DOUBLE_CLICK_TO_MAXIMIZE);
177             windowManager->WindowMaximize(true);
178         }
179         containerNode->OnWindowFocused();
180     });
181     eventHub->AddGesture(tapGesture);
182     eventHub->OnModifyDone();
183 }
184 
AddControlButtons(RefPtr<FrameNode> & containerNode,RefPtr<FrameNode> & containerTitleRow)185 RefPtr<FrameNode> ContainerModalViewEnhance::AddControlButtons(
186     RefPtr<FrameNode>& containerNode, RefPtr<FrameNode>& containerTitleRow)
187 {
188     auto pipeline = PipelineContext::GetCurrentContext();
189     CHECK_NULL_RETURN(pipeline, nullptr);
190     auto windowManager = pipeline->GetWindowManager();
191     CHECK_NULL_RETURN(windowManager, nullptr);
192 
193     RefPtr<FrameNode> maximizeBtn = BuildControlButton(InternalResource::ResourceId::IC_WINDOW_MAX,
194         [weak = AceType::WeakClaim(AceType::RawPtr(containerNode)),
195             wk = AceType::WeakClaim(AceType::RawPtr(windowManager))](GestureEvent& info) {
196             auto containerNode = weak.Upgrade();
197             CHECK_NULL_VOID(containerNode);
198             auto windowManager = wk.Upgrade();
199             CHECK_NULL_VOID(windowManager);
200             ResetHoverTimer();
201             bool isMoving = windowManager->WindowIsStartMoving();
202             if (isMoving) {
203                 LOGI("window is moving, maximization is not supported.");
204                 return;
205             }
206             auto mode = windowManager->GetWindowMode();
207             auto currentMode = windowManager->GetCurrentWindowMaximizeMode();
208             if (mode == WindowMode::WINDOW_MODE_FULLSCREEN || currentMode == MaximizeMode::MODE_AVOID_SYSTEM_BAR ||
209                 mode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY || mode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
210                 EventReport::ReportClickTitleMaximizeMenu(MAX_MENU_ITEM_MAXIMIZE, MAX_BUTTON_CLICK_TO_RECOVER);
211                 windowManager->WindowRecover();
212             } else {
213                 EventReport::ReportClickTitleMaximizeMenu(MAX_MENU_ITEM_MAXIMIZE, MAX_BUTTON_CLICK_TO_MAXIMIZE);
214                 windowManager->WindowMaximize(true);
215             }
216             containerNode->OnWindowFocused();
217         });
218     maximizeBtn->UpdateInspectorId("EnhanceMaximizeBtn");
219     BondingMaxBtnGestureEvent(maximizeBtn, containerNode);
220     BondingMaxBtnInputEvent(maximizeBtn, containerNode);
221     containerTitleRow->AddChild(maximizeBtn);
222 
223     RefPtr<FrameNode> minimizeBtn = BuildControlButton(InternalResource::ResourceId::IC_WINDOW_MIN,
224         [weak = AceType::WeakClaim(AceType::RawPtr(windowManager))](GestureEvent& info) {
225             auto windowManager = weak.Upgrade();
226             if (!windowManager) {
227                 LOGE("create minBtn callback func failed,windowManager is null!");
228                 return;
229             }
230             bool isMoving = windowManager->WindowIsStartMoving();
231             if (isMoving) {
232                 LOGI("window is moving, minimization is not supported.");
233                 return;
234             }
235             LOGI("minimize button clicked");
236             windowManager->WindowMinimize();
237         });
238     // minimizeBtn add empty panEvent to over fater container event
239     minimizeBtn->UpdateInspectorId("EnhanceMinimizeBtn");
240     containerTitleRow->AddChild(minimizeBtn);
241 
242     RefPtr<FrameNode> closeBtn = BuildControlButton(
243         InternalResource::ResourceId::IC_WINDOW_CLOSE,
244         [weak = AceType::WeakClaim(AceType::RawPtr(windowManager))](GestureEvent& info) {
245             auto windowManager = weak.Upgrade();
246             if (!windowManager) {
247                 LOGE("create closeBtn callback func failed,windowManager is null!");
248                 return;
249             }
250             bool isMoving = windowManager->WindowIsStartMoving();
251             if (isMoving) {
252                 LOGI("window is moving, close is not supported.");
253                 return;
254             }
255             LOGI("close button clicked");
256             windowManager->WindowClose();
257         },
258         true);
259     // closeBtn add empty panEvent to over fater container event
260     closeBtn->UpdateInspectorId("EnhanceCloseBtn");
261     containerTitleRow->AddChild(closeBtn);
262 
263     return containerTitleRow;
264 }
265 
BondingMaxBtnGestureEvent(RefPtr<FrameNode> & maximizeBtn,RefPtr<FrameNode> & containerNode)266 void ContainerModalViewEnhance::BondingMaxBtnGestureEvent(
267     RefPtr<FrameNode>& maximizeBtn, RefPtr<FrameNode>& containerNode)
268 {
269     auto pipeline = PipelineContext::GetCurrentContext();
270     auto windowManager = pipeline->GetWindowManager();
271     auto hub = maximizeBtn->GetOrCreateGestureEventHub();
272 
273     // add long press event
274     auto longPressCallback = [weakMaximizeBtn = AceType::WeakClaim(AceType::RawPtr(maximizeBtn))](GestureEvent& info) {
275         auto maximizeBtn = weakMaximizeBtn.Upgrade();
276         CHECK_NULL_VOID(maximizeBtn);
277         auto menuPosX = info.GetScreenLocation().GetX() - info.GetLocalLocation().GetX() - MENU_FLOAT_X.ConvertToPx();
278         auto menuPosY = info.GetScreenLocation().GetY() - info.GetLocalLocation().GetY() + MENU_FLOAT_Y.ConvertToPx();
279         OffsetF menuPosition { menuPosX, menuPosY };
280         CalculateMenuOffset(menuPosition);
281         ShowMaxMenu(maximizeBtn, menuOffset_);
282     };
283     // diable mouse left!
284     auto longPressEvent = AceType::MakeRefPtr<LongPressEvent>(longPressCallback);
285     hub->SetLongPressEvent(longPressEvent, false, true);
286 }
287 
BondingMaxBtnInputEvent(RefPtr<FrameNode> & maximizeBtn,RefPtr<FrameNode> & containerNode)288 void ContainerModalViewEnhance::BondingMaxBtnInputEvent(
289     RefPtr<FrameNode>& maximizeBtn, RefPtr<FrameNode>& containerNode)
290 {
291     auto pipeline = PipelineContext::GetCurrentContext();
292     auto windowManager = pipeline->GetWindowManager();
293     auto hub = maximizeBtn->GetOrCreateInputEventHub();
294     auto hoverMoveFuc = [](MouseInfo& info) {
295         sIsForbidMenuEvent_ = info.GetButton() == MouseButton::LEFT_BUTTON || info.GetScreenLocation().IsZero();
296         if (!sIsMenuPending_ && info.GetAction() == MouseAction::MOVE && !info.GetScreenLocation().IsZero()) {
297             auto menuPosX =
298                 info.GetScreenLocation().GetX() - info.GetLocalLocation().GetX() - MENU_FLOAT_X.ConvertToPx();
299             auto menuPosY =
300                 info.GetScreenLocation().GetY() - info.GetLocalLocation().GetY() + MENU_FLOAT_Y.ConvertToPx();
301             OffsetF menuPosition { menuPosX, menuPosY };
302             LOGI("ContainerModal menuPosition = %{public}s", menuPosition.ToString().c_str());
303             CalculateMenuOffset(menuPosition);
304         }
305     };
306     hub->AddOnMouseEvent(AceType::MakeRefPtr<InputEvent>(std::move(hoverMoveFuc)));
307 
308     // add hover in out event
309     auto containerPattern = containerNode->GetPattern<ContainerModalPattern>();
310     auto hoverEventFuc = [weakMaximizeBtn = AceType::WeakClaim(AceType::RawPtr(maximizeBtn)),
311                              weakContainerPattern = AceType::WeakClaim(AceType::RawPtr(containerPattern)),
312                              weakPipeline = AceType::WeakClaim(AceType::RawPtr(pipeline))](bool hover) {
313         auto pattern = weakContainerPattern.Upgrade();
314         CHECK_NULL_VOID(pattern);
315         if (!hover) {
316             ResetHoverTimer();
317             return;
318         }
319         if (sIsMenuPending_ || sIsForbidMenuEvent_ || !pattern->GetIsFocus()) {
320             return;
321         }
322         auto maximizeBtn = weakMaximizeBtn.Upgrade();
323         CHECK_NULL_VOID(maximizeBtn);
324         auto pipeline = weakPipeline.Upgrade();
325         CHECK_NULL_VOID(pipeline);
326         auto&& callback = [weakMaximizeBtn = AceType::WeakClaim(AceType::RawPtr(maximizeBtn))]() {
327             auto maximizeBtn = weakMaximizeBtn.Upgrade();
328             ShowMaxMenu(maximizeBtn, menuOffset_);
329         };
330         sContextTimer_.Reset(callback);
331         ACE_SCOPED_TRACE("ContainerModalEnhance::PendingMaxMenu");
332         pipeline->GetTaskExecutor()->PostDelayedTask(sContextTimer_, TaskExecutor::TaskType::UI, MENU_TASK_DELAY_TIME,
333             "ArkUIContainerModalShowMaxMenu");
334         sIsMenuPending_ = true;
335     };
336     hub->AddOnHoverEvent(AceType::MakeRefPtr<InputEvent>(std::move(hoverEventFuc)));
337 }
338 
ShowMaxMenu(const RefPtr<FrameNode> & targetNode,OffsetF menuPosition)339 RefPtr<FrameNode> ContainerModalViewEnhance::ShowMaxMenu(const RefPtr<FrameNode>& targetNode, OffsetF menuPosition)
340 {
341     LOGI("ContainerModal ShowMaxMenu called menuOffset_ = %{public}s", menuPosition.ToString().c_str());
342     if (!enableSplit_) {
343         LOGI("the app window is not support spilt menu");
344         return nullptr;
345     }
346     auto pipeline = PipelineContext::GetCurrentContext();
347     CHECK_NULL_RETURN(pipeline, nullptr);
348     auto windowManager = pipeline->GetWindowManager();
349     CHECK_NULL_RETURN(windowManager, nullptr);
350     // menu list
351     auto menuList = FrameNode::CreateFrameNode(
352         V2::LIST_COMPONENT_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ListPattern>());
353     auto listLayoutProperty = menuList->GetLayoutProperty<ListLayoutProperty>();
354     CHECK_NULL_RETURN(listLayoutProperty, nullptr);
355     listLayoutProperty->UpdateUserDefinedIdealSize(
356         CalcSize(CalcLength(MENU_CONTAINER_WIDTH), CalcLength(MENU_CONTAINER_HEIGHT)));
357     menuList->AddChild(BuildLeftSplitMenuItem());
358     menuList->AddChild(BuildRightSplitMenuItem());
359     auto subWindowManger = SubwindowManager::GetInstance();
360     CHECK_NULL_RETURN(subWindowManger, nullptr);
361     if ((!subWindowManger->GetSubwindow(Container::CurrentId()) ||
362             !subWindowManger->GetSubwindow(Container::CurrentId())->GetShown())) {
363         ACE_SCOPED_TRACE("ContainerModalViewEnhance::ShowMaxMenu");
364         MenuParam menuParam {};
365         menuParam.type = MenuType::CONTEXT_MENU;
366         SubwindowManager::GetInstance()->ShowMenuNG(menuList, menuParam, targetNode, menuPosition);
367     }
368     ResetHoverTimer();
369     return menuList;
370 }
371 
BuildLeftSplitMenuItem()372 RefPtr<FrameNode> ContainerModalViewEnhance::BuildLeftSplitMenuItem()
373 {
374     auto pipeline = PipelineContext::GetCurrentContext();
375     CHECK_NULL_RETURN(pipeline, nullptr);
376     auto windowManager = pipeline->GetWindowManager();
377     CHECK_NULL_RETURN(windowManager, nullptr);
378     auto leftSplitClickFunc = [weak = AceType::WeakClaim(AceType::RawPtr(windowManager))](GestureEvent& info) {
379         auto windowManager = weak.Upgrade();
380         if (!windowManager) {
381             LOGE("create leftsplit callback func failed,windowMannager is null!");
382             return;
383         }
384         EventReport::ReportClickTitleMaximizeMenu(MAX_MENU_ITEM_LEFT_SPLIT, MAX_MENU_DEFAULT_NOT_CHANGE);
385         windowManager->FireWindowSplitCallBack();
386     };
387     auto leftSplitEvent = AceType::MakeRefPtr<ClickEvent>(std::move(leftSplitClickFunc));
388     auto screenLeftRow = BuildMenuItem(Localization::GetInstance()->GetEntryLetters("window.leftSide"),
389         InternalResource::ResourceId::IC_WINDOW_MENU_SCREEN_L, leftSplitEvent, false);
390     screenLeftRow->UpdateInspectorId("EnhanceMenuScreenLeftRow");
391     return screenLeftRow;
392 }
393 
BuildRightSplitMenuItem()394 RefPtr<FrameNode> ContainerModalViewEnhance::BuildRightSplitMenuItem()
395 {
396     auto pipeline = PipelineContext::GetCurrentContext();
397     CHECK_NULL_RETURN(pipeline, nullptr);
398     auto windowManager = pipeline->GetWindowManager();
399     CHECK_NULL_RETURN(windowManager, nullptr);
400     auto rightSplitClickFunc = [weak = AceType::WeakClaim(AceType::RawPtr(windowManager))](GestureEvent& info) {
401         auto windowManager = weak.Upgrade();
402         if (!windowManager) {
403             LOGE("create rightSpiltBtn callback func failed, windowManager is null!");
404             return;
405         }
406         EventReport::ReportClickTitleMaximizeMenu(MAX_MENU_ITEM_RIGHT_SPLIT, MAX_MENU_DEFAULT_NOT_CHANGE);
407         windowManager->FireWindowSplitCallBack(false);
408     };
409     auto rightSplitEvent = AceType::MakeRefPtr<ClickEvent>(std::move(rightSplitClickFunc));
410     auto screenRightRow = BuildMenuItem(Localization::GetInstance()->GetEntryLetters("window.rightSide"),
411         InternalResource::ResourceId::IC_WINDOW_MENU_SCREEN_N, rightSplitEvent, false);
412     screenRightRow->UpdateInspectorId("EnhanceMenuScreenRightRow");
413     return screenRightRow;
414 }
415 
BuildMenuItem(std::string title,InternalResource::ResourceId resourceId,RefPtr<ClickEvent> event,bool chooseCurrent)416 RefPtr<FrameNode> ContainerModalViewEnhance::BuildMenuItem(
417     std::string title, InternalResource::ResourceId resourceId, RefPtr<ClickEvent> event, bool chooseCurrent)
418 {
419     auto pipeline = PipelineContext::GetCurrentContext();
420     CHECK_NULL_RETURN(pipeline, nullptr);
421     auto windowManager = pipeline->GetWindowManager();
422     CHECK_NULL_RETURN(windowManager, nullptr);
423 
424     // padding+pic+padding+text+padding+
425     auto containerTitleRow = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
426         AceType::MakeRefPtr<LinearLayoutPattern>(false));
427     // setRadius 8vp
428     auto render = containerTitleRow->GetRenderContext();
429     BorderRadiusProperty borderRadiusProperty;
430     borderRadiusProperty.SetRadius(MENU_ITEM_RADIUS);
431     render->UpdateBorderRadius(borderRadiusProperty);
432     // 232 48  leftPadding 4vp
433     auto layoutProperty = containerTitleRow->GetLayoutProperty<LinearLayoutProperty>();
434     layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(MENU_ITEM_WIDTH), CalcLength(MENU_ITEM_HEIGHT)));
435     layoutProperty->UpdateMainAxisAlign(FlexAlign::FLEX_START);
436     layoutProperty->UpdateCrossAxisAlign(FlexAlign::CENTER);
437     PaddingProperty rowLeftPadding;
438     rowLeftPadding.left = CalcLength(MENU_ITEM_LEFT_PADDING);
439     layoutProperty->UpdatePadding(rowLeftPadding);
440 
441     auto leftIcon = BuildMenuItemIcon(resourceId);
442     // text 144 22  padding 8vp
443     auto titleLabel = FrameNode::CreateFrameNode(
444         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
445     auto textLayoutProperty = titleLabel->GetLayoutProperty<TextLayoutProperty>();
446     textLayoutProperty->UpdateContent(title);
447     textLayoutProperty->UpdateMaxLines(MENU_ITEM_MAXLINES);
448     textLayoutProperty->UpdateFontSize(TITLE_TEXT_FONT_SIZE);
449     textLayoutProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
450     textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
451     textLayoutProperty->UpdateUserDefinedIdealSize(
452         CalcSize(CalcLength(MENU_ITEM_TEXT_WIDTH), CalcLength(MENU_ITEM_TEXT_HEIGHT)));
453     PaddingProperty padding;
454     padding.left = CalcLength(MENU_ITEM_TEXT_PADDING);
455     auto text = BuildMenuItemPadding(padding, titleLabel);
456 
457     // add icon and label
458     containerTitleRow->AddChild(leftIcon);
459     containerTitleRow->AddChild(text);
460     auto hub = containerTitleRow->GetOrCreateGestureEventHub();
461     CHECK_NULL_RETURN(hub, nullptr);
462     hub->AddClickEvent(event);
463     BondingMenuItemEvent(containerTitleRow);
464     return containerTitleRow;
465 }
466 
BondingMenuItemEvent(RefPtr<FrameNode> item)467 void ContainerModalViewEnhance::BondingMenuItemEvent(RefPtr<FrameNode> item)
468 {
469     auto inputHub = item->GetOrCreateInputEventHub();
470     auto theme = PipelineContext::GetCurrentContext()->GetTheme<ListItemTheme>();
471     CHECK_NULL_VOID(theme);
472     auto hoverFunc = [item, weak = AceType::WeakClaim(AceType::RawPtr(theme))](bool isHover) {
473         auto theme = weak.Upgrade();
474         auto renderContext = item->GetRenderContext();
475         if (isHover && theme) {
476             renderContext->UpdateBackgroundColor(theme->GetItemHoverColor());
477         } else {
478             renderContext->UpdateBackgroundColor(MENU_ITEM_COLOR);
479         }
480     };
481     auto hoverEvent = AceType::MakeRefPtr<InputEvent>(std::move(hoverFunc));
482     inputHub->AddOnHoverEvent(hoverEvent);
483 
484     auto clickFunc = [item, weak = AceType::WeakClaim(AceType::RawPtr(theme))](MouseInfo& info) -> void {
485         auto theme = weak.Upgrade();
486         if (MouseAction::PRESS == info.GetAction() && theme) {
487             auto renderContext = item->GetRenderContext();
488             renderContext->UpdateBackgroundColor(theme->GetClickColor());
489         }
490     };
491     auto clickEvent = AceType::MakeRefPtr<InputEvent>(std::move(clickFunc));
492     inputHub->AddOnMouseEvent(clickEvent);
493 }
494 
BuildMenuItemIcon(InternalResource::ResourceId resourceId)495 RefPtr<FrameNode> ContainerModalViewEnhance::BuildMenuItemIcon(InternalResource::ResourceId resourceId)
496 {
497     auto icon = FrameNode::CreateFrameNode(
498         V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
499     auto iconLayoutProperty = icon->GetLayoutProperty<ImageLayoutProperty>();
500     ImageSourceInfo sourceInfo;
501     sourceInfo.SetResourceId(resourceId);
502     auto theme = PipelineContext::GetCurrentContext()->GetTheme<AdvancedPatternTheme>();
503     if (theme) {
504         sourceInfo.SetFillColor(theme->GetPrimaryColor());
505     } else {
506         LOGI("BuildMenuItemIcon AdvancedPatternTheme is null");
507     }
508     iconLayoutProperty->UpdateImageSourceInfo(sourceInfo);
509     iconLayoutProperty->UpdateUserDefinedIdealSize(
510         CalcSize(CalcLength(TITLE_BUTTON_SIZE), CalcLength(TITLE_BUTTON_SIZE)));
511     icon->MarkModifyDone();
512     return icon;
513 }
514 
BuildMenuItemPadding(PaddingProperty padding,RefPtr<FrameNode> node)515 RefPtr<FrameNode> ContainerModalViewEnhance::BuildMenuItemPadding(PaddingProperty padding, RefPtr<FrameNode> node)
516 {
517     auto row = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
518         AceType::MakeRefPtr<LinearLayoutPattern>(false));
519 
520     auto rowLayoutProperty = row->GetLayoutProperty<LinearLayoutProperty>();
521     rowLayoutProperty->UpdatePadding(padding);
522     row->AddChild(node);
523 
524     return row;
525 }
526 
ResetHoverTimer()527 void ContainerModalViewEnhance::ResetHoverTimer()
528 {
529     sContextTimer_.Reset(nullptr);
530     sIsMenuPending_ = false;
531 }
532 
CalculateMenuOffset(OffsetF currentOffset)533 void ContainerModalViewEnhance::CalculateMenuOffset(OffsetF currentOffset)
534 {
535     auto screenWidth = SystemProperties::GetDevicePhysicalWidth();
536     auto screenHeight = SystemProperties::GetDevicePhysicalHeight();
537     auto offsetX = currentOffset.GetX();
538     auto offsetY = currentOffset.GetY();
539     auto menuWidth = MENU_CONTAINER_WIDTH.ConvertToPx() + CONTENT_PADDING.ConvertToPx() * 2;
540     auto menuHeight = MENU_CONTAINER_HEIGHT.ConvertToPx() + CONTENT_PADDING.ConvertToPx() * 2;
541     auto buttonWidth = TITLE_ICON_SIZE.ConvertToPx() + CONTENT_PADDING.ConvertToPx() * 2;
542     auto pipeline = PipelineContext::GetCurrentContext();
543     CHECK_NULL_VOID(pipeline);
544     auto titleHeight = pipeline->GetCustomTitleHeight().ConvertToPx();
545     if (offsetX < MENU_SAFETY_X.ConvertToPx()) {
546         LOGI("ContainerModalViewEnhance::RecalculateMenuOffset OffsetX cover screen left");
547         offsetX = offsetX + menuWidth - buttonWidth;
548     }
549     if (offsetX > screenWidth - menuWidth - MENU_SAFETY_X.ConvertToPx()) {
550         LOGI("ContainerModalViewEnhance::RecalculateMenuOffset OffsetX cover screen right");
551         offsetX = screenWidth - menuWidth - MENU_SAFETY_X.ConvertToPx();
552     }
553     if (offsetY > screenHeight - menuHeight - MENU_SAFETY_Y.ConvertToPx()) {
554         LOGI("ContainerModalViewEnhance::RecalculateMenuOffset OffsetX cover screen bottom");
555         offsetY = offsetY - menuHeight - titleHeight;
556     }
557     menuOffset_ = { offsetX, offsetY };
558     LOGI("ContainerModal menuOffset_ = %{public}s", menuOffset_.ToString().c_str());
559 }
560 
BuildGestureRow(RefPtr<FrameNode> & containerNode)561 RefPtr<FrameNode> ContainerModalViewEnhance::BuildGestureRow(RefPtr<FrameNode>& containerNode)
562 {
563     auto gestureRow = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
564         AceType::MakeRefPtr<LinearLayoutPattern>(false));
565     auto renderContext = gestureRow->GetRenderContext();
566     renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
567     renderContext->UpdatePosition(OffsetT<Dimension>());
568     SetTapGestureEvent(containerNode, gestureRow);
569     auto layoutProp = gestureRow->GetLayoutProperty();
570     layoutProp->UpdateUserDefinedIdealSize(
571         CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(CONTAINER_TITLE_HEIGHT)));
572     return gestureRow;
573 }
574 
575 } // namespace OHOS::Ace::NG
576