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/side_bar/side_bar_container_layout_algorithm.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/utils/utils.h"
21 #include "core/common/ace_application_info.h"
22 #include "core/common/container.h"
23 #include "core/components/common/layout/constants.h"
24 #include "core/components_ng/base/frame_node.h"
25 #include "core/components_ng/pattern/side_bar/side_bar_container_pattern.h"
26 #include "core/components_ng/property/calc_length.h"
27 #include "core/components_ng/property/measure_utils.h"
28 #include "core/pipeline_ng/pipeline_context.h"
29 
30 namespace OHOS::Ace::NG {
31 
32 namespace {
33 constexpr int32_t DEFAULT_MIN_CHILDREN_SIZE = 3;
34 constexpr Dimension DEFAULT_CONTROL_BUTTON_LEFT = 16.0_vp;
35 constexpr Dimension DEFAULT_CONTROL_BUTTON_TOP = 48.0_vp;
36 constexpr Dimension DEFAULT_MAX_SIDE_BAR_WIDTH = 280.0_vp;
37 constexpr Dimension DEFAULT_DIVIDER_STROKE_WIDTH = 1.0_vp;
38 constexpr Dimension DEFAULT_DIVIDER_START_MARGIN = 0.0_vp;
39 constexpr Dimension DEFAULT_DIVIDER_END_MARGIN = 0.0_vp;
40 
41 constexpr static int INDEX_CONTRON_BUTTON = 1;
42 constexpr static int INDEX_DIVIDER = 2;
43 constexpr static int INDEX_SIDE_BAR = 3;
44 constexpr static int32_t PLATFORM_VERSION_TEN = 10;
45 static Dimension DEFAULT_SIDE_BAR_WIDTH = 200.0_vp;
46 static Dimension DEFAULT_MIN_SIDE_BAR_WIDTH = 200.0_vp;
47 static Dimension DEFAULT_MIN_CONTENT_WIDTH = 0.0_vp;
48 constexpr Dimension CONTROL_BUTTON_PADDING = 12.0_vp;
49 } // namespace
50 
Measure(LayoutWrapper * layoutWrapper)51 void SideBarContainerLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
52 {
53     autoHide_ = false;
54     UpdateDefaultValueByVersion();
55     const auto& children = layoutWrapper->GetAllChildrenWithBuild(layoutWrapper->IsActive());
56     if (children.size() < DEFAULT_MIN_CHILDREN_SIZE) {
57         return;
58     }
59     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
60     CHECK_NULL_VOID(layoutProperty);
61     const auto& constraint = layoutProperty->GetLayoutConstraint();
62     auto idealSize = PipelineContext::GetCurrentContext()->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN ?
63     CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL,
64         layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT)).ConvertToSizeT() :
65     CreateIdealSize(
66         constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
67     layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
68 
69     AdjustMinAndMaxSideBarWidth(layoutWrapper);
70 
71     auto parentWidth = idealSize.Width();
72     realSideBarWidth_ = ConvertToPx(realSideBarWidthDimension_, constraint->scaleProperty, parentWidth).value_or(-1.0f);
73     if (needInitRealSideBarWidth_ || NearZero(realSideBarWidth_)) {
74         auto pipeline = PipelineContext::GetCurrentContext();
75         if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
76             GetAllPropertyValue(layoutProperty, parentWidth);
77         } else {
78             InitRealSideBarWidth(layoutWrapper, parentWidth);
79         }
80     }
81 
82     if (parentWidth >= typeUpdateWidth_) {
83         type_ = layoutProperty->GetSideBarContainerType().value_or(SideBarContainerType::EMBED);
84     }
85 
86     auto type = layoutProperty->GetSideBarContainerType().value_or(SideBarContainerType::EMBED);
87     if (type == SideBarContainerType::AUTO) {
88         AutoMode(layoutProperty, parentWidth);
89     }
90 
91     if ((parentWidth < typeUpdateWidth_) && (!layoutProperty->GetShowSideBar().has_value()) &&
92         (type_ != SideBarContainerType::OVERLAY)) {
93         if (isControlButtonClick_) {
94             type_ = SideBarContainerType::OVERLAY;
95         } else if (layoutProperty->GetAutoHide().value_or(true)) {
96             sideBarStatus_ = SideBarStatus::HIDDEN;
97             autoHide_ = true;
98         }
99     }
100     if (type_ != SideBarContainerType::OVERLAY) {
101         AutoChangeSideBarWidth(layoutProperty, parentWidth);
102     }
103 
104     /*
105      * child inverted order is: controlbutton, divider, sidebar, contentxxx, ..., content2, content1
106      * content only display the first one, use itor from end
107      */
108     int index = 0;
109     RefPtr<LayoutWrapper> dividerLayoutWrapper = nullptr;
110     for (auto it = children.rbegin(); it != children.rend(); ++it) {
111         index++;
112         if (index == INDEX_CONTRON_BUTTON) {
113             auto imgLayoutWrapper = (*it);
114             MeasureControlButton(layoutProperty, imgLayoutWrapper, parentWidth);
115         } else if (index == INDEX_DIVIDER) {
116             dividerLayoutWrapper = (*it);
117         } else if (index == INDEX_SIDE_BAR) {
118             auto sideBarLayoutWrapper = (*it);
119             MeasureSideBar(layoutProperty, sideBarLayoutWrapper);
120         } else { // other break
121             continue;
122         }
123     }
124 
125     if (dividerLayoutWrapper) {
126         MeasureDivider(layoutProperty, dividerLayoutWrapper, parentWidth);
127     }
128 
129     if (children.size() > DEFAULT_MIN_CHILDREN_SIZE) { // when sidebar only add one component, content is not display
130         auto contentLayoutWrapper = children.front();
131         MeasureSideBarContent(layoutProperty, contentLayoutWrapper, parentWidth);
132     }
133 
134     if (realSideBarWidthDimension_.Unit() == DimensionUnit::PERCENT) {
135         realSideBarWidthDimension_ = NearZero(parentWidth)
136                                          ? Dimension(0.0, DimensionUnit::PERCENT)
137                                          : Dimension(realSideBarWidth_ / parentWidth, DimensionUnit::PERCENT);
138     } else {
139         realSideBarWidthDimension_ = Dimension(realSideBarWidth_, DimensionUnit::PX);
140     }
141 }
142 
UpdateDefaultValueByVersion()143 void SideBarContainerLayoutAlgorithm::UpdateDefaultValueByVersion()
144 {
145     auto pipeline = PipelineContext::GetCurrentContext();
146     CHECK_NULL_VOID(pipeline);
147     if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
148         DEFAULT_SIDE_BAR_WIDTH = 240.0_vp;
149         DEFAULT_MIN_SIDE_BAR_WIDTH = 240.0_vp;
150         DEFAULT_MIN_CONTENT_WIDTH = 360.0_vp;
151     }
152 }
153 
GetSideBarLayoutWrapper(LayoutWrapper * layoutWrapper) const154 RefPtr<LayoutWrapper> SideBarContainerLayoutAlgorithm::GetSideBarLayoutWrapper(LayoutWrapper* layoutWrapper) const
155 {
156     CHECK_NULL_RETURN(layoutWrapper, nullptr);
157     const auto& children = layoutWrapper->GetAllChildrenWithBuild(layoutWrapper->IsActive());
158     if (children.size() < DEFAULT_MIN_CHILDREN_SIZE) {
159         return nullptr;
160     }
161 
162     int index = 0;
163     for (auto it = children.rbegin(); it != children.rend(); ++it) {
164         index++;
165         if (index == INDEX_SIDE_BAR) {
166             return (*it);
167         }
168     }
169 
170     return nullptr;
171 }
172 
AdjustMinAndMaxSideBarWidth(LayoutWrapper * layoutWrapper)173 void SideBarContainerLayoutAlgorithm::AdjustMinAndMaxSideBarWidth(LayoutWrapper* layoutWrapper)
174 {
175     adjustMinSideBarWidth_ = DEFAULT_MIN_SIDE_BAR_WIDTH;
176     adjustMaxSideBarWidth_ = DEFAULT_MAX_SIDE_BAR_WIDTH;
177     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
178     CHECK_NULL_VOID(layoutProperty);
179 
180     auto pipeline = PipelineContext::GetCurrentContext();
181     CHECK_NULL_VOID(pipeline);
182 
183     if (pipeline->GetMinPlatformVersion() < PLATFORM_VERSION_TEN) {
184         adjustMinSideBarWidth_ = layoutProperty->GetMinSideBarWidth().value_or(DEFAULT_MIN_SIDE_BAR_WIDTH);
185         adjustMaxSideBarWidth_ = layoutProperty->GetMaxSideBarWidth().value_or(DEFAULT_MAX_SIDE_BAR_WIDTH);
186         if (adjustMinSideBarWidth_ > adjustMaxSideBarWidth_) {
187             adjustMinSideBarWidth_ = DEFAULT_MIN_SIDE_BAR_WIDTH;
188             adjustMaxSideBarWidth_ = DEFAULT_MAX_SIDE_BAR_WIDTH;
189         }
190         return;
191     }
192 
193     auto sideBarLayoutWrapper = GetSideBarLayoutWrapper(layoutWrapper);
194     CHECK_NULL_VOID(sideBarLayoutWrapper);
195 
196     auto sideBarLayoutProperty = sideBarLayoutWrapper->GetLayoutProperty();
197     CHECK_NULL_VOID(sideBarLayoutProperty);
198 
199     auto&& calcConstraint = sideBarLayoutProperty->GetCalcLayoutConstraint();
200     if (layoutProperty->GetMinSideBarWidth().has_value()) {
201         adjustMinSideBarWidth_ = layoutProperty->GetMinSideBarWidthValue();
202     } else if (calcConstraint && calcConstraint->minSize.has_value() &&
203                calcConstraint->minSize.value().Width().has_value()) {
204         adjustMinSideBarWidth_ = calcConstraint->minSize->Width()->GetDimension();
205     }
206 
207     if (layoutProperty->GetMaxSideBarWidth().has_value()) {
208         adjustMaxSideBarWidth_ = layoutProperty->GetMaxSideBarWidthValue();
209     } else if (calcConstraint && calcConstraint->maxSize.has_value() && calcConstraint->maxSize->Width().has_value()) {
210         adjustMaxSideBarWidth_ = calcConstraint->maxSize->Width()->GetDimension();
211     }
212 
213     if (adjustMinSideBarWidth_ > adjustMaxSideBarWidth_) {
214         adjustMinSideBarWidth_ = DEFAULT_MIN_SIDE_BAR_WIDTH;
215         adjustMaxSideBarWidth_ = DEFAULT_MAX_SIDE_BAR_WIDTH;
216     }
217 }
218 
GetAllPropertyValue(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,float parentWidth)219 void SideBarContainerLayoutAlgorithm::GetAllPropertyValue(
220     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty, float parentWidth)
221 {
222     const auto& constraint = layoutProperty->GetLayoutConstraint();
223     const auto& scaleProperty = constraint->scaleProperty;
224     auto realSideBarWidth = layoutProperty->GetSideBarWidth().value_or(-1.0_vp);
225     auto minSideBarWidth = layoutProperty->GetMinSideBarWidth().value_or(-1.0_vp);
226     auto minContentWidth = layoutProperty->GetMinContentWidth().value_or(-1.0_vp);
227     auto maxSideBarWidth = layoutProperty->GetMaxSideBarWidth().value_or(-1.0_vp);
228 
229     realSideBarWidth_ = ConvertToPx(realSideBarWidth, scaleProperty, parentWidth).value_or(-1.0f);
230     if (preSideBarWidth_.IsValid()) {
231         realSideBarWidth_ = ConvertToPx(preSideBarWidth_, scaleProperty, parentWidth).value_or(-1.0f);
232     }
233 
234     minSideBarWidth_ = ConvertToPx(minSideBarWidth, scaleProperty, parentWidth).value_or(-1.0f);
235     minContentWidth_ = ConvertToPx(minContentWidth, scaleProperty, parentWidth).value_or(-1.0f);
236     maxSideBarWidth_ = ConvertToPx(maxSideBarWidth, scaleProperty, parentWidth).value_or(-1.0f);
237 
238     defaultRealSideBarWidth_ = ConvertToPx(DEFAULT_SIDE_BAR_WIDTH, scaleProperty, parentWidth).value_or(-1.0f);
239     defaultMinSideBarWidth_ = ConvertToPx(DEFAULT_MIN_SIDE_BAR_WIDTH, scaleProperty, parentWidth).value_or(-1.0f);
240     defaultMaxSideBarWidth_ = ConvertToPx(DEFAULT_MAX_SIDE_BAR_WIDTH, scaleProperty, parentWidth).value_or(-1.0f);
241     defaultMinContentWidth_ = ConvertToPx(DEFAULT_MIN_CONTENT_WIDTH, scaleProperty, parentWidth).value_or(-1.0f);
242 
243     MeasureTypeUpdateWidth();
244     minContentWidth_ = std::max(0.0f, minContentWidth_);
245     InitSideBarWidth(parentWidth);
246     MeasureRealSideBarWidth(parentWidth);
247 
248     auto sideBarContainerPattern = AceType::DynamicCast<SideBarContainerPattern>(pattern_.Upgrade());
249     sideBarContainerPattern->SetMinSideBarWidth(minSideBarWidth_);
250     sideBarContainerPattern->SetMaxSideBarWidth(maxSideBarWidth_);
251     sideBarContainerPattern->SetMinContentWidth(minContentWidth_);
252     sideBarContainerPattern->SetTypeUpdateWidth(typeUpdateWidth_);
253 }
254 
MeasureTypeUpdateWidth()255 void SideBarContainerLayoutAlgorithm::MeasureTypeUpdateWidth()
256 {
257     if (minSideBarWidth_ >= 0.0f && minContentWidth_ >= 0.0f) {
258         typeUpdateWidth_ = minSideBarWidth_ + minContentWidth_;
259         return;
260     } else if (minSideBarWidth_ >= 0.0f) {
261         typeUpdateWidth_ = minSideBarWidth_ + defaultMinContentWidth_;
262     } else if (minContentWidth_ >= 0.0f) {
263         typeUpdateWidth_ = minContentWidth_ + defaultMinSideBarWidth_;
264     } else {
265         typeUpdateWidth_ = defaultMinSideBarWidth_ + defaultMinContentWidth_;
266     }
267     if (typeUpdateWidth_ < defaultMinSideBarWidth_ + defaultMinContentWidth_) {
268         typeUpdateWidth_ = defaultMinSideBarWidth_ + defaultMinContentWidth_;
269     }
270 }
271 
InitSideBarWidth(float parentWidth)272 void SideBarContainerLayoutAlgorithm::InitSideBarWidth(float parentWidth)
273 {
274     if (minSideBarWidth_ >= parentWidth) {
275         minSideBarWidth_ = parentWidth;
276         maxSideBarWidth_ = parentWidth;
277         realSideBarWidth_ = parentWidth;
278         minContentWidth_ = 0.0f;
279         return;
280     }
281 
282     if (maxSideBarWidth_ >= parentWidth) {
283         maxSideBarWidth_ = parentWidth;
284         if (realSideBarWidth_ <= minSideBarWidth_) {
285             realSideBarWidth_ = minSideBarWidth_;
286         } else if (realSideBarWidth_ >= maxSideBarWidth_) {
287             realSideBarWidth_ = maxSideBarWidth_;
288         }
289         return;
290     }
291     if (minSideBarWidth_ >= 0.0f && maxSideBarWidth_ >= 0.0f) {
292         if (minSideBarWidth_ >= maxSideBarWidth_) {
293             maxSideBarWidth_ = minSideBarWidth_;
294             realSideBarWidth_ = minSideBarWidth_;
295             return;
296         }
297     }
298 
299     if (realSideBarWidth_ < 0.0f) {
300         return;
301     }
302     if (realSideBarWidth_ <= minSideBarWidth_) {
303         realSideBarWidth_ = minSideBarWidth_;
304         return;
305     } else if (realSideBarWidth_ >= maxSideBarWidth_ && maxSideBarWidth_ >= 0) {
306         realSideBarWidth_ = maxSideBarWidth_;
307         return;
308     } else if (realSideBarWidth_ >= parentWidth) {
309         realSideBarWidth_ = parentWidth;
310     }
311 }
312 
MeasureRealSideBarWidth(float parentWidth)313 void SideBarContainerLayoutAlgorithm::MeasureRealSideBarWidth(float parentWidth)
314 {
315     if (minSideBarWidth_ < 0.0f) {
316         if (realSideBarWidth_ >= 0.0f) {
317             minSideBarWidth_ = realSideBarWidth_;
318         } else if (maxSideBarWidth_ >= 0.0f) {
319             minSideBarWidth_ = maxSideBarWidth_;
320         } else {
321             minSideBarWidth_ = defaultMinSideBarWidth_;
322         }
323         if (minSideBarWidth_ >= defaultMinSideBarWidth_) {
324             minSideBarWidth_ = defaultMinSideBarWidth_;
325         }
326         if (minSideBarWidth_ >= parentWidth) {
327             minSideBarWidth_ = parentWidth;
328             maxSideBarWidth_ = parentWidth;
329             realSideBarWidth_ = parentWidth;
330             return;
331         }
332     }
333 
334     if (maxSideBarWidth_ < 0.0f) {
335         maxSideBarWidth_ = defaultMaxSideBarWidth_;
336         if (maxSideBarWidth_ <= realSideBarWidth_) {
337             maxSideBarWidth_ = realSideBarWidth_;
338         } else if (maxSideBarWidth_ <= minSideBarWidth_) {
339             maxSideBarWidth_ = minSideBarWidth_;
340             realSideBarWidth_ = minSideBarWidth_;
341             return;
342         }
343     }
344 
345     if (realSideBarWidth_ < 0.0f) {
346         realSideBarWidth_ = defaultRealSideBarWidth_;
347         if (realSideBarWidth_ <= minSideBarWidth_) {
348             realSideBarWidth_ = minSideBarWidth_;
349         } else if (realSideBarWidth_ >= maxSideBarWidth_) {
350             realSideBarWidth_ = maxSideBarWidth_;
351         }
352     }
353 }
354 
InitRealSideBarWidth(LayoutWrapper * layoutWrapper,float parentWidth)355 void SideBarContainerLayoutAlgorithm::InitRealSideBarWidth(LayoutWrapper* layoutWrapper, float parentWidth)
356 {
357     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
358     CHECK_NULL_VOID(layoutProperty);
359     auto constraint = layoutProperty->GetLayoutConstraint();
360     auto scaleProperty = constraint->scaleProperty;
361     auto sideBarWidth = layoutProperty->GetSideBarWidth().value_or(DEFAULT_SIDE_BAR_WIDTH);
362     auto sideBarWidthPx = ConvertToPx(sideBarWidth, scaleProperty, parentWidth).value_or(0);
363     auto minSideBarWidthPx = ConvertToPx(adjustMinSideBarWidth_, scaleProperty, parentWidth).value_or(0);
364     auto maxSideBarWidthPx = ConvertToPx(adjustMaxSideBarWidth_, scaleProperty, parentWidth).value_or(0);
365     if (minSideBarWidthPx > maxSideBarWidthPx) {
366         minSideBarWidthPx = ConvertToPx(DEFAULT_MIN_SIDE_BAR_WIDTH, scaleProperty, parentWidth).value_or(0);
367         maxSideBarWidthPx = ConvertToPx(DEFAULT_MAX_SIDE_BAR_WIDTH, scaleProperty, parentWidth).value_or(0);
368     }
369 
370     if (sideBarWidthPx <= minSideBarWidthPx) {
371         realSideBarWidth_ = minSideBarWidthPx;
372     } else if (sideBarWidthPx >= maxSideBarWidthPx) {
373         realSideBarWidth_ = maxSideBarWidthPx;
374     } else {
375         realSideBarWidth_ = sideBarWidthPx;
376     }
377 }
378 
AutoChangeSideBarWidth(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,float parentWidth)379 void SideBarContainerLayoutAlgorithm::AutoChangeSideBarWidth(
380     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty, float parentWidth)
381 {
382     /*
383      * When reducing component width, first reduce the width of the content to minContentWidth,
384      * and then reduce the width of the sidebar
385      */
386     const auto& constraint = layoutProperty->GetLayoutConstraint();
387     const auto& scaleProperty = constraint->scaleProperty;
388     auto dividerStrokeWidth = layoutProperty->GetDividerStrokeWidth().value_or(DEFAULT_DIVIDER_STROKE_WIDTH);
389     auto dividerStrokeWidthPx = ConvertToPx(dividerStrokeWidth, scaleProperty, parentWidth).value_or(1);
390     if ((realSideBarWidth_ + minContentWidth_ + dividerStrokeWidthPx) >= parentWidth) {
391         realSideBarWidth_ = parentWidth - minContentWidth_ - dividerStrokeWidthPx;
392     }
393     if (realSideBarWidth_ <= minSideBarWidth_) {
394         realSideBarWidth_ = minSideBarWidth_;
395     }
396 }
397 
AutoMode(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,float parentWidth)398 void SideBarContainerLayoutAlgorithm::AutoMode(
399     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty, float parentWidth)
400 {
401     /*
402      * SideBarContainer AUTO mode:
403      * When the component width is greater or equal to minNavBarWidth+minContentWidth,
404      * it is displayed in Embed mode;
405      * When the component width is smaller than minNavBarWidth+minContentWidth,
406      * it is displayed in Overlay mode.
407      */
408     if (parentWidth < typeUpdateWidth_) {
409         type_ = SideBarContainerType::OVERLAY;
410     } else {
411         type_ = SideBarContainerType::EMBED;
412     }
413 }
414 
MeasureSideBar(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & sideBarLayoutWrapper)415 void SideBarContainerLayoutAlgorithm::MeasureSideBar(
416     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty, const RefPtr<LayoutWrapper>& sideBarLayoutWrapper)
417 {
418     auto constraint = layoutProperty->GetLayoutConstraint();
419     auto sideBarIdealSize = PipelineContext::GetCurrentContext()->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN ?
420         CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL,
421         layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT)).ConvertToSizeT():
422         CreateIdealSize(constraint.value(), Axis::HORIZONTAL,
423         layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
424     const auto& padding = layoutProperty->CreatePaddingAndBorder();
425     realSideBarHeight_ = sideBarIdealSize.Height() - padding.top.value_or(0) - padding.bottom.value_or(0);
426     if (LessNotEqual(realSideBarHeight_, 0.0f)) {
427         realSideBarHeight_ = 0.0f;
428     }
429     sideBarIdealSize.SetWidth(realSideBarWidth_);
430     sideBarIdealSize.SetHeight(realSideBarHeight_);
431 
432     auto pipeline = PipelineContext::GetCurrentContext();
433     CHECK_NULL_VOID(pipeline);
434     if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
435         auto sideBarLayoutProperty = sideBarLayoutWrapper->GetLayoutProperty();
436         CHECK_NULL_VOID(sideBarLayoutProperty);
437         auto&& calcConstraint = sideBarLayoutProperty->GetCalcLayoutConstraint();
438         if (calcConstraint) {
439             if (calcConstraint->maxSize.has_value()) {
440                 auto maxWidth = CalcLength(realSideBarWidth_);
441                 auto maxHeight = CalcLength(realSideBarHeight_);
442                 calcConstraint->UpdateMaxSizeWithCheck(CalcSize(maxWidth, maxHeight));
443             }
444 
445             if (calcConstraint->minSize.has_value()) {
446                 auto minWidth = CalcLength(realSideBarWidth_);
447                 auto minHeight = CalcLength(realSideBarHeight_);
448                 calcConstraint->UpdateMinSizeWithCheck(CalcSize(minWidth, minHeight));
449             }
450         }
451     }
452 
453     auto sideBarConstraint = layoutProperty->CreateChildConstraint();
454     sideBarConstraint.selfIdealSize = OptionalSizeF(sideBarIdealSize);
455 
456     sideBarLayoutWrapper->Measure(sideBarConstraint);
457 }
458 
MeasureDivider(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & dividerLayoutWrapper,float parentWidth)459 void SideBarContainerLayoutAlgorithm::MeasureDivider(const RefPtr<SideBarContainerLayoutProperty>& layoutProperty,
460     const RefPtr<LayoutWrapper>& dividerLayoutWrapper, float parentWidth)
461 {
462     CHECK_NULL_VOID(layoutProperty);
463     CHECK_NULL_VOID(dividerLayoutWrapper);
464     auto constraint = layoutProperty->GetLayoutConstraint();
465     CHECK_NULL_VOID(constraint);
466     auto scaleProperty = constraint->scaleProperty;
467     auto dividerIdealSize = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN) ?
468     CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL,
469         layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT)).ConvertToSizeT() :
470     CreateIdealSize(
471         constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
472 
473     auto dividerStrokeWidth = layoutProperty->GetDividerStrokeWidth().value_or(DEFAULT_DIVIDER_STROKE_WIDTH);
474     auto dividerStartMargin = layoutProperty->GetDividerStartMargin().value_or(DEFAULT_DIVIDER_START_MARGIN);
475     auto dividerEndMargin = layoutProperty->GetDividerEndMargin().value_or(DEFAULT_DIVIDER_END_MARGIN);
476 
477     auto dividerStrokeWidthPx = ConvertToPx(dividerStrokeWidth, scaleProperty, parentWidth).value_or(1);
478     auto dividerStartMarginPx = ConvertToPx(dividerStartMargin, scaleProperty, parentWidth).value_or(0);
479     auto dividerEndMarginPx = ConvertToPx(dividerEndMargin, scaleProperty, parentWidth).value_or(0);
480 
481     dividerIdealSize.SetWidth(dividerStrokeWidthPx);
482     dividerIdealSize.SetHeight(realSideBarHeight_ - dividerStartMarginPx - dividerEndMarginPx);
483     realDividerWidth_ = dividerStrokeWidthPx;
484 
485     auto dividerLayoutConstraint = layoutProperty->CreateChildConstraint();
486     dividerLayoutConstraint.selfIdealSize = OptionalSizeF(dividerIdealSize);
487     dividerLayoutWrapper->Measure(dividerLayoutConstraint);
488 }
489 
MeasureSideBarContent(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & contentLayoutWrapper,float parentWidth)490 void SideBarContainerLayoutAlgorithm::MeasureSideBarContent(
491     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty, const RefPtr<LayoutWrapper>& contentLayoutWrapper,
492     float parentWidth)
493 {
494     auto sideBarPosition = GetSideBarPositionWithRtl(layoutProperty);
495     auto constraint = layoutProperty->GetLayoutConstraint();
496     const auto& padding = layoutProperty->CreatePaddingAndBorder();
497     auto contentWidth = parentWidth - padding.left.value_or(0) - padding.right.value_or(0);
498 
499     if (type_ == SideBarContainerType::EMBED) {
500         if (sideBarStatus_ == SideBarStatus::SHOW) {
501             contentWidth -= (realSideBarWidth_ + realDividerWidth_);
502         } else if (sideBarStatus_ == SideBarStatus::CHANGING) {
503             contentWidth = (sideBarPosition == SideBarPosition::START)
504                                ? (contentWidth - realSideBarWidth_ - realDividerWidth_ - currentOffset_)
505                                : (contentWidth - realDividerWidth_ + currentOffset_);
506         }
507     }
508     contentWidth = std::max(contentWidth, minContentWidth_);
509 
510     auto contentIdealSize = PipelineContext::GetCurrentContext()->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN ?
511     CreateIdealSizeByPercentRef(constraint.value(), Axis::HORIZONTAL,
512         layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT)).ConvertToSizeT() :
513     CreateIdealSize(
514         constraint.value(), Axis::HORIZONTAL, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT), true);
515     contentIdealSize.SetWidth(contentWidth);
516     contentIdealSize.SetHeight(realSideBarHeight_);
517     auto contentConstraint = layoutProperty->CreateChildConstraint();
518     contentConstraint.selfIdealSize = OptionalSizeF(contentIdealSize);
519     contentLayoutWrapper->Measure(contentConstraint);
520 }
521 
MeasureControlButton(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,const RefPtr<LayoutWrapper> & buttonLayoutWrapper,float parentWidth)522 void SideBarContainerLayoutAlgorithm::MeasureControlButton(const RefPtr<SideBarContainerLayoutProperty>& layoutProperty,
523     const RefPtr<LayoutWrapper>& buttonLayoutWrapper, float parentWidth)
524 {
525     auto constraint = layoutProperty->GetLayoutConstraint();
526     auto scaleProperty = constraint->scaleProperty;
527 
528     auto controlButtonWidth = controlImageWidth_ + CONTROL_BUTTON_PADDING * 2;
529     auto controlButtonHeight = controlImageHeight_ + CONTROL_BUTTON_PADDING * 2;
530     auto controlButtonWidthPx = ConvertToPx(controlButtonWidth, scaleProperty, parentWidth).value_or(0);
531     auto controlButtonHeightPx = ConvertToPx(controlButtonHeight, scaleProperty, parentWidth).value_or(0);
532 
533     auto controlButtonLayoutConstraint = layoutProperty->CreateChildConstraint();
534     controlButtonLayoutConstraint.selfIdealSize.SetWidth(controlButtonWidthPx);
535     controlButtonLayoutConstraint.selfIdealSize.SetHeight(controlButtonHeightPx);
536     buttonLayoutWrapper->Measure(controlButtonLayoutConstraint);
537 }
538 
Layout(LayoutWrapper * layoutWrapper)539 void SideBarContainerLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
540 {
541     const auto& children = layoutWrapper->GetAllChildrenWithBuild(layoutWrapper->IsActive());
542     if (children.size() < DEFAULT_MIN_CHILDREN_SIZE) {
543         return;
544     }
545 
546     int index = 0;
547     for (auto it = children.rbegin(); it != children.rend(); ++it) {
548         index++;
549         if (index == INDEX_CONTRON_BUTTON) {
550             auto controlButtonLayoutWrapper = (*it);
551             LayoutControlButton(layoutWrapper, controlButtonLayoutWrapper);
552         } else if (index == INDEX_DIVIDER) {
553             auto dividerLayoutWrapper = (*it);
554             LayoutDivider(layoutWrapper, dividerLayoutWrapper);
555         } else if (index == INDEX_SIDE_BAR) {
556             auto sideBarLayoutWrapper = (*it);
557             LayoutSideBar(layoutWrapper, sideBarLayoutWrapper);
558         } else { // other break
559             break;
560         }
561     }
562 
563     if (children.size() > DEFAULT_MIN_CHILDREN_SIZE) { // when sidebar only add one component, content is not display
564         auto contentLayoutWrapper = children.front();
565         LayoutSideBarContent(layoutWrapper, contentLayoutWrapper);
566     }
567 }
568 
LayoutControlButton(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & buttonLayoutWrapper)569 void SideBarContainerLayoutAlgorithm::LayoutControlButton(
570     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& buttonLayoutWrapper)
571 {
572     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
573     CHECK_NULL_VOID(layoutProperty);
574 
575     CHECK_NULL_VOID(layoutWrapper->GetGeometryNode());
576     auto parentWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
577     auto constraint = layoutProperty->GetLayoutConstraint();
578     auto scaleProperty = constraint->scaleProperty;
579     const auto& padding = layoutProperty->CreatePaddingAndBorder();
580     auto controlImageLeft = layoutProperty->GetControlButtonLeft().value_or(DEFAULT_CONTROL_BUTTON_LEFT);
581     auto controlImageTop = layoutProperty->GetControlButtonTop().value_or(DEFAULT_CONTROL_BUTTON_TOP);
582 
583     if (LessNotEqual(controlImageLeft.Value(), 0.0)) {
584         controlImageLeft = DEFAULT_CONTROL_BUTTON_LEFT;
585     }
586 
587     if (LessNotEqual(controlImageTop.Value(), 0.0)) {
588         controlImageTop = DEFAULT_CONTROL_BUTTON_TOP;
589     }
590     auto controlButtonLeft = controlImageLeft - CONTROL_BUTTON_PADDING;
591     auto controlButtonTop = controlImageTop - CONTROL_BUTTON_PADDING;
592 
593     auto controlButtonLeftPx = ConvertToPx(controlButtonLeft, scaleProperty, parentWidth).value_or(0);
594     auto controlButtonTopPx = ConvertToPx(controlButtonTop, scaleProperty, parentWidth).value_or(0);
595     controlButtonLeftPx += padding.left.value_or(0);
596     controlButtonTopPx += padding.top.value_or(0);
597     /*
598      * Control buttion left position need to special handle:
599      *   1. when sideBarPosition set to END and controlButtonLeft do not set in ButtonStyle
600      *   control button need to move follow the sidebar to the right
601      *   2. when sideBarPosition set to START or controlButtonLeft has set by user
602      *   control button keep before handle
603      *   3. if the controlButtonLeft has set, whether sideBarPosition set to START or END
604      *   control button offset the left, if value invalid set to default 16vp
605      */
606     auto sideBarPosition = GetSideBarPositionWithRtl(layoutProperty);
607     auto controlButtonWidth = controlImageWidth_ + CONTROL_BUTTON_PADDING * 2;
608 
609     if ((sideBarPosition == SideBarPosition::END) &&             // sideBarPosition is End, other pass
610         (!layoutProperty->GetControlButtonLeft().has_value())) { // origin value has not set
611         auto defaultControlButtonLeft = DEFAULT_CONTROL_BUTTON_LEFT - CONTROL_BUTTON_PADDING;
612         auto defaultControlButtonLeftPx = ConvertToPx(defaultControlButtonLeft, scaleProperty, parentWidth).value_or(0)
613             + padding.right.value_or(0);
614         auto controlButtonWidthPx = ConvertToPx(controlButtonWidth, scaleProperty, parentWidth).value_or(0);
615         controlButtonLeftPx = parentWidth - defaultControlButtonLeftPx - controlButtonWidthPx;
616     }
617 
618     auto imgOffset = OffsetF(controlButtonLeftPx, controlButtonTopPx);
619     buttonLayoutWrapper->GetGeometryNode()->SetMarginFrameOffset(imgOffset);
620     buttonLayoutWrapper->Layout();
621 }
622 
LayoutSideBar(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & sideBarLayoutWrapper)623 void SideBarContainerLayoutAlgorithm::LayoutSideBar(
624     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& sideBarLayoutWrapper)
625 {
626     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
627     CHECK_NULL_VOID(layoutProperty);
628 
629     CHECK_NULL_VOID(layoutWrapper->GetGeometryNode());
630     auto parentWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
631     auto sideBarPosition = GetSideBarPositionWithRtl(layoutProperty);
632     const auto& padding = layoutProperty->CreatePaddingAndBorder();
633     float sideBarOffsetX = padding.left.value_or(0);
634     float sideBarOffsetY = padding.top.value_or(0);
635 
636     switch (sideBarStatus_) {
637         case SideBarStatus::SHOW:
638             if (sideBarPosition == SideBarPosition::END) {
639                 sideBarOffsetX = parentWidth - realSideBarWidth_ - padding.right.value_or(0);
640             }
641             break;
642         case SideBarStatus::HIDDEN:
643             if (sideBarPosition == SideBarPosition::START) {
644                 sideBarOffsetX = -(realSideBarWidth_ + realDividerWidth_ - padding.left.value_or(0));
645             } else {
646                 sideBarOffsetX = parentWidth + realDividerWidth_ - padding.right.value_or(0);
647             }
648             break;
649         case SideBarStatus::CHANGING:
650             if (sideBarPosition == SideBarPosition::START) {
651                 sideBarOffsetX = currentOffset_ + padding.left.value_or(0);
652             } else {
653                 sideBarOffsetX = parentWidth - padding.right.value_or(0) + currentOffset_;
654             }
655             break;
656         default:
657             break;
658     }
659 
660     sideBarOffset_ = OffsetF(sideBarOffsetX, sideBarOffsetY);
661     sideBarLayoutWrapper->GetGeometryNode()->SetMarginFrameOffset(sideBarOffset_);
662     sideBarLayoutWrapper->Layout();
663 }
664 
LayoutSideBarContent(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & contentLayoutWrapper)665 void SideBarContainerLayoutAlgorithm::LayoutSideBarContent(
666     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& contentLayoutWrapper)
667 {
668     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
669     CHECK_NULL_VOID(layoutProperty);
670 
671     auto sideBarPosition = GetSideBarPositionWithRtl(layoutProperty);
672     const auto& padding = layoutProperty->CreatePaddingAndBorder();
673     float contentOffsetX = padding.left.value_or(0);
674     float contentOffsetY = padding.top.value_or(0);
675 
676     if (type_ == SideBarContainerType::EMBED && sideBarPosition == SideBarPosition::START) {
677         if (sideBarStatus_ == SideBarStatus::SHOW) {
678             contentOffsetX = realSideBarWidth_ + realDividerWidth_ + padding.left.value_or(0);
679         } else if (sideBarStatus_ == SideBarStatus::CHANGING) {
680             contentOffsetX = realSideBarWidth_ + realDividerWidth_ + currentOffset_ + padding.left.value_or(0);
681         }
682     }
683 
684     auto contentOffset = OffsetF(contentOffsetX, contentOffsetY);
685     contentLayoutWrapper->GetGeometryNode()->SetMarginFrameOffset(contentOffset);
686     contentLayoutWrapper->Layout();
687 }
688 
LayoutDivider(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & dividerLayoutWrapper)689 void SideBarContainerLayoutAlgorithm::LayoutDivider(
690     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& dividerLayoutWrapper)
691 {
692     CHECK_NULL_VOID(layoutWrapper);
693     CHECK_NULL_VOID(dividerLayoutWrapper);
694     auto layoutProperty = AceType::DynamicCast<SideBarContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
695     CHECK_NULL_VOID(layoutProperty);
696 
697     auto sideBarPosition = GetSideBarPositionWithRtl(layoutProperty);
698 
699     CHECK_NULL_VOID(layoutWrapper->GetGeometryNode());
700     auto parentWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
701     auto constraint = layoutProperty->GetLayoutConstraint();
702     CHECK_NULL_VOID(constraint);
703     auto scaleProperty = constraint->scaleProperty;
704 
705     auto dividerStartMargin = layoutProperty->GetDividerStartMargin().value_or(DEFAULT_DIVIDER_START_MARGIN);
706     auto dividerStartMarginPx = ConvertToPx(dividerStartMargin, scaleProperty, parentWidth).value_or(0);
707     const auto& padding = layoutProperty->CreatePaddingAndBorder();
708     float dividerOffsetX = padding.left.value_or(0);
709     float dividerOffsetY = padding.top.value_or(0);
710 
711     switch (sideBarStatus_) {
712         case SideBarStatus::SHOW:
713             if (sideBarPosition == SideBarPosition::START) {
714                 dividerOffsetX = realSideBarWidth_ + padding.left.value_or(0);
715             } else {
716                 dividerOffsetX = parentWidth - realSideBarWidth_ - realDividerWidth_ - padding.right.value_or(0);
717             }
718             break;
719         case SideBarStatus::HIDDEN:
720             if (sideBarPosition == SideBarPosition::START) {
721                 dividerOffsetX = -realDividerWidth_ + padding.left.value_or(0);
722             } else {
723                 dividerOffsetX = parentWidth - padding.right.value_or(0);
724             }
725             break;
726         case SideBarStatus::CHANGING:
727             if (sideBarPosition == SideBarPosition::START) {
728                 dividerOffsetX = realSideBarWidth_ + currentOffset_ + padding.left.value_or(0);
729             } else {
730                 dividerOffsetX = parentWidth - realDividerWidth_ + currentOffset_ - padding.right.value_or(0);
731             }
732             break;
733         default:
734             break;
735     }
736 
737     auto dividerOffset = OffsetF(dividerOffsetX, dividerStartMarginPx + dividerOffsetY);
738     CHECK_NULL_VOID(dividerLayoutWrapper->GetGeometryNode());
739     dividerLayoutWrapper->GetGeometryNode()->SetMarginFrameOffset(dividerOffset);
740     dividerLayoutWrapper->Layout();
741 }
742 
GetSideBarPositionWithRtl(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty)743 SideBarPosition SideBarContainerLayoutAlgorithm::GetSideBarPositionWithRtl(
744     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty)
745 {
746     auto sideBarPosition = layoutProperty->GetSideBarPosition().value_or(SideBarPosition::START);
747     if (layoutProperty->GetLayoutDirection() == TextDirection::RTL ||
748         AceApplicationInfo::GetInstance().IsRightToLeft()) {
749         sideBarPosition = (sideBarPosition == SideBarPosition::START) ? SideBarPosition::END : SideBarPosition::START;
750     }
751     return sideBarPosition;
752 }
753 } // namespace OHOS::Ace::NG
754