1 /*
2  * Copyright (c) 2021 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/common/properties/scroll_bar.h"
17 
18 namespace OHOS::Ace {
19 
InBarRegion(const Point & point) const20 bool ScrollBar::InBarRegion(const Point& point) const
21 {
22     if (NeedScrollBar() && shapeMode_ == ShapeMode::RECT) {
23         return touchRegion_.IsInRegion(point);
24     }
25     return false;
26 }
27 
UpdateScrollBarRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)28 void ScrollBar::UpdateScrollBarRegion(
29     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
30 {
31     if (!NearZero(estimatedHeight)) {
32         SetBarRegion(offset, size);
33         if (shapeMode_ == ShapeMode::RECT) {
34             SetRectTrickRegion(offset, size, lastOffset, estimatedHeight);
35         } else {
36             SetRoundTrickRegion(offset, size, lastOffset, estimatedHeight);
37         }
38     }
39 }
40 
SetBarRegion(const Offset & offset,const Size & size)41 void ScrollBar::SetBarRegion(const Offset& offset, const Size& size)
42 {
43     double normalWidth = NormalizeToPx(normalWidth_);
44     if (shapeMode_ == ShapeMode::RECT) {
45         double height = std::max(size.Height() - NormalizeToPx(reservedHeight_), 0.0);
46         if (positionMode_ == PositionMode::LEFT) {
47             barRect_ = Rect(0.0, 0.0, normalWidth, height) + offset;
48         } else if (positionMode_ == PositionMode::RIGHT) {
49             barRect_ = Rect(size.Width() - normalWidth - NormalizeToPx(padding_.Right()), 0.0,
50                 normalWidth, height) + offset;
51         } else if (positionMode_ == PositionMode::BOTTOM) {
52             auto scrollBarWidth = std::max(size.Width() - NormalizeToPx(reservedHeight_), 0.0);
53             barRect_ = Rect(0.0, size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()),
54                 scrollBarWidth, normalWidth) + offset;
55         }
56     }
57 }
58 
SetRectTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)59 void ScrollBar::SetRectTrickRegion(const Offset& offset, const Size& size,
60     const Offset& lastOffset, double estimatedHeight)
61 {
62     double mainSize = (positionMode_ == PositionMode::BOTTOM ? size.Width() : size.Height());
63     double barRegionSize = std::max(mainSize - NormalizeToPx(reservedHeight_), 0.0);
64     double activeSize = barRegionSize * mainSize / estimatedHeight - outBoundary_;
65     if (!NearEqual(mainSize, estimatedHeight)) {
66         if (!NearZero(outBoundary_)) {
67             activeSize = std::max(
68                 std::max(activeSize, NormalizeToPx(minHeight_) - outBoundary_), NormalizeToPx(minDynamicHeight_));
69         } else {
70             activeSize = std::max(activeSize, NormalizeToPx(minHeight_));
71         }
72         double lastMainOffset =
73             std::max(positionMode_ == PositionMode::BOTTOM ? lastOffset.GetX() : lastOffset.GetY(), 0.0);
74         double activeMainOffset = (mainSize - activeSize) * lastMainOffset / (estimatedHeight - mainSize);
75         activeMainOffset = std::min(activeMainOffset, barRegionSize - activeSize);
76         double normalWidth = NormalizeToPx(normalWidth_);
77         if (positionMode_ == PositionMode::LEFT) {
78             activeRect_ = Rect(-NormalizeToPx(position_), activeMainOffset, normalWidth, activeSize) + offset;
79             touchRegion_ = activeRect_ + Size(NormalizeToPx(touchWidth_), 0);
80         } else if (positionMode_ == PositionMode::RIGHT) {
81             double x = size.Width() - normalWidth - NormalizeToPx(padding_.Right()) + NormalizeToPx(position_);
82             activeRect_ = Rect(x, activeMainOffset, normalWidth, activeSize) + offset;
83             // Update the hot region
84             touchRegion_ =
85                 activeRect_ -
86                 Offset(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Right()),
87                     0.0) +
88                 Size(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_), 0);
89         } else if (positionMode_ == PositionMode::BOTTOM) {
90             auto positionY =
91                 size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()) + NormalizeToPx(position_);
92             activeRect_ = Rect(activeMainOffset, positionY, activeSize, normalWidth) + offset;
93             auto hotRegionOffset =
94                 Offset(0.0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_)
95                     - NormalizeToPx(padding_.Bottom()));
96             auto hotRegionSize = Size(0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_));
97             touchRegion_ = activeRect_ - hotRegionOffset + hotRegionSize;
98         }
99     }
100 }
101 
SetRoundTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)102 void ScrollBar::SetRoundTrickRegion(const Offset& offset, const Size& size,
103     const Offset& lastOffset, double estimatedHeight)
104 {
105     double diameter = std::min(size.Width(), size.Height());
106     if (!NearEqual(estimatedHeight, diameter)) {
107         double maxAngle = bottomAngle_ - topAngle_;
108         trickSweepAngle_ = std::max(diameter * maxAngle / estimatedHeight, minAngle_);
109         double lastOffsetY = std::max(lastOffset.GetY(), 0.0);
110         double trickStartAngle = (maxAngle - trickSweepAngle_) * lastOffsetY / (estimatedHeight - diameter);
111         trickStartAngle = std::clamp(0.0, trickStartAngle, maxAngle) - maxAngle * FACTOR_HALF;
112         if (positionMode_ == PositionMode::LEFT) {
113             if (trickStartAngle > 0.0) {
114                 trickStartAngle_ = STRAIGHT_ANGLE - trickStartAngle;
115             } else {
116                 trickStartAngle_ = -(trickStartAngle + STRAIGHT_ANGLE);
117             }
118             trickSweepAngle_ = -trickSweepAngle_;
119         } else {
120             trickStartAngle_ = trickStartAngle;
121         }
122     }
123 }
124 
NeedScrollBar() const125 bool ScrollBar::NeedScrollBar() const
126 {
127     return displayMode_ == DisplayMode::AUTO || displayMode_ == DisplayMode::ON;
128 }
129 
NeedPaint() const130 bool ScrollBar::NeedPaint() const
131 {
132     return NeedScrollBar() && isScrollable_;
133 }
134 
GetNormalWidthToPx() const135 double ScrollBar::GetNormalWidthToPx() const
136 {
137     return NormalizeToPx(normalWidth_);
138 }
139 
InitScrollBar(const WeakPtr<RenderNode> & scroll,const WeakPtr<PipelineContext> & context)140 void ScrollBar::InitScrollBar(const WeakPtr<RenderNode>& scroll, const WeakPtr<PipelineContext>& context)
141 {
142     pipelineContext_ = context;
143     if (NeedScrollBar()) {
144         if (!barController_) {
145             barController_ = AceType::MakeRefPtr<ScrollBarController>();
146         }
147         bool isVertical = (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT);
148         barController_->Initialize(context, isVertical);
149         barController_->SetScrollNode(scroll);
150         barController_->SetActiveWidth(activeWidth_);
151         barController_->SetInactiveWidth(inactiveWidth_);
152     }
153 }
154 
NormalizeToPx(const Dimension & dimension) const155 double ScrollBar::NormalizeToPx(const Dimension& dimension) const
156 {
157     auto pipelineContext = pipelineContext_.Upgrade();
158     if (!pipelineContext) {
159         LOGE("NormalizeToPx failed");
160         return 0.0;
161     }
162     return pipelineContext->NormalizeToPx(dimension);
163 }
164 
SetCallBack(const ScrollBarPositionCallback & callback,const ScrollBarEndCallback & barEndCallback,const ScrollBarEventCallback & scrollEndCallback)165 void ScrollBar::SetCallBack(const ScrollBarPositionCallback& callback, const ScrollBarEndCallback& barEndCallback,
166     const ScrollBarEventCallback& scrollEndCallback)
167 {
168     if (barController_) {
169         barController_->SetCallback(callback);
170         barController_->SetBarEndCallback(barEndCallback);
171         barController_->SetScrollEndCallback(scrollEndCallback);
172         barController_->SetTouchDownCallback([weakScrollBar = AceType::WeakClaim(this)](double value) {
173             auto scrollBar = weakScrollBar.Upgrade();
174             if (!scrollBar) {
175                 LOGE("scrollBar is released");
176                 return;
177             }
178             // value is normalized before animation
179             scrollBar->normalWidth_ = Dimension(value, DimensionUnit::PX);
180         });
181         barController_->SetTouchUpCallback([weakScrollBar = AceType::WeakClaim(this)](double value) {
182             auto scrollBar = weakScrollBar.Upgrade();
183             if (!scrollBar) {
184                 LOGE("scrollBar is released");
185                 return;
186             }
187             // value is normalized before animation
188             scrollBar->normalWidth_ = Dimension(value, DimensionUnit::PX);
189         });
190     }
191 }
192 
HandleScrollBarEnd()193 void ScrollBar::HandleScrollBarEnd()
194 {
195     if (displayMode_ == DisplayMode::AUTO) {
196         barController_->HandleScrollBarEnd();
197     }
198 }
199 
AddScrollBarController(const Offset & coordinateOffset,TouchTestResult & result)200 void ScrollBar::AddScrollBarController(const Offset& coordinateOffset, TouchTestResult& result)
201 {
202     if (barController_) {
203         barController_->SetCoordinateOffset(coordinateOffset);
204         result.emplace_back(barController_);
205     }
206 }
207 
SetActive(bool isActive)208 void ScrollBar::SetActive(bool isActive)
209 {
210     if (barController_) {
211         barController_->SetActive(isActive);
212     }
213 }
214 
IsActive() const215 bool ScrollBar::IsActive() const
216 {
217     if (barController_) {
218         return barController_->IsActive();
219     }
220     return false;
221 }
222 
GetRootSize() const223 Size ScrollBar::GetRootSize() const
224 {
225     auto context = pipelineContext_.Upgrade();
226     if (context) {
227         auto rootHeight = context->GetRootHeight();
228         auto rootWidth = context->GetRootWidth();
229         return Size(rootWidth, rootHeight);
230     } else {
231         return Size();
232     }
233 }
234 
Reset(const RefPtr<ScrollBar> & scrollBar)235 void ScrollBar::Reset(const RefPtr<ScrollBar>& scrollBar)
236 {
237     if (scrollBar) {
238         displayMode_ = scrollBar->GetDisplayMode();
239         backgroundColor_ = scrollBar->GetBackgroundColor();
240         foregroundColor_ = scrollBar->GetForegroundColor();
241         inactiveWidth_ = scrollBar->GetInactiveWidth();
242         normalWidth_ = scrollBar->GetNormalWidth();
243         activeWidth_ = scrollBar->GetActiveWidth();
244         touchWidth_ = scrollBar->GetTouchWidth();
245     }
246     if (!barController_) {
247         return;
248     }
249     if (displayMode_ == DisplayMode::AUTO) {
250         barController_->HandleScrollBarEnd();
251         return;
252     }
253     barController_->Reset();
254 }
255 
256 } // namespace OHOS::Ace