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