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