1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "core/components_ng/pattern/app_bar/atomic_service_pattern.h"
16 
17 #include "base/utils/utils.h"
18 #include "core/common/container.h"
19 #include "core/components_ng/pattern/button/button_pattern.h"
20 #include "core/components_ng/pattern/divider/divider_render_property.h"
21 #include "core/components_ng/pattern/image/image_layout_property.h"
22 #include "core/components_ng/pattern/image/image_render_property.h"
23 #include "core/components_ng/pattern/text/text_layout_property.h"
24 #include "core/components_ng/property/calc_length.h"
25 #include "core/components_ng/property/measure_property.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
29 constexpr int32_t ATOMIC_SERVICE_MIN_SIZE = 2;
30 constexpr int32_t FIRST_OVERLAY_INDEX = 1;
31 
BeforeCreateLayoutWrapper()32 void AtomicServicePattern::BeforeCreateLayoutWrapper()
33 {
34     auto pipeline = PipelineContext::GetCurrentContext();
35     CHECK_NULL_VOID(pipeline);
36     auto theme = pipeline->GetTheme<AppBarTheme>();
37     CHECK_NULL_VOID(theme);
38     auto menuBar = GetMenuBar();
39     auto safeArea = pipeline->GetSafeArea();
40     auto safeAreaLeft = safeArea.left_.Length();
41     auto safeAreaRight = safeArea.right_.Length();
42     if (safeAreaLeft_ != safeAreaLeft || safeAreaRight_ != safeAreaRight) {
43         safeAreaLeft_ = safeAreaLeft;
44         safeAreaRight_ = safeAreaRight;
45         bool isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
46         UpdateMenuBarLayout(theme, menuBar, isRtl);
47     }
48 
49     auto host = GetHost();
50     CHECK_NULL_VOID(host);
51     auto manager = pipeline->GetSafeAreaManager();
52     CHECK_NULL_VOID(manager);
53     manager->SetIsAtomicService(true);
54     manager->AddGeoRestoreNode(host);
55     auto systemSafeArea = manager->GetSystemSafeArea();
56     float topMargin = theme->GetMenuBarTopMargin().ConvertToPx();
57     topMargin += systemSafeArea.top_.Length();
58     UpdateOverlayLayout();
59     auto menuBarRow = GetMenuBarRow();
60     CHECK_NULL_VOID(menuBarRow);
61     auto renderContext = menuBarRow->GetRenderContext();
62     renderContext->UpdatePosition(OffsetT<Dimension>(0.0_vp, Dimension(topMargin, DimensionUnit::PX)));
63     if (settedColorMode.has_value()) {
64         UpdateMenuBarColor(theme, menuBar, settedColorMode.value());
65     } else {
66         UpdateMenuBarColor(theme, menuBar, SystemProperties::GetColorMode() != ColorMode::DARK);
67     }
68     UpdateLayoutMargin();
69 }
70 
UpdateLayoutMargin()71 void AtomicServicePattern::UpdateLayoutMargin()
72 {
73     auto pipeline = PipelineContext::GetCurrentContext();
74     CHECK_NULL_VOID(pipeline);
75     auto safeArea = pipeline->GetSafeArea();
76     auto atom = GetHost();
77     CHECK_NULL_VOID(atom);
78     MarginProperty margin;
79     margin.left = CalcLength(safeArea.left_.Length());
80     margin.right = CalcLength(safeArea.right_.Length());
81     margin.top = CalcLength(safeArea.top_.Length());
82     margin.bottom = CalcLength(safeArea.bottom_.Length());
83     // update stage margin
84     auto stage = AceType::DynamicCast<FrameNode>(atom->GetChildAtIndex(0));
85     CHECK_NULL_VOID(stage);
86     auto layoutProperty = stage->GetLayoutProperty();
87     CHECK_NULL_VOID(layoutProperty);
88     layoutProperty->UpdateMargin(margin);
89     stage->MarkModifyDone();
90     stage->MarkDirtyNode();
91 }
92 
UpdateOverlayLayout()93 void AtomicServicePattern::UpdateOverlayLayout()
94 {
95     auto atom = GetHost();
96     CHECK_NULL_VOID(atom);
97     if (atom->GetChildren().size() <= ATOMIC_SERVICE_MIN_SIZE) {
98         return;
99     }
100     for (int index = FIRST_OVERLAY_INDEX;
101         index <= static_cast<int32_t>(atom->GetChildren().size()) - ATOMIC_SERVICE_MIN_SIZE; index++) {
102         auto overlay = AceType::DynamicCast<FrameNode>(atom->GetChildAtIndex(index));
103         CHECK_NULL_VOID(overlay);
104         auto overlayRender = overlay->GetRenderContext();
105         overlayRender->UpdatePosition(OffsetT<Dimension>());
106         overlay->MarkModifyDone();
107         overlay->MarkDirtyNode();
108     }
109 }
110 
OnAttachToFrameNode()111 void AtomicServicePattern::OnAttachToFrameNode()
112 {
113     auto host = GetHost();
114     CHECK_NULL_VOID(host);
115     host->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
116     auto pipeline = PipelineContext::GetCurrentContext();
117     CHECK_NULL_VOID(pipeline);
118     host->GetRenderContext()->UpdateBackgroundColor(pipeline->GetAppBgColor());
119 }
120 
OnLanguageConfigurationUpdate()121 void AtomicServicePattern::OnLanguageConfigurationUpdate()
122 {
123     UpdateLayout();
124 }
125 
OnColorConfigurationUpdate()126 void AtomicServicePattern::OnColorConfigurationUpdate()
127 {
128     auto host = GetHost();
129     CHECK_NULL_VOID(host);
130     auto pipeline = PipelineContext::GetCurrentContext();
131     CHECK_NULL_VOID(pipeline);
132     host->GetRenderContext()->UpdateBackgroundColor(pipeline->GetAppBgColor());
133     if (settedColorMode.has_value()) {
134         UpdateColor(settedColorMode);
135     } else {
136         UpdateColor(SystemProperties::GetColorMode() != ColorMode::DARK);
137     }
138 }
139 
GetMenuBarRow()140 RefPtr<FrameNode> AtomicServicePattern::GetMenuBarRow()
141 {
142     auto atom = GetHost();
143     CHECK_NULL_RETURN(atom, nullptr);
144     auto menuBarRow = AceType::DynamicCast<FrameNode>(atom->GetChildren().back());
145     return menuBarRow;
146 }
147 
GetMenuBar()148 RefPtr<FrameNode> AtomicServicePattern::GetMenuBar()
149 {
150     auto menuBarRow = GetMenuBarRow();
151     CHECK_NULL_RETURN(menuBarRow, nullptr);
152     auto menuBar = AceType::DynamicCast<FrameNode>(menuBarRow->GetChildAtIndex(0));
153     return menuBar;
154 }
155 
GetMenuButton()156 RefPtr<FrameNode> AtomicServicePattern::GetMenuButton()
157 {
158     auto menuBar = GetMenuBar();
159     CHECK_NULL_RETURN(menuBar, nullptr);
160     auto menuButton = AceType::DynamicCast<FrameNode>(menuBar->GetChildAtIndex(0));
161     return menuButton;
162 }
163 
GetDivider()164 RefPtr<FrameNode> AtomicServicePattern::GetDivider()
165 {
166     auto menuBar = GetMenuBar();
167     CHECK_NULL_RETURN(menuBar, nullptr);
168     auto divider = AceType::DynamicCast<FrameNode>(menuBar->GetChildAtIndex(1));
169     return divider;
170 }
171 
GetCloseButton()172 RefPtr<FrameNode> AtomicServicePattern::GetCloseButton()
173 {
174     auto menuBar = GetMenuBar();
175     CHECK_NULL_RETURN(menuBar, nullptr);
176     auto closeButton = AceType::DynamicCast<FrameNode>(menuBar->GetChildAtIndex(2));
177     return closeButton;
178 }
179 
GetMenuIcon()180 RefPtr<FrameNode> AtomicServicePattern::GetMenuIcon()
181 {
182     auto menuButton = GetMenuButton();
183     CHECK_NULL_RETURN(menuButton, nullptr);
184     auto menuIcon = AceType::DynamicCast<FrameNode>(menuButton->GetChildAtIndex(0));
185     return menuIcon;
186 }
187 
GetCloseIcon()188 RefPtr<FrameNode> AtomicServicePattern::GetCloseIcon()
189 {
190     auto closeButton = GetCloseButton();
191     CHECK_NULL_RETURN(closeButton, nullptr);
192     auto closeIcon = AceType::DynamicCast<FrameNode>(closeButton->GetChildAtIndex(0));
193     return closeIcon;
194 }
195 
UpdateColor(std::optional<bool> isLight)196 void AtomicServicePattern::UpdateColor(std::optional<bool> isLight)
197 {
198     auto pipeline = PipelineContext::GetCurrentContext();
199     CHECK_NULL_VOID(pipeline);
200     auto theme = pipeline->GetTheme<AppBarTheme>();
201     if (!(isLight.has_value())) {
202         isLight = SystemProperties::GetColorMode() != ColorMode::DARK;
203     }
204     auto menuButton = GetMenuButton();
205     UpdateButtonColor(theme, menuButton, isLight.value());
206     auto divider = GetDivider();
207     UpdateDividerColor(theme, divider, isLight.value());
208     auto closeButton = GetCloseButton();
209     UpdateButtonColor(theme, closeButton, isLight.value());
210 
211     auto menuIcon = GetMenuIcon();
212     UpdateIconColor(theme, menuIcon, isLight.value());
213     auto closeIcon = GetCloseIcon();
214     UpdateIconColor(theme, closeIcon, isLight.value());
215 }
216 
UpdateMenuBarColor(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & menuBar,bool isLight)217 void AtomicServicePattern::UpdateMenuBarColor(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& menuBar, bool isLight)
218 {
219     CHECK_NULL_VOID(theme);
220     CHECK_NULL_VOID(menuBar);
221     auto renderContext = menuBar->GetRenderContext();
222     // background effect、border color
223     EffectOption option;
224     BorderColorProperty borderColor;
225     option.radius = theme->GetBlurRadius();
226     if (isLight) {
227         option.color = theme->GetBlurColorLight();
228         borderColor.SetColor(theme->GetBorderColorLight());
229     } else {
230         option.color = theme->GetBlurColorDark();
231         borderColor.SetColor(theme->GetBorderColorDark());
232     }
233     renderContext->UpdateBackgroundEffect(option);
234     renderContext->UpdateBorderColor(borderColor);
235 
236     menuBar->MarkModifyDone();
237     menuBar->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
238 }
239 
UpdateButtonColor(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & button,bool isLight)240 void AtomicServicePattern::UpdateButtonColor(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& button, bool isLight)
241 {
242     CHECK_NULL_VOID(theme);
243     CHECK_NULL_VOID(button);
244     // pressed color
245     auto buttonPattern = button->GetPattern<ButtonPattern>();
246     CHECK_NULL_VOID(buttonPattern);
247     if (isLight) {
248         buttonPattern->SetClickedColor(theme->GetClickEffectColorLight());
249     } else {
250         buttonPattern->SetClickedColor(theme->GetClickEffectColorDark());
251     }
252     // focus border color
253     if (isLight) {
254         buttonPattern->SetFocusBorderColor(theme->GetFocusedOutlineColorLight());
255     } else {
256         buttonPattern->SetFocusBorderColor(theme->GetFocusedOutlineColorDark());
257     }
258 
259     button->MarkModifyDone();
260     button->MarkDirtyNode();
261 }
262 
UpdateDividerColor(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & divider,bool isLight)263 void AtomicServicePattern::UpdateDividerColor(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& divider, bool isLight)
264 {
265     CHECK_NULL_VOID(theme);
266     CHECK_NULL_VOID(divider);
267 
268     auto renderProperty = divider->GetPaintProperty<DividerRenderProperty>();
269     if (isLight) {
270         renderProperty->UpdateDividerColor(theme->GetDividerColorLight());
271     } else {
272         renderProperty->UpdateDividerColor(theme->GetDividerColorDark());
273     }
274 
275     divider->MarkModifyDone();
276     divider->MarkDirtyNode();
277 }
278 
UpdateIconColor(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & icon,bool isLight)279 void AtomicServicePattern::UpdateIconColor(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& icon, bool isLight)
280 {
281     CHECK_NULL_VOID(theme);
282     CHECK_NULL_VOID(icon);
283     // fill color
284     auto color = isLight ? theme->GetIconColorLight() : theme->GetIconColorDark();
285     ACE_UPDATE_NODE_PAINT_PROPERTY(ImageRenderProperty, SvgFillColor, color, icon);
286     ACE_UPDATE_NODE_RENDER_CONTEXT(ForegroundColor, color, icon);
287     icon->MarkModifyDone();
288     icon->MarkDirtyNode();
289 }
290 
UpdateLayout()291 void AtomicServicePattern::UpdateLayout()
292 {
293     auto pipeline = PipelineContext::GetCurrentContext();
294     CHECK_NULL_VOID(pipeline);
295     auto theme = pipeline->GetTheme<AppBarTheme>();
296     CHECK_NULL_VOID(theme);
297     bool isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
298 
299     auto menuBar = GetMenuBar();
300     UpdateMenuBarLayout(theme, menuBar, isRtl);
301 
302     auto menuButton = GetMenuButton();
303     UpdateButtonLayout(theme, menuButton, !isRtl);
304     auto closeButton = GetCloseButton();
305     UpdateButtonLayout(theme, closeButton, isRtl);
306 
307     auto menuIcon = GetMenuIcon();
308     UpdateIconLayout(theme, menuIcon, !isRtl);
309     auto closeIcon = GetCloseIcon();
310     UpdateIconLayout(theme, closeIcon, isRtl);
311 }
312 
UpdateMenuBarLayout(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & menuBar,bool isRtl)313 void AtomicServicePattern::UpdateMenuBarLayout(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& menuBar, bool isRtl)
314 {
315     CHECK_NULL_VOID(theme);
316     CHECK_NULL_VOID(menuBar);
317 
318     MarginProperty margin;
319     auto pipeline = PipelineContext::GetCurrentContext();
320     CHECK_NULL_VOID(pipeline);
321     auto safeArea = pipeline->GetSafeArea();
322 
323     Dimension safeAreaLeft(pipeline->Px2VpWithCurrentDensity(safeArea.left_.Length()), DimensionUnit::VP);
324     Dimension safeAreaRight(pipeline->Px2VpWithCurrentDensity(safeArea.right_.Length()), DimensionUnit::VP);
325 
326     if (isRtl) {
327         margin.left = CalcLength(theme->GetMenuBarRightMargin() + safeAreaLeft);
328         margin.right = CalcLength(theme->GetMenuBarLeftMargin() + safeAreaRight);
329     } else {
330         margin.left = CalcLength(theme->GetMenuBarLeftMargin() + safeAreaLeft);
331         margin.right = CalcLength(theme->GetMenuBarRightMargin() + safeAreaRight);
332     }
333     menuBar->GetLayoutProperty<LinearLayoutProperty>()->UpdateMargin(margin);
334 
335     menuBar->MarkModifyDone();
336     menuBar->MarkDirtyNode();
337 }
338 
UpdateButtonLayout(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & button,bool isLeft)339 void AtomicServicePattern::UpdateButtonLayout(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& button, bool isLeft)
340 {
341     CHECK_NULL_VOID(theme);
342     CHECK_NULL_VOID(button);
343 
344     auto bent = theme->GetBentRadius();
345     auto rightAngle = theme->GetRightAngle();
346     auto leftBorderRadius = BorderRadiusProperty(bent, rightAngle, rightAngle, bent);
347     auto rightBorderRadius = BorderRadiusProperty(rightAngle, bent, bent, rightAngle);
348 
349     auto layoutProperty = button->GetLayoutProperty<ButtonLayoutProperty>();
350     layoutProperty->UpdateBorderRadius(isLeft ? leftBorderRadius : rightBorderRadius);
351 
352     button->MarkModifyDone();
353     button->MarkDirtyNode();
354 }
355 
UpdateIconLayout(RefPtr<AppBarTheme> & theme,RefPtr<FrameNode> & icon,bool isLeft)356 void AtomicServicePattern::UpdateIconLayout(RefPtr<AppBarTheme>& theme, RefPtr<FrameNode>& icon, bool isLeft)
357 {
358     CHECK_NULL_VOID(theme);
359     CHECK_NULL_VOID(icon);
360 
361     MarginProperty margin;
362     margin.top = CalcLength(theme->GetIconVerticalMargin());
363     margin.bottom = CalcLength(theme->GetIconVerticalMargin());
364     if (isLeft) {
365         margin.left = CalcLength(theme->GetIconOutsideMargin());
366         margin.right = CalcLength(theme->GetIconInsideMargin());
367     } else {
368         margin.left = CalcLength(theme->GetIconInsideMargin());
369         margin.right = CalcLength(theme->GetIconOutsideMargin());
370     }
371     icon->GetLayoutProperty<ImageLayoutProperty>()->UpdateMargin(margin);
372 
373     icon->MarkModifyDone();
374     icon->MarkDirtyNode();
375 }
376 } // namespace OHOS::Ace::NG
377