1 /*
2  * Copyright (c) 2023 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/swiper_indicator/indicator_common/swiper_arrow_pattern.h"
17 
18 #include "base/log/dump_log.h"
19 #include "base/utils/utils.h"
20 #include "core/components/theme/icon_theme.h"
21 #include "core/components_ng/base/frame_node.h"
22 #include "core/components_ng/event/gesture_event_hub.h"
23 #include "core/components_ng/pattern/swiper/swiper_pattern.h"
24 #include "core/components_ng/pattern/text/text_pattern.h"
25 #include "core/components_v2/inspector/inspector_constants.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
OnModifyDone()29 void SwiperArrowPattern::OnModifyDone()
30 {
31     Pattern::OnModifyDone();
32     if (isFirstCreate_) {
33         InitNavigationArrow();
34         auto swiperNode = GetSwiperNode();
35         CHECK_NULL_VOID(swiperNode);
36         auto swiperEventHub = swiperNode->GetEventHub<SwiperEventHub>();
37         CHECK_NULL_VOID(swiperEventHub);
38         InitSwiperChangeEvent(swiperEventHub);
39         index_ = GetSwiperArrowLayoutProperty()->GetIndex().value_or(0);
40         isFirstCreate_ = false;
41         InitEvent();
42         InitOnKeyEvent();
43     } else {
44         UpdateArrowContent();
45     }
46     UpdateButtonNode(index_);
47 }
48 
InitOnKeyEvent()49 void SwiperArrowPattern::InitOnKeyEvent()
50 {
51     auto host = GetHost();
52     CHECK_NULL_VOID(host);
53     auto focusHub = host->GetFocusHub();
54     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
55         auto pattern = wp.Upgrade();
56         if (pattern) {
57             return pattern->OnKeyEvent(event);
58         }
59         return false;
60     };
61     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
62 }
63 
OnKeyEvent(const KeyEvent & event)64 bool SwiperArrowPattern::OnKeyEvent(const KeyEvent& event)
65 {
66     if (event.action != KeyAction::DOWN) {
67         return false;
68     }
69 
70     if (event.code == KeyCode::KEY_ENTER || event.code == KeyCode::KEY_SPACE) {
71         OnClick();
72         return true;
73     }
74     return false;
75 }
76 
OnClick() const77 void SwiperArrowPattern::OnClick() const
78 {
79     auto swiperNode = GetSwiperNode();
80     CHECK_NULL_VOID(swiperNode);
81     auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
82     CHECK_NULL_VOID(swiperPattern);
83     auto swiperController = swiperPattern->GetSwiperController();
84     CHECK_NULL_VOID(swiperController);
85     auto host = GetHost();
86     CHECK_NULL_VOID(host);
87     if (host->GetTag() == V2::SWIPER_LEFT_ARROW_ETS_TAG) {
88         if (swiperPattern->IsHorizontalAndRightToLeft()) {
89             swiperController->ShowNext();
90         } else {
91             swiperController->ShowPrevious();
92         }
93         return;
94     }
95     if (host->GetTag() == V2::SWIPER_RIGHT_ARROW_ETS_TAG) {
96         if (swiperPattern->IsHorizontalAndRightToLeft()) {
97             swiperController->ShowPrevious();
98         } else {
99             swiperController->ShowNext();
100         }
101     }
102 }
103 
InitSwiperChangeEvent(const RefPtr<SwiperEventHub> & swiperEventHub)104 void SwiperArrowPattern::InitSwiperChangeEvent(const RefPtr<SwiperEventHub>& swiperEventHub)
105 {
106     ChangeEvent changeEvent = [weak = WeakClaim(this)](int32_t index) {
107         auto pattern = weak.Upgrade();
108         CHECK_NULL_VOID(pattern);
109         pattern->UpdateButtonNode(index);
110     };
111     if (swiperChangeEvent_) {
112         (*swiperChangeEvent_).swap(changeEvent);
113     } else {
114         swiperChangeEvent_ = std::make_shared<ChangeEvent>(std::move(changeEvent));
115         swiperEventHub->AddOnChangeEvent(swiperChangeEvent_);
116     }
117 }
118 
UpdateButtonNode(int32_t index)119 void SwiperArrowPattern::UpdateButtonNode(int32_t index)
120 {
121     index_ = index;
122     auto host = GetHost();
123     CHECK_NULL_VOID(host);
124     auto buttonNode = DynamicCast<FrameNode>(host->GetFirstChild());
125     CHECK_NULL_VOID(buttonNode);
126     auto symbolNode = DynamicCast<FrameNode>(buttonNode->GetFirstChild());
127     CHECK_NULL_VOID(symbolNode);
128     SetButtonVisible(isVisible_);
129     symbolNode->MarkModifyDone();
130 }
131 
InitEvent()132 void SwiperArrowPattern::InitEvent()
133 {
134     auto host = GetHost();
135     CHECK_NULL_VOID(host);
136     auto arrowGestureHub = host->GetOrCreateGestureEventHub();
137     CHECK_NULL_VOID(arrowGestureHub);
138     // Set hit test mode transparent to avoid blocking the touch event of child nodes and sibling nodes.
139     arrowGestureHub->SetHitTestMode(HitTestMode::HTMTRANSPARENT);
140     auto buttonNode = DynamicCast<FrameNode>(host->GetFirstChild());
141     CHECK_NULL_VOID(buttonNode);
142 
143     auto buttonGestureHub = buttonNode->GetOrCreateGestureEventHub();
144 
145     auto touchCallback = [weak = WeakClaim(this), buttonNode](const TouchEventInfo& info) {
146         auto pattern = weak.Upgrade();
147         CHECK_NULL_VOID(pattern);
148         pattern->ButtonTouchEvent(buttonNode, info.GetTouches().front().GetTouchType());
149     };
150     buttonTouchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
151     buttonGestureHub->AddTouchEvent(buttonTouchListener_);
152 
153     auto hoverCallback = [weak = WeakClaim(this), buttonNode](bool isHovered) {
154         auto pattern = weak.Upgrade();
155         CHECK_NULL_VOID(pattern);
156         pattern->ButtonOnHover(buttonNode, isHovered);
157     };
158     buttonOnHoverListener_ = MakeRefPtr<InputEvent>(std::move(hoverCallback));
159     auto buttonInputHub = buttonNode->GetOrCreateInputEventHub();
160     buttonInputHub->AddOnHoverEvent(buttonOnHoverListener_);
161 
162     auto clickCallback = [weak = WeakClaim(this)](const GestureEvent& info) {
163         auto pattern = weak.Upgrade();
164         CHECK_NULL_VOID(pattern);
165         pattern->ButtonClickEvent();
166     };
167     if (buttonClickListener_) {
168         buttonGestureHub->RemoveClickEvent(buttonClickListener_);
169     }
170     buttonClickListener_ = MakeRefPtr<ClickEvent>(std::move(clickCallback));
171     buttonGestureHub->AddClickEvent(buttonClickListener_);
172 }
173 
ButtonClickEvent()174 void SwiperArrowPattern::ButtonClickEvent()
175 {
176     auto swiperArrowLayoutProperty = GetSwiperArrowLayoutProperty();
177     CHECK_NULL_VOID(swiperArrowLayoutProperty);
178     if (!hoverOnClickFlag_ && swiperArrowLayoutProperty->GetHoverShowValue(false)) {
179         return;
180     }
181 
182     OnClick();
183 }
184 
InitNavigationArrow()185 void SwiperArrowPattern::InitNavigationArrow()
186 {
187     auto buttonNode = FrameNode::GetOrCreateFrameNode(V2::BUTTON_ETS_TAG,
188         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ButtonPattern>(); });
189     auto buttonNodeFocusHub = buttonNode->GetFocusHub();
190     CHECK_NULL_VOID(buttonNodeFocusHub);
191     buttonNodeFocusHub->SetParentFocusable(false);
192     auto swiperArrowLayoutProperty = GetSwiperArrowLayoutProperty();
193     CHECK_NULL_VOID(swiperArrowLayoutProperty);
194     auto symbolNode = FrameNode::GetOrCreateFrameNode(V2::SYMBOL_ETS_TAG,
195         ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
196     auto host = GetHost();
197     CHECK_NULL_VOID(host);
198     auto renderContext = host->GetRenderContext();
199     CHECK_NULL_VOID(renderContext);
200     BorderRadiusProperty radius;
201     radius.SetRadius(swiperArrowLayoutProperty->GetBackgroundSizeValue());
202     renderContext->UpdateBorderRadius(radius);
203     host->AddChild(buttonNode);
204     buttonNode->AddChild(symbolNode);
205     UpdateArrowContent();
206 }
207 
TotalCount() const208 int32_t SwiperArrowPattern::TotalCount() const
209 {
210     auto swiperNode = GetSwiperNode();
211     CHECK_NULL_RETURN(swiperNode, 0);
212     auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
213     CHECK_NULL_RETURN(swiperPattern, 0);
214     return swiperPattern->RealTotalCount() - 1;
215 }
216 
ButtonTouchEvent(RefPtr<FrameNode> buttonNode,TouchType touchType)217 void SwiperArrowPattern::ButtonTouchEvent(RefPtr<FrameNode> buttonNode, TouchType touchType)
218 {
219     auto swiperArrowLayoutProperty = GetSwiperArrowLayoutProperty();
220     CHECK_NULL_VOID(swiperArrowLayoutProperty);
221     const auto& renderContext = buttonNode->GetRenderContext();
222     CHECK_NULL_VOID(renderContext);
223     auto pipelineContext = PipelineBase::GetCurrentContext();
224     CHECK_NULL_VOID(pipelineContext);
225     auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
226     CHECK_NULL_VOID(swiperIndicatorTheme);
227     Color backgroundColor;
228     RefPtr<FrameNode> symbolNode = DynamicCast<FrameNode>(buttonNode->GetFirstChild());
229     CHECK_NULL_VOID(symbolNode);
230     auto symbolLayoutProperty = symbolNode->GetLayoutProperty<TextLayoutProperty>();
231     CHECK_NULL_VOID(symbolLayoutProperty);
232     if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
233         isTouch_ = false;
234         if (isHover_) {
235             backgroundColor = swiperIndicatorTheme->GetHoverArrowBackgroundColor();
236             renderContext->ResetBlendBgColor();
237             renderContext->BlendBgColor(backgroundColor);
238         } else {
239             renderContext->ResetBlendBgColor();
240         }
241     }
242     if (!hoverOnClickFlag_ && swiperArrowLayoutProperty->GetHoverShowValue(false)) {
243         return;
244     }
245     if (touchType == TouchType::DOWN) {
246         isTouch_ = true;
247         if (isHover_) {
248             backgroundColor = swiperIndicatorTheme->GetHoverArrowBackgroundColor().BlendColor(
249                 swiperIndicatorTheme->GetClickArrowBackgroundColor());
250         } else {
251             backgroundColor = swiperIndicatorTheme->GetClickArrowBackgroundColor();
252         }
253         renderContext->ResetBlendBgColor();
254         renderContext->BlendBgColor(backgroundColor);
255         auto symbolEffectOptions = symbolLayoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
256         symbolEffectOptions.SetEffectType(SymbolEffectType::BOUNCE);
257         symbolEffectOptions.SetIsTxtActive(true);
258         symbolEffectOptions.SetIsTxtActiveSource(1);
259         symbolLayoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
260         symbolNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
261     }
262 }
263 
ButtonOnHover(RefPtr<FrameNode> buttonNode,bool isHovered)264 void SwiperArrowPattern::ButtonOnHover(RefPtr<FrameNode> buttonNode, bool isHovered)
265 {
266     hoverOnClickFlag_ = isHovered;
267     isHover_ = isHovered;
268     const auto& renderContext = buttonNode->GetRenderContext();
269     CHECK_NULL_VOID(renderContext);
270     auto pipelineContext = PipelineBase::GetCurrentContext();
271     CHECK_NULL_VOID(pipelineContext);
272     auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
273     CHECK_NULL_VOID(swiperIndicatorTheme);
274     Color backgroundColor;
275 
276     auto swiperNode = GetSwiperNode();
277     CHECK_NULL_VOID(swiperNode);
278     auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
279     CHECK_NULL_VOID(swiperPattern);
280     auto swiperLayoutProperty = swiperPattern->GetLayoutProperty<SwiperLayoutProperty>();
281     CHECK_NULL_VOID(swiperLayoutProperty);
282     if (swiperLayoutProperty->GetHoverShowValue(false)) {
283         swiperPattern->ArrowHover(isHover_);
284     }
285     if (isHovered) {
286         if (isTouch_) {
287             backgroundColor = swiperIndicatorTheme->GetHoverArrowBackgroundColor().BlendColor(
288                 swiperIndicatorTheme->GetClickArrowBackgroundColor());
289         } else {
290             backgroundColor = swiperIndicatorTheme->GetHoverArrowBackgroundColor();
291         }
292         renderContext->ResetBlendBgColor();
293         renderContext->BlendBgColor(backgroundColor);
294     } else {
295         if (isTouch_) {
296             backgroundColor = swiperIndicatorTheme->GetClickArrowBackgroundColor();
297             renderContext->ResetBlendBgColor();
298             renderContext->BlendBgColor(backgroundColor);
299         } else {
300             renderContext->ResetBlendBgColor();
301         }
302     }
303 }
304 
SetButtonVisible(bool visible)305 void SwiperArrowPattern::SetButtonVisible(bool visible)
306 {
307     isVisible_ = visible;
308     auto host = GetHost();
309     CHECK_NULL_VOID(host);
310     auto buttonNode = DynamicCast<FrameNode>(host->GetFirstChild());
311     CHECK_NULL_VOID(buttonNode);
312     auto buttonNodeGestureHub = buttonNode->GetOrCreateGestureEventHub();
313     CHECK_NULL_VOID(buttonNodeGestureHub);
314     const auto& renderContext = buttonNode->GetRenderContext();
315     CHECK_NULL_VOID(renderContext);
316     auto swiperArrowLayoutProperty = GetSwiperArrowLayoutProperty();
317     CHECK_NULL_VOID(swiperArrowLayoutProperty);
318     auto isHoverShow = swiperArrowLayoutProperty->GetHoverShowValue(false);
319     auto hostFocusHub = host->GetFocusHub();
320     CHECK_NULL_VOID(hostFocusHub);
321     auto swiperNode = GetSwiperNode();
322     CHECK_NULL_VOID(swiperNode);
323     auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
324     CHECK_NULL_VOID(swiperPattern);
325     auto leftIndex = 0;
326     auto rightIndex = swiperPattern->TotalCount() - swiperPattern->GetDisplayCount();
327     if (swiperPattern->IsHorizontalAndRightToLeft()) {
328         leftIndex = swiperPattern->TotalCount() - swiperPattern->GetDisplayCount();
329         rightIndex = 0;
330     }
331     if ((host->GetTag() == V2::SWIPER_LEFT_ARROW_ETS_TAG && index_ == leftIndex) ||
332         (host->GetTag() == V2::SWIPER_RIGHT_ARROW_ETS_TAG && index_ == rightIndex)) {
333         if (!swiperArrowLayoutProperty->GetLoopValue(true)) {
334             renderContext->SetVisible(false);
335             // Set hit test mode NONE to make sure button not respond to the touch events when invisible.
336             buttonNodeGestureHub->SetHitTestMode(HitTestMode::HTMNONE);
337             hostFocusHub->SetParentFocusable(false);
338             hostFocusHub->LostSelfFocus();
339             return;
340         }
341     }
342     if (isHoverShow) {
343         hostFocusHub->SetParentFocusable(false);
344         hostFocusHub->LostSelfFocus();
345     } else {
346         hostFocusHub->SetParentFocusable(true);
347         visible = true;
348     }
349     renderContext->SetVisible(visible);
350     // Set hit test mode BLOCK to make sure button respond to the touch events when visible.
351     buttonNodeGestureHub->SetHitTestMode(visible ? HitTestMode::HTMBLOCK : HitTestMode::HTMNONE);
352 }
353 
UpdateArrowContent()354 void SwiperArrowPattern::UpdateArrowContent()
355 {
356     auto swiperArrowLayoutProperty = GetSwiperArrowLayoutProperty();
357     CHECK_NULL_VOID(swiperArrowLayoutProperty);
358     auto host = GetHost();
359     CHECK_NULL_VOID(host);
360     auto buttonNode = DynamicCast<FrameNode>(host->GetFirstChild());
361     CHECK_NULL_VOID(buttonNode);
362     buttonNode->GetRenderContext()->UpdateBackgroundColor(
363         swiperArrowLayoutProperty->GetIsShowBackgroundValue(false)
364             ? swiperArrowLayoutProperty->GetBackgroundColorValue(backgroundColor_)
365             : Color::TRANSPARENT);
366     auto buttonLayoutProperty = buttonNode->GetLayoutProperty<ButtonLayoutProperty>();
367     CHECK_NULL_VOID(buttonLayoutProperty);
368     buttonLayoutProperty->UpdateUserDefinedIdealSize(
369         CalcSize(CalcLength(swiperArrowLayoutProperty->GetBackgroundSizeValue()),
370             CalcLength(swiperArrowLayoutProperty->GetBackgroundSizeValue())));
371     backgroundColor_ = buttonNode->GetRenderContext()->GetBackgroundColorValue(Color::TRANSPARENT);
372     RefPtr<FrameNode> symbolNode = DynamicCast<FrameNode>(buttonNode->GetFirstChild());
373     CHECK_NULL_VOID(symbolNode);
374     auto symbolLayoutProperty = symbolNode->GetLayoutProperty<TextLayoutProperty>();
375     CHECK_NULL_VOID(symbolLayoutProperty);
376     auto swiperLayoutProperty = GetSwiperArrowLayoutProperty();
377     CHECK_NULL_VOID(swiperLayoutProperty);
378     auto pipelineContext = PipelineBase::GetCurrentContext();
379     CHECK_NULL_VOID(pipelineContext);
380     auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
381     CHECK_NULL_VOID(swiperIndicatorTheme);
382     bool isRtl = swiperLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
383     if (V2::SWIPER_LEFT_ARROW_ETS_TAG == GetHost()->GetTag()) {
384         if (swiperLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL) == Axis::HORIZONTAL) {
385             symbolLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo(
386                 isRtl ? swiperIndicatorTheme->GetRightSymbolId() : swiperIndicatorTheme->GetLeftSymbolId()));
387         } else {
388             symbolLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo(swiperIndicatorTheme->GetUpSymbolId()));
389         }
390     } else if (V2::SWIPER_RIGHT_ARROW_ETS_TAG == GetHost()->GetTag()) {
391         if (swiperLayoutProperty->GetDirection().value_or(Axis::HORIZONTAL) == Axis::HORIZONTAL) {
392             symbolLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo(
393                 isRtl ? swiperIndicatorTheme->GetLeftSymbolId() : swiperIndicatorTheme->GetRightSymbolId()));
394         } else {
395             symbolLayoutProperty->UpdateSymbolSourceInfo(SymbolSourceInfo(swiperIndicatorTheme->GetDownSymbolId()));
396         }
397     }
398     symbolLayoutProperty->UpdateFontSize(swiperArrowLayoutProperty->GetArrowSizeValue());
399     symbolLayoutProperty->UpdateSymbolColorList({ swiperArrowLayoutProperty->GetArrowColorValue() });
400     if (!swiperArrowLayoutProperty->GetEnabledValue(true)) {
401         buttonNode->GetRenderContext()->UpdateBackgroundColor(
402             backgroundColor_.BlendOpacity(swiperIndicatorTheme->GetArrowDisabledAlpha()));
403         symbolLayoutProperty->UpdateSymbolColorList({ swiperArrowLayoutProperty->GetArrowColorValue().BlendOpacity(
404             swiperIndicatorTheme->GetArrowDisabledAlpha()) });
405     }
406     symbolNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
407     symbolNode->MarkModifyDone();
408 }
409 
DumpAdvanceInfo()410 void SwiperArrowPattern::DumpAdvanceInfo()
411 {
412     DumpLog::GetInstance().AddDesc("index:" + std::to_string(index_));
413     isTouch_ ? DumpLog::GetInstance().AddDesc("isTouch:true") : DumpLog::GetInstance().AddDesc("isTouch:false");
414     isHover_ ? DumpLog::GetInstance().AddDesc("isHover:true") : DumpLog::GetInstance().AddDesc("isHover:false");
415     hoverOnClickFlag_ ? DumpLog::GetInstance().AddDesc("hoverOnClickFlag:true")
416                       : DumpLog::GetInstance().AddDesc("hoverOnClickFlag:false");
417 }
418 } // namespace OHOS::Ace::NG
419