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