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