• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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