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