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