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 if (!frameSize.IsPositive()) {
108 return;
109 }
110 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
111 CHECK_NULL_VOID(layoutProperty);
112 auto isRTL = layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
113 auto tabBarWrapper = layoutWrapper->GetChildByIndex(TAB_BAR_INDEX);
114 auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(DIVIDER_INDEX);
115 auto swiperWrapper = layoutWrapper->GetOrCreateChildByIndex(SWIPER_INDEX);
116 if (!tabBarWrapper || !dividerWrapper || !swiperWrapper) {
117 return;
118 }
119
120 auto offsetList = LayoutOffsetList(layoutWrapper, tabBarWrapper, frameSize);
121 auto tabsWidth = geometryNode->GetFrameSize().Width();
122 if (isRTL) {
123 auto swiperWidth = swiperWrapper->GetGeometryNode()->GetFrameSize().Width();
124 OffsetF swiperOffset =
125 OffsetF((tabsWidth - offsetList[SWIPER_INDEX].GetX() - swiperWidth), offsetList[SWIPER_INDEX].GetY());
126 swiperWrapper->GetGeometryNode()->SetMarginFrameOffset(swiperOffset);
127 swiperWrapper->Layout();
128
129 auto dividerWidth = dividerWrapper->GetGeometryNode()->GetFrameSize().Width();
130 OffsetF dividerOffset =
131 OffsetF((tabsWidth - offsetList[DIVIDER_INDEX].GetX() - dividerWidth), offsetList[DIVIDER_INDEX].GetY());
132 dividerWrapper->GetGeometryNode()->SetMarginFrameOffset(dividerOffset);
133 dividerWrapper->Layout();
134
135 auto tabBarWidth = tabBarWrapper->GetGeometryNode()->GetFrameSize().Width();
136 OffsetF tabBarOffset =
137 OffsetF((tabsWidth - offsetList[TAB_BAR_INDEX].GetX() - tabBarWidth), offsetList[TAB_BAR_INDEX].GetY());
138 tabBarWrapper->GetGeometryNode()->SetMarginFrameOffset(tabBarOffset);
139 tabBarWrapper->Layout();
140 return;
141 }
142 swiperWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[SWIPER_INDEX]);
143 swiperWrapper->Layout();
144
145 dividerWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[DIVIDER_INDEX]);
146 dividerWrapper->Layout();
147
148 tabBarWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[TAB_BAR_INDEX]);
149 tabBarWrapper->Layout();
150 }
151
LayoutOffsetList(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & tabBarWrapper,const SizeF & frameSize) const152 std::vector<OffsetF> TabsLayoutAlgorithm::LayoutOffsetList(
153 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& tabBarWrapper, const SizeF& frameSize) const
154 {
155 std::vector<OffsetF> offsetList;
156 OffsetF tabBarOffset;
157 OffsetF dividerOffset;
158 OffsetF swiperOffset;
159 auto axis = GetAxis(layoutWrapper);
160 auto barPosition = GetBarPosition(layoutWrapper);
161 auto divider = GetDivider(layoutWrapper);
162 auto tabBarGeometryNode = tabBarWrapper->GetGeometryNode();
163 CHECK_NULL_RETURN(tabBarGeometryNode, offsetList);
164 auto tabBarFrameSize = tabBarGeometryNode->GetMarginFrameSize();
165 auto dividerStrokeWidth = divider.isNull ? 0.0f : divider.strokeWidth.ConvertToPx();
166 auto dividerStartMargin = divider.startMargin.ConvertToPx();
167 auto layoutProperty = DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
168 CHECK_NULL_RETURN(layoutProperty, offsetList);
169 auto padding = layoutProperty->CreatePaddingAndBorder();
170 auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
171
172 if (axis == Axis::HORIZONTAL) {
173 float barPosX = (frameSize.MainSize(Axis::HORIZONTAL) - tabBarFrameSize.MainSize(Axis::HORIZONTAL)) / 2;
174 if (barPosition == BarPosition::START) {
175 tabBarOffset = OffsetF(barPosX, padding.Offset().GetY());
176 dividerOffset = OffsetF(dividerStartMargin + padding.Offset().GetX(),
177 tabBarFrameSize.MainSize(Axis::VERTICAL) + padding.Offset().GetY());
178 swiperOffset = barOverlap ? padding.Offset() : OffsetF(padding.Offset().GetX(),
179 tabBarFrameSize.MainSize(Axis::VERTICAL) + dividerStrokeWidth + padding.Offset().GetY());
180 } else {
181 tabBarOffset = OffsetF(barPosX, frameSize.MainSize(Axis::VERTICAL) -
182 tabBarFrameSize.MainSize(Axis::VERTICAL) - padding.bottom.value_or(0.0f));
183 dividerOffset = OffsetF(dividerStartMargin + padding.Offset().GetX(), frameSize.MainSize(Axis::VERTICAL) -
184 tabBarFrameSize.MainSize(Axis::VERTICAL) - padding.bottom.value_or(0.0f) - dividerStrokeWidth);
185 swiperOffset = padding.Offset();
186 }
187 } else {
188 float barPosY = (frameSize.MainSize(Axis::VERTICAL) - tabBarFrameSize.MainSize(Axis::VERTICAL)) / 2;
189 if (barPosition == BarPosition::START) {
190 tabBarOffset = OffsetF(padding.Offset().GetX(), barPosY);
191 dividerOffset = OffsetF(tabBarFrameSize.MainSize(Axis::HORIZONTAL) + padding.Offset().GetX(),
192 dividerStartMargin + padding.Offset().GetY());
193 swiperOffset = barOverlap ? padding.Offset() : OffsetF(tabBarFrameSize.MainSize(Axis::HORIZONTAL) +
194 padding.Offset().GetX() + dividerStrokeWidth, padding.Offset().GetY());
195 } else {
196 tabBarOffset = OffsetF(frameSize.MainSize(Axis::HORIZONTAL) - tabBarFrameSize.MainSize(Axis::HORIZONTAL) -
197 padding.right.value_or(0.0f), barPosY);
198 dividerOffset = OffsetF(frameSize.MainSize(Axis::HORIZONTAL) - tabBarFrameSize.MainSize(Axis::HORIZONTAL) -
199 padding.right.value_or(0.0f) - dividerStrokeWidth, dividerStartMargin + padding.Offset().GetY());
200 swiperOffset = padding.Offset();
201 }
202 }
203 offsetList.emplace_back(swiperOffset);
204 offsetList.emplace_back(dividerOffset);
205 offsetList.emplace_back(tabBarOffset);
206 return offsetList;
207 }
208
GetBarPosition(LayoutWrapper * layoutWrapper) const209 BarPosition TabsLayoutAlgorithm::GetBarPosition(LayoutWrapper* layoutWrapper) const
210 {
211 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
212 CHECK_NULL_RETURN(layoutProperty, BarPosition::START);
213 return layoutProperty->GetTabBarPosition().value_or(BarPosition::START);
214 }
215
GetAxis(LayoutWrapper * layoutWrapper) const216 Axis TabsLayoutAlgorithm::GetAxis(LayoutWrapper* layoutWrapper) const
217 {
218 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
219 CHECK_NULL_RETURN(layoutProperty, Axis::HORIZONTAL);
220 return layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
221 }
222
GetDivider(LayoutWrapper * layoutWrapper) const223 TabsItemDivider TabsLayoutAlgorithm::GetDivider(LayoutWrapper* layoutWrapper) const
224 {
225 auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
226 TabsItemDivider divider;
227 CHECK_NULL_RETURN(layoutProperty, divider);
228 return layoutProperty->GetDivider().value_or(divider);
229 }
230
MeasureDivider(const RefPtr<TabsLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & dividerWrapper,const SizeF & idealSize)231 float TabsLayoutAlgorithm::MeasureDivider(const RefPtr<TabsLayoutProperty>& layoutProperty,
232 const RefPtr<LayoutWrapper>& dividerWrapper, const SizeF& idealSize)
233 {
234 auto constraint = layoutProperty->GetLayoutConstraint();
235
236 auto dividerIdealSize = CreateIdealSize(
237 constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
238 TabsItemDivider defaultDivider;
239 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
240 auto parentWidth = idealSize.Width();
241 auto parentHeight = idealSize.Height();
242 auto divider = layoutProperty->GetDivider().value_or(defaultDivider);
243 auto dividerStrokeWidth = divider.strokeWidth.ConvertToPx();
244 auto dividerStartMargin = divider.startMargin.ConvertToPx();
245 auto dividerEndMargin = divider.endMargin.ConvertToPx();
246
247 if (axis == Axis::VERTICAL) {
248 dividerIdealSize.SetWidth(dividerStrokeWidth);
249 dividerIdealSize.SetHeight(parentHeight - dividerStartMargin - dividerEndMargin);
250 } else if (axis == Axis::HORIZONTAL) {
251 dividerIdealSize.SetWidth(parentWidth - dividerStartMargin - dividerEndMargin);
252 dividerIdealSize.SetHeight(dividerStrokeWidth);
253 }
254
255 auto dividerLayoutConstraint = layoutProperty->CreateChildConstraint();
256 dividerLayoutConstraint.selfIdealSize = OptionalSizeF(dividerIdealSize);
257 dividerWrapper->Measure(dividerLayoutConstraint);
258
259 return divider.isNull ? 0.0f : dividerStrokeWidth;
260 }
261
MeasureSwiper(const RefPtr<TabsLayoutProperty> & layoutProperty,RefPtr<LayoutWrapper> & swiperWrapper,const SizeF & idealSize,const SizeF & tabBarSize,const float dividerWidth)262 SizeF TabsLayoutAlgorithm::MeasureSwiper(const RefPtr<TabsLayoutProperty>& layoutProperty,
263 RefPtr<LayoutWrapper>& swiperWrapper, const SizeF& idealSize, const SizeF& tabBarSize, const float dividerWidth)
264 {
265 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
266 auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
267 bool autoWidth = layoutProperty->GetWidthAutoValue(false);
268 bool autoHeight = layoutProperty->GetHeightAutoValue(false);
269 SizeF parentIdealSize = idealSize;
270 auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
271 childLayoutConstraint.parentIdealSize = OptionalSizeF(idealSize);
272
273 if (axis == Axis::HORIZONTAL) {
274 if (!barOverlap) {
275 if (!autoHeight) {
276 childLayoutConstraint.selfIdealSize.SetHeight(
277 idealSize.Height() - tabBarSize.Height() - dividerWidth);
278 }
279 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
280 parentIdealSize.SetHeight(idealSize.Height() - tabBarSize.Height() - dividerWidth);
281 } else {
282 if (!autoHeight) {
283 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
284 }
285 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
286 parentIdealSize.SetHeight(idealSize.Height());
287 }
288 } else if (axis == Axis::VERTICAL) {
289 if (!barOverlap) {
290 if (!autoWidth) {
291 childLayoutConstraint.selfIdealSize.SetWidth(
292 idealSize.Width() - tabBarSize.Width() - dividerWidth);
293 }
294 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
295 parentIdealSize.SetWidth(idealSize.Width() - tabBarSize.Width() - dividerWidth);
296 } else {
297 if (!autoWidth) {
298 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
299 }
300 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
301 parentIdealSize.SetWidth(idealSize.Width());
302 }
303 }
304 childLayoutConstraint.parentIdealSize = OptionalSizeF(parentIdealSize);
305 swiperWrapper->Measure(childLayoutConstraint);
306
307 return swiperWrapper->GetGeometryNode()->GetMarginFrameSize();
308 }
309 } // namespace OHOS::Ace::NG
310