1  /*
2   * Copyright (c) 2022 Huawei Device Co., Ltd.
3   * Licensed under the Apache License, Version 2.0 (the "License");
4   * you may not use this file except in compliance with the License.
5   * You may obtain a copy of the License at
6   *
7   *     http://www.apache.org/licenses/LICENSE-2.0
8   *
9   * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  #include "core/components/container_modal/container_modal_component.h"
17  
18  #include "core/components/clip/clip_component.h"
19  #include "core/components/container_modal/container_modal_constants.h"
20  #include "core/components/container_modal/container_modal_element.h"
21  #include "core/components/container_modal/render_container_modal.h"
22  #include "core/components/padding/padding_component.h"
23  #include "core/components/tween/tween_component.h"
24  #include "core/components_v2/inspector/inspector_composed_component.h"
25  #include "core/gestures/pan_gesture.h"
26  
27  namespace OHOS::Ace {
28  namespace {
29  constexpr int32_t ROOT_DECOR_BASE = 3100000;
30  constexpr int32_t TITLE_ROW = ROOT_DECOR_BASE;
31  constexpr int32_t FLOATING_TITLE_ROW = ROOT_DECOR_BASE + 1;
32  constexpr int32_t TITLE_LABEL = ROOT_DECOR_BASE + 2;
33  constexpr int32_t FLOATING_TITLE_LABEL = ROOT_DECOR_BASE + 3;
34  constexpr int32_t WINDOW_SPLIT_BUTTON = ROOT_DECOR_BASE + 4;
35  constexpr int32_t WINDOW_MAX_RECOVER_BUTTON = ROOT_DECOR_BASE + 6;
36  constexpr int32_t WINDOW_MINIMIZE_BUTTON = ROOT_DECOR_BASE + 8;
37  constexpr int32_t WINDOW_CLOSE_BUTTON = ROOT_DECOR_BASE + 10;
38  constexpr int32_t WINDOW_BUTTON_INVALID = ROOT_DECOR_BASE + 12;
39  
40  const std::string SPLIT_LEFT_KEY = "container_modal_split_left_button";
41  const std::string MAXIMIZE_KEY = "container_modal_maximize_button";
42  const std::string MINIMIZE_KEY = "container_modal_minimize_button";
43  const std::string CLOSE_KEY = "container_modal_close_button";
44  }  // namespace
45  
Create(const WeakPtr<PipelineContext> & context,const RefPtr<Component> & child)46  RefPtr<Component> ContainerModalComponent::Create(
47      const WeakPtr<PipelineContext>& context, const RefPtr<Component>& child)
48  {
49      auto component = AceType::MakeRefPtr<ContainerModalComponent>(context);
50      component->SetChild(child);
51      component->BuildInnerChild();
52      return component;
53  }
54  
CreateElement()55  RefPtr<Element> ContainerModalComponent::CreateElement()
56  {
57      return AceType::MakeRefPtr<ContainerModalElement>();
58  }
59  
CreateRenderNode()60  RefPtr<RenderNode> ContainerModalComponent::CreateRenderNode()
61  {
62      return RenderContainerModal::Create();
63  }
64  
BuildTitle()65  RefPtr<Component> ContainerModalComponent::BuildTitle()
66  {
67      // build title box
68      auto titleBox = AceType::MakeRefPtr<BoxComponent>();
69      titleBox->SetHeight(CONTAINER_TITLE_HEIGHT);
70  
71      // BuildTitleChildren need this
72      CreateAccessibilityNode(DOM_FLEX_ROW, TITLE_ROW, -1);
73  
74      auto titleChildrenRow =
75          AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, BuildTitleChildren(false));
76  
77      // handle touch move and mouse move
78      PanDirection panDirection;
79      panDirection.type = PanDirection::ALL;
80      auto panGesture =
81          AceType::MakeRefPtr<PanGesture>(DEFAULT_PAN_FINGER, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
82      panGesture->SetOnActionStartId([contextWptr = context_](const GestureEvent&) {
83          auto context = contextWptr.Upgrade();
84          if (context) {
85              LOGI("container window start move.");
86              context->GetWindowManager()->WindowStartMove();
87          }
88      });
89      titleBox->AddGesture(GesturePriority::Low, panGesture);
90      titleBox->SetChild(titleChildrenRow);
91  
92      if (isDeclarative_) {
93          return AceType::MakeRefPtr<DisplayComponent>(AceType::MakeRefPtr<V2::InspectorComposedComponent>(
94              V2::InspectorComposedComponent::GenerateId(), V2::ROW_COMPONENT_TAG, titleBox));
95      } else {
96          return AceType::MakeRefPtr<DisplayComponent>(
97              AceType::MakeRefPtr<ComposedComponent>(std::to_string(TITLE_ROW), DOM_FLEX_ROW, titleBox));
98      }
99  }
100  
BuildFloatingTitle()101  RefPtr<Component> ContainerModalComponent::BuildFloatingTitle()
102  {
103      // build floating title box
104      auto titleDecoration = AceType::MakeRefPtr<Decoration>();
105      titleDecoration->SetBackgroundColor(CONTAINER_BACKGROUND_COLOR);
106  
107      auto titleBox = AceType::MakeRefPtr<BoxComponent>();
108      titleBox->SetHeight(CONTAINER_TITLE_HEIGHT);
109      titleBox->SetBackDecoration(titleDecoration);
110  
111      CreateAccessibilityNode(DOM_FLEX_ROW, FLOATING_TITLE_ROW, -1);
112  
113      auto floatingTitleChildrenRow =
114          AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, BuildTitleChildren(true));
115      titleBox->SetChild(floatingTitleChildrenRow);
116      if (isDeclarative_) {
117          return AceType::MakeRefPtr<TweenComponent>(
118              "ContainerModal", AceType::MakeRefPtr<V2::InspectorComposedComponent>(
119                                    V2::InspectorComposedComponent::GenerateId(), V2::ROW_COMPONENT_TAG, titleBox));
120      } else {
121          return AceType::MakeRefPtr<TweenComponent>("ContainerModal",
122              AceType::MakeRefPtr<ComposedComponent>(std::to_string(FLOATING_TITLE_ROW), DOM_FLEX_ROW, titleBox));
123      }
124  }
125  
BuildContent()126  RefPtr<Component> ContainerModalComponent::BuildContent()
127  {
128      auto contentBox = AceType::MakeRefPtr<BoxComponent>();
129      contentBox->SetChild(GetChild());
130      auto contentDecoration = AceType::MakeRefPtr<Decoration>();
131      auto context = context_.Upgrade();
132      if (context) {
133          contentDecoration->SetBackgroundColor(context->GetAppBgColor());
134      }
135      contentBox->SetBackDecoration(contentDecoration);
136  
137      auto clip = AceType::MakeRefPtr<ClipComponent>(contentBox);
138      clip->SetClipRadius(Radius(CONTAINER_INNER_RADIUS));
139      clip->SetFlexWeight(1.0);
140      return clip;
141  }
142  
BuildControlButton(InternalResource::ResourceId icon,std::function<void ()> && clickCallback,bool isFocus,bool isFloating)143  RefPtr<Component> ContainerModalComponent::BuildControlButton(
144      InternalResource::ResourceId icon, std::function<void()>&& clickCallback, bool isFocus, bool isFloating)
145  {
146      static std::unordered_map<InternalResource::ResourceId, std::pair<int32_t, std::string>> controlButtonIdMap = {
147          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_SPLIT_LEFT, { WINDOW_SPLIT_BUTTON, SPLIT_LEFT_KEY } },
148          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_SPLIT_LEFT,
149              { WINDOW_SPLIT_BUTTON, SPLIT_LEFT_KEY } },
150          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_RECOVER, { WINDOW_MAX_RECOVER_BUTTON, MAXIMIZE_KEY } },
151          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_MAXIMIZE, { WINDOW_MAX_RECOVER_BUTTON, MAXIMIZE_KEY } },
152          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_RECOVER,
153              { WINDOW_MAX_RECOVER_BUTTON, MAXIMIZE_KEY } },
154          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_MAXIMIZE,
155              { WINDOW_MAX_RECOVER_BUTTON, MAXIMIZE_KEY } },
156          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_MINIMIZE, { WINDOW_MINIMIZE_BUTTON, MINIMIZE_KEY } },
157          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_MINIMIZE,
158              { WINDOW_MINIMIZE_BUTTON, MINIMIZE_KEY } },
159          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_CLOSE, { WINDOW_CLOSE_BUTTON, CLOSE_KEY } },
160          { InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_CLOSE, { WINDOW_CLOSE_BUTTON, CLOSE_KEY } },
161      };
162  
163      auto image = AceType::MakeRefPtr<ImageComponent>(icon);
164      image->SetWidth(TITLE_ICON_SIZE);
165      image->SetHeight(TITLE_ICON_SIZE);
166      image->SetFocusable(false);
167      std::list<RefPtr<Component>> btnChildren;
168      btnChildren.emplace_back(image);
169  
170      auto button = AceType::MakeRefPtr<ButtonComponent>(btnChildren);
171      button->SetWidth(TITLE_BUTTON_SIZE);
172      button->SetHeight(TITLE_BUTTON_SIZE);
173      button->SetType(ButtonType::CIRCLE);
174      button->SetBackgroundColor(isFocus ? TITLE_BUTTON_BACKGROUND_COLOR : TITLE_BUTTON_BACKGROUND_COLOR_LOST_FOCUS);
175      button->SetClickedColor(TITLE_BUTTON_CLICKED_COLOR);
176      button->SetClickFunction(std::move(clickCallback));
177      button->SetFocusable(false);
178      std::string buttonKey = "";
179      auto iter = controlButtonIdMap.find(icon);
180      if (iter != controlButtonIdMap.end()) {
181          buttonKey = (iter->second).second;
182      }
183      button->SetInspectorKey(buttonKey.c_str());
184      if (!isDeclarative_) {
185          auto buttonId = WINDOW_BUTTON_INVALID;
186          auto iter = controlButtonIdMap.find(icon);
187          if (iter != controlButtonIdMap.end()) {
188              buttonId = isFloating ? (iter->second).first + 1 : (iter->second).first;
189          }
190          CreateAccessibilityNode(DOM_NODE_TAG_BUTTON, buttonId, isFloating ? FLOATING_TITLE_ROW : TITLE_ROW);
191          return AceType::MakeRefPtr<ComposedComponent>(std::to_string(buttonId), DOM_NODE_TAG_BUTTON, button);
192      } else {
193          return AceType::MakeRefPtr<V2::InspectorComposedComponent>(
194              V2::InspectorComposedComponent::GenerateId(), V2::BUTTON_COMPONENT_TAG, button);
195      }
196  }
197  
SetPadding(const RefPtr<Component> & component,const Dimension & leftPadding,const Dimension & rightPadding)198  RefPtr<Component> ContainerModalComponent::SetPadding(
199      const RefPtr<Component>& component, const Dimension& leftPadding, const Dimension& rightPadding)
200  {
201      auto paddingComponent = AceType::MakeRefPtr<PaddingComponent>();
202      paddingComponent->SetPaddingLeft(leftPadding);
203      paddingComponent->SetPaddingRight(rightPadding);
204      paddingComponent->SetPaddingTop((CONTAINER_TITLE_HEIGHT - TITLE_BUTTON_SIZE) / 2);
205      paddingComponent->SetPaddingBottom((CONTAINER_TITLE_HEIGHT - TITLE_BUTTON_SIZE) / 2);
206      paddingComponent->SetChild(component);
207      return paddingComponent;
208  }
209  
210  // Build ContainerModal FA structure
BuildInnerChild()211  void ContainerModalComponent::BuildInnerChild()
212  {
213      Border outerBorder;
214      outerBorder.SetBorderRadius(Radius(CONTAINER_OUTER_RADIUS));
215      outerBorder.SetColor(CONTAINER_BORDER_COLOR);
216      outerBorder.SetWidth(CONTAINER_BORDER_WIDTH);
217      auto containerDecoration = AceType::MakeRefPtr<Decoration>();
218      containerDecoration->SetBackgroundColor(CONTAINER_BACKGROUND_COLOR);
219      containerDecoration->SetBorder(outerBorder);
220  
221      auto column =
222          AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, std::list<RefPtr<Component>>());
223      column->AppendChild(BuildTitle());
224      column->AppendChild(BuildContent());
225      std::list<RefPtr<Component>> stackChildren;
226      stackChildren.emplace_back(column);
227      stackChildren.emplace_back(BuildFloatingTitle());
228      auto stackComponent = AceType::MakeRefPtr<StackComponent>(
229          Alignment::TOP_LEFT, StackFit::INHERIT, Overflow::OBSERVABLE, stackChildren);
230  
231      auto containerBox = AceType::MakeRefPtr<BoxComponent>();
232      containerBox->SetBackDecoration(containerDecoration);
233      containerBox->SetFlex(BoxFlex::FLEX_X);
234      containerBox->SetAlignment(Alignment::CENTER);
235  
236      Edge padding = Edge(CONTENT_PADDING, Dimension(0.0), CONTENT_PADDING, CONTENT_PADDING);
237      containerBox->SetPadding(padding);
238      containerBox->SetChild(stackComponent);
239      SetChild(containerBox);
240  }
241  
BuildTitleChildren(bool isFloating,bool isFocus,bool isFullWindow)242  std::list<RefPtr<Component>> ContainerModalComponent::BuildTitleChildren(bool isFloating, bool isFocus,
243      bool isFullWindow)
244  {
245      // title icon
246      if (!titleIcon_) {
247          titleIcon_ = AceType::MakeRefPtr<ImageComponent>();
248          titleIcon_->SetWidth(TITLE_ICON_SIZE);
249          titleIcon_->SetHeight(TITLE_ICON_SIZE);
250          titleIcon_->SetFocusable(false);
251      }
252  
253      // title text
254      if (!titleLabel_) {
255          titleLabel_ = AceType::MakeRefPtr<TextComponent>("");
256      }
257      TextStyle style;
258      style.SetFontSize(TITLE_TEXT_FONT_SIZE);
259      style.SetMaxLines(1);
260      style.SetTextColor(isFocus ? TITLE_TEXT_COLOR : TITLE_TEXT_COLOR_LOST_FOCUS);
261      style.SetFontWeight(FontWeight::W500);
262      style.SetAllowScale(false);
263      style.SetTextOverflow(TextOverflow::ELLIPSIS);
264      titleLabel_->SetTextStyle(style);
265      titleLabel_->SetFlexWeight(1.0);
266  
267      CreateAccessibilityNode(DOM_NODE_TAG_TEXT, isFloating ? FLOATING_TITLE_LABEL : TITLE_LABEL,
268          isFloating ? FLOATING_TITLE_ROW : TITLE_ROW);
269  
270      // title control button
271      auto windowManager = context_.Upgrade()->GetWindowManager();
272      auto leftSplitButton = isFocus ? InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_SPLIT_LEFT
273                                     : InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_SPLIT_LEFT;
274      auto titleLeftSplitButton = BuildControlButton(leftSplitButton, [windowManager]() {
275              if (windowManager) {
276                  LOGI("left split button clicked");
277                  windowManager->FireWindowSplitCallBack();
278              }
279          }, isFocus, isFloating);
280      auto maxRecoverButton = isFloating && isFullWindow ? InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_RECOVER
281                                                         : InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_MAXIMIZE;
282      if (!isFocus) {
283          maxRecoverButton = isFloating && isFullWindow ?
284              InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_RECOVER :
285              InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_MAXIMIZE;
286      }
287      auto titleMaximizeRecoverButton = BuildControlButton(maxRecoverButton, [windowManager]() {
288              if (windowManager) {
289                  auto mode = windowManager->GetWindowMode();
290                  if (mode == WindowMode::WINDOW_MODE_FULLSCREEN) {
291                      LOGI("recover button clicked");
292                      windowManager->WindowRecover();
293                  } else {
294                      LOGI("maximize button clicked");
295                      windowManager->WindowMaximize();
296                  }
297              }
298          }, isFocus, isFloating);
299      auto minimizeButton = isFocus ? InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_MINIMIZE
300                                    : InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_MINIMIZE;
301      auto titleMinimizeButton = BuildControlButton(minimizeButton, [windowManager]() {
302              if (windowManager) {
303                  LOGI("minimize button clicked");
304                  windowManager->WindowMinimize();
305              }
306          }, isFocus, isFloating);
307      auto closeButton = isFocus ? InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_CLOSE
308                                 : InternalResource::ResourceId::CONTAINER_MODAL_WINDOW_DEFOCUS_CLOSE;
309      auto titleCloseButton = BuildControlButton(closeButton, [windowManager]() {
310              if (windowManager) {
311                  LOGI("close button clicked");
312                  windowManager->WindowClose();
313              }
314          }, isFocus, isFloating);
315      std::list<RefPtr<Component>> titleChildren;
316      titleChildren.emplace_back(SetPadding(titleIcon_, TITLE_PADDING_START, TITLE_ELEMENT_MARGIN_HORIZONTAL));
317      if (isDeclarative_) {
318          auto inspectorTitle = AceType::MakeRefPtr<V2::InspectorComposedComponent>(
319              V2::InspectorComposedComponent::GenerateId(), V2::TEXT_COMPONENT_TAG, titleLabel_);
320          inspectorTitle->MarkNeedUpdate();
321          titleChildren.emplace_back(inspectorTitle);
322      } else {
323          titleChildren.emplace_back(AceType::MakeRefPtr<ComposedComponent>(
324              isFloating ? std::to_string(FLOATING_TITLE_LABEL) : std::to_string(TITLE_LABEL), DOM_NODE_TAG_TEXT,
325              titleLabel_));
326      }
327      auto rightPadding = SystemProperties::GetDeviceAccess() ? TITLE_ELEMENT_MARGIN_HORIZONTAL_ACCESS_DEVICE
328                                                              : TITLE_ELEMENT_MARGIN_HORIZONTAL;
329      if (!hideSplit_) {
330          titleChildren.emplace_back(SetPadding(titleLeftSplitButton, ZERO_PADDING, rightPadding));
331      }
332      if (!hideMaximize_) {
333          titleChildren.emplace_back(
334              SetPadding(titleMaximizeRecoverButton, ZERO_PADDING, rightPadding));
335      }
336      if (!hideMinimize_) {
337          titleChildren.emplace_back(SetPadding(titleMinimizeButton, ZERO_PADDING, rightPadding));
338      }
339      if (!hideClose_) {
340          titleChildren.emplace_back(SetPadding(titleCloseButton, ZERO_PADDING, TITLE_PADDING_END));
341      }
342      return titleChildren;
343  }
344  
CreateAccessibilityNode(const std::string & tag,int32_t nodeId,int32_t parentNodeId)345  void ContainerModalComponent::CreateAccessibilityNode(const std::string& tag, int32_t nodeId, int32_t parentNodeId)
346  {
347      auto context = context_.Upgrade();
348      if (context != nullptr && !isDeclarative_) {
349          auto accessibilityManager = context->GetAccessibilityManager();
350          if (accessibilityManager) {
351              accessibilityManager->CreateAccessibilityNode(tag, nodeId, parentNodeId, -1);
352          }
353      }
354  }
355  
356  } // namespace OHOS::Ace