1 /*
2 * Copyright (c) 2025 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/window_scene/helper/starting_window_layout_helper.h"
17
18 #include <algorithm>
19
20 #include "adapter/ohos/entrance/ace_view_ohos.h"
21 #include "base/geometry/dimension.h"
22 #include "base/memory/referenced.h"
23 #include "base/utils/utils.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/pattern/window_scene/scene/system_window_scene.h"
26 #include "core/components_v2/inspector/inspector_constants.h"
27 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
28 #include "core/components_ng/pattern/search/search_pattern.h"
29 #include "core/pipeline_ng/pipeline_context.h"
30 #include "session/host/include/session.h"
31 #include "session_manager/include/scene_session_manager.h"
32
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr Dimension FIXED_TOP_SAFE_AREA_HEIGHT_VP = 36.0_vp;
36 constexpr Dimension FIXED_BOTTOM_SAFE_AREA_HEIGHT_VP = 28.0_vp;
37 constexpr Dimension FIXED_BRAND_HORIZONTAL_MARGIN_VP = 24.0_vp;
38 constexpr Dimension MIN_BRAND_AREA_HEIGHT_VP = 100.0_vp;
39 constexpr Dimension MAX_BRAND_CONTENT_WIDTH_VP = 400.0_vp;
40 constexpr Dimension MIN_BRAND_CONTENT_HEIGHT_VP = 80.0_vp;
41 constexpr float UPPER_AREA_CONTAINER_HEIGHT_PERCENT = 0.7f;
42 constexpr float BRAND_CONTAINER_HEIGHT_PERCENT = 0.3f;
43 constexpr float BRAND_CONTENT_HEIGHT_PERCENT = 0.4f;
44 constexpr float ILLUSTRATION_CONTENT_SIZE_PERCENT = 0.8f;
45 constexpr float FULL_PERCENT = 1.0f;
46 constexpr float ZERO_FACTOR = 0.0f;
47 constexpr float HALF_FACTOR = 0.5f;
48 constexpr float DOUBLE_FACTOR = 2.0f;
49 const std::vector<std::pair<float, std::string>> WIDTH_BREAKPOINTS_VP = {
50 { 320.0f, "XSmall" },
51 { 600.0f, "Small" },
52 { 840.0f, "Medium" },
53 { 1440.0f, "Large" },
54 { FLT_MAX, "XLarge" },
55 };
56 const std::vector<std::pair<float, std::string>> ASPECT_RATIO_BREAKPOINTS = {
57 { 0.8f, "Landscape" },
58 { 1.2f, "Square" },
59 { FLT_MAX, "Portrait" },
60 };
61
BuildLayoutKey(float widthVp,float aspectRatio)62 std::string BuildLayoutKey(float widthVp, float aspectRatio)
63 {
64 std::string key = "-";
65 for (const auto& p : WIDTH_BREAKPOINTS_VP) {
66 if (LessOrEqual(widthVp, p.first)) {
67 key = p.second + key;
68 break;
69 }
70 }
71 for (const auto& p : ASPECT_RATIO_BREAKPOINTS) {
72 if (LessOrEqual(aspectRatio, p.first)) {
73 key += p.second;
74 break;
75 }
76 }
77 return key;
78 }
79
80 const std::map<std::string, ImageFit> STRING_TO_IMAGEFIT_MAP = {
81 { "Contain", ImageFit::CONTAIN },
82 { "Cover", ImageFit::COVER },
83 { "Auto", ImageFit::FITWIDTH }, // same as the mapping of image pattern
84 { "Fill", ImageFit::FILL },
85 { "ScaleDown", ImageFit::SCALE_DOWN },
86 { "None", ImageFit::NONE },
87 };
88
89 const std::map<std::string, LayoutParams> LAYOUT_RULES_MAP = {
90 { "XSmall-Landscape", { 128.0_vp } },
91 { "XSmall-Square", { 128.0_vp } },
92 { "XSmall-Portrait", { 128.0_vp } },
93 { "Small-Landscape", { 128.0_vp } },
94 { "Small-Square", { 128.0_vp } },
95 { "Small-Portrait", { 192.0_vp } },
96 { "Medium-Landscape", { 192.0_vp } },
97 { "Medium-Square", { 256.0_vp } },
98 { "Medium-Portrait", { 256.0_vp } },
99 { "Large-Landscape", { 256.0_vp } },
100 { "Large-Square", { 256.0_vp } },
101 { "Large-Portrait", { 256.0_vp } },
102 { "XLarge-Landscape", { 256.0_vp } },
103 { "XLarge-Square", { 256.0_vp } },
104 { "XLarge-Portrait", { 256.0_vp } },
105 };
106 } // namespace
107
CreateUpperAreaNode(const ImageSourceInfo & imageSource)108 RefPtr<FrameNode> CreateUpperAreaNode(const ImageSourceInfo& imageSource)
109 {
110 auto upperAreaNode = FrameNode::CreateFrameNode(
111 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
112 CHECK_NULL_RETURN(upperAreaNode, nullptr);
113 auto upperAreaLayoutProperty = upperAreaNode->GetLayoutProperty<ImageLayoutProperty>();
114 CHECK_NULL_RETURN(upperAreaLayoutProperty, nullptr);
115 upperAreaLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
116 upperAreaLayoutProperty->UpdateImageSourceInfo(imageSource);
117 upperAreaLayoutProperty->UpdateImageFit(ImageFit::SCALE_DOWN);
118 upperAreaNode->MarkModifyDone();
119 return upperAreaNode;
120 }
121
CreateBrandNode(const ImageSourceInfo & brandImageSource)122 RefPtr<FrameNode> CreateBrandNode(const ImageSourceInfo& brandImageSource)
123 {
124 auto brandNode = FrameNode::CreateFrameNode(
125 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
126 CHECK_NULL_RETURN(brandNode, nullptr);
127 auto brandLayoutProperty = brandNode->GetLayoutProperty<ImageLayoutProperty>();
128 CHECK_NULL_RETURN(brandLayoutProperty, nullptr);
129 brandLayoutProperty->UpdateImageSourceInfo(brandImageSource);
130 brandLayoutProperty->UpdateImageFit(ImageFit::SCALE_DOWN);
131 brandNode->MarkModifyDone();
132 return brandNode;
133 }
134
CreateBackgroundImageNode(const std::string & imageFit,const ImageSourceInfo & bgImageSource)135 RefPtr<FrameNode> CreateBackgroundImageNode(
136 const std::string& imageFit, const ImageSourceInfo& bgImageSource)
137 {
138 auto bgImgNode = FrameNode::CreateFrameNode(
139 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
140 CHECK_NULL_RETURN(bgImgNode, nullptr);
141 auto bgImgLayoutProperty = bgImgNode->GetLayoutProperty<ImageLayoutProperty>();
142 CHECK_NULL_RETURN(bgImgLayoutProperty, nullptr);
143 bgImgLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
144 bgImgLayoutProperty->UpdateImageSourceInfo(bgImageSource);
145 auto imageFitType = ImageFit::COVER;
146 if (STRING_TO_IMAGEFIT_MAP.find(imageFit) != STRING_TO_IMAGEFIT_MAP.end()) {
147 imageFitType = STRING_TO_IMAGEFIT_MAP.at(imageFit);
148 }
149 bgImgLayoutProperty->UpdateImageFit(imageFitType);
150 bgImgNode->MarkModifyDone();
151 return bgImgNode;
152 }
153
CreateStartingWindowNode(const Rosen::StartingWindowInfo & startingWindowInfo,const std::string & bundleName,const std::string & moduleName)154 RefPtr<FrameNode> StartingWindowLayoutHelper::CreateStartingWindowNode(
155 const Rosen::StartingWindowInfo& startingWindowInfo, const std::string& bundleName, const std::string& moduleName)
156 {
157 startingWindowInfo_ = startingWindowInfo;
158 // -root node of starting window
159 auto startingWindow = FrameNode::CreateFrameNode(
160 V2::STACK_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<StackPattern>());
161 CHECK_NULL_RETURN(startingWindow, nullptr);
162 auto startingWindowLayoutProperty = startingWindow->GetLayoutProperty<StackLayoutProperty>();
163 CHECK_NULL_RETURN(startingWindowLayoutProperty, nullptr);
164 startingWindowLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
165 startingWindow->GetRenderContext()->UpdateBackgroundColor(Color(startingWindowInfo_.backgroundColor_));
166 startingWindow->SetHitTestMode(HitTestMode::HTMNONE);
167
168 if (!startingWindowInfo_.backgroundImagePath_.empty()) {
169 auto bgImgNode = CreateBackgroundImageNode(startingWindowInfo_.backgroundImageFit_,
170 ImageSourceInfo(startingWindowInfo_.backgroundImagePath_, bundleName, moduleName));
171 startingWindow->AddChild(bgImgNode);
172 }
173
174 // --full size container
175 auto columnNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG,
176 ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LinearLayoutPattern>(true));
177 CHECK_NULL_RETURN(columnNode, nullptr);
178 auto columnLayoutProps = columnNode->GetLayoutProperty<LinearLayoutProperty>();
179 CHECK_NULL_RETURN(columnLayoutProps, nullptr);
180 columnLayoutProps->UpdateMeasureType(MeasureType::MATCH_PARENT);
181
182 auto upperAreaNode = CreateUpperAreaNode(ImageSourceInfo(
183 startingWindowInfo_.iconPath_.empty() ? startingWindowInfo_.illustrationPath_ : startingWindowInfo_.iconPath_,
184 bundleName, moduleName));
185 CHECK_NULL_RETURN(upperAreaNode, nullptr);
186 columnNode->AddChild(upperAreaNode);
187 auto brandNode = CreateBrandNode(ImageSourceInfo(startingWindowInfo_.brandingPath_, bundleName, moduleName));
188 CHECK_NULL_RETURN(brandNode, nullptr);
189 columnNode->AddChild(brandNode);
190 columnNode->MarkModifyDone();
191
192 startingWindow->AddChild(columnNode);
193 startingWindow->MarkModifyDone();
194 upperAreaNode_ = upperAreaNode;
195 brandNode_ = brandNode;
196 return startingWindow;
197 }
198
MeasureUpperAreaNode(const SizeF & parentSize,const RefPtr<FrameNode> upperAreaNode,bool needHideBrand)199 void StartingWindowLayoutHelper::MeasureUpperAreaNode(
200 const SizeF& parentSize, const RefPtr<FrameNode> upperAreaNode, bool needHideBrand)
201 {
202 auto upperAreaLayoutProperty = upperAreaNode->GetLayoutProperty<ImageLayoutProperty>();
203 CHECK_NULL_VOID(upperAreaLayoutProperty);
204 float upperHeightPercent = needHideBrand ? FULL_PERCENT : UPPER_AREA_CONTAINER_HEIGHT_PERCENT;
205 float upperContentSizePx = std::min(parentSize.Height() * HALF_FACTOR, parentSize.Width()) *
206 ILLUSTRATION_CONTENT_SIZE_PERCENT;
207 if (!startingWindowInfo_.iconPath_.empty()) {
208 upperAreaLayoutProperty->UpdateImageFit(ImageFit::CONTAIN);
209 CHECK_EQUAL_VOID(NearZero(parentSize.Width()), true);
210 auto key = BuildLayoutKey(Dimension(parentSize.Width(), DimensionUnit::PX).ConvertToVp(),
211 parentSize.Height() / parentSize.Width());
212 if (LAYOUT_RULES_MAP.find(key) != LAYOUT_RULES_MAP.end()) {
213 upperContentSizePx = LAYOUT_RULES_MAP.at(key).iconSize.ConvertToPx();
214 }
215 }
216 float totalSafeAreaHeightPx = FIXED_TOP_SAFE_AREA_HEIGHT_VP.ConvertToPx() +
217 (needHideBrand ? FIXED_BOTTOM_SAFE_AREA_HEIGHT_VP.ConvertToPx() : ZERO_FACTOR);
218 float upperContainerHeightPx = parentSize.Height() * upperHeightPercent - totalSafeAreaHeightPx;
219 float upperContentVerticalMarginPx = (upperContainerHeightPx - upperContentSizePx) * HALF_FACTOR;
220 MarginProperty upperContentMargin = {
221 .top = CalcLength(Dimension(
222 upperContentVerticalMarginPx + FIXED_TOP_SAFE_AREA_HEIGHT_VP.ConvertToPx(), DimensionUnit::PX)),
223 .bottom = CalcLength(Dimension(upperContentVerticalMarginPx, DimensionUnit::PX)),
224 };
225 upperAreaLayoutProperty->UpdateMargin(upperContentMargin);
226 upperAreaLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(
227 CalcLength(Dimension(upperContentSizePx, DimensionUnit::PX)),
228 CalcLength(Dimension(upperContentSizePx, DimensionUnit::PX))));
229 }
230
MeasureChildNode(const SizeF & parentSize)231 void StartingWindowLayoutHelper::MeasureChildNode(const SizeF& parentSize)
232 {
233 auto upperAreaNode = upperAreaNode_.Upgrade();
234 CHECK_NULL_VOID(upperAreaNode);
235 auto brandNode = brandNode_.Upgrade();
236 CHECK_NULL_VOID(brandNode);
237 bool needHideBrand = LessOrEqual(parentSize.Height() * BRAND_CONTAINER_HEIGHT_PERCENT,
238 MIN_BRAND_AREA_HEIGHT_VP.ConvertToPx());
239 MeasureUpperAreaNode(parentSize, upperAreaNode, needHideBrand);
240
241 auto brandLayoutProperty = brandNode->GetLayoutProperty<ImageLayoutProperty>();
242 CHECK_NULL_VOID(brandLayoutProperty);
243 if (needHideBrand) {
244 brandLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE, true);
245 return;
246 }
247 brandLayoutProperty->UpdateVisibility(VisibleType::VISIBLE, true);
248 float brandWidthPx = std::min(parentSize.Width() - FIXED_BRAND_HORIZONTAL_MARGIN_VP.ConvertToPx() * DOUBLE_FACTOR,
249 MAX_BRAND_CONTENT_WIDTH_VP.ConvertToPx());
250 float brandContainerHeightPx = parentSize.Height() * BRAND_CONTAINER_HEIGHT_PERCENT -
251 FIXED_BOTTOM_SAFE_AREA_HEIGHT_VP.ConvertToPx();
252 float brandHeightPx = std::max(brandContainerHeightPx * BRAND_CONTENT_HEIGHT_PERCENT,
253 static_cast<float>(MIN_BRAND_CONTENT_HEIGHT_VP.ConvertToPx()));
254 float brandVerticalMarginPx = (brandContainerHeightPx - brandHeightPx) * HALF_FACTOR;
255 float brandHorizontalMarginPx = (parentSize.Width() - brandWidthPx) * HALF_FACTOR;
256 MarginProperty brandMargin = {
257 .left = CalcLength(Dimension(brandHorizontalMarginPx, DimensionUnit::PX)),
258 .right = CalcLength(Dimension(brandHorizontalMarginPx, DimensionUnit::PX)),
259 .top = CalcLength(Dimension(brandVerticalMarginPx, DimensionUnit::PX)),
260 .bottom = CalcLength(Dimension(brandVerticalMarginPx + FIXED_BOTTOM_SAFE_AREA_HEIGHT_VP.ConvertToPx(),
261 DimensionUnit::PX)),
262 };
263 brandLayoutProperty->UpdateMargin(brandMargin);
264 brandLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(
265 CalcLength(Dimension(brandWidthPx, DimensionUnit::PX)),
266 CalcLength(Dimension(brandHeightPx, DimensionUnit::PX))));
267 }
268 } // namespace OHOS::Ace::NG
269