• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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_v2/list/render_list_item.h"
17 
18 #include "core/components/box/box_component.h"
19 #include "core/components/box/render_box.h"
20 #include "core/components/button/button_component.h"
21 #include "core/components/image/image_component.h"
22 #include "core/components_v2/list/render_list.h"
23 
24 namespace OHOS::Ace::V2 {
25 namespace {
26 
27 constexpr Dimension ICON_WIDTH = 24.0_px;
28 constexpr Dimension BUTTON_WIDTH = 72.0_px;
29 
30 constexpr double SWIPER_TH = 0.25;
31 constexpr double SWIPER_SPEED_TH = 1200;
32 constexpr double SWIPE_RATIO = 0.6;
33 constexpr double SWIPE_SPRING_MASS = 1;
34 constexpr double SWIPE_SPRING_STIFFNESS = 228;
35 constexpr double SWIPE_SPRING_DAMPING = 30;
36 
37 } // namespace
38 
Create()39 RefPtr<RenderNode> RenderListItem::Create()
40 {
41     return AceType::MakeRefPtr<RenderListItem>();
42 }
43 
Update(const RefPtr<Component> & component)44 void RenderListItem::Update(const RefPtr<Component>& component)
45 {
46     auto item = AceType::DynamicCast<ListItemComponent>(component);
47     if (!item) {
48         LOGW("Not ListItemComponent, nothing to do");
49         return;
50     }
51 
52     component_ = item;
53     SetEditMode(false);
54 
55     if (IsDeletable()) {
56         CreateDeleteButton();
57     } else {
58         button_.Reset();
59     }
60 
61     onSelectId_ = item->GetOnSelectId();
62     selectable_ = item->GetSelectable();
63     isDragStart_ = item->IsDragStart();
64     borderRadius_ = item->GetBorderRadius();
65     edgeEffect_ = item->GetEdgeEffect();
66 
67     theme_ = GetTheme<ListItemTheme>();
68     if (!theme_) {
69         LOGE("theme is null");
70     }
71 
72     MarkNeedLayout();
73 }
74 
Paint(RenderContext & context,const Offset & offset)75 void RenderListItem::Paint(RenderContext& context, const Offset& offset)
76 {
77     if (swiperStart_) {
78         PaintChild(swiperStart_, context, offset);
79     }
80     if (swiperEnd_) {
81         PaintChild(swiperEnd_, context, offset);
82     }
83     if (child_) {
84         PaintChild(child_, context, offset);
85     }
86     if (button_ && editMode_) {
87         PaintChild(button_, context, offset);
88     }
89 }
90 
PerfLayoutSwiperMode()91 void RenderListItem::PerfLayoutSwiperMode()
92 {
93     child_ = GetItemChildRenderNode();
94     if (!child_) {
95         return;
96     }
97     const auto& layoutParam = GetLayoutParam();
98     child_->Layout(layoutParam);
99     const auto& childSize = child_->GetLayoutSize();
100     double maxMainSize = GetMainSize(childSize);
101 
102     if (GreatNotEqual(curOffset_, 0.0)) {
103         swiperEnd_.Reset();
104         auto swiperStart = GetSwiperStartRenderNode();
105         if (swiperStart) {
106             Size startSize = startSize_;
107             if (!swiperStart_) {
108                 swiperStart->Layout(LayoutParam(layoutParam.GetMaxSize(), Size()));
109                 startSize_ = swiperStart->GetLayoutSize();
110                 startSize = startSize_;
111             } else if (GreatNotEqual(curOffset_, GetCrossSize(startSize_))) {
112                 Size maxSize = MakeValue<Size>(GetMainSize(layoutParam.GetMaxSize()), curOffset_);
113                 swiperStart->Layout(LayoutParam(maxSize, MakeValue<Size>(0.0, curOffset_)));
114                 startSize = swiperStart->GetLayoutSize();
115             }
116             maxMainSize = std::max(maxMainSize, GetMainSize(startSize));
117             double startPos = curOffset_ - GetCrossSize(startSize);
118             /* 2:averages */
119             swiperStart->SetPosition(MakeValue<Offset>((maxMainSize - GetMainSize(startSize)) / 2, startPos));
120         }
121         swiperStart_ = swiperStart;
122     } else {
123         swiperStart_.Reset();
124         auto swiperEnd = GetSwiperEndRenderNode();
125         if (swiperEnd) {
126             Size endSize = endSize_;
127             if (!swiperEnd_) {
128                 swiperEnd->Layout(LayoutParam(layoutParam.GetMaxSize(), Size()));
129                 endSize_ = swiperEnd->GetLayoutSize();
130                 endSize = endSize_;
131             } else if (GreatNotEqual(-curOffset_, GetCrossSize(endSize_))) {
132                 Size maxSize = MakeValue<Size>(GetMainSize(layoutParam.GetMaxSize()), -curOffset_);
133                 swiperEnd->Layout(LayoutParam(maxSize, MakeValue<Size>(0.0, -curOffset_)));
134                 endSize = swiperEnd->GetLayoutSize();
135             }
136             maxMainSize = std::max(maxMainSize, GetMainSize(endSize));
137             double startPos = GetCrossSize(childSize) + curOffset_;
138              /* 2:averages */
139             swiperEnd->SetPosition(MakeValue<Offset>((maxMainSize - GetMainSize(endSize)) / 2, startPos));
140         }
141         swiperEnd_ = swiperEnd;
142     }
143     child_->SetPosition(MakeValue<Offset>((maxMainSize - GetMainSize(childSize)) / 2, curOffset_)); /* 2:averages */
144     SetLayoutSize(layoutParam.Constrain(MakeValue<Size>(maxMainSize, GetCrossSize(childSize))));
145 }
146 
PerformLayout()147 void RenderListItem::PerformLayout()
148 {
149     if (!NearZero(curOffset_)) {
150         PerfLayoutSwiperMode();
151         return;
152     }
153     swiperStart_.Reset();
154     swiperEnd_.Reset();
155 
156     const auto& layoutParam = GetLayoutParam();
157     Size childLayoutSize;
158     Size buttonLayoutSize;
159 
160     if (button_ && editMode_) {
161         button_->Layout(LayoutParam(layoutParam.GetMaxSize(), Size()));
162         buttonLayoutSize = button_->GetLayoutSize();
163     }
164 
165     child_ = GetItemChildRenderNode();
166     if (child_) {
167         auto maxSize = layoutParam.GetMaxSize();
168         auto minSize = layoutParam.GetMinSize();
169         if (!NearEqual(maxSize.Width(), Size::INFINITE_SIZE)) {
170             maxSize.SetWidth(std::max(maxSize.Width() - buttonLayoutSize.Width(), 0.0));
171             minSize.SetWidth(std::min(minSize.Width(), maxSize.Width()));
172         }
173 
174         child_->Layout(LayoutParam(maxSize, minSize));
175         childLayoutSize = child_->GetLayoutSize();
176     }
177 
178     double width = childLayoutSize.Width() + buttonLayoutSize.Width();
179     double height = std::max(childLayoutSize.Height(), buttonLayoutSize.Height());
180 
181     if (child_) {
182         child_->SetPosition(Offset(0.0, (height - childLayoutSize.Height()) / 2.0));
183     }
184 
185     if (button_ && editMode_) {
186         button_->SetPosition(Offset(childLayoutSize.Width(), (height - buttonLayoutSize.Height()) / 2.0));
187     }
188 
189     SetLayoutSize(layoutParam.Constrain(Size(width, height)));
190 }
191 
SetEditMode(bool editMode)192 void RenderListItem::SetEditMode(bool editMode)
193 {
194     if (editMode_ == editMode) {
195         return;
196     }
197 
198     editMode_ = editMode;
199     if (!button_) {
200         return;
201     }
202 
203     if (editMode_) {
204         AddChild(button_);
205     } else {
206         RemoveChild(button_);
207     }
208 }
209 
CreateDeleteButton()210 void RenderListItem::CreateDeleteButton()
211 {
212     if (!button_) {
213         auto imageComponent = AceType::MakeRefPtr<ImageComponent>(InternalResource::ResourceId::CLOSE_SVG);
214         imageComponent->SetImageSourceSize({ ICON_WIDTH, ICON_WIDTH });
215         imageComponent->SetFitMaxSize(true);
216 
217         auto buttonComponent = AceType::MakeRefPtr<ButtonComponent>(std::list<RefPtr<Component>>());
218         buttonComponent->SetType(ButtonType::ICON);
219         buttonComponent->SetWidth(BUTTON_WIDTH);
220         buttonComponent->SetHeight(BUTTON_WIDTH);
221 
222         buttonComponent->SetClickFunction([weak = AceType::WeakClaim(this)] {
223             auto spThis = weak.Upgrade();
224             if (spThis) {
225                 ResumeEventCallback(spThis, &RenderListItem::GetOnDeleteClick, spThis);
226             }
227         });
228 
229         auto button = buttonComponent->CreateRenderNode();
230         button->Attach(context_);
231         button->Update(buttonComponent);
232         auto image = imageComponent->CreateRenderNode();
233         image->Attach(context_);
234         image->Update(imageComponent);
235 
236         auto boxComponent = AceType::MakeRefPtr<BoxComponent>();
237         boxComponent->SetEnableDebugBoundary(true);
238         auto buttonBox = boxComponent->CreateRenderNode();
239         buttonBox->Attach(context_);
240         buttonBox->Update(boxComponent);
241 
242         button->AddChild(buttonBox);
243         buttonBox->AddChild(image);
244         button_ = button;
245     }
246 }
247 
UpdateTouchRect()248 void RenderListItem::UpdateTouchRect()
249 {
250     RenderNode::UpdateTouchRect();
251     if (button_ && IsResponseRegion()) {
252         auto buttonTouchRect = button_->GetPaintRect();
253         std::vector<Rect> touchRectList;
254         for (auto& region : responseRegion_) {
255             double x = GetPxValue(touchRect_.Width(), region.GetOffset().GetX());
256             double y = GetPxValue(touchRect_.Height(), region.GetOffset().GetY());
257             double width = GetPxValue(buttonTouchRect.Width(), region.GetWidth());
258             double height = GetPxValue(buttonTouchRect.Height(), region.GetHeight());
259             Rect responseRegion(buttonTouchRect.GetOffset().GetX() + x,
260                 buttonTouchRect.GetOffset().GetY() + y, width, height);
261             touchRectList.emplace_back(responseRegion);
262         }
263         button_->ChangeTouchRectList(touchRectList);
264     }
265 }
266 
InitDragRecognizer()267 void RenderListItem::InitDragRecognizer()
268 {
269     if (!springController_) {
270         springController_ = CREATE_ANIMATOR(context_);
271     }
272     if (dragDetector_) {
273         return;
274     }
275 
276     if (IsVertical()) {
277         dragDetector_ = AceType::MakeRefPtr<HorizontalDragRecognizer>();
278     } else {
279         dragDetector_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
280     }
281     if (!dragDetector_) {
282         return;
283     }
284 
285     auto weak = AceType::WeakClaim(this);
286     dragDetector_->SetOnDragStart([weak](const DragStartInfo& info) {
287         auto client = weak.Upgrade();
288         if (client) {
289             client->HandleDragStart(info);
290         }
291     });
292 
293     dragDetector_->SetOnDragUpdate([weak](const DragUpdateInfo& info) {
294         auto client = weak.Upgrade();
295         if (client) {
296             client->HandleDragUpdate(info);
297         }
298     });
299 
300     dragDetector_->SetOnDragEnd([weak](const DragEndInfo& info) {
301         auto client = weak.Upgrade();
302         if (client) {
303             client->HandleDragEnd(info);
304         }
305     });
306 }
307 
HandleDragStart(const DragStartInfo & info)308 void RenderListItem::HandleDragStart(const DragStartInfo& info)
309 {
310     if (springController_ && !springController_->IsStopped()) {
311         // clear stop listener before stop
312         springController_->ClearStopListeners();
313         springController_->Stop();
314     }
315 }
316 
CalculateFriction(double gamma)317 double RenderListItem::CalculateFriction(double gamma)
318 {
319     double ratio = theme_ ? theme_->GetItemSwipeRatio() : SWIPE_RATIO;
320     if (GreatOrEqual(gamma, 1.0)) {
321         gamma = 1.0;
322     }
323     return ratio * std::pow(1.0 - gamma, SQUARE);
324 }
325 
GetFriction()326 double RenderListItem::GetFriction()
327 {
328     if (GreatNotEqual(curOffset_, 0.0)) {
329         double width =  swiperStart_ ? GetCrossSize(startSize_) : 0.0;
330         double itemWidth = GetCrossSize(child_->GetLayoutSize());
331         if (width < curOffset_) {
332             return CalculateFriction((curOffset_ - width) / (itemWidth - width));
333         }
334     } else if (LessNotEqual(curOffset_, 0.0)) {
335         double width = swiperEnd_ ? GetCrossSize(endSize_) : 0.0;
336         double itemWidth = GetCrossSize(child_->GetLayoutSize());
337         if (width < -curOffset_) {
338             return CalculateFriction((-curOffset_ - width) / (itemWidth - width));
339         }
340     }
341     return 1.0;
342 }
343 
UpdatePostion(double delta)344 void RenderListItem::UpdatePostion(double delta)
345 {
346     double offset = curOffset_;
347     curOffset_ += delta;
348     if (edgeEffect_ == SwipeEdgeEffect::None) {
349         if (swiperStart_ && GreatNotEqual(curOffset_, GetCrossSize(startSize_))) {
350             curOffset_ = GetCrossSize(startSize_);
351         } else if (swiperEnd_ && GreatNotEqual(-curOffset_, GetCrossSize(endSize_))) {
352             curOffset_ = -GetCrossSize(endSize_);
353         }
354         if (Negative(curOffset_) && !GetSwiperEndRenderNode()) {
355             curOffset_ = 0.0;
356         } else if (Positive(curOffset_) && !GetSwiperStartRenderNode()) {
357             curOffset_ = 0.0;
358         }
359     }
360     if (!NearEqual(offset, curOffset_)) {
361         MarkNeedLayout();
362     }
363 }
364 
HandleDragUpdate(const DragUpdateInfo & info)365 void RenderListItem::HandleDragUpdate(const DragUpdateInfo& info)
366 {
367     double delta = info.GetMainDelta();
368     delta *= GetFriction();
369     UpdatePostion(delta);
370 }
371 
StartSpringMotion(double start,double end,double velocity)372 void RenderListItem::StartSpringMotion(double start, double end, double velocity)
373 {
374     if (!springController_) {
375         return;
376     }
377 
378     double mass = theme_ ? theme_->GetItemSwipeSpringMass() : SWIPE_SPRING_MASS;
379     double stiffness = theme_ ? theme_->GetItemSwipeSpringStiffness() : SWIPE_SPRING_STIFFNESS;
380     double damping = theme_ ? theme_->GetItemSwipeSpringDamping() : SWIPE_SPRING_DAMPING;
381     const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
382     AceType::MakeRefPtr<SpringProperty>(mass, stiffness, damping);
383 
384     springMotion_ = AceType::MakeRefPtr<SpringMotion>(start, end, velocity, DEFAULT_OVER_SPRING_PROPERTY);
385     springMotion_->AddListener([weakScroll = AceType::WeakClaim(this), start, end](double position) {
386         auto listItem = weakScroll.Upgrade();
387         if (listItem) {
388             if (listItem->edgeEffect_ == SwipeEdgeEffect::None &&
389                 ((GreatNotEqual(end, start) && GreatOrEqual(position, end)) ||
390                 (LessNotEqual(end, start) && LessOrEqual(position, end)))) {
391                 listItem->springController_->ClearStopListeners();
392                 listItem->springController_->Stop();
393                 position = end;
394             }
395             listItem->UpdatePostion(position - listItem->curOffset_);
396         }
397     });
398     springController_->ClearStopListeners();
399     springController_->PlayMotion(springMotion_);
400     springController_->AddStopListener([weak = AceType::WeakClaim(this)]() {
401         auto listItem = weak.Upgrade();
402         if (listItem) {
403             listItem->MarkNeedLayout(true);
404         }
405     });
406 }
407 
HandleDragEnd(const DragEndInfo & info)408 void RenderListItem::HandleDragEnd(const DragEndInfo& info)
409 {
410     double end = 0;
411     double friction = GetFriction();
412     double threshold = theme_ ? theme_->GetItemSwipeThreshold() : SWIPER_TH;
413     double speedThreshold = theme_ ? theme_->GetItemSwipeSpeedThreshold() : SWIPER_SPEED_TH;
414     bool reachRightSpeed = info.GetMainVelocity() > speedThreshold;
415     bool reachLeftSpeed = -info.GetMainVelocity() > speedThreshold;
416     if (GreatNotEqual(curOffset_, 0.0) && swiperStart_) {
417         double width = GetCrossSize(startSize_);
418         if (swipeIndex == ListItemSwipeIndex::ITEM_CHILD && (curOffset_ > width * threshold || reachRightSpeed)) {
419             swipeIndex = ListItemSwipeIndex::SWIPER_START;
420         } else if (swipeIndex == ListItemSwipeIndex::SWIPER_START &&
421             (curOffset_ < width * (1 - threshold) || reachLeftSpeed)) {
422             swipeIndex = ListItemSwipeIndex::ITEM_CHILD;
423         } else if (swipeIndex == ListItemSwipeIndex::SWIPER_END) {
424             swipeIndex = ListItemSwipeIndex::ITEM_CHILD;
425         }
426         end = width * static_cast<int32_t>(swipeIndex);
427     } else if (LessNotEqual(curOffset_, 0.0) && swiperEnd_) {
428         double width = GetCrossSize(endSize_);
429         if (swipeIndex == ListItemSwipeIndex::ITEM_CHILD && (width * threshold < -curOffset_ || reachLeftSpeed)) {
430             swipeIndex = ListItemSwipeIndex::SWIPER_END;
431         } else if (swipeIndex == ListItemSwipeIndex::SWIPER_END &&
432             (-curOffset_ < width * (1 - threshold) || reachRightSpeed)) {
433             swipeIndex = ListItemSwipeIndex::ITEM_CHILD;
434         } else if (swipeIndex == ListItemSwipeIndex::SWIPER_START) {
435             swipeIndex = ListItemSwipeIndex::ITEM_CHILD;
436         }
437         end = width * static_cast<int32_t>(swipeIndex);
438     }
439     StartSpringMotion(curOffset_, end, info.GetMainVelocity() * friction);
440 }
441 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)442 void RenderListItem::OnTouchTestHit(
443     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
444 {
445     if (IsDisabled()) {
446         return;
447     }
448 
449     if (GetSwiperStartRenderNode() || GetSwiperEndRenderNode()) {
450         InitDragRecognizer();
451     } else {
452         dragDetector_.Reset();
453         springController_.Reset();
454     }
455     if (dragDetector_) {
456         dragDetector_->SetCoordinateOffset(coordinateOffset);
457         result.emplace_back(dragDetector_);
458     }
459 }
460 
IsVertical() const461 bool RenderListItem::IsVertical() const
462 {
463     RefPtr<RenderNode> parent = GetParent().Upgrade();
464     RefPtr<RenderList> renderList = AceType::DynamicCast<RenderList>(parent);
465     if (!renderList) {
466         return true;
467     }
468     return renderList->IsVertical();
469 }
470 
471 } // namespace OHOS::Ace::V2
472