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