1 /*
2 * Copyright (c) 2022-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/tabs/tabs_layout_algorithm.h"
17
18 #include "base/geometry/axis.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/geometry/ng/size_t.h"
21 #include "base/log/ace_trace.h"
22 #include "base/utils/utils.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/layout/layout_algorithm.h"
25 #include "core/components_ng/property/layout_constraint.h"
26 #include "core/components_ng/property/measure_property.h"
27 #include "core/components_ng/property/measure_utils.h"
28
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr int32_t SWIPER_INDEX = 0;
32 constexpr int32_t DIVIDER_INDEX = 1;
33 constexpr int32_t TAB_BAR_INDEX = 2;
34 } // namespace
35
Measure(LayoutWrapper * layoutWrapper)36 void TabsLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
37 {
38 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
39 CHECK_NULL_VOID(layoutProperty);
40 auto geometryNode = layoutWrapper->GetGeometryNode();
41 CHECK_NULL_VOID(geometryNode);
42 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
43 auto constraint = layoutProperty->GetLayoutConstraint();
44 auto idealSize = CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL,
45 layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT)).ConvertToSizeT();
46 if (GreaterOrEqualToInfinity(idealSize.Width()) || GreaterOrEqualToInfinity(idealSize.Height())) {
47 geometryNode->SetFrameSize(SizeF());
48 return;
49 }
50 geometryNode->SetFrameSize(idealSize);
51 MinusPaddingToSize(layoutProperty->CreatePaddingAndBorder(), idealSize);
52 auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
53 childLayoutConstraint.parentIdealSize = OptionalSizeF(idealSize);
54 float dividerStrokeWidth = 0.0f;
55
56 // Measure tab bar.
57 auto tabBarWrapper = layoutWrapper->GetOrCreateChildByIndex(TAB_BAR_INDEX);
58 SizeF tabBarSize;
59 if (tabBarWrapper) {
60 tabBarWrapper->Measure(childLayoutConstraint);
61 tabBarSize = tabBarWrapper->GetGeometryNode()->GetMarginFrameSize();
62 }
63
64 // Measure divider.
65 auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(DIVIDER_INDEX);
66 if (dividerWrapper) {
67 dividerStrokeWidth = MeasureDivider(layoutProperty, dividerWrapper, idealSize);
68 }
69
70 auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
71
72 // Measure swiper.
73 auto swiperWrapper = layoutWrapper->GetOrCreateChildByIndex(SWIPER_INDEX);
74 if (swiperWrapper) {
75 swiperWrapper->GetLayoutProperty()->UpdateLayoutDirection(layoutProperty->GetNonAutoLayoutDirection());
76 }
77 SizeF swiperSize;
78 if (swiperWrapper && swiperWrapper->GetHostNode() && swiperWrapper->GetHostNode()->TotalChildCount() > 0) {
79 swiperSize = MeasureSwiper(layoutProperty, swiperWrapper, idealSize, tabBarSize, dividerStrokeWidth);
80 } else if (swiperWrapper && swiperWrapper->GetGeometryNode()) {
81 swiperWrapper->GetGeometryNode()->SetFrameSize(SizeF());
82 }
83
84 auto paddingH = layoutProperty->CreatePaddingAndBorder().Width();
85 auto paddingV = layoutProperty->CreatePaddingAndBorder().Height();
86 if ((axis == Axis::VERTICAL) && layoutProperty->GetWidthAutoValue(false)) {
87 if (!barOverlap) {
88 geometryNode->SetFrameWidth(tabBarSize.Width() + dividerStrokeWidth + swiperSize.Width() + paddingH);
89 } else {
90 geometryNode->SetFrameWidth(dividerStrokeWidth + swiperSize.Width() + paddingH);
91 }
92 } else if ((axis == Axis::HORIZONTAL) && layoutProperty->GetHeightAutoValue(false)) {
93 if (!barOverlap) {
94 geometryNode->SetFrameHeight(tabBarSize.Height() + dividerStrokeWidth + swiperSize.Height() + paddingV);
95 } else {
96 geometryNode->SetFrameHeight(dividerStrokeWidth + swiperSize.Height() + paddingV);
97 }
98 }
99 }
100
Layout(LayoutWrapper * layoutWrapper)101 void TabsLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
102 {
103 CHECK_NULL_VOID(layoutWrapper);
104 auto geometryNode = layoutWrapper->GetGeometryNode();
105 CHECK_NULL_VOID(geometryNode);
106 auto frameSize = geometryNode->GetFrameSize();
107
108 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
109 CHECK_NULL_VOID(layoutProperty);
110 auto isRTL = layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
111 auto tabBarWrapper = layoutWrapper->GetChildByIndex(TAB_BAR_INDEX);
112 auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(DIVIDER_INDEX);
113 auto swiperWrapper = layoutWrapper->GetOrCreateChildByIndex(SWIPER_INDEX);
114 if (!tabBarWrapper || !dividerWrapper || !swiperWrapper) {
115 return;
116 }
117
118 std::vector<OffsetF> offsetList;
119 if (frameSize.IsPositive()) {
120 offsetList = LayoutOffsetList(layoutWrapper, tabBarWrapper, frameSize);
121 } else {
122 offsetList = { OffsetF(), OffsetF(), OffsetF() };
123 }
124 auto tabsWidth = geometryNode->GetFrameSize().Width();
125 if (isRTL) {
126 auto swiperWidth = swiperWrapper->GetGeometryNode()->GetFrameSize().Width();
127 OffsetF swiperOffset =
128 OffsetF((tabsWidth - offsetList[SWIPER_INDEX].GetX() - swiperWidth), offsetList[SWIPER_INDEX].GetY());
129 swiperWrapper->GetGeometryNode()->SetMarginFrameOffset(swiperOffset);
130 swiperWrapper->Layout();
131
132 auto dividerWidth = dividerWrapper->GetGeometryNode()->GetFrameSize().Width();
133 OffsetF dividerOffset =
134 OffsetF((tabsWidth - offsetList[DIVIDER_INDEX].GetX() - dividerWidth), offsetList[DIVIDER_INDEX].GetY());
135 dividerWrapper->GetGeometryNode()->SetMarginFrameOffset(dividerOffset);
136 dividerWrapper->Layout();
137
138 auto tabBarWidth = tabBarWrapper->GetGeometryNode()->GetFrameSize().Width();
139 OffsetF tabBarOffset =
140 OffsetF((tabsWidth - offsetList[TAB_BAR_INDEX].GetX() - tabBarWidth), offsetList[TAB_BAR_INDEX].GetY());
141 tabBarWrapper->GetGeometryNode()->SetMarginFrameOffset(tabBarOffset);
142 tabBarWrapper->Layout();
143 return;
144 }
145 swiperWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[SWIPER_INDEX]);
146 swiperWrapper->Layout();
147
148 dividerWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[DIVIDER_INDEX]);
149 dividerWrapper->Layout();
150
151 tabBarWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[TAB_BAR_INDEX]);
152 tabBarWrapper->Layout();
153 }
154
LayoutOffsetList(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & tabBarWrapper,const SizeF & frameSize) const155 std::vector<OffsetF> TabsLayoutAlgorithm::LayoutOffsetList(
156 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& tabBarWrapper, const SizeF& frameSize) const
157 {
158 std::vector<OffsetF> offsetList;
159 OffsetF tabBarOffset;
160 OffsetF dividerOffset;
161 OffsetF swiperOffset;
162 auto axis = GetAxis(layoutWrapper);
163 auto barPosition = GetBarPosition(layoutWrapper);
164 auto divider = GetDivider(layoutWrapper);
165 auto tabBarGeometryNode = tabBarWrapper->GetGeometryNode();
166 CHECK_NULL_RETURN(tabBarGeometryNode, offsetList);
167 auto tabBarFrameSize = tabBarGeometryNode->GetMarginFrameSize();
168 auto dividerStrokeWidth = divider.isNull ? 0.0f : divider.strokeWidth.ConvertToPx();
169 auto dividerStartMargin = divider.startMargin.ConvertToPx();
170 auto layoutProperty = DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
171 CHECK_NULL_RETURN(layoutProperty, offsetList);
172 auto padding = layoutProperty->CreatePaddingAndBorder();
173 auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
174
175 if (axis == Axis::HORIZONTAL) {
176 float barPosX = (frameSize.MainSize(Axis::HORIZONTAL) - tabBarFrameSize.MainSize(Axis::HORIZONTAL)) / 2;
177 if (barPosition == BarPosition::START) {
178 tabBarOffset = OffsetF(barPosX, padding.Offset().GetY());
179 dividerOffset = OffsetF(dividerStartMargin + padding.Offset().GetX(),
180 tabBarFrameSize.MainSize(Axis::VERTICAL) + padding.Offset().GetY());
181 swiperOffset = barOverlap ? padding.Offset() : OffsetF(padding.Offset().GetX(),
182 tabBarFrameSize.MainSize(Axis::VERTICAL) + dividerStrokeWidth + padding.Offset().GetY());
183 } else {
184 tabBarOffset = OffsetF(barPosX, frameSize.MainSize(Axis::VERTICAL) -
185 tabBarFrameSize.MainSize(Axis::VERTICAL) - padding.bottom.value_or(0.0f));
186 dividerOffset = OffsetF(dividerStartMargin + padding.Offset().GetX(), frameSize.MainSize(Axis::VERTICAL) -
187 tabBarFrameSize.MainSize(Axis::VERTICAL) - padding.bottom.value_or(0.0f) - dividerStrokeWidth);
188 swiperOffset = padding.Offset();
189 }
190 } else {
191 float barPosY = (frameSize.MainSize(Axis::VERTICAL) - tabBarFrameSize.MainSize(Axis::VERTICAL)) / 2;
192 if (barPosition == BarPosition::START) {
193 tabBarOffset = OffsetF(padding.Offset().GetX(), barPosY);
194 dividerOffset = OffsetF(tabBarFrameSize.MainSize(Axis::HORIZONTAL) + padding.Offset().GetX(),
195 dividerStartMargin + padding.Offset().GetY());
196 swiperOffset = barOverlap ? padding.Offset() : OffsetF(tabBarFrameSize.MainSize(Axis::HORIZONTAL) +
197 padding.Offset().GetX() + dividerStrokeWidth, padding.Offset().GetY());
198 } else {
199 tabBarOffset = OffsetF(frameSize.MainSize(Axis::HORIZONTAL) - tabBarFrameSize.MainSize(Axis::HORIZONTAL) -
200 padding.right.value_or(0.0f), barPosY);
201 dividerOffset = OffsetF(frameSize.MainSize(Axis::HORIZONTAL) - tabBarFrameSize.MainSize(Axis::HORIZONTAL) -
202 padding.right.value_or(0.0f) - dividerStrokeWidth, dividerStartMargin + padding.Offset().GetY());
203 swiperOffset = padding.Offset();
204 }
205 }
206 offsetList.emplace_back(swiperOffset);
207 offsetList.emplace_back(dividerOffset);
208 offsetList.emplace_back(tabBarOffset);
209 return offsetList;
210 }
211
GetBarPosition(LayoutWrapper * layoutWrapper) const212 BarPosition TabsLayoutAlgorithm::GetBarPosition(LayoutWrapper* layoutWrapper) const
213 {
214 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
215 CHECK_NULL_RETURN(layoutProperty, BarPosition::START);
216 return layoutProperty->GetTabBarPosition().value_or(BarPosition::START);
217 }
218
GetAxis(LayoutWrapper * layoutWrapper) const219 Axis TabsLayoutAlgorithm::GetAxis(LayoutWrapper* layoutWrapper) const
220 {
221 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
222 CHECK_NULL_RETURN(layoutProperty, Axis::HORIZONTAL);
223 return layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
224 }
225
GetDivider(LayoutWrapper * layoutWrapper) const226 TabsItemDivider TabsLayoutAlgorithm::GetDivider(LayoutWrapper* layoutWrapper) const
227 {
228 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
229 TabsItemDivider divider;
230 CHECK_NULL_RETURN(layoutProperty, divider);
231 return layoutProperty->GetDivider().value_or(divider);
232 }
233
MeasureDivider(const RefPtr<TabsLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & dividerWrapper,const SizeF & idealSize)234 float TabsLayoutAlgorithm::MeasureDivider(const RefPtr<TabsLayoutProperty>& layoutProperty,
235 const RefPtr<LayoutWrapper>& dividerWrapper, const SizeF& idealSize)
236 {
237 auto constraint = layoutProperty->GetLayoutConstraint();
238
239 auto dividerIdealSize = CreateIdealSize(
240 constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
241 TabsItemDivider defaultDivider;
242 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
243 auto parentWidth = idealSize.Width();
244 auto parentHeight = idealSize.Height();
245 auto divider = layoutProperty->GetDivider().value_or(defaultDivider);
246 auto dividerStrokeWidth = divider.strokeWidth.ConvertToPx();
247 auto dividerStartMargin = divider.startMargin.ConvertToPx();
248 auto dividerEndMargin = divider.endMargin.ConvertToPx();
249
250 if (axis == Axis::VERTICAL) {
251 dividerIdealSize.SetWidth(dividerStrokeWidth);
252 dividerIdealSize.SetHeight(parentHeight - dividerStartMargin - dividerEndMargin);
253 } else if (axis == Axis::HORIZONTAL) {
254 dividerIdealSize.SetWidth(parentWidth - dividerStartMargin - dividerEndMargin);
255 dividerIdealSize.SetHeight(dividerStrokeWidth);
256 }
257
258 auto dividerLayoutConstraint = layoutProperty->CreateChildConstraint();
259 dividerLayoutConstraint.selfIdealSize = OptionalSizeF(dividerIdealSize);
260 dividerWrapper->Measure(dividerLayoutConstraint);
261
262 return divider.isNull ? 0.0f : dividerStrokeWidth;
263 }
264
MeasureSwiper(const RefPtr<TabsLayoutProperty> & layoutProperty,RefPtr<LayoutWrapper> & swiperWrapper,const SizeF & idealSize,const SizeF & tabBarSize,const float dividerWidth)265 SizeF TabsLayoutAlgorithm::MeasureSwiper(const RefPtr<TabsLayoutProperty>& layoutProperty,
266 RefPtr<LayoutWrapper>& swiperWrapper, const SizeF& idealSize, const SizeF& tabBarSize, const float dividerWidth)
267 {
268 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
269 auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
270 bool autoWidth = layoutProperty->GetWidthAutoValue(false);
271 bool autoHeight = layoutProperty->GetHeightAutoValue(false);
272 SizeF parentIdealSize = idealSize;
273 auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
274 childLayoutConstraint.parentIdealSize = OptionalSizeF(idealSize);
275
276 if (axis == Axis::HORIZONTAL) {
277 if (!barOverlap) {
278 if (!autoHeight) {
279 childLayoutConstraint.selfIdealSize.SetHeight(
280 idealSize.Height() - tabBarSize.Height() - dividerWidth);
281 }
282 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
283 parentIdealSize.SetHeight(idealSize.Height() - tabBarSize.Height() - dividerWidth);
284 } else {
285 if (!autoHeight) {
286 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
287 }
288 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
289 parentIdealSize.SetHeight(idealSize.Height());
290 }
291 } else if (axis == Axis::VERTICAL) {
292 if (!barOverlap) {
293 if (!autoWidth) {
294 childLayoutConstraint.selfIdealSize.SetWidth(
295 idealSize.Width() - tabBarSize.Width() - dividerWidth);
296 }
297 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
298 parentIdealSize.SetWidth(idealSize.Width() - tabBarSize.Width() - dividerWidth);
299 } else {
300 if (!autoWidth) {
301 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
302 }
303 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
304 parentIdealSize.SetWidth(idealSize.Width());
305 }
306 }
307 childLayoutConstraint.parentIdealSize = OptionalSizeF(parentIdealSize);
308 swiperWrapper->Measure(childLayoutConstraint);
309
310 return swiperWrapper->GetGeometryNode()->GetMarginFrameSize();
311 }
312 } // namespace OHOS::Ace::NG
313