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