1 /*
2  * Copyright (c) 2022 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/list/list_item_layout_algorithm.h"
17 
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/utils/utils.h"
20 #include "core/components_ng/layout/box_layout_algorithm.h"
21 #include "core/components_ng/pattern/list/list_item_layout_property.h"
22 #include "core/components_ng/property/measure_utils.h"
23 
24 namespace OHOS::Ace::NG {
25 
IsRTLAndVertical(LayoutWrapper * layoutWrapper) const26 bool ListItemLayoutAlgorithm::IsRTLAndVertical(LayoutWrapper* layoutWrapper) const
27 {
28     auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
29     if (layoutDirection == TextDirection::RTL && axis_ == Axis::VERTICAL) {
30         return true;
31     } else {
32         return false;
33     }
34 }
35 
SetReverseValue(LayoutWrapper * layoutWrapper,float offset)36 float ListItemLayoutAlgorithm::SetReverseValue(LayoutWrapper* layoutWrapper, float offset)
37 {
38     if (IsRTLAndVertical(layoutWrapper)) {
39         return -offset;
40     } else {
41         return offset;
42     }
43 }
44 
Measure(LayoutWrapper * layoutWrapper)45 void ListItemLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
46 {
47     layoutWrapper->RemoveAllChildInRenderTree();
48 
49     std::list<RefPtr<LayoutWrapper>> childList;
50     auto layoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
51     auto child = layoutWrapper->GetOrCreateChildByIndex(childNodeIndex_);
52     if (child) {
53         child->Measure(layoutConstraint);
54         childList.push_back(child);
55     }
56     PerformMeasureSelfWithChildList(layoutWrapper, childList);
57     auto mainSize = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
58     if (NonPositive(mainSize)) {
59         curOffset_ = 0.0f;
60         return;
61     }
62     if (Positive(curOffset_) && startNodeIndex_ >= 0) {
63         auto startLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
64         if (!NearZero(startNodeSize_) && curOffset_ > startNodeSize_) {
65             startLayoutConstraint.maxSize.SetCrossSize(curOffset_, axis_);
66             startLayoutConstraint.minSize.SetCrossSize(curOffset_, axis_);
67         }
68         startLayoutConstraint.maxSize.SetMainSize(mainSize, axis_);
69         startLayoutConstraint.percentReference.SetMainSize(mainSize, axis_);
70         auto startNode = layoutWrapper->GetOrCreateChildByIndex(startNodeIndex_);
71         CHECK_NULL_VOID(startNode);
72         startNode->Measure(startLayoutConstraint);
73         if (NearZero(startNodeSize_) || curOffset_ < endNodeSize_) {
74             startNodeSize_ = startNode->GetGeometryNode()->GetMarginFrameSize().CrossSize(axis_);
75         }
76         curOffset_ = NearZero(startNodeSize_) && !hasStartDeleteArea_ ? 0.0f : curOffset_;
77     } else if (Negative(curOffset_) && endNodeIndex_ >= 0) {
78         auto endLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
79         if (!NearZero(endNodeSize_) && -curOffset_ > endNodeSize_) {
80             endLayoutConstraint.maxSize.SetCrossSize(-curOffset_, axis_);
81             endLayoutConstraint.minSize.SetCrossSize(-curOffset_, axis_);
82         }
83         endLayoutConstraint.maxSize.SetMainSize(mainSize, axis_);
84         endLayoutConstraint.percentReference.SetMainSize(mainSize, axis_);
85         auto endNode = layoutWrapper->GetOrCreateChildByIndex(endNodeIndex_);
86         CHECK_NULL_VOID(endNode);
87         endNode->Measure(endLayoutConstraint);
88         if (NearZero(endNodeSize_) || -curOffset_ < endNodeSize_) {
89             endNodeSize_ = endNode->GetGeometryNode()->GetMarginFrameSize().CrossSize(axis_);
90         }
91         curOffset_ = NearZero(endNodeSize_) && !hasEndDeleteArea_ ? 0.0f : curOffset_;
92     }
93 }
94 
Layout(LayoutWrapper * layoutWrapper)95 void ListItemLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
96 {
97     // update child position.
98     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
99     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
100     MinusPaddingToSize(padding, size);
101     auto paddingOffset = padding.Offset();
102     auto align = Alignment::CENTER;
103     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
104         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
105     }
106     // Update child position.
107     if (Positive(curOffset_) && startNodeIndex_ >= 0) {
108         auto child = layoutWrapper->GetOrCreateChildByIndex(startNodeIndex_);
109         CHECK_NULL_VOID(child);
110         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
111         float crossOffset = IsRTLAndVertical(layoutWrapper) ?
112             (size.CrossSize(axis_) - curOffset_) : (curOffset_ - childSize.CrossSize(axis_));
113         float mainOffset = (size.MainSize(axis_) - childSize.MainSize(axis_)) / 2;
114         OffsetF offset = axis_ == Axis::VERTICAL ? OffsetF(crossOffset, mainOffset) : OffsetF(mainOffset, crossOffset);
115         child->GetGeometryNode()->SetMarginFrameOffset(paddingOffset + offset);
116         child->Layout();
117     } else if (Negative(curOffset_) && endNodeIndex_ >= 0) {
118         auto child = layoutWrapper->GetOrCreateChildByIndex(endNodeIndex_);
119         CHECK_NULL_VOID(child);
120         auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
121         float crossOffset = IsRTLAndVertical(layoutWrapper) ?
122             (-curOffset_ - childSize.CrossSize(axis_)) : (size.CrossSize(axis_) + curOffset_);
123         float mainOffset = (size.MainSize(axis_) - childSize.MainSize(axis_)) / 2;
124         OffsetF offset = axis_ == Axis::VERTICAL ? OffsetF(crossOffset, mainOffset) : OffsetF(mainOffset, crossOffset);
125         child->GetGeometryNode()->SetMarginFrameOffset(paddingOffset + offset);
126         child->Layout();
127     }
128     auto child = layoutWrapper->GetOrCreateChildByIndex(childNodeIndex_);
129     if (child) {
130         auto translate =
131             Alignment::GetAlignPosition(size, child->GetGeometryNode()->GetMarginFrameSize(), align) + paddingOffset;
132         OffsetF offset = axis_ == Axis::VERTICAL ? OffsetF(SetReverseValue(layoutWrapper, curOffset_), 0.0f) :
133             OffsetF(0.0f, curOffset_);
134         child->GetGeometryNode()->SetMarginFrameOffset(translate + offset);
135         child->Layout();
136     }
137     // Update content position.
138     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
139     if (content) {
140         auto translate = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
141         content->SetOffset(translate);
142     }
143 }
144 } // namespace OHOS::Ace::NG