1 /*
2  * Copyright (c) 2024 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 #include "frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_utils.h"
16 
17 #include "base/utils/string_utils.h"
18 #include "core/components_ng/layout/layout_wrapper.h"
19 #include "core/components_ng/pattern/waterflow/water_flow_item_layout_property.h"
20 namespace OHOS::Ace::NG {
21 namespace {
22 const std::string UNIT_AUTO = "auto";
23 }
PreParseArgs(const std::string & args)24 std::string WaterFlowLayoutUtils::PreParseArgs(const std::string& args)
25 {
26     if (args.empty() || args.find(UNIT_AUTO) == std::string::npos) {
27         return args;
28     }
29     std::string rowsArgs;
30     std::vector<std::string> strs;
31     StringUtils::StringSplitter(args, ' ', strs);
32     std::string current;
33     size_t rowArgSize = strs.size();
34     for (size_t i = 0; i < rowArgSize; ++i) {
35         current = strs[i];
36         // "auto" means 1fr in waterflow
37         if (strs[i] == std::string(UNIT_AUTO)) {
38             current = "1fr";
39         }
40         rowsArgs += ' ' + current;
41     }
42     return rowsArgs;
43 }
44 
GetItemPosition(const RefPtr<WaterFlowLayoutInfo> & info,int32_t index,float mainGap)45 FlowItemPosition WaterFlowLayoutUtils::GetItemPosition(
46     const RefPtr<WaterFlowLayoutInfo>& info, int32_t index, float mainGap)
47 {
48     auto crossIndex = info->GetCrossIndex(index);
49     // already in layoutInfo
50     if (crossIndex != -1) {
51         return { crossIndex, info->GetStartMainPos(crossIndex, index) };
52     }
53     int32_t segment = info->GetSegment(index);
54     auto itemIndex = info->GetCrossIndexForNextItem(segment);
55     if (static_cast<int32_t>(info->segmentStartPos_.size()) <= segment) {
56         TAG_LOGI(AceLogTag::ACE_WATERFLOW, "The size of segmentStartPos %{public}zu is less than expected %{public}d.",
57             info->segmentStartPos_.size(), segment);
58         return { itemIndex.crossIndex, 0.0f };
59     }
60     if (itemIndex.lastItemIndex < 0) {
61         return { itemIndex.crossIndex, info->segmentStartPos_[segment] };
62     }
63     auto mainHeight = info->GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex);
64     return { itemIndex.crossIndex, mainHeight + mainGap };
65 }
66 
CreateChildConstraint(const ConstraintParams & params,const RefPtr<WaterFlowLayoutProperty> & props,const RefPtr<LayoutWrapper> & child)67 LayoutConstraintF WaterFlowLayoutUtils::CreateChildConstraint(
68     const ConstraintParams& params, const RefPtr<WaterFlowLayoutProperty>& props, const RefPtr<LayoutWrapper>& child)
69 {
70     auto itemConstraint = props->CreateChildConstraint();
71     auto itemMainSize = params.mainSize;
72     auto itemIdealSize =
73         params.axis == Axis::VERTICAL ? SizeF(params.crossSize, itemMainSize) : SizeF(itemMainSize, params.crossSize);
74 
75     itemConstraint.maxSize = itemIdealSize;
76     itemConstraint.maxSize.SetMainSize(Infinity<float>(), params.axis);
77     itemConstraint.percentReference = itemIdealSize;
78 
79     CHECK_NULL_RETURN(props->HasItemLayoutConstraint() && !params.haveUserDefSize, itemConstraint);
80 
81     OptionalSizeF childMinSize;
82     OptionalSizeF childMaxSize;
83     // Waterflow ItemLayoutConstraint
84     auto itemMinSize = props->GetItemMinSize();
85     if (itemMinSize.has_value()) {
86         childMinSize =
87             ConvertToOptionalSize(itemMinSize.value(), props->GetLayoutConstraint()->scaleProperty, itemIdealSize);
88     }
89     auto itemMaxSize = props->GetItemMaxSize();
90     if (itemMaxSize.has_value()) {
91         childMaxSize =
92             ConvertToOptionalSize(itemMaxSize.value(), props->GetLayoutConstraint()->scaleProperty, itemIdealSize);
93     }
94 
95     if (childMaxSize.AtLeastOneValid()) {
96         itemConstraint.maxSize.UpdateSizeWhenSmaller(childMaxSize.ConvertToSizeT());
97     }
98     if (childMinSize.AtLeastOneValid()) {
99         itemConstraint.minSize.UpdateSizeWhenLarger(childMinSize.ConvertToSizeT());
100     }
101 
102     // FlowItem layoutConstraint
103     CHECK_NULL_RETURN(child, itemConstraint);
104     auto childLayoutProperty = AceType::DynamicCast<WaterFlowItemLayoutProperty>(child->GetLayoutProperty());
105     CHECK_NULL_RETURN(childLayoutProperty, itemConstraint);
106     if (childLayoutProperty->HasLayoutConstraint()) {
107         if (childLayoutProperty->GetMaxSize().has_value()) {
108             itemConstraint.UpdateMaxSizeWithCheck(ConvertToSize(childLayoutProperty->GetMaxSize().value(),
109                 itemConstraint.scaleProperty, itemConstraint.percentReference));
110         }
111         if (childLayoutProperty->GetMinSize().has_value()) {
112             itemConstraint.UpdateMinSizeWithCheck(ConvertToSize(childLayoutProperty->GetMinSize().value(),
113                 itemConstraint.scaleProperty, itemConstraint.percentReference));
114         }
115     }
116 
117     childLayoutProperty->ResetCalcMinSize();
118     childLayoutProperty->ResetCalcMaxSize();
119 
120     childLayoutProperty->UpdateItemCalcMaxSize(CalcSize(CalcLength(itemConstraint.maxSize.Width(), DimensionUnit::PX),
121         CalcLength(itemConstraint.maxSize.Height(), DimensionUnit::PX)));
122     childLayoutProperty->UpdateItemCalcMinSize(CalcSize(CalcLength(itemConstraint.minSize.Width(), DimensionUnit::PX),
123         CalcLength(itemConstraint.minSize.Height(), DimensionUnit::PX)));
124 
125     return itemConstraint;
126 }
127 
PreMeasureSelf(LayoutWrapper * wrapper,Axis axis)128 std::pair<SizeF, bool> WaterFlowLayoutUtils::PreMeasureSelf(LayoutWrapper* wrapper, Axis axis)
129 {
130     const auto& props = wrapper->GetLayoutProperty();
131     auto size = CreateIdealSize(props->GetLayoutConstraint().value(), axis, props->GetMeasureType(), true);
132     auto matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(size, axis));
133     if (!matchChildren) {
134         wrapper->GetGeometryNode()->SetFrameSize(size);
135     }
136     MinusPaddingToSize(props->CreatePaddingAndBorder(), size);
137     wrapper->GetGeometryNode()->SetContentSize(size);
138     return { size, matchChildren };
139 }
140 
MeasureFooter(LayoutWrapper * wrapper,Axis axis)141 float WaterFlowLayoutUtils::MeasureFooter(LayoutWrapper* wrapper, Axis axis)
142 {
143     auto footer = wrapper->GetOrCreateChildByIndex(0);
144     CHECK_NULL_RETURN(footer, 0.0f);
145     auto layoutProperty = wrapper->GetLayoutProperty();
146     auto footerConstraint = layoutProperty->CreateChildConstraint();
147     footer->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_CONTENT);
148     footer->Measure(footerConstraint);
149     auto itemSize = footer->GetGeometryNode()->GetMarginFrameSize();
150     return GetMainAxisSize(itemSize, axis);
151 }
152 
GetUserDefHeight(const RefPtr<WaterFlowSections> & sections,int32_t seg,int32_t idx)153 float WaterFlowLayoutUtils::GetUserDefHeight(const RefPtr<WaterFlowSections>& sections, int32_t seg, int32_t idx)
154 {
155     CHECK_NULL_RETURN(sections, -1.0f);
156     const auto& section = sections->GetSectionInfo()[seg];
157     if (section.onGetItemMainSizeByIndex) {
158         Dimension len(section.onGetItemMainSizeByIndex(idx), DimensionUnit::VP);
159         if (len.IsNegative()) {
160             return 0.0f;
161         }
162         return len.ConvertToPx();
163     }
164     return -1.0f;
165 }
166 
UpdateItemIdealSize(const RefPtr<LayoutWrapper> & item,Axis axis,float userHeight)167 void WaterFlowLayoutUtils::UpdateItemIdealSize(const RefPtr<LayoutWrapper>& item, Axis axis, float userHeight)
168 {
169     auto props = item->GetLayoutProperty();
170     // get previously user defined ideal width
171     std::optional<CalcLength> crossSize;
172     const auto& layoutConstraint = props->GetCalcLayoutConstraint();
173     if (layoutConstraint && layoutConstraint->selfIdealSize) {
174         crossSize = axis == Axis::VERTICAL ? layoutConstraint->selfIdealSize->Width()
175                                            : layoutConstraint->selfIdealSize->Height();
176     }
177     props->UpdateUserDefinedIdealSize(axis == Axis::VERTICAL ? CalcSize(crossSize, CalcLength(userHeight))
178                                                              : CalcSize(CalcLength(userHeight), crossSize));
179 }
180 } // namespace OHOS::Ace::NG
181