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
16 #include "core/components_ng/pattern/toast/toast_layout_algorithm.h"
17
18 #include "core/common/ace_engine.h"
19 #include "core/components_ng/pattern/toast/toast_pattern.h"
20 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
21
22 namespace OHOS::Ace::NG {
23 namespace {
24 constexpr Dimension LIMIT_SPACING = 8.0_vp;
25 } // namespace
26
UpdateToastAlign(int32_t & alignment)27 void UpdateToastAlign(int32_t& alignment)
28 {
29 bool isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
30 if (alignment == static_cast<int32_t>(ToastAlignment::TOP_START)) {
31 if (isRtl) {
32 alignment = static_cast<int32_t>(ToastAlignment::TOP_END);
33 }
34 } else if (alignment == static_cast<int32_t>(ToastAlignment::TOP_END)) {
35 if (isRtl) {
36 alignment = static_cast<int32_t>(ToastAlignment::TOP_START);
37 }
38 } else if (alignment == static_cast<int32_t>(ToastAlignment::CENTER_START)) {
39 if (isRtl) {
40 alignment = static_cast<int32_t>(ToastAlignment::CENTER_END);
41 }
42 } else if (alignment == static_cast<int32_t>(ToastAlignment::CENTER_END)) {
43 if (isRtl) {
44 alignment = static_cast<int32_t>(ToastAlignment::CENTER_START);
45 }
46 } else if (alignment == static_cast<int32_t>(ToastAlignment::BOTTOM_START)) {
47 if (isRtl) {
48 alignment = static_cast<int32_t>(ToastAlignment::BOTTOM_END);
49 }
50 } else if (alignment == static_cast<int32_t>(ToastAlignment::BOTTOM_END)) {
51 if (isRtl) {
52 alignment = static_cast<int32_t>(ToastAlignment::BOTTOM_START);
53 }
54 }
55 }
56
Layout(LayoutWrapper * layoutWrapper)57 void ToastLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
58 {
59 CHECK_NULL_VOID(layoutWrapper);
60 auto frameNode = layoutWrapper->GetHostNode();
61 CHECK_NULL_VOID(frameNode);
62 auto toastPattern = frameNode->GetPattern<ToastPattern>();
63 CHECK_NULL_VOID(toastPattern);
64 auto toastProperty = frameNode->GetLayoutProperty<ToastLayoutProperty>();
65 CHECK_NULL_VOID(toastProperty);
66 auto alignment = toastPattern->GetToastInfo().alignment;
67 UpdateToastAlign(alignment);
68 auto align = Alignment::ParseAlignment(alignment);
69 if (align.has_value()) {
70 toastProperty->UpdateToastAlignment(align.value());
71 } else {
72 toastProperty->ResetToastAlignment();
73 }
74 auto offset = toastPattern->GetToastInfo().offset;
75 if (offset.has_value()) {
76 bool isRtl = AceApplicationInfo::GetInstance().IsRightToLeft();
77 Dimension offsetX = isRtl ? offset->GetX() * (-1) : offset->GetX();
78 offset->SetX(offsetX);
79 toastProperty->UpdateToastOffset(offset.value());
80 } else {
81 toastProperty->ResetToastOffset();
82 }
83 auto text = layoutWrapper->GetOrCreateChildByIndex(0);
84 text->Layout();
85 }
86
GetLineCount(const RefPtr<LayoutWrapper> & textWrapper,LayoutConstraintF & layoutConstraint)87 size_t GetLineCount(const RefPtr<LayoutWrapper>& textWrapper, LayoutConstraintF& layoutConstraint)
88 {
89 textWrapper->Measure(layoutConstraint);
90 auto layoutAlgorithmWrapper = AceType::DynamicCast<LayoutAlgorithmWrapper>(textWrapper->GetLayoutAlgorithm());
91 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
92 auto textLayoutAlgorithm = AceType::DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
93 CHECK_NULL_RETURN(textLayoutAlgorithm, 0);
94 auto paragraph = textLayoutAlgorithm->GetSingleParagraph();
95 CHECK_NULL_RETURN(paragraph, 0);
96 auto paragLineCount = paragraph->GetLineCount();
97 return paragLineCount;
98 }
99
Measure(LayoutWrapper * layoutWrapper)100 void ToastLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
101 {
102 CHECK_NULL_VOID(layoutWrapper);
103 auto toastProps = DynamicCast<ToastLayoutProperty>(layoutWrapper->GetLayoutProperty());
104 CHECK_NULL_VOID(toastProps);
105 auto toastNode = layoutWrapper->GetHostNode();
106 CHECK_NULL_VOID(toastNode);
107 auto toastPattern = toastNode->GetPattern<ToastPattern>();
108 CHECK_NULL_VOID(toastPattern);
109 toastPattern->InitWrapperRect(layoutWrapper, toastProps);
110 auto text = layoutWrapper->GetOrCreateChildByIndex(0);
111 auto layoutConstraint = GetTextLayoutConstraint(layoutWrapper);
112 // TextAlign should be START when lines of text are greater than 1
113 if (GetLineCount(text, layoutConstraint) > 1) {
114 auto textLayoutProp = DynamicCast<TextLayoutProperty>(text->GetLayoutProperty());
115 CHECK_NULL_VOID(textLayoutProp);
116 auto context = toastNode->GetContext();
117 CHECK_NULL_VOID(context);
118 auto toastTheme = context->GetTheme<ToastTheme>();
119 CHECK_NULL_VOID(toastTheme);
120 textLayoutProp->UpdateTextAlign(toastTheme->GetMultiLineTextAlign());
121 }
122 text->Measure(layoutConstraint);
123 PerformMeasureSelf(layoutWrapper);
124 }
125
GetTextLayoutConstraint(LayoutWrapper * layoutWrapper)126 LayoutConstraintF ToastLayoutAlgorithm::GetTextLayoutConstraint(LayoutWrapper* layoutWrapper)
127 {
128 auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
129 auto frameNode = layoutWrapper->GetHostNode();
130 CHECK_NULL_RETURN(frameNode, layoutConstraint);
131 auto toastPattern = frameNode->GetPattern<ToastPattern>();
132 CHECK_NULL_RETURN(toastPattern, layoutConstraint);
133 auto toastProperty = frameNode->GetLayoutProperty<ToastLayoutProperty>();
134 CHECK_NULL_RETURN(toastProperty, layoutConstraint);
135 auto context = toastPattern->GetToastContext();
136 CHECK_NULL_RETURN(context, layoutConstraint);
137 auto safeAreaManager = context->GetSafeAreaManager();
138 auto keyboardInset = 0;
139 if (safeAreaManager) {
140 auto inset = safeAreaManager->GetKeyboardInset().Length();
141 keyboardInset = NearEqual(inset, 0.0f) ? safeAreaManager->GetRawKeyboardHeight() : inset;
142 }
143 auto deviceHeight = context->GetRootHeight();
144 auto keyboardOffset = deviceHeight - keyboardInset;
145 if (toastPattern->IsAlignedWithHostWindow() && GreatNotEqual(keyboardInset, 0)) {
146 deviceHeight = toastPattern->GetUiExtensionHostWindowRect().Height();
147
148 auto currentId = Container::CurrentId();
149 auto container = Container::Current();
150 CHECK_NULL_RETURN(container, layoutConstraint);
151 if (container->IsSubContainer()) {
152 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(currentId);
153 auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
154 CHECK_NULL_RETURN(parentContainer, layoutConstraint);
155 CHECK_NULL_RETURN(parentContainer->IsUIExtensionWindow(), layoutConstraint);
156 auto toastSubwindow = SubwindowManager::GetInstance()->GetToastSubwindow(parentContainer);
157 if (toastSubwindow) {
158 auto parentWindowRect = toastSubwindow->GetParentWindowRect();
159 keyboardOffset = deviceHeight - keyboardInset - toastPattern->GetUiExtensionHostWindowRect().Bottom() +
160 parentWindowRect.Bottom();
161 }
162 }
163 }
164 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN) && GreatNotEqual(keyboardInset, 0) &&
165 (toastPattern->IsDefaultToast() || toastPattern->IsTopMostToast())) {
166 auto maxHeight = keyboardOffset - toastPattern->GetLimitPos().Value() - LIMIT_SPACING.ConvertToPx();
167 layoutConstraint.maxSize.SetHeight(maxHeight);
168 }
169 return layoutConstraint;
170 }
171 } // namespace OHOS::Ace::NG
172