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/toast/toast_pattern.h"
16 #include "core/animation/animation_util.h"
17
18 #include "base/subwindow/subwindow_manager.h"
19 #include "base/utils/system_properties.h"
20 #include "base/utils/utils.h"
21 #include "base/log/dump_log.h"
22 #include "core/common/ace_engine.h"
23 #include "core/common/container.h"
24 #include "core/components/common/layout/grid_system_manager.h"
25 #include "core/components/dialog/dialog_theme.h"
26 #include "core/components_ng/layout/layout_wrapper.h"
27 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
28 #include "core/components_ng/pattern/text/text_layout_property.h"
29 #include "core/pipeline/pipeline_base.h"
30 #include "core/pipeline_ng/pipeline_context.h"
31
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr int32_t API_VERSION_9 = 9;
35 constexpr Dimension ADAPT_TOAST_MIN_FONT_SIZE = 12.0_fp;
36
37 // get main window's pipeline
GetMainPipelineContext()38 RefPtr<PipelineContext> GetMainPipelineContext()
39 {
40 auto containerId = Container::CurrentId();
41 RefPtr<PipelineContext> context;
42 if (containerId >= MIN_SUBCONTAINER_ID) {
43 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
44 auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
45 CHECK_NULL_RETURN(parentContainer, nullptr);
46 context = AceType::DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
47 } else {
48 context = PipelineContext::GetCurrentContext();
49 }
50 return context;
51 }
52 } // namespace
53
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & changeConfig)54 bool ToastPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& changeConfig)
55 {
56 CHECK_NULL_RETURN(dirty, false);
57 auto context = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
58 CHECK_NULL_RETURN(context, false);
59 auto toastNode = dirty->GetHostNode();
60 CHECK_NULL_RETURN(toastNode, false);
61 auto toastContext = toastNode->GetRenderContext();
62 CHECK_NULL_RETURN(toastContext, false);
63 auto dialogTheme = context->GetTheme<DialogTheme>();
64 CHECK_NULL_RETURN(dialogTheme, false);
65 expandDisplay_ = dialogTheme->GetExpandDisplay() || IsShowInFreeMultiWindow();
66 OffsetT<Dimension> offset { GetOffsetX(dirty), GetOffsetY(dirty) };
67 // show in the float subwindow
68 if (!IsSystemTopMost() && (IsUIExtensionSubWindow() || (!IsDefaultToast() && expandDisplay_))) {
69 auto nodeContext = toastNode->GetContextWithCheck();
70 CHECK_NULL_RETURN(nodeContext, false);
71 auto subwindowOffset = nodeContext->GetDisplayWindowRectInfo().GetOffset();
72 if (!IsUIExtensionSubWindow() && (!NearEqual(subwindowOffset.GetX(), 0) ||
73 !NearEqual(subwindowOffset.GetY(), 0))) {
74 TAG_LOGW(AceLogTag::ACE_OVERLAY, "toast subwindow offset, x: %{public}f, y: %{public}f",
75 subwindowOffset.GetX(), subwindowOffset.GetY());
76 }
77 OffsetT<Dimension> displayWindowOffset = { Dimension(context->GetDisplayWindowRectInfo().GetOffset().GetX() -
78 subwindowOffset.GetX()), Dimension(context->GetDisplayWindowRectInfo().GetOffset().GetY() -
79 subwindowOffset.GetY()) };
80 TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast displayWindowOffset, x: %{public}.2f vp, y: %{public}.2f vp",
81 displayWindowOffset.GetX().ConvertToVp(), displayWindowOffset.GetY().ConvertToVp());
82 offset += displayWindowOffset;
83 }
84 auto func = [toastContext, offset]() { toastContext->UpdateOffset(offset); };
85 auto toastProp = DynamicCast<ToastLayoutProperty>(dirty->GetLayoutProperty());
86 CHECK_NULL_RETURN(toastProp, false);
87 auto showMode = toastProp->GetShowModeValue(ToastShowMode::DEFAULT);
88 if (showMode == ToastShowMode::TOP_MOST) {
89 auto keyboardAnimationConfig = context->GetKeyboardAnimationConfig();
90 auto safeAreaManager = context->GetSafeAreaManager();
91 auto keyboardHeight = safeAreaManager ? safeAreaManager->GetKeyboardInset().Length() : 0;
92 if (safeAreaManager && NearEqual(keyboardHeight, 0.0f)) {
93 keyboardHeight = safeAreaManager->GetRawKeyboardHeight();
94 }
95 AnimationOption option = AnimationUtil::CreateKeyboardAnimationOption(keyboardAnimationConfig, keyboardHeight);
96 context->Animate(option, option.GetCurve(), func);
97 } else {
98 func();
99 }
100 return true;
101 }
102
GetOffsetX(const RefPtr<LayoutWrapper> & layoutWrapper)103 Dimension ToastPattern::GetOffsetX(const RefPtr<LayoutWrapper>& layoutWrapper)
104 {
105 auto context = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
106 CHECK_NULL_RETURN(context, Dimension(0.0));
107 auto text = layoutWrapper->GetOrCreateChildByIndex(0);
108 CHECK_NULL_RETURN(text, Dimension(0.0));
109 auto rootWidth = context->GetRootWidth();
110 if (IsSystemTopMost()) {
111 rootWidth = static_cast<double>(SystemProperties::GetDeviceWidth());
112 }
113 auto toastProp = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
114 CHECK_NULL_RETURN(toastProp, Dimension(0.0));
115 auto textWidth = text->GetGeometryNode()->GetMarginFrameSize().Width();
116 Alignment alignment = toastProp->GetToastAlignmentValue(Alignment::BOTTOM_CENTER);
117 Dimension offsetX;
118 if (alignment == Alignment::TOP_LEFT || alignment == Alignment::CENTER_LEFT ||
119 alignment == Alignment::BOTTOM_LEFT) {
120 offsetX = Dimension(0.0);
121 } else if (alignment == Alignment::TOP_RIGHT || alignment == Alignment::CENTER_RIGHT ||
122 alignment == Alignment::BOTTOM_RIGHT) {
123 offsetX = Dimension(rootWidth - textWidth);
124 } else {
125 offsetX = Dimension((rootWidth - textWidth) / 2.0f);
126 }
127 return offsetX + toastProp->GetToastOffsetValue(DimensionOffset()).GetX();
128 }
129
GetOffsetY(const RefPtr<LayoutWrapper> & layoutWrapper)130 Dimension ToastPattern::GetOffsetY(const RefPtr<LayoutWrapper>& layoutWrapper)
131 {
132 auto context = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
133 CHECK_NULL_RETURN(context, Dimension(0.0));
134 auto text = layoutWrapper->GetOrCreateChildByIndex(0);
135 CHECK_NULL_RETURN(text, Dimension(0.0));
136 auto rootHeight = IsSystemTopMost() ? static_cast<double>(SystemProperties::GetDeviceHeight())
137 : context->GetRootHeight();
138 auto toastProp = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
139 CHECK_NULL_RETURN(toastProp, Dimension(0.0));
140 auto textHeight = text->GetGeometryNode()->GetMarginFrameSize().Height();
141 Dimension offsetY;
142 auto safeAreaManager = context->GetSafeAreaManager();
143 auto safeAreaOffset = safeAreaManager ? safeAreaManager->GetSafeAreaWithoutProcess().bottom_.Length() : 0;
144 auto showMode = toastProp->GetShowModeValue(ToastShowMode::DEFAULT);
145 auto keyboardInset = safeAreaManager ? safeAreaManager->GetKeyboardInset().Length() : 0;
146 if (safeAreaManager && NearEqual(keyboardInset, 0.0f)) {
147 keyboardInset = safeAreaManager->GetRawKeyboardHeight();
148 }
149 auto keyboardOffset = GreatNotEqual(keyboardInset, 0) ? keyboardInset : 0;
150 if (showMode == ToastShowMode::DEFAULT) {
151 safeAreaOffset = std::max(keyboardOffset, safeAreaOffset);
152 }
153 // Get toastBottom and update defaultBottom_
154 auto toastBottom = GetBottomValue(layoutWrapper);
155 if (!toastProp->HasToastAlignment()) {
156 toastBottom_ = toastBottom;
157 if (context->GetMinPlatformVersion() > API_VERSION_9) {
158 offsetY = Dimension(rootHeight - toastBottom - textHeight - safeAreaOffset);
159 } else {
160 offsetY = Dimension(rootHeight - toastBottom);
161 }
162 } else {
163 Alignment alignment = toastProp->GetToastAlignmentValue(Alignment::BOTTOM_CENTER);
164 if (alignment == Alignment::TOP_LEFT || alignment == Alignment::TOP_CENTER ||
165 alignment == Alignment::TOP_RIGHT) {
166 // Top Needs Avoid System Navigation Bar
167 auto safeAreaManager = context->GetSafeAreaManager();
168 auto sysTop = safeAreaManager ? safeAreaManager->GetSystemSafeArea().top_.Length() : 0.0f;
169 offsetY = Dimension(sysTop);
170 } else if (alignment == Alignment::CENTER_LEFT || alignment == Alignment::CENTER ||
171 alignment == Alignment::CENTER_RIGHT) {
172 offsetY = Dimension((rootHeight - textHeight - safeAreaOffset) / 2.0f);
173 } else {
174 offsetY = Dimension(rootHeight - textHeight - safeAreaOffset);
175 }
176 }
177 //TOP_MOST Toast need to avoid keyboard
178 auto deviceHeight = context->GetRootHeight();
179 if (showMode == ToastShowMode::TOP_MOST && (offsetY.Value() + textHeight > deviceHeight - keyboardOffset)) {
180 offsetY = Dimension(deviceHeight - keyboardOffset - defaultBottom_.ConvertToPx() -textHeight);
181 }
182 TAG_LOGD(AceLogTag::ACE_OVERLAY,
183 "toast device height: %{public}.2f, keyboardOffset: %{public}d, "
184 "textHeight: %{public}.2f, offsetY: %{public}.2f",
185 deviceHeight, (uint32_t)keyboardOffset, textHeight, offsetY.Value());
186 return offsetY + toastProp->GetToastOffsetValue(DimensionOffset()).GetY();
187 }
188
GetBottomValue(const RefPtr<LayoutWrapper> & layoutWrapper)189 double ToastPattern::GetBottomValue(const RefPtr<LayoutWrapper>& layoutWrapper)
190 {
191 // Obtain the height relative to the main window
192 auto pipeline = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
193 CHECK_NULL_RETURN(pipeline, 0.0);
194 auto rootHeight = Dimension(pipeline->GetRootHeight());
195 if (IsSystemTopMost()) {
196 rootHeight = Dimension(static_cast<double>(SystemProperties::GetDeviceHeight()));
197 }
198 auto toastTheme = pipeline->GetTheme<ToastTheme>();
199 CHECK_NULL_RETURN(toastTheme, 0.0);
200
201 auto toastProp = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
202 CHECK_NULL_RETURN(toastProp, 0.0);
203 defaultBottom_ = toastTheme->GetBottom();
204 auto toastBottom = toastProp->GetBottomValue(defaultBottom_);
205 if (toastBottom.Unit() == DimensionUnit::PERCENT) {
206 toastBottom = rootHeight * toastBottom.Value();
207 }
208 return GreatOrEqual(toastBottom.ConvertToPx(), 0.0) ? toastBottom.ConvertToPx()
209 : toastTheme->GetBottom().ConvertToPx();
210 }
211
BeforeCreateLayoutWrapper()212 void ToastPattern::BeforeCreateLayoutWrapper()
213 {
214 PopupBasePattern::BeforeCreateLayoutWrapper();
215
216 auto toastNode = GetHost();
217 CHECK_NULL_VOID(toastNode);
218 auto pipelineContext = IsDefaultToast() ? toastNode->GetContextRefPtr() : GetMainPipelineContext();
219 if (!pipelineContext) {
220 TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast get pipelineContext failed");
221 return;
222 }
223 auto textNode = DynamicCast<FrameNode>(toastNode->GetFirstChild());
224 CHECK_NULL_VOID(textNode);
225 UpdateTextSizeConstraint(textNode);
226 }
227
UpdateToastSize(const RefPtr<FrameNode> & toast)228 void ToastPattern::UpdateToastSize(const RefPtr<FrameNode>& toast)
229 {
230 CHECK_NULL_VOID(toast);
231 auto toastProperty = toast->GetLayoutProperty<ToastLayoutProperty>();
232 CHECK_NULL_VOID(toastProperty);
233 auto context = PipelineBase::GetCurrentContext();
234 CHECK_NULL_VOID(context);
235 auto rootWidth = Dimension(context->GetRootWidth());
236 if (IsSystemTopMost()) {
237 rootWidth = Dimension(static_cast<double>(SystemProperties::GetDeviceWidth()));
238 }
239 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
240 auto limitWidth = Dimension(GetTextMaxWidth());
241 toastProperty->UpdateUserDefinedIdealSize(CalcSize(NG::CalcLength(limitWidth), std::nullopt));
242 } else {
243 toastProperty->UpdateUserDefinedIdealSize(CalcSize(NG::CalcLength(rootWidth), std::nullopt));
244 }
245 }
246
UpdateTextSizeConstraint(const RefPtr<FrameNode> & text)247 void ToastPattern::UpdateTextSizeConstraint(const RefPtr<FrameNode>& text)
248 {
249 CHECK_NULL_VOID(text);
250 auto context = PipelineBase::GetCurrentContext();
251 CHECK_NULL_VOID(context);
252 auto gridColumnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::TOAST);
253 auto parent = gridColumnInfo->GetParent();
254 if (parent) {
255 parent->BuildColumnWidth(context->GetRootWidth());
256 }
257 auto maxWidth = Dimension(gridColumnInfo->GetMaxWidth());
258 auto textLayoutProperty = text->GetLayoutProperty();
259 CHECK_NULL_VOID(textLayoutProperty);
260
261 auto toastTheme = context->GetTheme<ToastTheme>();
262 CHECK_NULL_VOID(toastTheme);
263 auto minWidth = Dimension(toastTheme->GetMinWidth().ConvertToPx());
264 auto minHeight = Dimension(toastTheme->GetMinHeight().ConvertToPx());
265 textLayoutProperty->UpdateCalcMinSize(CalcSize(NG::CalcLength(minWidth), NG::CalcLength(minHeight)));
266
267 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
268 auto limitHeight = GetTextMaxHeight();
269 textLayoutProperty->UpdateCalcMaxSize(
270 CalcSize(NG::CalcLength(maxWidth), NG::CalcLength(Dimension(limitHeight))));
271
272 auto textProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
273 CHECK_NULL_VOID(textProperty);
274 auto toastMaxFontSize = toastTheme->GetTextStyle().GetFontSize();
275 textProperty->UpdateAdaptMaxFontSize(toastMaxFontSize);
276 textProperty->UpdateAdaptMinFontSize(ADAPT_TOAST_MIN_FONT_SIZE);
277 textProperty->UpdateHeightAdaptivePolicy(TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST);
278
279 auto textLineHeight = GetTextLineHeight(text);
280 if (textLineHeight > 0) {
281 auto maxLines = static_cast<int32_t>(limitHeight / textLineHeight);
282 textProperty->UpdateMaxLines(maxLines);
283 }
284 } else {
285 textLayoutProperty->UpdateCalcMaxSize(CalcSize(NG::CalcLength(maxWidth), std::nullopt));
286 }
287 }
288
OnColorConfigurationUpdate()289 void ToastPattern::OnColorConfigurationUpdate()
290 {
291 auto host = GetHost();
292 CHECK_NULL_VOID(host);
293 auto textContext = host->GetRenderContext();
294 CHECK_NULL_VOID(textContext);
295 auto pipelineContext = PipelineBase::GetCurrentContext();
296 CHECK_NULL_VOID(pipelineContext);
297 auto toastTheme = pipelineContext->GetTheme<ToastTheme>();
298 CHECK_NULL_VOID(toastTheme);
299 auto textColor = toastTheme->GetTextStyle().GetTextColor();
300 auto textLayoutProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
301 CHECK_NULL_VOID(textLayoutProperty);
302 auto toastInfo = GetToastInfo();
303 textLayoutProperty->UpdateTextColor(toastInfo.textColor.value_or(textColor));
304 host->SetNeedCallChildrenUpdate(false);
305 ToastView::UpdateToastNodeStyle(host);
306 }
307
OnAttachToFrameNode()308 void ToastPattern::OnAttachToFrameNode()
309 {
310 auto containerId = Container::CurrentId();
311 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
312 auto pipeline =
313 parentContainerId < 0 ? PipelineContext::GetCurrentContext() : PipelineContext::GetMainPipelineContext();
314 CHECK_NULL_VOID(pipeline);
315 auto callbackId =
316 pipeline->RegisterFoldDisplayModeChangedCallback([parentContainerId](FoldDisplayMode foldDisplayMode) {
317 if (foldDisplayMode == FoldDisplayMode::FULL || foldDisplayMode == FoldDisplayMode::MAIN) {
318 TAG_LOGI(AceLogTag::ACE_OVERLAY, "Window status changes, displayMode is %{public}d", foldDisplayMode);
319 SubwindowManager::GetInstance()->ResizeWindowForFoldStatus(parentContainerId);
320 }
321 });
322 UpdateFoldDisplayModeChangedCallbackId(callbackId);
323 }
324
OnDetachFromFrameNode(FrameNode * node)325 void ToastPattern::OnDetachFromFrameNode(FrameNode* node)
326 {
327 auto containerId = Container::CurrentId();
328 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
329 auto pipeline =
330 parentContainerId < 0 ? PipelineContext::GetCurrentContext() : PipelineContext::GetMainPipelineContext();
331 CHECK_NULL_VOID(pipeline);
332 if (HasFoldDisplayModeChangedCallbackId()) {
333 pipeline->UnRegisterFoldDisplayModeChangedCallback(foldDisplayModeChangedCallbackId_.value_or(-1));
334 }
335 }
336
IsShowInFreeMultiWindow()337 bool ToastPattern::IsShowInFreeMultiWindow()
338 {
339 auto currentId = Container::CurrentId();
340 auto container = Container::Current();
341 if (!container) {
342 TAG_LOGW(AceLogTag::ACE_OVERLAY, "container is null");
343 return false;
344 }
345 if (container->IsSubContainer()) {
346 currentId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
347 container = AceEngine::Get().GetContainer(currentId);
348 if (!container) {
349 TAG_LOGW(AceLogTag::ACE_OVERLAY, "parent container is null");
350 return false;
351 }
352 }
353 return container->IsFreeMultiWindow();
354 }
355
IsUIExtensionSubWindow()356 bool ToastPattern::IsUIExtensionSubWindow()
357 {
358 if (IsDefaultToast()) {
359 return false;
360 }
361
362 auto currentId = Container::CurrentId();
363 auto container = Container::Current();
364 CHECK_NULL_RETURN(container, false);
365 if (container->IsSubContainer()) {
366 currentId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
367 container = AceEngine::Get().GetContainer(currentId);
368 CHECK_NULL_RETURN(container, false);
369 }
370 return container->IsUIExtensionWindow();
371 }
372
DumpInfo()373 void ToastPattern::DumpInfo()
374 {
375 DumpLog::GetInstance().AddDesc("Message: " + toastInfo_.message);
376 DumpLog::GetInstance().AddDesc("Duration: " + std::to_string(toastInfo_.duration));
377 DumpLog::GetInstance().AddDesc("Bottom: " + toastInfo_.bottom);
378 std::string isRightToLeft = toastInfo_.isRightToLeft ? "true" : "false";
379 DumpLog::GetInstance().AddDesc("IsRightToLeft: " + isRightToLeft);
380 std::string showMode = toastInfo_.showMode == ToastShowMode::DEFAULT ? "DEFAULT" : "TOP_MOST";
381 DumpLog::GetInstance().AddDesc("ShowMode: " + showMode);
382 auto host = GetHost();
383 CHECK_NULL_VOID(host);
384 auto toastProp = DynamicCast<ToastLayoutProperty>(host->GetLayoutProperty());
385 CHECK_NULL_VOID(toastProp);
386 if (!toastProp->HasToastAlignment()) {
387 DumpLog::GetInstance().AddDesc("Alignment: NONE");
388 } else {
389 DumpLog::GetInstance().AddDesc(
390 "Alignment: " + toastProp->GetToastAlignmentValue().GetAlignmentStr(toastProp->GetLayoutDirection()));
391 }
392 auto offset = toastProp->GetToastOffsetValue(DimensionOffset());
393 DumpLog::GetInstance().AddDesc(
394 "Offset: { dx: " + offset.GetX().ToString() + " dy: " + offset.GetY().ToString() + " }");
395 }
396
GetTextMaxHeight()397 double ToastPattern::GetTextMaxHeight()
398 {
399 auto pipelineContext = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
400 CHECK_NULL_RETURN(pipelineContext, 0.0);
401 double deviceHeight = 0.0;
402 if (IsSystemTopMost()) {
403 deviceHeight = static_cast<double>(SystemProperties::GetDeviceHeight());
404 TAG_LOGD(AceLogTag::ACE_OVERLAY, "SystemTopMost toast get device height: %{public}f.", deviceHeight);
405 } else if (IsUIExtensionSubWindow()) {
406 auto toastNode = GetHost();
407 CHECK_NULL_RETURN(toastNode, 0.0);
408 auto nodeContext = toastNode->GetContextWithCheck();
409 CHECK_NULL_RETURN(nodeContext, 0.0);
410 deviceHeight = nodeContext->GetDisplayWindowRectInfo().Height();
411 TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast in UIExtension subwindow, device height: %{public}f.", deviceHeight);
412 } else {
413 deviceHeight = pipelineContext->GetRootHeight();
414 TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast get device height: %{public}f.", deviceHeight);
415 }
416 if (LessOrEqual(deviceHeight, 0.0)) {
417 TAG_LOGE(AceLogTag::ACE_OVERLAY, "toast get device height is invalid.");
418 deviceHeight = static_cast<double>(SystemProperties::GetDeviceHeight());
419 }
420 auto safeAreaManager = pipelineContext->GetSafeAreaManager();
421 auto bottom = safeAreaManager ? safeAreaManager->GetSafeAreaWithoutProcess().bottom_.Length() : 0;
422 auto top = safeAreaManager ? safeAreaManager->GetSafeAreaWithoutProcess().top_.Length() : 0;
423 auto maxHeight = deviceHeight - bottom - top - toastBottom_;
424 auto limitHeight = (deviceHeight - bottom - top) * 0.65;
425 if (GreatNotEqual(maxHeight, limitHeight)) {
426 maxHeight = limitHeight;
427 }
428
429 maxHeight = GreatOrEqual(maxHeight, 0.0) ? maxHeight : 0.0;
430 return maxHeight;
431 }
432
GetTextMaxWidth()433 double ToastPattern::GetTextMaxWidth()
434 {
435 auto pipelineContext = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
436 CHECK_NULL_RETURN(pipelineContext, 0.0);
437 double deviceWidth = 0.0;
438 if (IsSystemTopMost()) {
439 deviceWidth = static_cast<double>(SystemProperties::GetDeviceWidth());
440 TAG_LOGD(AceLogTag::ACE_OVERLAY, "SystemTopMost toast get device width: %{public}f.", deviceWidth);
441 } else if (IsUIExtensionSubWindow()) {
442 auto toastNode = GetHost();
443 CHECK_NULL_RETURN(toastNode, 0.0);
444 auto nodeContext = toastNode->GetContextWithCheck();
445 CHECK_NULL_RETURN(nodeContext, 0.0);
446 deviceWidth = nodeContext->GetDisplayWindowRectInfo().Width();
447 TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast in UIExtension subwindow, device width: %{public}f.", deviceWidth);
448 } else {
449 deviceWidth = pipelineContext->GetRootWidth();
450 TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast get device width: %{public}f.", deviceWidth);
451 }
452 if (LessOrEqual(deviceWidth, 0.0)) {
453 TAG_LOGE(AceLogTag::ACE_OVERLAY, "toast get device width is invalid.");
454 deviceWidth = static_cast<double>(SystemProperties::GetDeviceWidth());
455 }
456 auto toastTheme = pipelineContext->GetTheme<ToastTheme>();
457 CHECK_NULL_RETURN(toastTheme, 0.0);
458 auto marging = toastTheme->GetMarging();
459 auto maxWidth = deviceWidth - marging.Left().ConvertToPx() - marging.Right().ConvertToPx();
460 auto maxLimitWidth = toastTheme->GetMaxWidth();
461 if (GreatNotEqual(maxWidth, maxLimitWidth.ConvertToPx())) {
462 maxWidth = maxLimitWidth.ConvertToPx();
463 }
464 return maxWidth;
465 }
466
GetTextLineHeight(const RefPtr<FrameNode> & textNode)467 int32_t ToastPattern::GetTextLineHeight(const RefPtr<FrameNode>& textNode)
468 {
469 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
470 CHECK_NULL_RETURN(textLayoutProperty, 0);
471 auto layoutConstraint = textLayoutProperty->GetLayoutConstraint();
472 auto textLayoutWrapper = textNode->CreateLayoutWrapper();
473 CHECK_NULL_RETURN(textLayoutWrapper, 0);
474 textLayoutWrapper->Measure(layoutConstraint);
475 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(textLayoutWrapper->GetLayoutAlgorithm());
476 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
477 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
478 CHECK_NULL_RETURN(textLayoutAlgorithm, 0);
479 auto paragraph = textLayoutAlgorithm->GetSingleParagraph();
480 CHECK_NULL_RETURN(paragraph, 0);
481 auto paragHeight = paragraph->GetHeight();
482 auto paragLineCount = paragraph->GetLineCount();
483 int32_t paragLineHeight = 0;
484 if (paragLineCount > 0) {
485 paragLineHeight = static_cast<int32_t>(paragHeight / paragLineCount);
486 }
487 return paragLineHeight;
488 }
489 } // namespace OHOS::Ace::NG
490