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