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::GetCurrentContextSafelyWithCheck();
49     }
50     return context;
51 }
52 } // namespace
53 
InitWrapperRect(LayoutWrapper * layoutWrapper,const RefPtr<ToastLayoutProperty> & toastProps)54 void ToastPattern::InitWrapperRect(LayoutWrapper* layoutWrapper, const RefPtr<ToastLayoutProperty>& toastProps)
55 {
56     // init toast wrapper rect with different settings.
57     auto pipelineContext =
58         IsDefaultToast() ? PipelineContext::GetCurrentContextSafelyWithCheck() : GetMainPipelineContext();
59     CHECK_NULL_VOID(pipelineContext);
60     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
61     CHECK_NULL_VOID(safeAreaManager);
62     float safeAreaTop = safeAreaManager->GetSystemSafeArea().top_.Length();
63     const auto& safeArea = toastProps->GetSafeAreaInsets();
64     // Default Toast need to avoid keyboard, but the Top mode doesn't need.
65     float safeAreaBottom =
66         safeArea ? safeArea->bottom_.Length() : safeAreaManager->GetSafeAreaWithoutProcess().bottom_.Length();
67     if (IsSystemTopMost()) {
68         wrapperRect_ = pipelineContext->GetDisplayWindowRectInfo();
69         auto windowSize = GetSystemTopMostSubwindowSize();
70         wrapperRect_.SetRect(0, safeAreaTop, static_cast<double>(windowSize.Width()),
71             static_cast<double>(windowSize.Height()) - safeAreaTop - safeAreaBottom);
72     } else {
73         wrapperRect_ = pipelineContext->GetDisplayWindowRectInfo();
74         wrapperRect_.SetRect(wrapperRect_.Left(), safeAreaTop,
75             pipelineContext->GetRootWidth(), pipelineContext->GetRootHeight() - safeAreaTop - safeAreaBottom);
76     }
77 
78     isHoverMode_ = pipelineContext->IsHalfFoldHoverStatus();
79     if (isHoverMode_ && toastInfo_.enableHoverMode) {
80         UpdateHoverModeRect(toastProps, safeAreaManager, safeAreaTop, safeAreaBottom);
81     }
82 }
83 
UpdateHoverModeRect(const RefPtr<ToastLayoutProperty> & toastProps,const RefPtr<SafeAreaManager> & safeAreaManager,float safeAreaTop,float safeAreaBottom)84 void ToastPattern::UpdateHoverModeRect(const RefPtr<ToastLayoutProperty>& toastProps,
85     const RefPtr<SafeAreaManager>& safeAreaManager, float safeAreaTop, float safeAreaBottom)
86 {
87     auto hoverModeArea = toastProps->GetHoverModeAreaValue(HoverModeAreaType::TOP_SCREEN);
88     auto container = Container::CurrentSafelyWithCheck();
89     auto displayInfo = container->GetDisplayInfo();
90     auto foldCreaseRects = displayInfo->GetCurrentFoldCreaseRegion();
91     float foldCreaseTop = 0.0f;
92     float foldCreaseBottom = 0.0f;
93     if (!foldCreaseRects.empty()) {
94         auto foldCrease = foldCreaseRects.front();
95         foldCreaseTop = foldCrease.Top();
96         foldCreaseBottom = foldCrease.Bottom();
97     }
98     bool isKeyboardShow = false;
99     auto showMode = ToastShowMode::DEFAULT;
100     switch (hoverModeArea) {
101         case HoverModeAreaType::TOP_SCREEN:
102             wrapperRect_.SetRect(wrapperRect_.Left(), safeAreaTop, wrapperRect_.Width(), foldCreaseTop - safeAreaTop);
103             break;
104         case HoverModeAreaType::BOTTOM_SCREEN:
105             isKeyboardShow = safeAreaManager->GetKeyboardInset().IsValid();
106             showMode = toastProps->GetShowModeValue(ToastShowMode::DEFAULT);
107             // if keyboard is show, wrapper rect change to the up half screen.
108             if (isKeyboardShow && showMode != ToastShowMode::SYSTEM_TOP_MOST) {
109                 wrapperRect_.SetRect(wrapperRect_.Left(), safeAreaTop,
110                     wrapperRect_.Width(), foldCreaseTop - safeAreaTop);
111             } else {
112                 wrapperRect_.SetRect(wrapperRect_.Left(), foldCreaseBottom,
113                     wrapperRect_.Width(), wrapperRect_.Height() - foldCreaseBottom + safeAreaTop);
114             }
115             break;
116         default:
117             break;
118     }
119 }
120 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & changeConfig)121 bool ToastPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& changeConfig)
122 {
123     CHECK_NULL_RETURN(dirty, false);
124     auto context = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
125     CHECK_NULL_RETURN(context, false);
126     auto toastNode = dirty->GetHostNode();
127     CHECK_NULL_RETURN(toastNode, false);
128     auto toastContext = toastNode->GetRenderContext();
129     CHECK_NULL_RETURN(toastContext, false);
130     auto dialogTheme = context->GetTheme<DialogTheme>();
131     CHECK_NULL_RETURN(dialogTheme, false);
132     expandDisplay_ = dialogTheme->GetExpandDisplay() || IsShowInFreeMultiWindow();
133     OffsetT<Dimension> offset { GetOffsetX(dirty), GetOffsetY(dirty) };
134     // show in the float subwindow
135     if (!IsSystemTopMost() && (IsUIExtensionSubWindow() || (!IsDefaultToast() && expandDisplay_))) {
136         auto nodeContext = toastNode->GetContextWithCheck();
137         CHECK_NULL_RETURN(nodeContext, false);
138         auto subwindowOffset = nodeContext->GetDisplayWindowRectInfo().GetOffset();
139         if (!IsUIExtensionSubWindow() && (!NearEqual(subwindowOffset.GetX(), 0) ||
140             !NearEqual(subwindowOffset.GetY(), 0))) {
141             TAG_LOGW(AceLogTag::ACE_OVERLAY, "toast subwindow offset, x: %{public}f, y: %{public}f",
142                 subwindowOffset.GetX(), subwindowOffset.GetY());
143         }
144         OffsetT<Dimension> displayWindowOffset = { Dimension(context->GetDisplayWindowRectInfo().GetOffset().GetX() -
145             subwindowOffset.GetX()), Dimension(context->GetDisplayWindowRectInfo().GetOffset().GetY() -
146             subwindowOffset.GetY()) };
147         TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast displayWindowOffset, x: %{public}.2f vp, y: %{public}.2f vp",
148             displayWindowOffset.GetX().ConvertToVp(), displayWindowOffset.GetY().ConvertToVp());
149         offset += displayWindowOffset;
150     }
151     auto func = [toastContext, offset]() { toastContext->UpdateOffset(offset); };
152     auto toastProp = DynamicCast<ToastLayoutProperty>(dirty->GetLayoutProperty());
153     CHECK_NULL_RETURN(toastProp, false);
154     auto showMode = toastProp->GetShowModeValue(ToastShowMode::DEFAULT);
155     if (showMode == ToastShowMode::TOP_MOST) {
156         auto keyboardAnimationConfig = context->GetKeyboardAnimationConfig();
157         auto safeAreaManager = context->GetSafeAreaManager();
158         auto keyboardHeight = safeAreaManager ? safeAreaManager->GetKeyboardInset().Length() : 0;
159         if (safeAreaManager && NearEqual(keyboardHeight, 0.0f)) {
160             keyboardHeight = safeAreaManager->GetRawKeyboardHeight();
161         }
162         AnimationOption option = AnimationUtil::CreateKeyboardAnimationOption(keyboardAnimationConfig, keyboardHeight);
163         context->Animate(option, option.GetCurve(), func);
164     } else {
165         // animation effect of the toast position change
166         AnimationOption option;
167         auto translationCurve = AceType::MakeRefPtr<ResponsiveSpringMotion>(0.35f, 1.0f, 0.0f);
168         context->Animate(option, translationCurve, func);
169     }
170     return true;
171 }
172 
GetOffsetX(const RefPtr<LayoutWrapper> & layoutWrapper)173 Dimension ToastPattern::GetOffsetX(const RefPtr<LayoutWrapper>& layoutWrapper)
174 {
175     auto context = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
176     CHECK_NULL_RETURN(context, Dimension(0.0));
177     auto text = layoutWrapper->GetOrCreateChildByIndex(0);
178     CHECK_NULL_RETURN(text, Dimension(0.0));
179     auto rootWidth = wrapperRect_.Width();
180     if (IsSystemTopMost()) {
181         auto windowSize = GetSystemTopMostSubwindowSize();
182         rootWidth = static_cast<double>(windowSize.Height());
183     }
184     auto toastProp = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
185     CHECK_NULL_RETURN(toastProp, Dimension(0.0));
186     auto textWidth = text->GetGeometryNode()->GetMarginFrameSize().Width();
187     Alignment alignment = toastProp->GetToastAlignmentValue(Alignment::BOTTOM_CENTER);
188     Dimension offsetX;
189     if (alignment == Alignment::TOP_LEFT || alignment == Alignment::CENTER_LEFT ||
190         alignment == Alignment::BOTTOM_LEFT) {
191         offsetX = Dimension(0.0);
192     } else if (alignment == Alignment::TOP_RIGHT || alignment == Alignment::CENTER_RIGHT ||
193                alignment == Alignment::BOTTOM_RIGHT) {
194         offsetX = Dimension(rootWidth - textWidth);
195     } else {
196         offsetX = Dimension((rootWidth - textWidth) / 2.0f);
197     }
198     return offsetX + toastProp->GetToastOffsetValue(DimensionOffset()).GetX();
199 }
200 
GetOffsetY(const RefPtr<LayoutWrapper> & layoutWrapper)201 Dimension ToastPattern::GetOffsetY(const RefPtr<LayoutWrapper>& layoutWrapper)
202 {
203     auto context = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
204     CHECK_NULL_RETURN(context, Dimension(0.0));
205     auto text = layoutWrapper->GetOrCreateChildByIndex(0);
206     CHECK_NULL_RETURN(text, Dimension(0.0));
207     auto rootHeight = wrapperRect_.Height();
208     auto toastProp = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
209     CHECK_NULL_RETURN(toastProp, Dimension(0.0));
210     auto textHeight = text->GetGeometryNode()->GetMarginFrameSize().Height();
211     Dimension offsetY;
212     // Get toastBottom and update defaultBottom_
213     auto toastBottom = GetBottomValue(layoutWrapper);
214     if (!toastProp->HasToastAlignment()) {
215         toastBottom_ = toastBottom;
216         if (context->GetMinPlatformVersion() > API_VERSION_9) {
217             offsetY = Dimension(rootHeight - toastBottom - textHeight);
218         } else {
219             offsetY = Dimension(rootHeight - toastBottom);
220         }
221     } else {
222         Alignment alignment = toastProp->GetToastAlignmentValue(Alignment::BOTTOM_CENTER);
223         if (alignment == Alignment::TOP_LEFT || alignment == Alignment::TOP_CENTER ||
224             alignment == Alignment::TOP_RIGHT) {
225             offsetY = Dimension(0.0f);
226         } else if (alignment == Alignment::CENTER_LEFT || alignment == Alignment::CENTER ||
227                    alignment == Alignment::CENTER_RIGHT) {
228             offsetY = Dimension((rootHeight - textHeight) / 2.0f);
229         } else {
230             offsetY = Dimension(rootHeight - textHeight);
231         }
232     }
233     //TOP_MOST Toast need to avoid keyboard
234     auto deviceHeight = context->GetRootHeight();
235     auto safeAreaManager = context->GetSafeAreaManager();
236     auto keyboardInset = safeAreaManager ? safeAreaManager->GetKeyboardInset().Length() : 0;
237     auto keyboardOffset = GreatNotEqual(keyboardInset, 0) ? keyboardInset : 0;
238     auto showMode = toastProp->GetShowModeValue(ToastShowMode::DEFAULT);
239     if (showMode == ToastShowMode::TOP_MOST && (offsetY.Value() + textHeight > deviceHeight - keyboardOffset)) {
240         offsetY = Dimension(deviceHeight - keyboardOffset - defaultBottom_.ConvertToPx() -textHeight);
241     }
242     TAG_LOGD(AceLogTag::ACE_OVERLAY,
243         "toast device height: %{public}.2f, keyboardOffset: %{public}d, "
244         "textHeight: %{public}.2f, offsetY: %{public}.2f",
245         deviceHeight, (uint32_t)keyboardOffset, textHeight, offsetY.Value());
246     return offsetY + toastProp->GetToastOffsetValue(DimensionOffset()).GetY();
247 }
248 
GetBottomValue(const RefPtr<LayoutWrapper> & layoutWrapper)249 double ToastPattern::GetBottomValue(const RefPtr<LayoutWrapper>& layoutWrapper)
250 {
251     // Obtain the height relative to the main window
252     auto pipeline = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
253     CHECK_NULL_RETURN(pipeline, 0.0);
254     auto rootHeight = Dimension(wrapperRect_.Height());
255     if (IsSystemTopMost()) {
256         rootHeight = Dimension(static_cast<double>(SystemProperties::GetDeviceHeight()));
257     }
258     auto toastTheme = pipeline->GetTheme<ToastTheme>();
259     CHECK_NULL_RETURN(toastTheme, 0.0);
260 
261     auto toastProp = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
262     CHECK_NULL_RETURN(toastProp, 0.0);
263     defaultBottom_ = toastTheme->GetBottom();
264     auto toastBottom = toastProp->GetBottomValue(defaultBottom_);
265     if (toastBottom.Unit() == DimensionUnit::PERCENT) {
266         toastBottom = rootHeight * toastBottom.Value();
267     }
268     return GreatOrEqual(toastBottom.ConvertToPx(), 0.0) ? toastBottom.ConvertToPx()
269                                                         : toastTheme->GetBottom().ConvertToPx();
270 }
271 
BeforeCreateLayoutWrapper()272 void ToastPattern::BeforeCreateLayoutWrapper()
273 {
274     PopupBasePattern::BeforeCreateLayoutWrapper();
275 
276     auto toastNode = GetHost();
277     CHECK_NULL_VOID(toastNode);
278     auto pipelineContext = IsDefaultToast() ? toastNode->GetContextRefPtr() : GetMainPipelineContext();
279     if (!pipelineContext) {
280         TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast get pipelineContext failed");
281         return;
282     }
283     auto textNode = DynamicCast<FrameNode>(toastNode->GetFirstChild());
284     CHECK_NULL_VOID(textNode);
285     UpdateTextSizeConstraint(textNode);
286 }
287 
UpdateToastSize(const RefPtr<FrameNode> & toast)288 void ToastPattern::UpdateToastSize(const RefPtr<FrameNode>& toast)
289 {
290     CHECK_NULL_VOID(toast);
291     auto toastProperty = toast->GetLayoutProperty<ToastLayoutProperty>();
292     CHECK_NULL_VOID(toastProperty);
293     auto rootWidth = Dimension(wrapperRect_.Width());
294     if (IsSystemTopMost()) {
295         rootWidth = Dimension(static_cast<double>(SystemProperties::GetDeviceWidth()));
296     }
297     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
298         auto limitWidth = Dimension(GetTextMaxWidth());
299         toastProperty->UpdateUserDefinedIdealSize(CalcSize(NG::CalcLength(limitWidth), std::nullopt));
300     } else {
301         toastProperty->UpdateUserDefinedIdealSize(CalcSize(NG::CalcLength(rootWidth), std::nullopt));
302     }
303 }
304 
UpdateTextSizeConstraint(const RefPtr<FrameNode> & text)305 void ToastPattern::UpdateTextSizeConstraint(const RefPtr<FrameNode>& text)
306 {
307     CHECK_NULL_VOID(text);
308     auto context = PipelineBase::GetCurrentContext();
309     CHECK_NULL_VOID(context);
310     auto gridColumnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::TOAST);
311     auto parent = gridColumnInfo->GetParent();
312     if (parent) {
313         parent->BuildColumnWidth(context->GetRootWidth());
314     }
315     auto maxWidth = Dimension(gridColumnInfo->GetMaxWidth());
316     auto textLayoutProperty = text->GetLayoutProperty();
317     CHECK_NULL_VOID(textLayoutProperty);
318 
319     auto toastTheme = context->GetTheme<ToastTheme>();
320     CHECK_NULL_VOID(toastTheme);
321     auto minWidth = Dimension(toastTheme->GetMinWidth().ConvertToPx());
322     auto minHeight = Dimension(toastTheme->GetMinHeight().ConvertToPx());
323     textLayoutProperty->UpdateCalcMinSize(CalcSize(NG::CalcLength(minWidth), NG::CalcLength(minHeight)));
324 
325     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
326         auto limitHeight = GetTextMaxHeight();
327         textLayoutProperty->UpdateCalcMaxSize(
328             CalcSize(NG::CalcLength(maxWidth), NG::CalcLength(Dimension(limitHeight))));
329 
330         auto textProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
331         CHECK_NULL_VOID(textProperty);
332         auto toastMaxFontSize = toastTheme->GetTextStyle().GetFontSize();
333         textProperty->UpdateAdaptMaxFontSize(toastMaxFontSize);
334         textProperty->UpdateAdaptMinFontSize(ADAPT_TOAST_MIN_FONT_SIZE);
335         textProperty->UpdateHeightAdaptivePolicy(TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST);
336 
337         auto textLineHeight = GetTextLineHeight(text);
338         if (textLineHeight > 0) {
339             auto maxLines = static_cast<int32_t>(limitHeight / textLineHeight);
340             textProperty->UpdateMaxLines(maxLines);
341         }
342     } else {
343         textLayoutProperty->UpdateCalcMaxSize(CalcSize(NG::CalcLength(maxWidth), std::nullopt));
344     }
345 }
346 
OnColorConfigurationUpdate()347 void ToastPattern::OnColorConfigurationUpdate()
348 {
349     auto host = GetHost();
350     CHECK_NULL_VOID(host);
351     auto textContext = host->GetRenderContext();
352     CHECK_NULL_VOID(textContext);
353     auto pipelineContext = PipelineBase::GetCurrentContext();
354     CHECK_NULL_VOID(pipelineContext);
355     auto toastTheme = pipelineContext->GetTheme<ToastTheme>();
356     CHECK_NULL_VOID(toastTheme);
357     auto textColor = toastTheme->GetTextStyle().GetTextColor();
358     auto textLayoutProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
359     CHECK_NULL_VOID(textLayoutProperty);
360     auto toastInfo = GetToastInfo();
361     textLayoutProperty->UpdateTextColor(toastInfo.textColor.value_or(textColor));
362     host->SetNeedCallChildrenUpdate(false);
363     ToastView::UpdateToastNodeStyle(host);
364 }
365 
OnAttachToFrameNode()366 void ToastPattern::OnAttachToFrameNode()
367 {
368     auto containerId = Container::CurrentId();
369     auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
370     auto pipeline =
371         parentContainerId < 0 ? PipelineContext::GetCurrentContext() : PipelineContext::GetMainPipelineContext();
372     CHECK_NULL_VOID(pipeline);
373     auto callbackId =
374         pipeline->RegisterFoldDisplayModeChangedCallback([parentContainerId](FoldDisplayMode foldDisplayMode) {
375             if (foldDisplayMode == FoldDisplayMode::FULL || foldDisplayMode == FoldDisplayMode::MAIN) {
376                 TAG_LOGI(AceLogTag::ACE_OVERLAY, "Window status changes, displayMode is %{public}d", foldDisplayMode);
377                 SubwindowManager::GetInstance()->ResizeWindowForFoldStatus(parentContainerId);
378             }
379         });
380     UpdateFoldDisplayModeChangedCallbackId(callbackId);
381     auto halfFoldHoverCallbackId =
382         pipeline->RegisterHalfFoldHoverChangedCallback([weak = WeakClaim(this)](bool isHoverMode) {
383             auto pattern = weak.Upgrade();
384             CHECK_NULL_VOID(pattern);
385             auto host = pattern->GetHost();
386             CHECK_NULL_VOID(host);
387             if (isHoverMode != pattern->isHoverMode_) {
388                 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
389             }
390         });
391     UpdateHalfFoldHoverChangedCallbackId(halfFoldHoverCallbackId);
392 }
393 
OnDetachFromFrameNode(FrameNode * node)394 void ToastPattern::OnDetachFromFrameNode(FrameNode* node)
395 {
396     auto containerId = Container::CurrentId();
397     auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
398     auto pipeline =
399         parentContainerId < 0 ? PipelineContext::GetCurrentContext() : PipelineContext::GetMainPipelineContext();
400     CHECK_NULL_VOID(pipeline);
401     if (HasFoldDisplayModeChangedCallbackId()) {
402         pipeline->UnRegisterFoldDisplayModeChangedCallback(foldDisplayModeChangedCallbackId_.value_or(-1));
403     }
404     if (HasHalfFoldHoverChangedCallbackId()) {
405         pipeline->UnRegisterHalfFoldHoverChangedCallback(halfFoldHoverChangedCallbackId_.value_or(-1));
406     }
407 }
408 
IsShowInFreeMultiWindow()409 bool ToastPattern::IsShowInFreeMultiWindow()
410 {
411     auto currentId = Container::CurrentId();
412     auto container = Container::Current();
413     if (!container) {
414         TAG_LOGW(AceLogTag::ACE_OVERLAY, "container is null");
415         return false;
416     }
417     if (container->IsSubContainer()) {
418         currentId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
419         container = AceEngine::Get().GetContainer(currentId);
420         if (!container) {
421             TAG_LOGW(AceLogTag::ACE_OVERLAY, "parent container is null");
422             return false;
423         }
424     }
425     return container->IsFreeMultiWindow();
426 }
427 
IsUIExtensionSubWindow()428 bool ToastPattern::IsUIExtensionSubWindow()
429 {
430     if (IsDefaultToast()) {
431         return false;
432     }
433 
434     auto currentId = Container::CurrentId();
435     auto container = Container::Current();
436     CHECK_NULL_RETURN(container, false);
437     if (container->IsSubContainer()) {
438         currentId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
439         container = AceEngine::Get().GetContainer(currentId);
440         CHECK_NULL_RETURN(container, false);
441     }
442     return container->IsUIExtensionWindow();
443 }
444 
DumpInfo()445 void ToastPattern::DumpInfo()
446 {
447     DumpLog::GetInstance().AddDesc("Message: " + toastInfo_.message);
448     DumpLog::GetInstance().AddDesc("Duration: " + std::to_string(toastInfo_.duration));
449     DumpLog::GetInstance().AddDesc("Bottom: " + toastInfo_.bottom);
450     std::string isRightToLeft = toastInfo_.isRightToLeft ? "true" : "false";
451     DumpLog::GetInstance().AddDesc("IsRightToLeft: " + isRightToLeft);
452     std::string showMode = toastInfo_.showMode == ToastShowMode::DEFAULT ? "DEFAULT" : "TOP_MOST";
453     DumpLog::GetInstance().AddDesc("ShowMode: " + showMode);
454     auto host = GetHost();
455     CHECK_NULL_VOID(host);
456     auto toastProp = DynamicCast<ToastLayoutProperty>(host->GetLayoutProperty());
457     CHECK_NULL_VOID(toastProp);
458     if (!toastProp->HasToastAlignment()) {
459         DumpLog::GetInstance().AddDesc("Alignment: NONE");
460     } else {
461         DumpLog::GetInstance().AddDesc(
462             "Alignment: " + toastProp->GetToastAlignmentValue().GetAlignmentStr(toastProp->GetLayoutDirection()));
463     }
464     auto offset = toastProp->GetToastOffsetValue(DimensionOffset());
465     std::string enableHoverMode = toastInfo_.enableHoverMode ? "true" : "false";
466     DumpLog::GetInstance().AddDesc(
467         "Offset: { dx: " + offset.GetX().ToString() + " dy: " + offset.GetY().ToString() + " }");
468     DumpLog::GetInstance().AddDesc("EnableHoverMode: " + enableHoverMode);
469     std::string hoverModeAreaType =
470         toastInfo_.hoverModeArea == HoverModeAreaType::TOP_SCREEN ? "TOP_SCREEN" : "BOTTOM_SCREEN";
471     DumpLog::GetInstance().AddDesc("HoverModeArea: " + hoverModeAreaType);
472 }
473 
GetTextMaxHeight()474 double ToastPattern::GetTextMaxHeight()
475 {
476     auto pipelineContext = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
477     CHECK_NULL_RETURN(pipelineContext, 0.0);
478     double deviceHeight = 0.0;
479     if (IsSystemTopMost()) {
480         auto windowSize = GetSystemTopMostSubwindowSize();
481         deviceHeight = static_cast<double>(windowSize.Height());
482         TAG_LOGD(AceLogTag::ACE_OVERLAY, "SystemTopMost toast get device height: %{public}f.", deviceHeight);
483     } else if (IsUIExtensionSubWindow()) {
484         auto toastNode = GetHost();
485         CHECK_NULL_RETURN(toastNode, 0.0);
486         auto nodeContext = toastNode->GetContextWithCheck();
487         CHECK_NULL_RETURN(nodeContext, 0.0);
488         deviceHeight = nodeContext->GetDisplayWindowRectInfo().Height();
489         TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast in UIExtension subwindow, device height: %{public}f.", deviceHeight);
490     } else {
491         deviceHeight = pipelineContext->GetRootHeight();
492         TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast get device height: %{public}f.", deviceHeight);
493     }
494     if (LessOrEqual(deviceHeight, 0.0)) {
495         TAG_LOGE(AceLogTag::ACE_OVERLAY, "toast get device height is invalid.");
496         deviceHeight = static_cast<double>(SystemProperties::GetDeviceHeight());
497     }
498     auto safeAreaManager = pipelineContext->GetSafeAreaManager();
499     auto bottom = safeAreaManager ? safeAreaManager->GetSafeAreaWithoutProcess().bottom_.Length() : 0;
500     auto top = safeAreaManager ? safeAreaManager->GetSafeAreaWithoutProcess().top_.Length() : 0;
501     auto maxHeight = deviceHeight - bottom - top - toastBottom_;
502     auto limitHeight = (deviceHeight - bottom - top) * 0.65;
503     if (GreatNotEqual(maxHeight, limitHeight)) {
504         maxHeight = limitHeight;
505     }
506 
507     maxHeight = GreatOrEqual(maxHeight, 0.0) ? maxHeight : 0.0;
508     return maxHeight;
509 }
510 
GetTextMaxWidth()511 double ToastPattern::GetTextMaxWidth()
512 {
513     auto pipelineContext = IsDefaultToast() ? PipelineContext::GetCurrentContext() : GetMainPipelineContext();
514     CHECK_NULL_RETURN(pipelineContext, 0.0);
515     double deviceWidth = 0.0;
516     if (IsSystemTopMost()) {
517         auto windowSize = GetSystemTopMostSubwindowSize();
518         deviceWidth = static_cast<double>(windowSize.Width());
519         TAG_LOGD(AceLogTag::ACE_OVERLAY, "SystemTopMost toast get device width: %{public}f.", deviceWidth);
520     } else if (IsUIExtensionSubWindow()) {
521         auto toastNode = GetHost();
522         CHECK_NULL_RETURN(toastNode, 0.0);
523         auto nodeContext = toastNode->GetContextWithCheck();
524         CHECK_NULL_RETURN(nodeContext, 0.0);
525         deviceWidth = nodeContext->GetDisplayWindowRectInfo().Width();
526         TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast in UIExtension subwindow, device width: %{public}f.", deviceWidth);
527     } else {
528         deviceWidth = pipelineContext->GetRootWidth();
529         TAG_LOGD(AceLogTag::ACE_OVERLAY, "toast get device width: %{public}f.", deviceWidth);
530     }
531     if (LessOrEqual(deviceWidth, 0.0)) {
532         TAG_LOGE(AceLogTag::ACE_OVERLAY, "toast get device width is invalid.");
533         deviceWidth = static_cast<double>(SystemProperties::GetDeviceWidth());
534     }
535     auto toastTheme = pipelineContext->GetTheme<ToastTheme>();
536     CHECK_NULL_RETURN(toastTheme, 0.0);
537     auto marging = toastTheme->GetMarging();
538     auto maxWidth = deviceWidth - marging.Left().ConvertToPx() - marging.Right().ConvertToPx();
539     auto maxLimitWidth = toastTheme->GetMaxWidth();
540     if (GreatNotEqual(maxWidth, maxLimitWidth.ConvertToPx())) {
541         maxWidth = maxLimitWidth.ConvertToPx();
542     }
543     return maxWidth;
544 }
545 
GetTextLineHeight(const RefPtr<FrameNode> & textNode)546 int32_t ToastPattern::GetTextLineHeight(const RefPtr<FrameNode>& textNode)
547 {
548     auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
549     CHECK_NULL_RETURN(textLayoutProperty, 0);
550     auto layoutConstraint = textLayoutProperty->GetLayoutConstraint();
551     auto textLayoutWrapper = textNode->CreateLayoutWrapper();
552     CHECK_NULL_RETURN(textLayoutWrapper, 0);
553     textLayoutWrapper->Measure(layoutConstraint);
554     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(textLayoutWrapper->GetLayoutAlgorithm());
555     CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
556     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
557     CHECK_NULL_RETURN(textLayoutAlgorithm, 0);
558     auto paragraph = textLayoutAlgorithm->GetSingleParagraph();
559     CHECK_NULL_RETURN(paragraph, 0);
560     auto paragHeight = paragraph->GetHeight();
561     auto paragLineCount = paragraph->GetLineCount();
562     int32_t paragLineHeight = 0;
563     if (paragLineCount > 0) {
564         paragLineHeight = static_cast<int32_t>(paragHeight / paragLineCount);
565     }
566     return paragLineHeight;
567 }
568 
GetSystemTopMostSubwindowSize() const569 NG::SizeF ToastPattern::GetSystemTopMostSubwindowSize() const
570 {
571     SizeF windowSize = {
572         static_cast<float>(SystemProperties::GetDeviceWidth()),
573         static_cast<float>(SystemProperties::GetDeviceHeight())
574     };
575     auto containerId = Container::CurrentId();
576     if (containerId < 0) {
577         auto container = Container::GetActive();
578         if (container) {
579             containerId = container->GetInstanceId();
580         }
581     }
582     auto parentContainerId = containerId >= MIN_SUBCONTAINER_ID ?
583         SubwindowManager::GetInstance()->GetParentContainerId(containerId) : containerId;
584     auto subwindow = SubwindowManager::GetInstance()->GetSystemToastWindow(parentContainerId);
585     if (subwindow) {
586         auto rect = subwindow->GetRect();
587         if (GreatNotEqual(rect.Width(), 0.0f) && GreatNotEqual(rect.Height(), 0.0f)) {
588             windowSize.SetWidth(rect.Width());
589             windowSize.SetHeight(rect.Height());
590         }
591     }
592     return windowSize;
593 }
594 } // namespace OHOS::Ace::NG
595