1 /*
2 * Copyright (c) 2025 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 #include "core/components_ng/pattern/scroll/inner/scroll_bar_2d.h"
16
17 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
18 #include "core/components_ng/pattern/scrollable/scrollable_paint_property.h"
19 namespace OHOS::Ace::NG {
InitGestures(ScrollBar & bar,Axis axis)20 void ScrollBar2D::InitGestures(ScrollBar& bar, Axis axis)
21 {
22 bar.SetAxis(axis);
23 bar.SetMarkNeedRenderFunc([weak = WeakPtr(pattern_.GetHost())]() {
24 auto node = weak.Upgrade();
25 if (node) {
26 node->MarkNeedRenderOnly();
27 }
28 });
29 auto scrollCallback = [weak = WeakClaim(&pattern_), axis](double offset, int32_t source, bool isMouseWheelScroll) {
30 auto pattern = weak.Upgrade();
31 CHECK_NULL_RETURN(pattern, false);
32 pattern->FreeScrollBy(axis == Axis::VERTICAL ? OffsetF { 0.0f, offset } : OffsetF { offset, 0.0f });
33 return true;
34 };
35 bar.SetScrollPositionCallback(std::move(scrollCallback));
36 auto scrollEnd = [weak = WeakClaim(&pattern_)]() {
37 auto pattern = weak.Upgrade();
38 CHECK_NULL_VOID(pattern);
39 pattern->OnScrollEndCallback();
40 };
41 bar.SetScrollEndCallback(std::move(scrollEnd));
42
43 auto gestureHub = pattern_.GetGestureHub();
44 CHECK_NULL_VOID(gestureHub);
45 auto inputHub = pattern_.GetInputHub();
46 CHECK_NULL_VOID(inputHub);
47 bar.SetGestureEvent();
48 bar.SetMouseEvent();
49 bar.SetHoverEvent();
50 gestureHub->AddTouchEvent(bar.GetTouchEvent());
51 inputHub->AddOnMouseEvent(bar.GetMouseEvent());
52 inputHub->AddOnHoverEvent(bar.GetHoverEvent());
53 }
54
ScrollBar2D(ScrollPattern & pattern)55 ScrollBar2D::ScrollBar2D(ScrollPattern& pattern) : pattern_(pattern)
56 {
57 CHECK_NULL_VOID(vertical_ && horizontal_);
58 vertical_->SetAxis(Axis::VERTICAL);
59 horizontal_->SetAxis(Axis::HORIZONTAL);
60 horizontal_->SetPositionMode(PositionMode::BOTTOM);
61 vertical_->SetPositionMode(PositionMode::RIGHT);
62
63 InitGestures(*vertical_, Axis::VERTICAL);
64 InitGestures(*horizontal_, Axis::HORIZONTAL);
65
66 auto scrollableEvent = pattern_.GetScrollableEvent();
67 CHECK_NULL_VOID(scrollableEvent);
68 scrollableEvent->SetInBarRegionCallback([weak = WeakClaim(this)](const PointF& point, SourceType source) {
69 auto self = weak.Upgrade();
70 CHECK_NULL_RETURN(self && self->vertical_ && self->horizontal_, false);
71 const Point pointDouble { point.GetX(), point.GetY() };
72 if (source == SourceType::MOUSE) {
73 return self->vertical_->InBarHoverRegion(pointDouble) || self->horizontal_->InBarHoverRegion(pointDouble);
74 }
75 return self->vertical_->InBarTouchRegion(pointDouble) || self->horizontal_->InBarTouchRegion(pointDouble);
76 });
77 scrollableEvent->SetInBarRectRegionCallback([weak = WeakClaim(this)](const PointF& point, SourceType source) {
78 auto self = weak.Upgrade();
79 CHECK_NULL_RETURN(self && self->vertical_ && self->horizontal_, false);
80 const Point pointDouble { point.GetX(), point.GetY() };
81 return self->vertical_->InBarRectRegion(pointDouble) || self->horizontal_->InBarRectRegion(pointDouble);
82 });
83 scrollableEvent->SetBarCollectTouchTargetCallback(
84 [weak = WeakClaim(this)](const OffsetF& coordinateOffset, const GetEventTargetImpl& getEventTargetImpl,
85 TouchTestResult& result, const RefPtr<FrameNode>& frameNode, const RefPtr<TargetComponent>& targetComponent,
86 ResponseLinkResult& responseLinkResult) {
87 auto self = weak.Upgrade();
88 CHECK_NULL_VOID(self && self->vertical_ && self->horizontal_);
89 self->vertical_->OnCollectTouchTarget(
90 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
91 self->horizontal_->OnCollectTouchTarget(
92 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
93 });
94 scrollableEvent->SetBarCollectClickAndLongPressTargetCallback(
95 [weak = WeakClaim(this)](const OffsetF& coordinateOffset, const GetEventTargetImpl& getEventTargetImpl,
96 TouchTestResult& result, const RefPtr<FrameNode>& frameNode, const RefPtr<TargetComponent>& targetComponent,
97 ResponseLinkResult& responseLinkResult) {
98 auto self = weak.Upgrade();
99 CHECK_NULL_VOID(self && self->vertical_ && self->horizontal_);
100 self->vertical_->OnCollectLongPressTarget(
101 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
102 self->horizontal_->OnCollectLongPressTarget(
103 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
104 });
105 }
106
RemoveGestures(ScrollBar & bar)107 void ScrollBar2D::RemoveGestures(ScrollBar& bar)
108 {
109 auto gestureHub = pattern_.GetGestureHub();
110 CHECK_NULL_VOID(gestureHub);
111 auto inputHub = pattern_.GetInputHub();
112 CHECK_NULL_VOID(inputHub);
113 gestureHub->RemoveTouchEvent(bar.GetTouchEvent());
114 inputHub->RemoveOnMouseEvent(bar.GetMouseEvent());
115 inputHub->RemoveOnHoverEvent(bar.GetHoverEvent());
116 }
117
~ScrollBar2D()118 ScrollBar2D::~ScrollBar2D()
119 {
120 CHECK_NULL_VOID(vertical_ && horizontal_);
121 RemoveGestures(*vertical_);
122 RemoveGestures(*horizontal_);
123
124 auto* ctx = pattern_.GetRenderContext();
125 CHECK_NULL_VOID(ctx);
126 ctx->RemoveOverlayModifier(painter_);
127 }
128
129 namespace {
ConfigureScrollBar(const std::unique_ptr<ScrollBarProperty> & property,ScrollBar & bar)130 void ConfigureScrollBar(const std::unique_ptr<ScrollBarProperty>& property, ScrollBar& bar)
131 {
132 const auto mode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
133 if (mode != bar.GetDisplayMode()) {
134 bar.SetDisplayMode(mode);
135 if (mode == DisplayMode::AUTO) {
136 bar.ScheduleDisappearDelayTask();
137 }
138 }
139
140 const auto& barWidth = property->GetScrollBarWidth();
141 if (barWidth) {
142 bar.SetActiveWidth(*barWidth);
143 bar.SetTouchWidth(*barWidth);
144 bar.SetInactiveWidth(*barWidth);
145 bar.SetNormalWidth(*barWidth);
146 bar.SetIsUserNormalWidth(true);
147 } else {
148 bar.SetIsUserNormalWidth(false);
149 }
150 const auto& barColor = property->GetScrollBarColor();
151 if (barColor) {
152 bar.SetForegroundColor(*barColor, false);
153 }
154 const auto margin = property->GetScrollBarMargin().value_or(ScrollBar2D::DEFAULT_MARGIN);
155 if (bar.GetScrollBarMargin() != margin) {
156 bar.SetScrollBarMargin(margin);
157 bar.FlushBarWidth();
158 }
159 }
160
UpdateBorderRadius(ScrollBar & bar,const RenderContext * ctx)161 void UpdateBorderRadius(ScrollBar& bar, const RenderContext* ctx)
162 {
163 CHECK_NULL_VOID(ctx);
164 const auto radius = ctx->GetBorderRadius();
165 if (radius && radius != bar.GetHostBorderRadius()) {
166 bar.SetHostBorderRadius(*radius);
167 bar.CalcReservedHeight();
168 }
169 }
170
GetOverScroll(float offset,float scrollableDistance)171 inline float GetOverScroll(float offset, float scrollableDistance)
172 {
173 return Positive(offset) ? offset : std::max(-(scrollableDistance + offset), 0.0f);
174 }
175 } // namespace
176
Update(const std::unique_ptr<ScrollBarProperty> & props)177 void ScrollBar2D::Update(const std::unique_ptr<ScrollBarProperty>& props)
178 {
179 CHECK_NULL_VOID(vertical_ && horizontal_);
180 ConfigureScrollBar(props, *vertical_);
181 ConfigureScrollBar(props, *horizontal_);
182
183 const PositionMode verticalMode = pattern_.IsRTL() ? PositionMode::LEFT : PositionMode::RIGHT;
184 vertical_->SetPositionMode(verticalMode);
185
186 const auto* renderContext = pattern_.GetRenderContext();
187 UpdateBorderRadius(*vertical_, renderContext);
188 UpdateBorderRadius(*horizontal_, renderContext);
189 }
190
191 namespace {
192 struct LayoutData {
193 SizeF viewSize;
194 Axis axis;
195 OffsetF offset;
196 float contentLength = 0.0f;
197 };
SyncScrollBarLayout(ScrollBar & bar,const LayoutData & data)198 void SyncScrollBarLayout(ScrollBar& bar, const LayoutData& data)
199 {
200 const float scrollableArea = data.contentLength - data.viewSize.MainSize(data.axis);
201 bar.SetScrollable(Positive(scrollableArea));
202 bar.ScheduleDisappearDelayTask();
203 bar.SetOutBoundary(GetOverScroll(data.offset.GetMainOffset(data.axis), scrollableArea));
204
205 bar.UpdateScrollBarRegion({}, { data.viewSize.Width(), data.viewSize.Height() },
206 { -data.offset.GetX(), -data.offset.GetY() }, data.contentLength, 0);
207 bar.MarkNeedRender();
208 }
209 } // namespace
210
SyncLayout(const OffsetF & offset,const SizeF & viewSize,const SizeF & content)211 void ScrollBar2D::SyncLayout(const OffsetF& offset, const SizeF& viewSize, const SizeF& content)
212 {
213 CHECK_NULL_VOID(vertical_ && horizontal_);
214 SyncScrollBarLayout(*vertical_, { viewSize, Axis::VERTICAL, offset, content.Height() });
215 SyncScrollBarLayout(*horizontal_, { viewSize, Axis::HORIZONTAL, offset, content.Width() });
216 }
217
ResetAnimationSignals()218 void ScrollBar2D::ResetAnimationSignals()
219 {
220 CHECK_NULL_VOID(vertical_ && horizontal_);
221 vertical_->SetHoverAnimationType(HoverAnimationType::NONE);
222 vertical_->SetOpacityAnimationType(OpacityAnimationType::NONE);
223 horizontal_->SetHoverAnimationType(HoverAnimationType::NONE);
224 horizontal_->SetOpacityAnimationType(OpacityAnimationType::NONE);
225 }
226
OnScrollStart()227 void ScrollBar2D::OnScrollStart()
228 {
229 CHECK_NULL_VOID(vertical_ && horizontal_);
230 vertical_->PlayScrollBarAppearAnimation();
231 horizontal_->PlayScrollBarAppearAnimation();
232 }
OnScrollEnd()233 void ScrollBar2D::OnScrollEnd()
234 {
235 CHECK_NULL_VOID(vertical_ && horizontal_);
236 vertical_->ScheduleDisappearDelayTask();
237 horizontal_->ScheduleDisappearDelayTask();
238 }
239 } // namespace OHOS::Ace::NG
240