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