• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 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 "core/components_ng/base/frame_node.h"
19 #include "core/components_ng/property/measure_utils.h"
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 constexpr int32_t SWIPER_INDEX = 0;
24 constexpr int32_t DIVIDER_INDEX = 1;
25 constexpr int32_t TAB_BAR_INDEX = 2;
26 constexpr int32_t EFFECT_INDEX = 3;
27 } // namespace
28 
Measure(LayoutWrapper * layoutWrapper)29 void TabsLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
30 {
31     auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
32     CHECK_NULL_VOID(layoutProperty);
33     auto geometryNode = layoutWrapper->GetGeometryNode();
34     CHECK_NULL_VOID(geometryNode);
35     auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
36     auto constraint = layoutProperty->GetLayoutConstraint();
37     auto tabsIdealSize = CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL,
38         layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT));
39     auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
40     if (layoutPolicy.has_value()) {
41         auto widthLayoutPolicy = layoutPolicy.value().widthLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
42         auto heightLayoutPolicy = layoutPolicy.value().heightLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
43 
44         // When the width or height parameter is MATCH_PARENT, set its value to the parent's value.
45         auto layoutPolicySize = ConstrainIdealSizeByLayoutPolicy(constraint.value(),
46             widthLayoutPolicy, heightLayoutPolicy, axis);
47         tabsIdealSize.UpdateIllegalSizeWithCheck(layoutPolicySize);
48     }
49     auto idealSize = tabsIdealSize.ConvertToSizeT();
50     if (GreaterOrEqualToInfinity(idealSize.Width()) || GreaterOrEqualToInfinity(idealSize.Height())) {
51         geometryNode->SetFrameSize(SizeF());
52         return;
53     }
54     geometryNode->SetFrameSize(idealSize);
55     MinusPaddingToSize(layoutProperty->CreatePaddingAndBorder(), idealSize);
56     auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
57     childLayoutConstraint.parentIdealSize = OptionalSizeF(idealSize);
58     float dividerStrokeWidth = 0.0f;
59 
60     // Measure tab bar.
61     auto tabBarWrapper = layoutWrapper->GetOrCreateChildByIndex(TAB_BAR_INDEX);
62     SizeF tabBarSize;
63     if (tabBarWrapper) {
64         tabBarWrapper->Measure(childLayoutConstraint);
65         tabBarSize = tabBarWrapper->GetGeometryNode()->GetMarginFrameSize();
66     }
67 
68     // Measure effect node.
69     auto effectWrapper = layoutWrapper->GetOrCreateChildByIndex(EFFECT_INDEX);
70     if (effectWrapper) {
71         MeasureEffectNode(layoutProperty, effectWrapper, idealSize);
72     }
73 
74     // Measure divider.
75     auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(DIVIDER_INDEX);
76     if (dividerWrapper) {
77         dividerStrokeWidth = MeasureDivider(layoutProperty, dividerWrapper, idealSize);
78     }
79 
80     auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
81 
82     // Measure swiper.
83     auto swiperWrapper = layoutWrapper->GetOrCreateChildByIndex(SWIPER_INDEX);
84     if (swiperWrapper) {
85         swiperWrapper->GetLayoutProperty()->UpdateLayoutDirection(layoutProperty->GetNonAutoLayoutDirection());
86     }
87     SizeF swiperSize;
88     if (swiperWrapper && swiperWrapper->GetHostNode() && swiperWrapper->GetHostNode()->TotalChildCount() > 0) {
89         swiperSize = MeasureSwiper(layoutProperty, swiperWrapper, idealSize, tabBarSize, dividerStrokeWidth);
90     } else if (swiperWrapper && swiperWrapper->GetGeometryNode()) {
91         swiperWrapper->GetGeometryNode()->SetFrameSize(SizeF());
92     }
93 
94     auto paddingH = layoutProperty->CreatePaddingAndBorder().Width();
95     auto paddingV = layoutProperty->CreatePaddingAndBorder().Height();
96     if ((axis == Axis::VERTICAL) && layoutProperty->GetWidthAutoValue(false)) {
97         if (!barOverlap) {
98             geometryNode->SetFrameWidth(tabBarSize.Width() + dividerStrokeWidth + swiperSize.Width() + paddingH);
99         } else {
100             geometryNode->SetFrameWidth(dividerStrokeWidth + swiperSize.Width() + paddingH);
101         }
102     } else if ((axis == Axis::HORIZONTAL) && layoutProperty->GetHeightAutoValue(false)) {
103         if (!barOverlap) {
104             geometryNode->SetFrameHeight(tabBarSize.Height() + dividerStrokeWidth + swiperSize.Height() + paddingV);
105         } else {
106             geometryNode->SetFrameHeight(dividerStrokeWidth + swiperSize.Height() + paddingV);
107         }
108     }
109 }
110 
Layout(LayoutWrapper * layoutWrapper)111 void TabsLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
112 {
113     CHECK_NULL_VOID(layoutWrapper);
114     auto geometryNode = layoutWrapper->GetGeometryNode();
115     CHECK_NULL_VOID(geometryNode);
116     auto frameSize = geometryNode->GetFrameSize();
117 
118     auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
119     CHECK_NULL_VOID(layoutProperty);
120     auto tabBarWrapper = layoutWrapper->GetChildByIndex(TAB_BAR_INDEX);
121     auto dividerWrapper = layoutWrapper->GetOrCreateChildByIndex(DIVIDER_INDEX);
122     auto effectWrapper = layoutWrapper->GetChildByIndex(EFFECT_INDEX);
123     auto swiperWrapper = layoutWrapper->GetOrCreateChildByIndex(SWIPER_INDEX);
124     if (!tabBarWrapper || !dividerWrapper || !swiperWrapper) {
125         return;
126     }
127 
128     std::vector<OffsetF> offsetList = { OffsetF(), OffsetF(), OffsetF(), OffsetF() };
129     if (frameSize.IsPositive()) {
130         MinusPaddingToSize(layoutProperty->CreatePaddingAndBorder(), frameSize);
131         offsetList = LayoutOffsetList(layoutWrapper, tabBarWrapper, effectWrapper, frameSize);
132     }
133     if (layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL) {
134         auto tabsWidth = geometryNode->GetFrameSize().Width();
135         auto swiperWidth = swiperWrapper->GetGeometryNode()->GetFrameSize().Width();
136         auto& swiperOffset = offsetList[SWIPER_INDEX];
137         swiperOffset = OffsetF((tabsWidth - swiperOffset.GetX() - swiperWidth), swiperOffset.GetY());
138         auto dividerWidth = dividerWrapper->GetGeometryNode()->GetFrameSize().Width();
139         auto& dividerOffset = offsetList[DIVIDER_INDEX];
140         dividerOffset = OffsetF((tabsWidth - dividerOffset.GetX() - dividerWidth), dividerOffset.GetY());
141         if (effectWrapper) {
142             auto effectWidth = effectWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
143             auto& effectOffset = offsetList[EFFECT_INDEX];
144             effectOffset = OffsetF((tabsWidth - effectOffset.GetX() - effectWidth), effectOffset.GetY());
145         }
146         auto tabBarWidth = tabBarWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
147         auto& tabBarOffset = offsetList[TAB_BAR_INDEX];
148         tabBarOffset = OffsetF((tabsWidth - tabBarOffset.GetX() - tabBarWidth), tabBarOffset.GetY());
149     }
150     swiperWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[SWIPER_INDEX]);
151     swiperWrapper->Layout();
152 
153     dividerWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[DIVIDER_INDEX]);
154     dividerWrapper->Layout();
155 
156     if (effectWrapper) {
157         effectWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[EFFECT_INDEX]);
158         effectWrapper->Layout();
159     }
160 
161     tabBarWrapper->GetGeometryNode()->SetMarginFrameOffset(offsetList[TAB_BAR_INDEX]);
162     tabBarWrapper->Layout();
163 }
164 
LayoutOffsetList(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & tabBarWrapper,const RefPtr<LayoutWrapper> & effectNodeWrapper,const SizeF & frameSize) const165 std::vector<OffsetF> TabsLayoutAlgorithm::LayoutOffsetList(
166     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& tabBarWrapper,
167     const RefPtr<LayoutWrapper>& effectNodeWrapper, const SizeF& frameSize) const
168 {
169     std::vector<OffsetF> offsetList;
170     OffsetF tabBarOffset;
171     OffsetF dividerOffset;
172     OffsetF swiperOffset;
173     OffsetF effectOffset;
174     auto axis = GetAxis(layoutWrapper);
175     auto barPosition = GetBarPosition(layoutWrapper);
176     auto divider = GetDivider(layoutWrapper);
177     auto tabBarGeometryNode = tabBarWrapper->GetGeometryNode();
178     auto effectNodeGeometryNode = effectNodeWrapper ? effectNodeWrapper->GetGeometryNode() : RefPtr<GeometryNode>();
179     CHECK_NULL_RETURN(tabBarGeometryNode, offsetList);
180     auto tabBarFrameSize = tabBarGeometryNode->GetMarginFrameSize();
181     auto effectNodeFrameSize = effectNodeGeometryNode ? effectNodeGeometryNode->GetMarginFrameSize() : SizeF();
182     auto dividerStrokeWidth = divider.isNull ? 0.0f : divider.strokeWidth.ConvertToPx();
183     auto dividerStartMargin = divider.startMargin.ConvertToPx();
184     auto layoutProperty = DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
185     CHECK_NULL_RETURN(layoutProperty, offsetList);
186     auto paddingOffset = layoutProperty->CreatePaddingAndBorder().Offset();
187     auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
188 
189     if (axis == Axis::HORIZONTAL) {
190         float barPosX = (frameSize.MainSize(axis) - tabBarFrameSize.MainSize(axis)) / 2 + paddingOffset.GetX();
191         float effectPosX = (frameSize.MainSize(axis) - effectNodeFrameSize.MainSize(axis)) / 2 + paddingOffset.GetX();
192         if (barPosition == BarPosition::START) {
193             tabBarOffset = OffsetF(barPosX, paddingOffset.GetY());
194             effectOffset = OffsetF(effectPosX, paddingOffset.GetY());
195             dividerOffset = OffsetF(dividerStartMargin + paddingOffset.GetX(),
196                 tabBarFrameSize.CrossSize(axis) + paddingOffset.GetY());
197             swiperOffset = barOverlap ? paddingOffset : OffsetF(paddingOffset.GetX(),
198                 tabBarFrameSize.CrossSize(axis) + dividerStrokeWidth + paddingOffset.GetY());
199         } else {
200             tabBarOffset = OffsetF(barPosX, frameSize.CrossSize(axis) -
201                 tabBarFrameSize.CrossSize(axis) + paddingOffset.GetY());
202             effectOffset = OffsetF(effectPosX, frameSize.CrossSize(axis) -
203                 effectNodeFrameSize.CrossSize(axis) + paddingOffset.GetY());
204             dividerOffset = OffsetF(dividerStartMargin + paddingOffset.GetX(), frameSize.CrossSize(axis) -
205                 tabBarFrameSize.CrossSize(axis) + paddingOffset.GetY() - dividerStrokeWidth);
206             swiperOffset = paddingOffset;
207         }
208     } else {
209         float barPosY = (frameSize.MainSize(axis) - tabBarFrameSize.MainSize(axis)) / 2 + paddingOffset.GetY();
210         float effectPosY = (frameSize.MainSize(axis) - effectNodeFrameSize.MainSize(axis)) / 2 + paddingOffset.GetY();
211         if (barPosition == BarPosition::START) {
212             tabBarOffset = OffsetF(paddingOffset.GetX(), barPosY);
213             effectOffset = OffsetF(paddingOffset.GetX(), effectPosY);
214             dividerOffset = OffsetF(tabBarFrameSize.CrossSize(axis) + paddingOffset.GetX(),
215                 dividerStartMargin + paddingOffset.GetY());
216             swiperOffset = barOverlap ? paddingOffset : OffsetF(tabBarFrameSize.CrossSize(axis) +
217                 paddingOffset.GetX() + dividerStrokeWidth, paddingOffset.GetY());
218         } else {
219             tabBarOffset = OffsetF(frameSize.CrossSize(axis) - tabBarFrameSize.CrossSize(axis) +
220                 paddingOffset.GetX(), barPosY);
221             effectOffset = OffsetF(frameSize.CrossSize(axis) - effectNodeFrameSize.CrossSize(axis) +
222                 paddingOffset.GetX(), effectPosY);
223             dividerOffset = OffsetF(frameSize.CrossSize(axis) - tabBarFrameSize.CrossSize(axis) +
224                 paddingOffset.GetX() - dividerStrokeWidth, dividerStartMargin + paddingOffset.GetY());
225             swiperOffset = paddingOffset;
226         }
227     }
228     offsetList.emplace_back(swiperOffset);
229     offsetList.emplace_back(dividerOffset);
230     offsetList.emplace_back(tabBarOffset);
231     offsetList.emplace_back(effectOffset);
232     return offsetList;
233 }
234 
GetBarPosition(LayoutWrapper * layoutWrapper) const235 BarPosition TabsLayoutAlgorithm::GetBarPosition(LayoutWrapper* layoutWrapper) const
236 {
237     auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
238     CHECK_NULL_RETURN(layoutProperty, BarPosition::START);
239     return layoutProperty->GetTabBarPosition().value_or(BarPosition::START);
240 }
241 
GetAxis(LayoutWrapper * layoutWrapper) const242 Axis TabsLayoutAlgorithm::GetAxis(LayoutWrapper* layoutWrapper) const
243 {
244     auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
245     CHECK_NULL_RETURN(layoutProperty, Axis::HORIZONTAL);
246     return layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
247 }
248 
GetDivider(LayoutWrapper * layoutWrapper) const249 TabsItemDivider TabsLayoutAlgorithm::GetDivider(LayoutWrapper* layoutWrapper) const
250 {
251     auto layoutProperty = AceType::DynamicCast<TabsLayoutProperty>(layoutWrapper->GetLayoutProperty());
252     TabsItemDivider divider;
253     CHECK_NULL_RETURN(layoutProperty, divider);
254     return layoutProperty->GetDivider().value_or(divider);
255 }
256 
MeasureDivider(const RefPtr<TabsLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & dividerWrapper,const SizeF & idealSize)257 float TabsLayoutAlgorithm::MeasureDivider(const RefPtr<TabsLayoutProperty>& layoutProperty,
258     const RefPtr<LayoutWrapper>& dividerWrapper, const SizeF& idealSize)
259 {
260     auto constraint = layoutProperty->GetLayoutConstraint();
261 
262     auto dividerIdealSize = CreateIdealSize(
263         constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
264     TabsItemDivider defaultDivider;
265     auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
266     auto parentWidth = idealSize.Width();
267     auto parentHeight = idealSize.Height();
268     auto divider = layoutProperty->GetDivider().value_or(defaultDivider);
269     auto dividerStrokeWidth = divider.strokeWidth.ConvertToPx();
270     auto dividerStartMargin = divider.startMargin.ConvertToPx();
271     auto dividerEndMargin = divider.endMargin.ConvertToPx();
272 
273     if (axis == Axis::VERTICAL) {
274         dividerIdealSize.SetWidth(dividerStrokeWidth);
275         dividerIdealSize.SetHeight(parentHeight - dividerStartMargin - dividerEndMargin);
276     } else if (axis == Axis::HORIZONTAL) {
277         dividerIdealSize.SetWidth(parentWidth - dividerStartMargin - dividerEndMargin);
278         dividerIdealSize.SetHeight(dividerStrokeWidth);
279     }
280 
281     auto dividerLayoutConstraint = layoutProperty->CreateChildConstraint();
282     dividerLayoutConstraint.selfIdealSize = OptionalSizeF(dividerIdealSize);
283     dividerWrapper->Measure(dividerLayoutConstraint);
284 
285     return divider.isNull ? 0.0f : dividerStrokeWidth;
286 }
287 
MeasureEffectNode(const RefPtr<TabsLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & effectNodeWrapper,const SizeF & idealSize)288 void TabsLayoutAlgorithm::MeasureEffectNode(const RefPtr<TabsLayoutProperty>& layoutProperty,
289     const RefPtr<LayoutWrapper>& effectNodeWrapper, const SizeF& idealSize)
290 {
291     auto constraint = layoutProperty->GetLayoutConstraint();
292 
293     auto effectNodeIdealSize = CreateIdealSize(
294         constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
295     TabsEffectNodeOption defaultEffectNodeOption;
296     auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
297     auto parentWidth = idealSize.Width();
298     auto parentHeight = idealSize.Height();
299     auto effectNodeOption = layoutProperty->GetEffectNodeOption().value_or(defaultEffectNodeOption);
300     auto effectNodeWidth = effectNodeOption.strokeWidth.ConvertToPx();
301 
302     if (axis == Axis::VERTICAL) {
303         effectNodeIdealSize.SetWidth(effectNodeWidth);
304         effectNodeIdealSize.SetHeight(parentHeight);
305     } else if (axis == Axis::HORIZONTAL) {
306         effectNodeIdealSize.SetWidth(parentWidth);
307         effectNodeIdealSize.SetHeight(effectNodeWidth);
308     }
309 
310     auto effectNodeLayoutConstraint = layoutProperty->CreateChildConstraint();
311     effectNodeLayoutConstraint.selfIdealSize = OptionalSizeF(effectNodeIdealSize);
312     effectNodeWrapper->Measure(effectNodeLayoutConstraint);
313 }
314 
MeasureSwiper(const RefPtr<TabsLayoutProperty> & layoutProperty,RefPtr<LayoutWrapper> & swiperWrapper,const SizeF & idealSize,const SizeF & tabBarSize,const float dividerWidth)315 SizeF TabsLayoutAlgorithm::MeasureSwiper(const RefPtr<TabsLayoutProperty>& layoutProperty,
316     RefPtr<LayoutWrapper>& swiperWrapper, const SizeF& idealSize, const SizeF& tabBarSize, const float dividerWidth)
317 {
318     auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
319     auto barOverlap = layoutProperty->GetBarOverlap().value_or(false);
320     bool autoWidth = layoutProperty->GetWidthAutoValue(false);
321     bool autoHeight = layoutProperty->GetHeightAutoValue(false);
322     SizeF parentIdealSize = idealSize;
323     auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
324     childLayoutConstraint.parentIdealSize = OptionalSizeF(idealSize);
325 
326     if (axis == Axis::HORIZONTAL) {
327         if (!barOverlap) {
328             if (!autoHeight) {
329                 childLayoutConstraint.selfIdealSize.SetHeight(
330                     idealSize.Height() - tabBarSize.Height() - dividerWidth);
331             }
332             childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
333             parentIdealSize.SetHeight(idealSize.Height() - tabBarSize.Height() - dividerWidth);
334         } else {
335             if (!autoHeight) {
336                 childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
337             }
338             childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
339             parentIdealSize.SetHeight(idealSize.Height());
340         }
341     } else if (axis == Axis::VERTICAL) {
342         if (!barOverlap) {
343             if (!autoWidth) {
344                 childLayoutConstraint.selfIdealSize.SetWidth(
345                     idealSize.Width() - tabBarSize.Width() - dividerWidth);
346             }
347             childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
348             parentIdealSize.SetWidth(idealSize.Width() - tabBarSize.Width() - dividerWidth);
349         } else {
350             if (!autoWidth) {
351                 childLayoutConstraint.selfIdealSize.SetWidth(idealSize.Width());
352             }
353             childLayoutConstraint.selfIdealSize.SetHeight(idealSize.Height());
354             parentIdealSize.SetWidth(idealSize.Width());
355         }
356     }
357     childLayoutConstraint.parentIdealSize = OptionalSizeF(parentIdealSize);
358     swiperWrapper->Measure(childLayoutConstraint);
359 
360     return swiperWrapper->GetGeometryNode()->GetMarginFrameSize();
361 }
362 } // namespace OHOS::Ace::NG
363