1 /*
2  * Copyright (c) 2022-2024 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/container_modal_pattern.h"
17 
18 #include "base/resource/internal_resource.h"
19 #include "base/subwindow/subwindow_manager.h"
20 #include "base/utils/utils.h"
21 #include "core/common/container.h"
22 #include "core/common/container_scope.h"
23 #include "core/components_ng/pattern/button/button_event_hub.h"
24 #include "core/components_ng/pattern/container_modal/container_modal_theme.h"
25 #include "core/components_ng/pattern/button/button_layout_property.h"
26 #include "core/components_ng/pattern/image/image_layout_property.h"
27 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
28 #include "core/components_ng/pattern/text/text_layout_property.h"
29 #include "core/image/image_source_info.h"
30 
31 namespace OHOS::Ace::NG {
32 
33 namespace {
34 constexpr int32_t LEFT_SPLIT_BUTTON_INDEX = 0;
35 constexpr int32_t MAX_RECOVER_BUTTON_INDEX = 1;
36 constexpr int32_t MINIMIZE_BUTTON_INDEX = 2;
37 constexpr int32_t CLOSE_BUTTON_INDEX = 3;
38 constexpr int32_t TITLE_POPUP_DURATION = 200;
39 constexpr double MOUSE_MOVE_POPUP_DISTANCE = 5.0; // 5.0px
40 constexpr double MOVE_POPUP_DISTANCE_X = 40.0;    // 40.0px
41 constexpr double MOVE_POPUP_DISTANCE_Y = 20.0;    // 20.0px
42 constexpr double TITLE_POPUP_DISTANCE = 37.0;     // 37vp height of title
43 } // namespace
44 
UpdateRowHeight(const RefPtr<FrameNode> & row,Dimension height)45 void UpdateRowHeight(const RefPtr<FrameNode>& row, Dimension height)
46 {
47     CHECK_NULL_VOID(row);
48     auto layoutProperty = row->GetLayoutProperty<LinearLayoutProperty>();
49     CHECK_NULL_VOID(layoutProperty);
50     layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(height)));
51     row->MarkModifyDone();
52     row->MarkDirtyNode();
53 }
54 
ShowTitle(bool isShow,bool hasDeco,bool needUpdate)55 void ContainerModalPattern::ShowTitle(bool isShow, bool hasDeco, bool needUpdate)
56 {
57     auto containerNode = GetHost();
58     CHECK_NULL_VOID(containerNode);
59     auto customTitleRow = GetCustomTitleRow();
60     CHECK_NULL_VOID(customTitleRow);
61     auto floatingTitleRow = GetFloatingTitleRow();
62     CHECK_NULL_VOID(floatingTitleRow);
63     if (needUpdate) {
64         LOGI("title is need update, isFocus_: %{public}d", isFocus_);
65         ChangeCustomTitle(isFocus_);
66         ChangeControlButtons(isFocus_);
67         return;
68     }
69 
70     auto pipelineContext = PipelineContext::GetCurrentContext();
71     CHECK_NULL_VOID(pipelineContext);
72     auto theme = pipelineContext->GetTheme<ContainerModalTheme>();
73     auto stackNode = GetStackNode();
74     CHECK_NULL_VOID(stackNode);
75     auto windowManager = pipelineContext->GetWindowManager();
76     CHECK_NULL_VOID(windowManager);
77     windowMode_ = windowManager->GetWindowMode();
78     hasDeco_ = hasDeco;
79     LOGI("ShowTitle isShow: %{public}d, windowMode: %{public}d, hasDeco: %{public}d", isShow, windowMode_, hasDeco_);
80     if (!hasDeco_) {
81         isShow = false;
82     }
83 
84     // set container window show state to RS
85     pipelineContext->SetContainerWindow(isShow);
86 
87     // update container modal padding and border
88     auto layoutProperty = containerNode->GetLayoutProperty();
89     CHECK_NULL_VOID(layoutProperty);
90     layoutProperty->UpdateAlignment(Alignment::TOP_LEFT);
91     PaddingProperty padding;
92     if (isShow && customTitleSettedShow_) {
93         padding = { CalcLength(CONTENT_PADDING), CalcLength(CONTENT_PADDING), std::nullopt,
94             CalcLength(CONTENT_PADDING) };
95     }
96     layoutProperty->UpdatePadding(padding);
97     BorderWidthProperty borderWidth;
98     borderWidth.SetBorderWidth(isShow ? CONTAINER_BORDER_WIDTH : 0.0_vp);
99     layoutProperty->UpdateBorderWidth(borderWidth);
100 
101     auto renderContext = containerNode->GetRenderContext();
102     CHECK_NULL_VOID(renderContext);
103     renderContext->UpdateBackgroundColor(theme->GetBackGroundColor(isFocus_));
104     BorderRadiusProperty borderRadius;
105     borderRadius.SetRadius(isShow ? CONTAINER_OUTER_RADIUS : 0.0_vp);
106     renderContext->UpdateBorderRadius(borderRadius);
107     BorderColorProperty borderColor;
108     borderColor.SetColor(isShow ? CONTAINER_BORDER_COLOR : Color::TRANSPARENT);
109     renderContext->UpdateBorderColor(borderColor);
110 
111     // update stack content border
112     auto stackLayoutProperty = stackNode->GetLayoutProperty();
113     CHECK_NULL_VOID(stackLayoutProperty);
114     stackLayoutProperty->UpdateLayoutWeight(1.0f);
115 
116     auto stackRenderContext = stackNode->GetRenderContext();
117     CHECK_NULL_VOID(stackRenderContext);
118     BorderRadiusProperty stageBorderRadius;
119     stageBorderRadius.SetRadius(isShow ? GetStackNodeRadius() : 0.0_vp);
120     stackRenderContext->UpdateBorderRadius(stageBorderRadius);
121     stackRenderContext->SetClipToBounds(true);
122 
123     auto customTitleLayoutProperty = customTitleRow->GetLayoutProperty();
124     CHECK_NULL_VOID(customTitleLayoutProperty);
125     customTitleLayoutProperty->UpdateVisibility(
126         (isShow && customTitleSettedShow_) ? VisibleType::VISIBLE : VisibleType::GONE);
127     auto floatingLayoutProperty = floatingTitleRow->GetLayoutProperty();
128     CHECK_NULL_VOID(floatingLayoutProperty);
129     floatingLayoutProperty->UpdateVisibility(VisibleType::GONE);
130 
131     auto controlButtonsNode = GetControlButtonRow();
132     CHECK_NULL_VOID(controlButtonsNode);
133     auto controlButtonsLayoutProperty = controlButtonsNode->GetLayoutProperty();
134     CHECK_NULL_VOID(controlButtonsLayoutProperty);
135     AddPanEvent(controlButtonsNode);
136     ChangeFloatingTitle(isFocus_);
137     ChangeControlButtons(isFocus_);
138 
139     auto controlButtonsContext = controlButtonsNode->GetRenderContext();
140     CHECK_NULL_VOID(controlButtonsContext);
141     controlButtonsLayoutProperty->UpdateVisibility(isShow ? VisibleType::VISIBLE : VisibleType::GONE);
142 }
143 
InitContainerEvent()144 void ContainerModalPattern::InitContainerEvent()
145 {
146     bool isChangeTitleStyle = SystemProperties::GetTitleStyleEnabled();
147     if (isChangeTitleStyle) {
148         return;
149     }
150     auto containerNode = GetHost();
151     CHECK_NULL_VOID(containerNode);
152     auto touchEventHub = containerNode->GetOrCreateGestureEventHub();
153     CHECK_NULL_VOID(touchEventHub);
154     auto controlButtonsNode = GetControlButtonRow();
155     CHECK_NULL_VOID(controlButtonsNode);
156     auto controlButtonsLayoutProperty = controlButtonsNode->GetLayoutProperty();
157     CHECK_NULL_VOID(controlButtonsLayoutProperty);
158     auto controlButtonsContext = controlButtonsNode->GetRenderContext();
159     CHECK_NULL_VOID(controlButtonsContext);
160 
161     auto floatingTitleRow = GetFloatingTitleRow();
162     CHECK_NULL_VOID(floatingTitleRow);
163     auto floatingLayoutProperty = floatingTitleRow->GetLayoutProperty();
164     CHECK_NULL_VOID(floatingLayoutProperty);
165     auto floatingContext = floatingTitleRow->GetRenderContext();
166     CHECK_NULL_VOID(floatingContext);
167 
168     auto containerNodeContext = containerNode->GetContext();
169     CHECK_NULL_VOID(containerNodeContext);
170     auto titlePopupDistance = TITLE_POPUP_DISTANCE * containerNodeContext->GetDensity();
171     AnimationOption option;
172     option.SetDuration(TITLE_POPUP_DURATION);
173     option.SetCurve(Curves::EASE_IN_OUT);
174 
175     // init touch event
176     touchEventHub->SetTouchEvent([controlButtonsLayoutProperty, floatingLayoutProperty, controlButtonsContext,
177                                      floatingContext, option, titlePopupDistance,
178                                      weak = WeakClaim(this)](TouchEventInfo& info) {
179         auto container = weak.Upgrade();
180         CHECK_NULL_VOID(container);
181         if (!container->hasDeco_) {
182             return;
183         }
184         if (info.GetChangedTouches().begin()->GetGlobalLocation().GetY() <= titlePopupDistance) {
185             // step1. Record the coordinates of the start of the touch.
186             if (info.GetChangedTouches().begin()->GetTouchType() == TouchType::DOWN) {
187                 container->moveX_ = static_cast<float>(info.GetChangedTouches().begin()->GetGlobalLocation().GetX());
188                 container->moveY_ = static_cast<float>(info.GetChangedTouches().begin()->GetGlobalLocation().GetY());
189                 return;
190             }
191             if (info.GetChangedTouches().begin()->GetTouchType() != TouchType::MOVE ||
192                 !container->CanShowFloatingTitle()) {
193                 return;
194             }
195 
196             // step2. Calculate the coordinates of touch move relative to touch down.
197             auto deltaMoveX = fabs(info.GetChangedTouches().begin()->GetGlobalLocation().GetX() - container->moveX_);
198             auto deltaMoveY = info.GetChangedTouches().begin()->GetGlobalLocation().GetY() - container->moveY_;
199             // step3. If the horizontal distance of the touch move does not exceed 10px and the vertical distance
200             // exceeds 20px, the floating title will be displayed.
201             if (deltaMoveX <= MOVE_POPUP_DISTANCE_X && deltaMoveY >= MOVE_POPUP_DISTANCE_Y) {
202                 controlButtonsContext->OnTransformTranslateUpdate(
203                     { 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
204                 controlButtonsLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
205                 AnimationUtils::Animate(option, [controlButtonsContext]() {
206                     controlButtonsContext->OnTransformTranslateUpdate({ 0.0f, 0.0f, 0.0f });
207                 });
208                 floatingContext->OnTransformTranslateUpdate({ 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
209                 floatingLayoutProperty->UpdateVisibility(
210                     container->floatingTitleSettedShow_ ? VisibleType::VISIBLE : VisibleType::GONE);
211                 AnimationUtils::Animate(option, [floatingContext]() {
212                     floatingContext->OnTransformTranslateUpdate({ 0.0f, 0.0f, 0.0f });
213                 });
214             }
215             return;
216         }
217         if (info.GetChangedTouches().begin()->GetTouchType() != TouchType::DOWN) {
218             return;
219         }
220         if (floatingLayoutProperty->GetVisibilityValue(VisibleType::GONE) != VisibleType::VISIBLE) {
221             return;
222         }
223         // step4. Touch other area to hide floating title.
224         AnimationUtils::Animate(
225             option,
226             [controlButtonsContext, floatingContext, titlePopupDistance]() {
227                 controlButtonsContext->OnTransformTranslateUpdate(
228                     { 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
229                 floatingContext->OnTransformTranslateUpdate({ 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
230             },
231             [floatingLayoutProperty, id = Container::CurrentId()]() {
232                 ContainerScope scope(id);
233                 floatingLayoutProperty->UpdateVisibility(VisibleType::GONE);
234             });
235     });
236 
237     // init mouse event
238     auto mouseEventHub = containerNode->GetOrCreateInputEventHub();
239     CHECK_NULL_VOID(mouseEventHub);
240     mouseEventHub->SetMouseEvent([controlButtonsLayoutProperty, floatingLayoutProperty, controlButtonsContext,
241                                      floatingContext, option, titlePopupDistance,
242                                      weak = WeakClaim(this)](MouseInfo& info) {
243         auto container = weak.Upgrade();
244         CHECK_NULL_VOID(container);
245         auto action = info.GetAction();
246         if ((action != MouseAction::MOVE && action != MouseAction::WINDOW_LEAVE) || !container->hasDeco_) {
247             return;
248         }
249         if (info.GetLocalLocation().GetY() <= MOUSE_MOVE_POPUP_DISTANCE && container->CanShowFloatingTitle()) {
250             controlButtonsContext->OnTransformTranslateUpdate({ 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
251             controlButtonsLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
252             AnimationUtils::Animate(option, [controlButtonsContext]() {
253                 controlButtonsContext->OnTransformTranslateUpdate({ 0.0f, 0.0f, 0.0f });
254             });
255             floatingContext->OnTransformTranslateUpdate({ 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
256             floatingLayoutProperty->UpdateVisibility(
257                 container->floatingTitleSettedShow_ ? VisibleType::VISIBLE : VisibleType::GONE);
258             AnimationUtils::Animate(option, [floatingContext]() {
259                 floatingContext->OnTransformTranslateUpdate({ 0.0f, 0.0f, 0.0f });
260             });
261         }
262 
263         if (!container->CanHideFloatingTitle()) {
264             return;
265         }
266         if ((info.GetLocalLocation().GetY() >= titlePopupDistance || action == MouseAction::WINDOW_LEAVE) &&
267             floatingLayoutProperty->GetVisibilityValue(VisibleType::GONE) == VisibleType::VISIBLE) {
268             AnimationUtils::Animate(
269                 option,
270                 [controlButtonsContext, floatingContext, titlePopupDistance]() {
271                     controlButtonsContext->OnTransformTranslateUpdate(
272                         { 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
273                     floatingContext->OnTransformTranslateUpdate(
274                         { 0.0f, static_cast<float>(-titlePopupDistance), 0.0f });
275                 },
276                 [floatingLayoutProperty]() {
277                     floatingLayoutProperty->UpdateVisibility(VisibleType::GONE);
278                 });
279         }
280     });
281 }
282 
AddPanEvent(const RefPtr<FrameNode> & controlButtonsNode)283 void ContainerModalPattern::AddPanEvent(const RefPtr<FrameNode>& controlButtonsNode)
284 {
285     auto eventHub = controlButtonsNode->GetOrCreateGestureEventHub();
286     CHECK_NULL_VOID(eventHub);
287     PanDirection panDirection;
288     panDirection.type = PanDirection::ALL;
289 
290     if (!panEvent_) {
291         auto pipeline = PipelineContext::GetCurrentContext();
292         CHECK_NULL_VOID(pipeline);
293         auto windowManager = pipeline->GetWindowManager();
294         CHECK_NULL_VOID(windowManager);
295         // touch the title to move the floating window
296         auto panActionStart = [wk = WeakClaim(RawPtr(windowManager))](const GestureEvent& event) {
297             auto windowManager = wk.Upgrade();
298             CHECK_NULL_VOID(windowManager);
299             if ((windowManager->GetCurrentWindowMaximizeMode() != MaximizeMode::MODE_AVOID_SYSTEM_BAR) &&
300                 (event.GetSourceTool() != SourceTool::TOUCHPAD)) {
301                 windowManager->WindowStartMove();
302                 SubwindowManager::GetInstance()->ClearToastInSubwindow();
303             }
304         };
305         panEvent_ = MakeRefPtr<PanEvent>(std::move(panActionStart), nullptr, nullptr, nullptr);
306     }
307     eventHub->AddPanEvent(panEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
308 }
309 
RemovePanEvent(const RefPtr<FrameNode> & controlButtonsNode)310 void ContainerModalPattern::RemovePanEvent(const RefPtr<FrameNode>& controlButtonsNode)
311 {
312     auto eventHub = controlButtonsNode->GetOrCreateGestureEventHub();
313     CHECK_NULL_VOID(eventHub);
314 
315     if (!panEvent_) {
316         return;
317     }
318     eventHub->RemovePanEvent(panEvent_);
319 }
320 
OnWindowFocused()321 void ContainerModalPattern::OnWindowFocused()
322 {
323     WindowFocus(true);
324 }
325 
OnWindowUnfocused()326 void ContainerModalPattern::OnWindowUnfocused()
327 {
328     WindowFocus(false);
329 }
330 
OnWindowForceUnfocused()331 void ContainerModalPattern::OnWindowForceUnfocused() {}
332 
WindowFocus(bool isFocus)333 void ContainerModalPattern::WindowFocus(bool isFocus)
334 {
335     auto theme = PipelineContext::GetCurrentContext()->GetTheme<ContainerModalTheme>();
336     isFocus_ = isFocus;
337     auto containerNode = GetHost();
338     CHECK_NULL_VOID(containerNode);
339 
340     // update container modal background
341     auto renderContext = containerNode->GetRenderContext();
342     CHECK_NULL_VOID(renderContext);
343     renderContext->UpdateBackgroundColor(theme->GetBackGroundColor(isFocus));
344     BorderColorProperty borderColor;
345     borderColor.SetColor(isFocus ? CONTAINER_BORDER_COLOR : CONTAINER_BORDER_COLOR_LOST_FOCUS);
346     renderContext->UpdateBorderColor(borderColor);
347 
348     ChangeCustomTitle(isFocus);
349     ChangeFloatingTitle(isFocus);
350     ChangeControlButtons(isFocus);
351 }
352 
ChangeCustomTitle(bool isFocus)353 void ContainerModalPattern::ChangeCustomTitle(bool isFocus)
354 {
355     // update custom title label
356     auto customTitleNode = GetCustomTitleNode();
357     CHECK_NULL_VOID(customTitleNode);
358     isFocus ? customTitleNode->FireOnWindowFocusedCallback() : customTitleNode->FireOnWindowUnfocusedCallback();
359 }
360 
ChangeControlButtons(bool isFocus)361 void ContainerModalPattern::ChangeControlButtons(bool isFocus)
362 {
363     auto containerNode = GetHost();
364     CHECK_NULL_VOID(containerNode);
365     auto controlButtonsNode = GetControlButtonRow();
366     CHECK_NULL_VOID(controlButtonsNode);
367 
368     // update leftSplit button
369     auto leftSplitButton =
370         AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, LEFT_SPLIT_BUTTON_INDEX));
371     ChangeTitleButtonIcon(leftSplitButton,
372         isFocus ? InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_SPLIT_LEFT
373                 : InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_SPLIT_LEFT,
374         isFocus, false);
375 
376     // hide leftSplit button when window mode is WINDOW_MODE_SPLIT_PRIMARY type or split button can not show
377     bool hideLeftSplit = hideSplitButton_ || windowMode_ == WindowMode::WINDOW_MODE_SPLIT_PRIMARY;
378     leftSplitButton->GetLayoutProperty()->UpdateVisibility(hideLeftSplit ? VisibleType::GONE : VisibleType::VISIBLE);
379 
380     // update maximize button
381     auto maximizeButton =
382         AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, MAX_RECOVER_BUTTON_INDEX));
383     auto pipeline = PipelineContext::GetCurrentContext();
384     auto windowManager = pipeline->GetWindowManager();
385     MaximizeMode mode = windowManager->GetCurrentWindowMaximizeMode();
386     InternalResource::ResourceId maxId;
387     if (mode == MaximizeMode::MODE_AVOID_SYSTEM_BAR || windowMode_ == WindowMode::WINDOW_MODE_FULLSCREEN) {
388         maxId = InternalResource::ResourceId::IC_WINDOW_RESTORES;
389     } else {
390         maxId = InternalResource::ResourceId::IC_WINDOW_MAX;
391     }
392 
393     ChangeTitleButtonIcon(maximizeButton, maxId, isFocus, false);
394     // update minimize button
395     auto minimizeButton =
396         AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, MINIMIZE_BUTTON_INDEX));
397     ChangeTitleButtonIcon(minimizeButton,
398         InternalResource::ResourceId::IC_WINDOW_MIN, isFocus, false);
399 
400     // update close button
401     auto closeButton = AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, CLOSE_BUTTON_INDEX));
402     ChangeTitleButtonIcon(closeButton,
403         InternalResource::ResourceId::IC_WINDOW_CLOSE, isFocus, true);
404 }
405 
ChangeFloatingTitle(bool isFocus)406 void ContainerModalPattern::ChangeFloatingTitle(bool isFocus)
407 {
408     // update floating custom title label
409     auto customFloatingTitleNode = GetFloatingTitleNode();
410     CHECK_NULL_VOID(customFloatingTitleNode);
411     isFocus ? customFloatingTitleNode->FireOnWindowFocusedCallback()
412             : customFloatingTitleNode->FireOnWindowUnfocusedCallback();
413 }
414 
ChangeTitleButtonIcon(const RefPtr<FrameNode> & buttonNode,InternalResource::ResourceId icon,bool isFocus,bool isCloseBtn)415 void ContainerModalPattern::ChangeTitleButtonIcon(
416     const RefPtr<FrameNode>& buttonNode, InternalResource::ResourceId icon, bool isFocus, bool isCloseBtn)
417 {
418     auto theme = PipelineContext::GetCurrentContext()->GetTheme<ContainerModalTheme>();
419     auto renderContext = buttonNode->GetRenderContext();
420     CHECK_NULL_VOID(renderContext);
421     auto colorType = isFocus ? ControlBtnColorType::NORMAL : ControlBtnColorType::UNFOCUS;
422     auto color = theme->GetControlBtnColor(isCloseBtn, colorType);
423     renderContext->UpdateBackgroundColor(color);
424     auto buttonIcon = AceType::DynamicCast<FrameNode>(buttonNode->GetChildren().front());
425     CHECK_NULL_VOID(buttonIcon);
426     ImageSourceInfo imageSourceInfo;
427     imageSourceInfo.SetResourceId(icon);
428     colorType = isFocus ? ControlBtnColorType::NORMAL_FILL : ControlBtnColorType::UNFOCUS_FILL;
429     color = theme->GetControlBtnColor(isCloseBtn,  colorType);
430     imageSourceInfo.SetFillColor(color);
431     auto imageLayoutProperty = buttonIcon->GetLayoutProperty<ImageLayoutProperty>();
432     imageLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
433     buttonIcon->MarkModifyDone();
434     buttonNode->MarkModifyDone();
435 }
436 
CanShowFloatingTitle()437 bool ContainerModalPattern::CanShowFloatingTitle()
438 {
439     auto floatingTitleRow = GetFloatingTitleRow();
440     CHECK_NULL_RETURN(floatingTitleRow, false);
441     auto floatingLayoutProperty = floatingTitleRow->GetLayoutProperty();
442     CHECK_NULL_RETURN(floatingLayoutProperty, false);
443 
444     if (windowMode_ != WindowMode::WINDOW_MODE_FULLSCREEN && windowMode_ != WindowMode::WINDOW_MODE_SPLIT_PRIMARY &&
445         windowMode_ != WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
446         LOGI("Window is not full screen or split screen, can not show floating title.");
447         return false;
448     }
449 
450     if (floatingLayoutProperty->GetVisibilityValue(VisibleType::GONE) == VisibleType::VISIBLE) {
451         LOGI("Floating tittle is visible now, no need to show again.");
452         return false;
453     }
454     return true;
455 }
456 
SetAppTitle(const std::string & title)457 void ContainerModalPattern::SetAppTitle(const std::string& title)
458 {
459     TAG_LOGI(AceLogTag::ACE_APPBAR, "SetAppTitle successfully");
460     auto customTitleNode = GetCustomTitleNode();
461     CHECK_NULL_VOID(customTitleNode);
462     customTitleNode->FireAppTitleCallback(title);
463 
464     auto customFloatingTitleNode = GetFloatingTitleNode();
465     CHECK_NULL_VOID(customFloatingTitleNode);
466     customFloatingTitleNode->FireAppTitleCallback(title);
467 }
468 
SetAppIcon(const RefPtr<PixelMap> & icon)469 void ContainerModalPattern::SetAppIcon(const RefPtr<PixelMap>& icon)
470 {
471     CHECK_NULL_VOID(icon);
472     LOGI("SetAppIcon successfully");
473     auto customTitleNode = GetCustomTitleNode();
474     CHECK_NULL_VOID(customTitleNode);
475     customTitleNode->FireAppIconCallback(icon);
476 
477     auto customFloatingTitleNode = GetFloatingTitleNode();
478     CHECK_NULL_VOID(customFloatingTitleNode);
479     customFloatingTitleNode->FireAppIconCallback(icon);
480 }
481 
SetTitleButtonHide(const RefPtr<FrameNode> & controlButtonsNode,bool hideSplit,bool hideMaximize,bool hideMinimize,bool hideClose)482 void ContainerModalPattern::SetTitleButtonHide(
483     const RefPtr<FrameNode>& controlButtonsNode, bool hideSplit, bool hideMaximize, bool hideMinimize, bool hideClose)
484 {
485     auto leftSplitButton =
486         AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, LEFT_SPLIT_BUTTON_INDEX));
487     CHECK_NULL_VOID(leftSplitButton);
488     leftSplitButton->GetLayoutProperty()->UpdateVisibility(hideSplit ? VisibleType::GONE : VisibleType::VISIBLE);
489     leftSplitButton->MarkDirtyNode();
490 
491     auto maximizeButton =
492         AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, MAX_RECOVER_BUTTON_INDEX));
493     CHECK_NULL_VOID(maximizeButton);
494     maximizeButton->GetLayoutProperty()->UpdateVisibility(hideMaximize ? VisibleType::GONE : VisibleType::VISIBLE);
495     maximizeButton->MarkDirtyNode();
496 
497     auto minimizeButton = AceType::DynamicCast<FrameNode>(
498         GetTitleItemByIndex(controlButtonsNode, MINIMIZE_BUTTON_INDEX));
499     CHECK_NULL_VOID(minimizeButton);
500     minimizeButton->GetLayoutProperty()->UpdateVisibility(hideMinimize ? VisibleType::GONE : VisibleType::VISIBLE);
501     minimizeButton->MarkDirtyNode();
502 
503     auto closeButton = AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsNode, CLOSE_BUTTON_INDEX));
504     CHECK_NULL_VOID(closeButton);
505     closeButton->GetLayoutProperty()->UpdateVisibility(hideClose ? VisibleType::GONE : VisibleType::VISIBLE);
506     closeButton->MarkDirtyNode();
507 }
508 
SetContainerButtonHide(bool hideSplit,bool hideMaximize,bool hideMinimize,bool hideClose)509 void ContainerModalPattern::SetContainerButtonHide(bool hideSplit, bool hideMaximize, bool hideMinimize, bool hideClose)
510 {
511     auto controlButtonsRow = GetControlButtonRow();
512     CHECK_NULL_VOID(controlButtonsRow);
513     SetTitleButtonHide(controlButtonsRow, hideSplit, hideMaximize, hideMinimize, hideClose);
514     hideSplitButton_ = hideSplit;
515     TAG_LOGI(AceLogTag::ACE_APPBAR,
516         "Set containerModal button status successfully, "
517         "hideSplit: %{public}d, hideMaximize: %{public}d, "
518         "hideMinimize: %{public}d, hideClose: %{public}d",
519         hideSplit, hideMaximize, hideMinimize, hideClose);
520 }
521 
SetCloseButtonStatus(bool isEnabled)522 void ContainerModalPattern::SetCloseButtonStatus(bool isEnabled)
523 {
524     auto controlButtonsRow = GetControlButtonRow();
525     CHECK_NULL_VOID(controlButtonsRow);
526 
527     // set closeButton enable or disable
528     auto closeButton = AceType::DynamicCast<FrameNode>(GetTitleItemByIndex(controlButtonsRow, CLOSE_BUTTON_INDEX));
529     CHECK_NULL_VOID(closeButton);
530     auto buttonEvent = closeButton->GetEventHub<ButtonEventHub>();
531     CHECK_NULL_VOID(buttonEvent);
532     buttonEvent->SetEnabled(isEnabled);
533     LOGI("Set close button status %{public}s", isEnabled ? "enable" : "disable");
534 }
535 
UpdateGestureRowVisible()536 void ContainerModalPattern::UpdateGestureRowVisible()
537 {
538     auto gestureRow = GetGestureRow();
539     CHECK_NULL_VOID(gestureRow);
540     auto customTitleRow = GetCustomTitleRow();
541     CHECK_NULL_VOID(customTitleRow);
542     auto buttonsRow = GetControlButtonRow();
543     CHECK_NULL_VOID(buttonsRow);
544     auto gestureRowProp = gestureRow->GetLayoutProperty();
545     auto customTitleRowProp = customTitleRow->GetLayoutProperty();
546     auto buttonsRowProp = buttonsRow->GetLayoutProperty();
547     if (customTitleRowProp->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE &&
548         buttonsRowProp->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::VISIBLE) {
549         gestureRowProp->UpdateVisibility(VisibleType::VISIBLE);
550     } else {
551         gestureRowProp->UpdateVisibility(VisibleType::GONE);
552     }
553 }
554 
SetContainerModalTitleVisible(bool customTitleSettedShow,bool floatingTitleSettedShow)555 void ContainerModalPattern::SetContainerModalTitleVisible(bool customTitleSettedShow, bool floatingTitleSettedShow)
556 {
557     LOGI("ContainerModal customTitleSettedShow=%{public}d, floatingTitleSettedShow=%{public}d", customTitleSettedShow,
558         floatingTitleSettedShow);
559     customTitleSettedShow_ = customTitleSettedShow;
560     auto customTitleRow = GetCustomTitleRow();
561     CHECK_NULL_VOID(customTitleRow);
562     auto customTitleRowProp = customTitleRow->GetLayoutProperty();
563     if (!customTitleSettedShow) {
564         customTitleRowProp->UpdateVisibility(VisibleType::GONE);
565     } else if (CanShowCustomTitle()) {
566         customTitleRowProp->UpdateVisibility(VisibleType::VISIBLE);
567     }
568     floatingTitleSettedShow_ = floatingTitleSettedShow;
569     auto floatingTitleRow = GetFloatingTitleRow();
570     CHECK_NULL_VOID(floatingTitleRow);
571     auto floatingTitleRowProp = floatingTitleRow->GetLayoutProperty();
572     if (!floatingTitleSettedShow) {
573         floatingTitleRowProp->UpdateVisibility(VisibleType::GONE);
574     }
575 
576     auto buttonsRow = GetControlButtonRow();
577     CHECK_NULL_VOID(buttonsRow);
578     buttonsRow->SetHitTestMode(HitTestMode::HTMTRANSPARENT_SELF);
579     UpdateGestureRowVisible();
580     TrimFloatingWindowLayout();
581 }
582 
SetContainerModalTitleHeight(int32_t height)583 void ContainerModalPattern::SetContainerModalTitleHeight(int32_t height)
584 {
585     LOGI("ContainerModal SetContainerModalTitleHeight height=%{public}d", height);
586     if (height < 0) {
587         height = 0;
588     }
589     titleHeight_ = Dimension(Dimension(height, DimensionUnit::PX).ConvertToVp(), DimensionUnit::VP);
590     auto customTitleRow = GetCustomTitleRow();
591     UpdateRowHeight(customTitleRow, titleHeight_);
592     auto controlButtonsRow = GetControlButtonRow();
593     UpdateRowHeight(controlButtonsRow, titleHeight_);
594     auto gestureRow = GetGestureRow();
595     UpdateRowHeight(gestureRow, titleHeight_);
596     CallButtonsRectChange();
597 }
598 
GetContainerModalTitleHeight()599 int32_t ContainerModalPattern::GetContainerModalTitleHeight()
600 {
601     return static_cast<int32_t>(ceil(titleHeight_.ConvertToPx()));
602 }
603 
GetContainerModalButtonsRect(RectF & containerModal,RectF & buttons)604 bool ContainerModalPattern::GetContainerModalButtonsRect(RectF& containerModal, RectF& buttons)
605 {
606     auto column = GetColumnNode();
607     CHECK_NULL_RETURN(column, false);
608     auto columnRect = column->GetGeometryNode()->GetFrameRect();
609     containerModal = columnRect;
610     if (columnRect.Width() == 0) {
611         LOGW("Get rect of buttons failed, the rect is measuring.");
612         return false;
613     }
614 
615     auto controlButtonsRow = GetControlButtonRow();
616     CHECK_NULL_RETURN(controlButtonsRow, false);
617     auto children = controlButtonsRow->GetChildren();
618     RectF firstButtonRect;
619     RectF lastButtonRect;
620     for (auto& child : children) {
621         auto node = AceType::DynamicCast<FrameNode>(child);
622         if (node->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) != VisibleType::VISIBLE) {
623             continue;
624         }
625         auto rect = node->GetGeometryNode()->GetFrameRect();
626         if (firstButtonRect.Width() == 0) {
627             firstButtonRect = rect;
628         }
629         lastButtonRect = rect;
630     }
631     buttons = firstButtonRect.CombineRectT(lastButtonRect);
632     if (buttons.Width() == 0) {
633         LOGW("Get rect of buttons failed, buttons are hidden");
634         return false;
635     }
636 
637     auto widthByPx = (TITLE_PADDING_START + TITLE_PADDING_END).ConvertToPx() + buttons.Width();
638     auto isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
639     if (isRtl) {
640         buttons.SetLeft(0);
641     } else {
642         buttons.SetLeft(containerModal.Width() - widthByPx);
643     }
644     buttons.SetTop(0);
645     buttons.SetWidth(widthByPx);
646     buttons.SetHeight(titleHeight_.ConvertToPx());
647     return true;
648 }
649 
SubscribeContainerModalButtonsRectChange(std::function<void (RectF & containerModal,RectF & buttons)> && callback)650 void ContainerModalPattern::SubscribeContainerModalButtonsRectChange(
651     std::function<void(RectF& containerModal, RectF& buttons)>&& callback)
652 {
653     controlButtonsRectChangeCallback_ = std::move(callback);
654 }
655 
GetWindowPaintRectWithoutMeasureAndLayout(RectInt & rect)656 void ContainerModalPattern::GetWindowPaintRectWithoutMeasureAndLayout(RectInt& rect)
657 {
658     auto host = GetHost();
659     CHECK_NULL_VOID(host);
660     auto layoutProperty = host->GetLayoutProperty();
661     CHECK_NULL_VOID(layoutProperty);
662     auto titleHeight = round(GetCustomTitleHeight().ConvertToPx());
663     auto padding = layoutProperty->CreatePaddingAndBorder();
664     rect.SetRect(padding.Offset().GetX(), padding.Offset().GetY() + titleHeight, rect.Width() - padding.Width(),
665         rect.Height() - padding.Height() - titleHeight);
666 }
667 
CallButtonsRectChange()668 void ContainerModalPattern::CallButtonsRectChange()
669 {
670     CHECK_NULL_VOID(controlButtonsRectChangeCallback_);
671     RectF containerModal;
672     RectF buttons;
673     GetContainerModalButtonsRect(containerModal, buttons);
674     if (buttonsRect_ == buttons) {
675         return;
676     }
677     buttonsRect_ = buttons;
678     auto taskExecutor = Container::CurrentTaskExecutor();
679     CHECK_NULL_VOID(taskExecutor);
680     taskExecutor->PostTask(
681         [containerModal, buttons, cb = controlButtonsRectChangeCallback_]() mutable {
682             if (cb) {
683                 cb(containerModal, buttons);
684             }
685         },
686         TaskExecutor::TaskType::JS, "ArkUIContainerModalButtonsRectChange");
687 }
688 
InitTitle()689 void ContainerModalPattern::InitTitle()
690 {
691     auto pipeline = PipelineContext::GetCurrentContext();
692     CHECK_NULL_VOID(pipeline);
693     auto themeManager = pipeline->GetThemeManager();
694     CHECK_NULL_VOID(themeManager);
695     auto themeConstants = themeManager->GetThemeConstants();
696     CHECK_NULL_VOID(themeConstants);
697     auto id = pipeline->GetWindowManager()->GetAppIconId();
698     auto pixelMap = themeConstants->GetPixelMap(id);
699     if (pixelMap) {
700         RefPtr<PixelMap> icon = PixelMap::CreatePixelMap(&pixelMap);
701         SetAppIcon(icon);
702     } else {
703         LOGW("Cannot get pixelmap, try media path."); // use themeConstants GetMediaPath
704     }
705     SetAppTitle(themeConstants->GetString(pipeline->GetWindowManager()->GetAppLabelId()));
706 }
707 
Init()708 void ContainerModalPattern::Init()
709 {
710     InitContainerEvent();
711     InitTitle();
712     InitLayoutProperty();
713 }
714 
OnColorConfigurationUpdate()715 void ContainerModalPattern::OnColorConfigurationUpdate()
716 {
717     WindowFocus(isFocus_);
718 }
719 
InitLayoutProperty()720 void ContainerModalPattern::InitLayoutProperty()
721 {
722     auto containerModal = GetHost();
723     auto column = GetColumnNode();
724     auto stack = GetStackNode();
725     auto content = GetContentNode();
726     CHECK_NULL_VOID(content);
727     auto buttonsRow = GetControlButtonRow();
728     CHECK_NULL_VOID(buttonsRow);
729     auto contentProperty = content->GetLayoutProperty();
730     auto buttonsRowProperty = buttonsRow->GetLayoutProperty<LinearLayoutProperty>();
731 
732     containerModal->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
733     column->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
734     stack->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
735     contentProperty->UpdateMeasureType(MeasureType::MATCH_CONTENT);
736     contentProperty->UpdateUserDefinedIdealSize(
737         CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(1.0, DimensionUnit::PERCENT)));
738     buttonsRowProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
739     auto buttonHeight = (CONTAINER_TITLE_HEIGHT == titleHeight_) ? CONTAINER_TITLE_HEIGHT : titleHeight_;
740     buttonsRowProperty->UpdateUserDefinedIdealSize(
741         CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(buttonHeight)));
742     buttonsRowProperty->UpdateMainAxisAlign(FlexAlign::FLEX_END);
743     buttonsRowProperty->UpdateCrossAxisAlign(FlexAlign::CENTER);
744 
745     InitTitleRowLayoutProperty(GetCustomTitleRow());
746     InitTitleRowLayoutProperty(GetFloatingTitleRow());
747     InitButtonsLayoutProperty();
748 
749     containerModal->MarkModifyDone();
750 }
751 
InitTitleRowLayoutProperty(RefPtr<FrameNode> titleRow)752 void ContainerModalPattern::InitTitleRowLayoutProperty(RefPtr<FrameNode> titleRow)
753 {
754     CHECK_NULL_VOID(titleRow);
755     auto titleRowProperty = titleRow->GetLayoutProperty<LinearLayoutProperty>();
756     CHECK_NULL_VOID(titleRowProperty);
757     titleRowProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
758     titleRowProperty->UpdateUserDefinedIdealSize(
759         CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(CONTAINER_TITLE_HEIGHT)));
760     titleRowProperty->UpdateMainAxisAlign(FlexAlign::FLEX_START);
761     titleRowProperty->UpdateCrossAxisAlign(FlexAlign::CENTER);
762     auto isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
763     PaddingProperty padding;
764     auto sidePadding = isRtl ? &padding.left : & padding.right;
765     *sidePadding = GetControlButtonRowWidth();
766     titleRowProperty->UpdatePadding(padding);
767 }
768 
GetControlButtonRowWidth()769 CalcLength ContainerModalPattern::GetControlButtonRowWidth()
770 {
771     auto row = GetControlButtonRow();
772     // default
773     int32_t buttonNum = 0;
774     const auto& children = row->GetChildren();
775     for (const auto& child : children) {
776         auto childButton = AceType::DynamicCast<FrameNode>(child);
777         if (childButton && childButton->IsVisible()) {
778             buttonNum++;
779         }
780     }
781     return CalcLength(TITLE_ELEMENT_MARGIN_HORIZONTAL * (buttonNum - 1) + TITLE_BUTTON_SIZE * buttonNum +
782                       TITLE_PADDING_START + TITLE_PADDING_END);
783 }
784 
InitColumnTouchTestFunc()785 void ContainerModalPattern::InitColumnTouchTestFunc()
786 {
787     auto column = GetColumnNode();
788     CHECK_NULL_VOID(column);
789     auto eventHub = column->GetOrCreateGestureEventHub();
790     auto func = [](const std::vector<TouchTestInfo>& touchInfo) -> TouchResult {
791         TouchResult touchRes;
792         TouchResult defaultRes;
793         touchRes.strategy = TouchTestStrategy::FORWARD_COMPETITION;
794         defaultRes.strategy = TouchTestStrategy::DEFAULT;
795         defaultRes.id = "";
796         for (auto info : touchInfo) {
797             if (info.id.compare(CONTAINER_MODAL_STACK_ID) == 0) {
798                 touchRes.id = info.id;
799                 return touchRes;
800             }
801         }
802         return defaultRes;
803     };
804     eventHub->SetOnTouchTestFunc(func);
805 }
806 
InitButtonsLayoutProperty()807 void ContainerModalPattern::InitButtonsLayoutProperty()
808 {
809     auto buttonsRow = GetControlButtonRow();
810     CHECK_NULL_VOID(buttonsRow);
811     auto isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
812     auto buttons = buttonsRow->GetChildren();
813     for (uint64_t index = 0; index < buttons.size(); index++) {
814         auto space = (index == buttons.size() - 1) ? TITLE_PADDING_END : TITLE_ELEMENT_MARGIN_HORIZONTAL;
815         MarginProperty margin;
816         if (isRtl) {
817             margin.left = CalcLength(space);
818             margin.right = CalcLength();
819         } else {
820             margin.left = CalcLength();
821             margin.right = CalcLength(space);
822         }
823         auto button = AceType::DynamicCast<FrameNode>(buttonsRow->GetChildAtIndex(index));
824         CHECK_NULL_VOID(button);
825         auto layoutProp = button->GetLayoutProperty<ButtonLayoutProperty>();
826         layoutProp->UpdateMargin(margin);
827         button->MarkModifyDone();
828         button->MarkDirtyNode();
829     }
830 }
831 
OnLanguageConfigurationUpdate()832 void ContainerModalPattern::OnLanguageConfigurationUpdate()
833 {
834     InitTitle();
835     InitLayoutProperty();
836 }
837 
GetCustomTitleHeight()838 Dimension ContainerModalPattern::GetCustomTitleHeight()
839 {
840     auto customTitleRow = GetCustomTitleRow();
841     Dimension zeroHeight;
842     CHECK_NULL_RETURN(customTitleRow, zeroHeight);
843     auto property = customTitleRow->GetLayoutProperty();
844     if (property->GetVisibilityValue(VisibleType::VISIBLE) != VisibleType::VISIBLE) {
845         return zeroHeight;
846     }
847     return titleHeight_;
848 }
849 
GetStackNodeRadius()850 Dimension ContainerModalPattern::GetStackNodeRadius()
851 {
852     Dimension radius = customTitleSettedShow_ ? CONTAINER_INNER_RADIUS : CONTAINER_OUTER_RADIUS;
853     auto trimRadiusPx = Dimension(round(radius.ConvertToPx() * 2) / 2.0);
854     auto trimRadiusVp = Dimension(trimRadiusPx.ConvertToVp(), DimensionUnit::VP);
855     return trimRadiusVp;
856 }
857 
CanShowCustomTitle()858 bool ContainerModalPattern::CanShowCustomTitle()
859 {
860     auto buttonsRow = GetControlButtonRow();
861     CHECK_NULL_RETURN(buttonsRow, false);
862     auto visibility = buttonsRow->GetLayoutProperty()->GetVisibilityValue(VisibleType::GONE);
863     return visibility == VisibleType::VISIBLE;
864 }
865 
TrimFloatingWindowLayout()866 void ContainerModalPattern::TrimFloatingWindowLayout()
867 {
868     if (windowMode_ != WindowMode::WINDOW_MODE_FLOATING) {
869         return;
870     }
871     auto stack = GetStackNode();
872     CHECK_NULL_VOID(stack);
873     auto stackRender = stack->GetRenderContext();
874     BorderRadiusProperty borderRadius;
875     borderRadius.SetRadius(GetStackNodeRadius());
876     stackRender->UpdateBorderRadius(borderRadius);
877     auto host = GetHost();
878     CHECK_NULL_VOID(host);
879     auto hostProp = host->GetLayoutProperty();
880     PaddingProperty padding;
881     auto customtitleRow = GetCustomTitleRow();
882     CHECK_NULL_VOID(customtitleRow);
883     auto customTitleRowProp = customtitleRow->GetLayoutProperty();
884     if (customTitleRowProp->GetVisibilityValue(VisibleType::GONE) == VisibleType::VISIBLE) {
885         padding = { CalcLength(CONTENT_PADDING), CalcLength(CONTENT_PADDING), std::nullopt,
886             CalcLength(CONTENT_PADDING) };
887     }
888     hostProp->UpdatePadding(padding);
889 }
890 } // namespace OHOS::Ace::NG