• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/side_bar/side_bar_container_pattern.h"
17 
18 #include "core/components_ng/base/frame_node.h"
19 #include "core/components_ng/pattern/image/image_pattern.h"
20 #include "core/components_ng/property/measure_utils.h"
21 #include "core/components_v2/inspector/inspector_constants.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 #include "core/pipeline_ng/ui_task_scheduler.h"
24 
25 namespace OHOS::Ace::NG {
26 
27 namespace {
28 constexpr int32_t SLIDE_TRANSLATE_DURATION = 400;
29 constexpr float RATIO_NEGATIVE = -1.0f;
30 constexpr float RATIO_ZERO = 0.0f;
31 constexpr Dimension DEFAULT_DRAG_REGION = 20.0_vp;
32 constexpr Dimension DEFAULT_MIN_SIDE_BAR_WIDTH = 200.0_vp;
33 constexpr Dimension DEFAULT_MAX_SIDE_BAR_WIDTH = 280.0_vp;
34 } // namespace
35 
OnAttachToFrameNode()36 void SideBarContainerPattern::OnAttachToFrameNode()
37 {
38     auto host = GetHost();
39     CHECK_NULL_VOID(host);
40     host->GetRenderContext()->SetClipToFrame(true);
41 }
42 
OnUpdateShowSideBar(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty)43 void SideBarContainerPattern::OnUpdateShowSideBar(const RefPtr<SideBarContainerLayoutProperty>& layoutProperty)
44 {
45     CHECK_NULL_VOID(layoutProperty);
46 
47     auto newShowSideBar = layoutProperty->GetShowSideBar().value_or(true);
48     if (newShowSideBar != showSideBar_) {
49         SetSideBarStatus(newShowSideBar ? SideBarStatus::SHOW : SideBarStatus::HIDDEN);
50     }
51 }
52 
OnUpdateShowControlButton(const RefPtr<SideBarContainerLayoutProperty> & layoutProperty,const RefPtr<FrameNode> & host)53 void SideBarContainerPattern::OnUpdateShowControlButton(
54     const RefPtr<SideBarContainerLayoutProperty>& layoutProperty, const RefPtr<FrameNode>& host)
55 {
56     CHECK_NULL_VOID(layoutProperty);
57     CHECK_NULL_VOID(host);
58 
59     auto showControlButton = layoutProperty->GetShowControlButton().value_or(true);
60 
61     auto children = host->GetChildren();
62     if (children.empty()) {
63         LOGE("OnUpdateShowControlButton: children is empty.");
64         return;
65     }
66 
67     auto controlButtonNode = children.back();
68     if (controlButtonNode->GetTag() != V2::IMAGE_ETS_TAG || !AceType::InstanceOf<FrameNode>(controlButtonNode)) {
69         LOGE("OnUpdateShowControlButton: Get control button failed.");
70         return;
71     }
72 
73     auto imgFrameNode = AceType::DynamicCast<FrameNode>(controlButtonNode);
74     auto imageLayoutProperty = imgFrameNode->GetLayoutProperty<ImageLayoutProperty>();
75     CHECK_NULL_VOID(imageLayoutProperty);
76 
77     imageLayoutProperty->UpdateVisibility(showControlButton ? VisibleType::VISIBLE : VisibleType::GONE);
78     imgFrameNode->MarkModifyDone();
79 }
80 
OnModifyDone()81 void SideBarContainerPattern::OnModifyDone()
82 {
83     CreateAnimation();
84     InitSideBar();
85 
86     auto host = GetHost();
87     CHECK_NULL_VOID(host);
88     auto hub = host->GetEventHub<EventHub>();
89     CHECK_NULL_VOID(hub);
90     auto gestureHub = hub->GetOrCreateGestureEventHub();
91     CHECK_NULL_VOID(gestureHub);
92 
93     InitDragEvent(gestureHub);
94 
95     auto layoutProperty = host->GetLayoutProperty<SideBarContainerLayoutProperty>();
96     OnUpdateShowSideBar(layoutProperty);
97     OnUpdateShowControlButton(layoutProperty, host);
98 }
99 
InitDragEvent(const RefPtr<GestureEventHub> & gestureHub)100 void SideBarContainerPattern::InitDragEvent(const RefPtr<GestureEventHub>& gestureHub)
101 {
102     CHECK_NULL_VOID_NOLOG(!dragEvent_);
103 
104     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
105         auto pattern = weak.Upgrade();
106         CHECK_NULL_VOID_NOLOG(pattern);
107         pattern->HandleDragStart();
108     };
109 
110     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
111         auto pattern = weak.Upgrade();
112         CHECK_NULL_VOID_NOLOG(pattern);
113         pattern->HandleDragUpdate(static_cast<float>(info.GetOffsetX()));
114     };
115 
116     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
117         auto pattern = weak.Upgrade();
118         CHECK_NULL_VOID_NOLOG(pattern);
119         pattern->HandleDragEnd();
120     };
121 
122     auto actionCancelTask = [weak = WeakClaim(this)]() {
123         auto pattern = weak.Upgrade();
124         CHECK_NULL_VOID_NOLOG(pattern);
125         pattern->HandleDragEnd();
126     };
127 
128     dragEvent_ = MakeRefPtr<DragEvent>(
129         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
130     PanDirection panDirection = { .type = PanDirection::HORIZONTAL };
131     gestureHub->SetDragEvent(dragEvent_, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
132 }
133 
InitSideBar()134 void SideBarContainerPattern::InitSideBar()
135 {
136     auto host = GetHost();
137     CHECK_NULL_VOID(host);
138 
139     auto layoutProperty = host->GetLayoutProperty<SideBarContainerLayoutProperty>();
140     CHECK_NULL_VOID(layoutProperty);
141 
142     auto showSideBar = layoutProperty->GetShowSideBar().value_or(true);
143     sideBarStatus_ = showSideBar ? SideBarStatus::SHOW : SideBarStatus::HIDDEN;
144 }
145 
CreateAnimation()146 void SideBarContainerPattern::CreateAnimation()
147 {
148     auto host = GetHost();
149     CHECK_NULL_VOID(host);
150 
151     if (!controller_) {
152         controller_ = AceType::MakeRefPtr<Animator>(host->GetContext());
153     }
154 
155     auto weak = AceType::WeakClaim(this);
156     if (!rightToLeftAnimation_) {
157         rightToLeftAnimation_ =
158             AceType::MakeRefPtr<CurveAnimation<float>>(RATIO_ZERO, RATIO_NEGATIVE, Curves::FRICTION);
159         rightToLeftAnimation_->AddListener(Animation<float>::ValueCallback([weak](float value) {
160             auto pattern = weak.Upgrade();
161             if (pattern) {
162                 pattern->UpdateSideBarPosition(value);
163             }
164         }));
165     }
166 
167     if (!leftToRightAnimation_) {
168         leftToRightAnimation_ =
169             AceType::MakeRefPtr<CurveAnimation<float>>(RATIO_NEGATIVE, RATIO_ZERO, Curves::FRICTION);
170         leftToRightAnimation_->AddListener(Animation<float>::ValueCallback([weak](float value) {
171             auto pattern = weak.Upgrade();
172             if (pattern) {
173                 pattern->UpdateSideBarPosition(value);
174             }
175         }));
176     }
177 }
178 
InitControlButtonTouchEvent(const RefPtr<GestureEventHub> & gestureHub)179 void SideBarContainerPattern::InitControlButtonTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
180 {
181     CHECK_NULL_VOID_NOLOG(!controlButtonClickEvent_);
182 
183     auto clickTask = [weak = WeakClaim(this)](const GestureEvent& info) {
184         auto pattern = weak.Upgrade();
185         CHECK_NULL_VOID_NOLOG(pattern);
186         pattern->DoSideBarAnimation();
187     };
188     controlButtonClickEvent_ = MakeRefPtr<ClickEvent>(std::move(clickTask));
189     gestureHub->AddClickEvent(controlButtonClickEvent_);
190 }
191 
DoSideBarAnimation()192 void SideBarContainerPattern::DoSideBarAnimation()
193 {
194     auto host = GetHost();
195     CHECK_NULL_VOID(host);
196 
197     CHECK_NULL_VOID(controller_);
198     CHECK_NULL_VOID(leftToRightAnimation_);
199     CHECK_NULL_VOID(rightToLeftAnimation_);
200 
201     if (!controller_->IsStopped()) {
202         controller_->Stop();
203     }
204 
205     auto weak = AceType::WeakClaim(this);
206     controller_->ClearStopListeners();
207     controller_->ClearInterpolators();
208     controller_->SetDuration(SLIDE_TRANSLATE_DURATION);
209 
210     auto layoutProperty = GetLayoutProperty<SideBarContainerLayoutProperty>();
211     CHECK_NULL_VOID(layoutProperty);
212     auto sideBarPosition = layoutProperty->GetSideBarPosition().value_or(SideBarPosition::START);
213     bool isSideBarStart = sideBarPosition == SideBarPosition::START;
214 
215     if (sideBarStatus_ == SideBarStatus::HIDDEN) {
216         controller_->AddInterpolator(isSideBarStart ? leftToRightAnimation_ : rightToLeftAnimation_);
217         controller_->AddStopListener([weak]() {
218             auto pattern = weak.Upgrade();
219             CHECK_NULL_VOID_NOLOG(pattern);
220             pattern->SetSideBarStatus(SideBarStatus::SHOW);
221             pattern->FireChangeEvent(true);
222             pattern->UpdateControlButtonIcon();
223         });
224     } else {
225         controller_->AddInterpolator(isSideBarStart ? rightToLeftAnimation_ : leftToRightAnimation_);
226         controller_->AddStopListener([weak]() {
227             auto pattern = weak.Upgrade();
228             CHECK_NULL_VOID_NOLOG(pattern);
229             pattern->SetSideBarStatus(SideBarStatus::HIDDEN);
230             pattern->FireChangeEvent(false);
231             pattern->UpdateControlButtonIcon();
232         });
233     }
234     controller_->Play();
235     UpdateControlButtonIcon();
236 }
237 
UpdateSideBarPosition(float value)238 void SideBarContainerPattern::UpdateSideBarPosition(float value)
239 {
240     auto host = GetHost();
241     CHECK_NULL_VOID(host);
242 
243     if (sideBarStatus_ != SideBarStatus::CHANGING) {
244         sideBarStatus_ = SideBarStatus::CHANGING;
245         UpdateControlButtonIcon();
246     }
247 
248     currentOffset_ = value * realSideBarWidth_;
249     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
250 }
251 
FireChangeEvent(bool isShow)252 void SideBarContainerPattern::FireChangeEvent(bool isShow)
253 {
254     auto sideBarContainerEventHub = GetEventHub<SideBarContainerEventHub>();
255     CHECK_NULL_VOID(sideBarContainerEventHub);
256 
257     sideBarContainerEventHub->FireChangeEvent(isShow);
258 }
259 
UpdateControlButtonIcon()260 void SideBarContainerPattern::UpdateControlButtonIcon()
261 {
262     auto layoutProperty = GetLayoutProperty<SideBarContainerLayoutProperty>();
263     CHECK_NULL_VOID(layoutProperty);
264 
265     auto host = GetHost();
266     CHECK_NULL_VOID(host);
267 
268     auto children = host->GetChildren();
269     if (children.empty()) {
270         LOGE("UpdateControlButtonIcon: children is empty.");
271         return;
272     }
273 
274     auto controlButtonNode = children.back();
275     if (controlButtonNode->GetTag() != V2::IMAGE_ETS_TAG || !AceType::InstanceOf<FrameNode>(controlButtonNode)) {
276         LOGE("UpdateControlButtonIcon: Get control button failed.");
277         return;
278     }
279 
280     auto imgFrameNode = AceType::DynamicCast<FrameNode>(controlButtonNode);
281     auto imgRenderContext = imgFrameNode->GetRenderContext();
282     auto imageLayoutProperty = imgFrameNode->GetLayoutProperty<ImageLayoutProperty>();
283     CHECK_NULL_VOID(imageLayoutProperty);
284     auto imgSourceInfo = imageLayoutProperty->GetImageSourceInfoValue();
285 
286     switch (sideBarStatus_) {
287         case SideBarStatus::SHOW:
288             if (layoutProperty->GetControlButtonShowIconStr().has_value()) {
289                 imgSourceInfo.SetSrc(layoutProperty->GetControlButtonShowIconStr().value());
290             } else {
291                 imgSourceInfo.SetResourceId(InternalResource::ResourceId::SIDE_BAR);
292             }
293             break;
294         case SideBarStatus::HIDDEN:
295             if (layoutProperty->GetControlButtonHiddenIconStr().has_value()) {
296                 imgSourceInfo.SetSrc(layoutProperty->GetControlButtonHiddenIconStr().value());
297             } else {
298                 imgSourceInfo.SetResourceId(InternalResource::ResourceId::SIDE_BAR);
299             }
300             break;
301         case SideBarStatus::CHANGING:
302             if (layoutProperty->GetControlButtonSwitchingIconStr().has_value()) {
303                 imgSourceInfo.SetSrc(layoutProperty->GetControlButtonSwitchingIconStr().value());
304             } else {
305                 imgSourceInfo.SetResourceId(InternalResource::ResourceId::SIDE_BAR);
306             }
307             break;
308         default:
309             break;
310     }
311 
312     imageLayoutProperty->UpdateImageSourceInfo(imgSourceInfo);
313     imgFrameNode->MarkModifyDone();
314 }
315 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)316 bool SideBarContainerPattern::OnDirtyLayoutWrapperSwap(
317     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
318 {
319     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
320     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
321     auto layoutAlgorithm = DynamicCast<SideBarContainerLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
322     CHECK_NULL_RETURN(layoutAlgorithm, false);
323 
324     UpdateResponseRegion(layoutAlgorithm);
325 
326     if (needInitRealSideBarWidth_) {
327         needInitRealSideBarWidth_ = false;
328     }
329 
330     return false;
331 }
332 
UpdateResponseRegion(const RefPtr<SideBarContainerLayoutAlgorithm> & layoutAlgorithm)333 void SideBarContainerPattern::UpdateResponseRegion(const RefPtr<SideBarContainerLayoutAlgorithm>& layoutAlgorithm)
334 {
335     auto layoutProperty = GetLayoutProperty<SideBarContainerLayoutProperty>();
336     CHECK_NULL_VOID(layoutProperty);
337     auto constraint = layoutProperty->GetLayoutConstraint();
338     auto scaleProperty = constraint->scaleProperty;
339     auto halfDragRegionWidth = ConvertToPx(DEFAULT_DRAG_REGION, scaleProperty).value_or(0);
340     auto dragRegionWidth = halfDragRegionWidth * 2;
341 
342     realSideBarWidth_ = layoutAlgorithm->GetRealSideBarWidth();
343     auto dragRegionHeight = layoutAlgorithm->GetRealSideBarHeight();
344     auto dragRectOffset = layoutAlgorithm->GetSideBarOffset();
345 
346     auto sideBarPosition = layoutProperty->GetSideBarPosition().value_or(SideBarPosition::START);
347     if (sideBarPosition == SideBarPosition::START) {
348         dragRectOffset.SetX(dragRectOffset.GetX() + realSideBarWidth_ - halfDragRegionWidth);
349     } else {
350         dragRectOffset.SetX(dragRectOffset.GetX() - halfDragRegionWidth);
351     }
352 
353     dragRect_.SetOffset(dragRectOffset);
354     dragRect_.SetSize(SizeF(dragRegionWidth, dragRegionHeight));
355 
356     auto eventHub = GetEventHub<EventHub>();
357     CHECK_NULL_VOID(eventHub);
358     auto gestureEventHub = eventHub->GetOrCreateGestureEventHub();
359     CHECK_NULL_VOID(gestureEventHub);
360 
361     gestureEventHub->MarkResponseRegion(true);
362     std::vector<DimensionRect> responseRegion;
363     DimensionOffset responseOffset(dragRectOffset);
364     DimensionRect responseRect(Dimension(dragRect_.Width(), DimensionUnit::PX),
365         Dimension(dragRect_.Height(), DimensionUnit::PX), responseOffset);
366     responseRegion.emplace_back(responseRect);
367     gestureEventHub->SetResponseRegion(responseRegion);
368 }
369 
HandleDragStart()370 void SideBarContainerPattern::HandleDragStart()
371 {
372     if (sideBarStatus_ != SideBarStatus::SHOW) {
373         return;
374     }
375 
376     preSidebarWidth_ = realSideBarWidth_;
377 }
378 
HandleDragUpdate(float xOffset)379 void SideBarContainerPattern::HandleDragUpdate(float xOffset)
380 {
381     if (sideBarStatus_ != SideBarStatus::SHOW) {
382         return;
383     }
384 
385     auto layoutProperty = GetLayoutProperty<SideBarContainerLayoutProperty>();
386     CHECK_NULL_VOID(layoutProperty);
387 
388     auto minSideBarWidth = layoutProperty->GetMinSideBarWidth().value_or(DEFAULT_MIN_SIDE_BAR_WIDTH);
389     auto maxSideBarWidth = layoutProperty->GetMaxSideBarWidth().value_or(DEFAULT_MAX_SIDE_BAR_WIDTH);
390 
391     auto host = GetHost();
392     CHECK_NULL_VOID(host);
393     auto geometryNode = host->GetGeometryNode();
394     CHECK_NULL_VOID(geometryNode);
395 
396     auto frameSize = geometryNode->GetFrameSize();
397     auto parentWidth = frameSize.Width();
398     auto constraint = layoutProperty->GetLayoutConstraint();
399     auto scaleProperty = constraint->scaleProperty;
400     auto minSideBarWidthPx = ConvertToPx(minSideBarWidth, scaleProperty, parentWidth).value_or(0);
401     auto maxSideBarWidthPx = ConvertToPx(maxSideBarWidth, scaleProperty, parentWidth).value_or(0);
402 
403     auto sideBarPosition = layoutProperty->GetSideBarPosition().value_or(SideBarPosition::START);
404     bool isSideBarStart = sideBarPosition == SideBarPosition::START;
405 
406     auto sideBarLine = preSidebarWidth_ + (isSideBarStart ? xOffset : -xOffset);
407 
408     if (sideBarLine > minSideBarWidthPx && sideBarLine < maxSideBarWidthPx) {
409         realSideBarWidth_ = sideBarLine;
410         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
411         return;
412     }
413 
414     if (sideBarLine >= maxSideBarWidthPx) {
415         realSideBarWidth_ = maxSideBarWidthPx;
416         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
417         return;
418     }
419 
420     auto halfDragRegionWidth = dragRect_.Width() / 2;
421     if (sideBarLine > minSideBarWidthPx - halfDragRegionWidth) {
422         realSideBarWidth_ = minSideBarWidthPx;
423         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
424         return;
425     }
426     realSideBarWidth_ = minSideBarWidthPx;
427 
428     auto autoHide_ = layoutProperty->GetAutoHide().value_or(true);
429     if (autoHide_) {
430         DoSideBarAnimation();
431     }
432 }
433 
HandleDragEnd()434 void SideBarContainerPattern::HandleDragEnd()
435 {
436     if (sideBarStatus_ != SideBarStatus::SHOW) {
437         return;
438     }
439 
440     preSidebarWidth_ = realSideBarWidth_;
441 }
442 
443 } // namespace OHOS::Ace::NG
444