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