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