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