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