1 /*
2  * Copyright (c) 2021 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/list/list_watch_layout_manager.h"
17 
18 #include "core/components/list/render_list_item_group.h"
19 
20 namespace OHOS::Ace {
21 
22 namespace {
23 
24 const double HALF_SIZE = 0.5;
25 
26 } // namespace
27 
ListWatchLayoutManager(RenderList & renderList)28 ListWatchLayoutManager::ListWatchLayoutManager(RenderList& renderList)
29     : ListLayoutManager(renderList) {}
30 
PerformLayout()31 void ListWatchLayoutManager::PerformLayout()
32 {
33     renderList_.SetCenterIndex(INVALID_INDEX);
34     if (renderList_.GetAddDeleteEffect()) {
35         AnimationForItemUpdate();
36     } else {
37         RefreshLayout();
38     }
39 
40     int32_t itemIndex = GetIndexByPosition(head_);
41     renderList_.RecycleHead(itemIndex - 1); // Recycle head items.
42     double curMainSize = GetItemPosition(itemIndex);
43     LayoutParam innerLayout = MakeInnerLayoutParam(crossAxisAlign_);
44     auto itemChild = renderList_.GetChildByIndex(itemIndex);
45     int32_t firstIndex = itemIndex;
46     while (itemChild) {
47         UpdateItemGroupAttr(itemChild);
48         itemChild->Layout(AdjustLayoutParam(itemChild, innerLayout));
49         if (itemIndex == ZERO_INDEX) {
50             firstChildSize_ = renderList_.GetMainSize(itemChild->GetLayoutSize());
51         }
52         lastChildSize_ = renderList_.GetMainSize(itemChild->GetLayoutSize());
53 
54         SetChildPosition(itemChild, itemIndex, curMainSize);
55         itemPosition_[itemIndex] = curMainSize;
56         curMainSize += renderList_.GetMainSize(itemChild->GetLayoutSize());
57         if (curMainSize >= tail_) {
58             break;
59         }
60         CalculateItemState(itemChild);
61         itemChild = renderList_.GetChildByIndex(++itemIndex);
62     }
63     RequestMoreItemsIfNeeded(firstIndex, itemIndex);
64     renderList_.RecycleTail(itemIndex + 1); // Recycle tail items.
65 
66     if (renderList_.GetCenterIndex() == INVALID_INDEX) {
67         MarkAllItemBlur();
68     }
69     if (renderList_.IsCenterLayout() && firstChildSize_ < renderList_.GetMainSize(viewPort_)) {
70         if (NearZero(lastChildSize_)) {
71             lastChildSize_ = firstChildSize_;
72         }
73         curMainSize = AdjustLayoutSize(curMainSize);
74     }
75 
76     Size layoutSize = renderList_.MakeValue<Size>(curMainSize, renderList_.GetCrossSize(viewPort_));
77     renderList_.SetLayoutSize(layoutSize);
78     renderList_.CalculateStickyItem(position_);
79     ShowItemFocusAnimation();
80     HandleItemStateAndEffect();
81     firstLayout_ = false;
82 }
83 
SetChildPosition(const RefPtr<RenderNode> & child,int32_t index,double mainSize)84 void ListWatchLayoutManager::SetChildPosition(const RefPtr<RenderNode>& child, int32_t index, double mainSize)
85 {
86     double mainLen = renderList_.GetMainSize(viewPort_);
87     double crossLen = renderList_.GetCrossSize(viewPort_);
88     double mainAxis = mainSize;
89     double crossAxis = 0.0;
90     auto listItemChild = RenderListItem::GetRenderListItem(child);
91     FlexAlign selfAlign = listItemChild ? listItemChild->GetSelfAlign() : FlexAlign::AUTO;
92     FlexAlign align = selfAlign == FlexAlign::AUTO ? crossAxisAlign_ : selfAlign;
93     if (rightToLeft_) {
94         if (align == FlexAlign::FLEX_END) {
95             align = FlexAlign::FLEX_START;
96         } else if (align == FlexAlign::FLEX_START) {
97             align = FlexAlign::FLEX_END;
98         }
99     }
100     switch (align) {
101         case FlexAlign::FLEX_END:
102             crossAxis = crossLen - renderList_.GetCrossSize(child->GetLayoutSize());
103             break;
104         case FlexAlign::CENTER:
105             crossAxis = (crossLen - renderList_.GetCrossSize(child->GetLayoutSize())) / 2.0;
106             break;
107         case FlexAlign::STRETCH:
108         case FlexAlign::FLEX_START:
109         default:
110             break;
111     }
112     auto isPlatformFive = IsPlatformFive();
113 
114     if (isVertical_) {
115         if (direction_ == FlexDirection::COLUMN_REVERSE) {
116             mainAxis = mainLen - renderList_.GetMainSize(child->GetLayoutSize()) - mainAxis;
117         }
118 
119         if (isPlatformFive && renderList_.IsCenterLayout() && firstChildSize_ < renderList_.GetMainSize(viewPort_)) {
120             mainAxis += (renderList_.GetMainSize(viewPort_) - firstChildSize_) * HALF_SIZE;
121         }
122         child->SetPosition(Offset(crossAxis, mainAxis) + position_);
123     } else {
124         if (IsRowReverse()) {
125             mainAxis = mainLen - renderList_.GetMainSize(child->GetLayoutSize()) - mainAxis;
126         }
127         if (isPlatformFive && renderList_.IsCenterLayout() && firstChildSize_ < renderList_.GetMainSize(viewPort_)) {
128             mainAxis += (renderList_.GetMainSize(viewPort_) - firstChildSize_) * HALF_SIZE;
129         }
130         child->SetPosition(Offset(mainAxis, crossAxis) + position_);
131     }
132 }
133 
HandleItemStateAndEffect()134 void ListWatchLayoutManager::HandleItemStateAndEffect()
135 {
136     auto isFromRotate = false;
137     auto parentScroll = AceType::DynamicCast<RenderScroll>(renderList_.GetParent().Upgrade());
138     if (parentScroll) {
139         isFromRotate = parentScroll->IsFromRotate();
140     }
141     RefPtr<RenderListItem> listItem;
142     for (const auto& item : renderList_.GetItems()) {
143         if (!item.second || item.second->GetChildren().empty()) {
144             continue;
145         }
146 
147         listItem = AceType::DynamicCast<RenderListItem>(item.second->GetChildren().front());
148         if (!listItem) {
149             break;
150         }
151         auto index = listItem->GetIndex();
152         auto centerIndex = renderList_.GetCenterIndex();
153         if (index == centerIndex - 1 || index == centerIndex + 1 ||
154             (centerIndex == INVALID_INDEX && index == renderList_.GetCurrentMaxIndex())) {
155             listItem->SetCurrentState(ItemState::NEARBY);
156         }
157         listItem->HandleItemEffect(isFromRotate);
158     }
159 }
160 
CalculateItemState(RefPtr<RenderNode> & item)161 void ListWatchLayoutManager::CalculateItemState(RefPtr<RenderNode>& item)
162 {
163     auto centerItem = RenderListItem::GetRenderListItem(item);
164     auto isVertical = direction_ == FlexDirection::COLUMN || direction_ == FlexDirection::COLUMN_REVERSE;
165     if (centerItem) {
166         if (centerItem->IsItemCenter(isVertical, viewPort_)) {
167             centerItem->SetCurrentState(ItemState::FOCUS);
168             renderList_.SetCenterIndex(centerItem->GetIndex());
169         } else {
170             centerItem->SetCurrentState(ItemState::BLUR);
171         }
172         centerItem->CalculateScaleFactorOnWatch();
173     }
174 }
175 
MarkAllItemBlur()176 void ListWatchLayoutManager::MarkAllItemBlur()
177 {
178     RefPtr<RenderListItem> listItem;
179     for (const auto& item : renderList_.GetItems()) {
180         if (!item.second || item.second->GetChildren().empty()) {
181             continue;
182         }
183 
184         listItem = AceType::DynamicCast<RenderListItem>(item.second->GetChildren().front());
185         if (!listItem) {
186             break;
187         } else {
188             listItem->SetCurrentState(ItemState::BLUR);
189         }
190     }
191     renderList_.SetCenterIndex(INVALID_INDEX);
192 }
193 
GetFirstChildSize(RefPtr<RenderNode> & itemChild)194 double ListWatchLayoutManager::GetFirstChildSize(RefPtr<RenderNode>& itemChild)
195 {
196     auto renderListItem = RenderListItem::GetRenderListItem(itemChild);
197     if (!renderListItem) {
198         return renderList_.GetMainSize(itemChild->GetLayoutSize());
199     }
200     auto renderListItemGroup = AceType::DynamicCast<RenderListItemGroup>(renderListItem);
201     if (!renderListItemGroup) {
202         return renderList_.GetMainSize(itemChild->GetLayoutSize());
203     }
204     auto children = renderListItemGroup->GetChildren();
205     auto rIter = children.rbegin();
206     while (rIter != children.rend()) {
207         auto child = *rIter++;
208         auto listItemNode = RenderListItem::GetRenderListItem(child);
209         if (listItemNode && listItemNode->GetPrimary()) {
210             return renderList_.GetMainSize(child->GetLayoutSize());
211         }
212     }
213     return renderList_.GetMainSize(itemChild->GetLayoutSize());
214 }
215 
AdjustLayoutSize(double size)216 double ListWatchLayoutManager::AdjustLayoutSize(double size)
217 {
218     if (!renderList_.IsCenterLayout()) {
219         return size;
220     }
221     if (NearZero(lastChildSize_)) {
222         lastChildSize_ = firstChildSize_;
223     }
224     if (IsPlatformFive()) {
225         double changeSize = size + renderList_.GetMainSize(viewPort_);
226         changeSize = changeSize - (firstChildSize_ + lastChildSize_) * HALF_SIZE;
227         return changeSize;
228     } else {
229         return size + (renderList_.GetMainSize(viewPort_) - lastChildSize_) * HALF_SIZE;
230     }
231 }
232 
IsPlatformFive()233 bool ListWatchLayoutManager::IsPlatformFive()
234 {
235     auto context = renderList_.GetContext().Upgrade();
236     const static int32_t PLATFORM_VERSION_FIVE = 5;
237     return context && context->GetMinPlatformVersion() <= PLATFORM_VERSION_FIVE;
238 }
239 
240 } // namespace OHOS::Ace