1 /*
2 * Copyright (c) 2022-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
16 #include "core/components_ng/pattern/indexer/indexer_layout_algorithm.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/size_t.h"
20 #include "base/utils/utils.h"
21 #include "core/components/common/layout/layout_param.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/base/view_stack_processor.h"
24 #include "core/components_ng/layout/layout_wrapper.h"
25 #include "core/components_ng/pattern/indexer/indexer_pattern.h"
26 #include "core/components_ng/pattern/indexer/indexer_theme.h"
27 #include "core/components_ng/pattern/text/text_model.h"
28 #include "core/components_ng/pattern/text/text_pattern.h"
29 #include "core/components_ng/property/layout_constraint.h"
30 #include "core/components_ng/property/measure_property.h"
31 #include "core/components_ng/property/measure_utils.h"
32
33 namespace OHOS::Ace::NG {
34
Measure(LayoutWrapper * layoutWrapper)35 void IndexerLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
36 {
37 auto indexerLayoutProperty = AceType::DynamicCast<IndexerLayoutProperty>(layoutWrapper->GetLayoutProperty());
38 CHECK_NULL_VOID(indexerLayoutProperty);
39 auto childCount = layoutWrapper->GetTotalChildCount();
40 if (indexerLayoutProperty->GetIsPopupValue(false)) {
41 MeasurePopup(layoutWrapper, childCount);
42 childCount--;
43 }
44
45 LayoutConstraintF layoutConstraint;
46 if (indexerLayoutProperty->GetLayoutConstraint().has_value()) {
47 layoutConstraint = indexerLayoutProperty->GetLayoutConstraint().value();
48 }
49 OptionalSize<float> selfIdealSize = layoutConstraint.selfIdealSize;
50 Dimension itemSize = indexerLayoutProperty->GetItemSize().value_or(Dimension(INDEXER_ITEM_SIZE, DimensionUnit::VP));
51 itemSize_ = ConvertToPx(itemSize, layoutConstraint.scaleProperty, layoutConstraint.maxSize.Height()).value();
52
53 auto defaultHorizontalPadding = Dimension(INDEXER_PADDING_LEFT, DimensionUnit::VP).ConvertToPx();
54 auto defaultVerticalPadding = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
55 ? Dimension(INDEXER_PADDING_TOP_API_TWELVE, DimensionUnit::VP).ConvertToPx()
56 : Dimension(INDEXER_PADDING_TOP, DimensionUnit::VP).ConvertToPx();
57 const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorderWithDefault(
58 static_cast<float>(defaultHorizontalPadding), static_cast<float>(defaultVerticalPadding), 0, 0);
59 auto verticalPadding = (padding.top.value_or(0) + padding.bottom.value_or(0));
60 auto horizontalPadding = padding.left.value_or(0.0f) + padding.right.value_or(0.0f);
61
62 auto adaptiveWidth = indexerLayoutProperty->GetAdaptiveWidthValue(false);
63 auto maxItemWidth = adaptiveWidth ? GetMaxItemWidth(layoutWrapper) : 0.0f;
64 auto contentWidth = adaptiveWidth ? (GreatOrEqual(maxItemWidth, itemSize_) ? maxItemWidth : itemSize_) : itemSize_;
65 auto frameWidth = selfIdealSize.Width().has_value()
66 ? selfIdealSize.Width().value()
67 : std::clamp(contentWidth + horizontalPadding, 0.0f, layoutConstraint.maxSize.Width());
68 itemWidth_ = GreatNotEqual(frameWidth - horizontalPadding, 0.0f) ? frameWidth - horizontalPadding : 0.0f;
69
70 auto contentHeight = childCount * itemSize_;
71 float maxFrameHeight =
72 selfIdealSize.Height().has_value() ? selfIdealSize.Height().value() : layoutConstraint.maxSize.Height();
73 maxContentHeight_ = GreatNotEqual(maxFrameHeight - verticalPadding, 0.0f) ? maxFrameHeight - verticalPadding : 0.0f;
74 contentHeight = GreatNotEqual(contentHeight, maxContentHeight_) ? maxContentHeight_ : contentHeight;
75 itemHeight_ = GreatNotEqual(contentHeight, 0.0f) && childCount > 0 ? contentHeight / childCount : 0.0f;
76 float frameHeight = selfIdealSize.Height().has_value()
77 ? selfIdealSize.Height().value()
78 : std::clamp(contentHeight + verticalPadding, 0.0f, layoutConstraint.maxSize.Height());
79
80 auto childLayoutConstraint = indexerLayoutProperty->CreateChildConstraint();
81 for (int32_t index = 0; index < childCount; index++) {
82 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
83 CHECK_NULL_VOID(childWrapper);
84 childLayoutConstraint.UpdateSelfMarginSizeWithCheck(OptionalSizeF(itemWidth_, itemHeight_));
85 childWrapper->Measure(childLayoutConstraint);
86 }
87
88 layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(frameWidth, frameHeight));
89 }
90
Layout(LayoutWrapper * layoutWrapper)91 void IndexerLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
92 {
93 CHECK_NULL_VOID(layoutWrapper);
94 auto layoutProperty = DynamicCast<IndexerLayoutProperty>(layoutWrapper->GetLayoutProperty());
95 CHECK_NULL_VOID(layoutProperty);
96 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
97 auto defaultHorizontalPadding = Dimension(INDEXER_PADDING_LEFT, DimensionUnit::VP).ConvertToPx();
98 auto defaultVerticalPadding = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
99 ? Dimension(INDEXER_PADDING_TOP_API_TWELVE, DimensionUnit::VP).ConvertToPx()
100 : Dimension(INDEXER_PADDING_TOP, DimensionUnit::VP).ConvertToPx();
101 const auto& padding = layoutProperty->CreatePaddingAndBorderWithDefault(
102 static_cast<float>(defaultHorizontalPadding), static_cast<float>(defaultVerticalPadding), 0, 0);
103 auto size = frameSize;
104 MinusPaddingToSize(padding, size);
105 auto left = padding.left.value_or(0);
106 auto top = padding.top.value_or(0);
107 auto paddingOffset = OffsetF(left, top);
108 auto align = Alignment::CENTER;
109 if (layoutProperty->GetPositionProperty()) {
110 align = layoutProperty->GetPositionProperty()->GetAlignment().value_or(align);
111 }
112 SizeF contentSize(itemWidth_, 0);
113 auto childCount = layoutWrapper->GetTotalChildCount();
114 if (layoutProperty->GetIsPopupValue(false)) {
115 const auto& child = layoutWrapper->GetChildByIndex(childCount - 1);
116 auto offset = GetPositionOfPopupNode(layoutProperty, frameSize.Width());
117 child->GetHostNode()->GetRenderContext()->UpdatePosition(offset);
118 child->Layout();
119 childCount -= 1;
120 }
121 for (int32_t i = 0; i < childCount; i++) {
122 const auto& child = layoutWrapper->GetChildByIndex(i);
123 SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
124 contentSize.AddHeight(childSize.Height());
125 }
126 auto translate = Alignment::GetAlignPosition(size, contentSize, align);
127 for (int32_t i = 0; i < childCount; i++) {
128 const auto& child = layoutWrapper->GetChildByIndex(i);
129 child->GetGeometryNode()->SetMarginFrameOffset(
130 translate + paddingOffset + OffsetF(0, itemHeight_ * i));
131 child->Layout();
132 }
133 }
134
GetPositionOfPopupNode(const RefPtr<IndexerLayoutProperty> & layoutProperty,float indexerWidth)135 OffsetT<Dimension> IndexerLayoutAlgorithm::GetPositionOfPopupNode(
136 const RefPtr<IndexerLayoutProperty>& layoutProperty, float indexerWidth)
137 {
138 auto alignment = layoutProperty->GetAlignStyle().value_or(NG::AlignStyle::END);
139 auto userDefinePositionX =
140 layoutProperty->GetPopupPositionX().value_or(Dimension(NG::BUBBLE_POSITION_X, DimensionUnit::VP)).ConvertToPx();
141 auto userDefinePositionY =
142 layoutProperty->GetPopupPositionY().value_or(Dimension(NG::BUBBLE_POSITION_Y, DimensionUnit::VP)).ConvertToPx();
143 auto userDefineSpace = layoutProperty->GetPopupHorizontalSpace();
144
145 auto padding = layoutProperty->CreatePaddingWithoutBorder();
146 auto left = padding.left.value_or(0);
147 auto top = padding.top.value_or(0);
148 if (IsPopupAtLeft(layoutProperty, alignment)) {
149 userDefinePositionX = (userDefineSpace ? userDefineSpace.value().ConvertToPx() + indexerWidth
150 : userDefinePositionX + indexerWidth / 2) - left;
151 } else {
152 auto bubbleSize = Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
153 userDefinePositionX = (userDefineSpace ? -userDefineSpace.value().ConvertToPx()
154 : -userDefinePositionX + indexerWidth / 2) - left - bubbleSize;
155 }
156 userDefinePositionY -= top;
157 return OffsetT<Dimension>(Dimension(userDefinePositionX), Dimension(userDefinePositionY));
158 }
159
IsPopupAtLeft(const RefPtr<IndexerLayoutProperty> & layoutProperty,NG::AlignStyle alignment) const160 bool IndexerLayoutAlgorithm::IsPopupAtLeft(
161 const RefPtr<IndexerLayoutProperty>& layoutProperty, NG::AlignStyle alignment) const
162 {
163 auto layoutDirection = layoutProperty->GetNonAutoLayoutDirection();
164 bool isRtl = layoutDirection == TextDirection::RTL;
165 if (alignment == NG::AlignStyle::LEFT || (alignment == NG::AlignStyle::START && (!isRtl)) ||
166 (alignment == NG::AlignStyle::END && isRtl)) {
167 return true;
168 } else {
169 return false;
170 }
171 }
172
GetMaxItemWidth(LayoutWrapper * layoutWrapper)173 float IndexerLayoutAlgorithm::GetMaxItemWidth(LayoutWrapper* layoutWrapper)
174 {
175 auto indexerLayoutProperty = AceType::DynamicCast<IndexerLayoutProperty>(layoutWrapper->GetLayoutProperty());
176 CHECK_NULL_RETURN(indexerLayoutProperty, INDEXER_ZERO_WIDTH);
177 auto childLayoutConstraint = indexerLayoutProperty->CreateChildConstraint();
178 auto maxItemWidth = INDEXER_ZERO_WIDTH;
179 auto childCount = layoutWrapper->GetTotalChildCount();
180 if (indexerLayoutProperty->GetIsPopupValue(false)) {
181 childCount--;
182 }
183 for (int32_t index = 0; index < childCount; index++) {
184 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
185 CHECK_NULL_RETURN(childWrapper, INDEXER_ZERO_WIDTH);
186 childWrapper->Measure(childLayoutConstraint);
187 auto node = childWrapper->GetGeometryNode();
188 auto size = node->GetFrameSize();
189 if (size.Width() > maxItemWidth) {
190 maxItemWidth = size.Width();
191 }
192 }
193 return maxItemWidth;
194 }
195
MeasurePopup(LayoutWrapper * layoutWrapper,uint32_t childCount)196 void IndexerLayoutAlgorithm::MeasurePopup(LayoutWrapper* layoutWrapper, uint32_t childCount)
197 {
198 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(childCount - 1);
199 CHECK_NULL_VOID(childWrapper);
200 auto childLayoutProperty = AceType::DynamicCast<LinearLayoutProperty>(childWrapper->GetLayoutProperty());
201 CHECK_NULL_VOID(childLayoutProperty);
202 auto layoutConstraint = childLayoutProperty->GetLayoutConstraint();
203 layoutConstraint->Reset();
204 childWrapper->Measure(layoutConstraint);
205 }
206 } // namespace OHOS::Ace::NG
207