2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h>
22 #include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
23 #include <dali-toolkit/internal/controls/scrollable/scrollable-impl.h>
24 #include <dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h>
30 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
32 // local helper function to resize the height of the bounce actor
33 float GetBounceActorHeight(float width, float defaultHeight)
35 return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? defaultHeight : defaultHeight * 0.5f;
38 const float MAX_OVERSHOOT_NOTIFY_AMOUNT = 0.99f; // maximum amount to set notification for increased overshoot, beyond this we just wait for it to reduce again
39 const float MIN_OVERSHOOT_NOTIFY_AMOUNT = Math::MACHINE_EPSILON_0; // minimum amount to set notification for reduced overshoot, beyond this we just wait for it to increase again
40 const float OVERSHOOT_NOTIFY_STEP = 0.01f; // amount to set notifications beyond current overshoot value
50 ScrollOvershootIndicator::ScrollOvershootIndicator()
56 ScrollOvershootIndicator::~ScrollOvershootIndicator()
60 ScrollOvershootIndicator* ScrollOvershootIndicator::New()
62 ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator();
63 return scrollOvershootPtr;
66 void ScrollOvershootIndicator::AttachToScrollable(Scrollable& scrollable)
70 mEffectX = ScrollOvershootEffectRipple::New(false, scrollable);
75 mEffectY = ScrollOvershootEffectRipple::New(true, scrollable);
80 void ScrollOvershootIndicator::DetachFromScrollable(Scrollable& scrollable)
84 mEffectX->Remove(scrollable);
88 mEffectY->Remove(scrollable);
92 void ScrollOvershootIndicator::Reset()
98 void ScrollOvershootIndicator::SetOvershootEffectColor(const Vector4& color)
102 mEffectX->SetOvershootEffectColor(color);
106 mEffectY->SetOvershootEffectColor(color);
110 ScrollOvershootEffect::ScrollOvershootEffect(bool vertical)
111 : mVertical(vertical)
115 bool ScrollOvershootEffect::IsVertical() const
120 ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical, Scrollable& scrollable)
121 : ScrollOvershootEffect(vertical),
122 mAttachedScrollView(scrollable),
123 mOvershootProperty(Property::INVALID_INDEX),
124 mEffectOvershootProperty(Property::INVALID_INDEX),
126 mOvershootSize(scrollable.GetOvershootSize()),
127 mAnimationStateFlags(0)
129 mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
130 mOvershootOverlay.SetProperty(Actor::Property::COLOR, mAttachedScrollView.GetOvershootEffectColor());
131 mOvershootOverlay.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
132 mOvershootOverlay.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
133 mOvershootOverlay.SetProperty(Actor::Property::VISIBLE, false);
136 void ScrollOvershootEffectRipple::Apply()
138 Actor self = mAttachedScrollView.Self();
139 mOvershootProperty = IsVertical() ? Toolkit::ScrollView::Property::OVERSHOOT_Y : Toolkit::ScrollView::Property::OVERSHOOT_X;
141 // make sure height is set, since we only create a constraint for image width
142 mOvershootSize = mAttachedScrollView.GetOvershootSize();
143 mOvershootOverlay.SetProperty(Actor::Property::SIZE, mOvershootSize);
145 mAttachedScrollView.AddOverlay(mOvershootOverlay);
147 UpdatePropertyNotifications();
150 void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable)
152 if(mOvershootOverlay)
154 if(mOvershootIncreaseNotification)
156 scrollable.Self().RemovePropertyNotification(mOvershootIncreaseNotification);
157 mOvershootIncreaseNotification.Reset();
159 if(mOvershootDecreaseNotification)
161 scrollable.Self().RemovePropertyNotification(mOvershootDecreaseNotification);
162 mOvershootDecreaseNotification.Reset();
164 scrollable.RemoveOverlay(mOvershootOverlay);
168 void ScrollOvershootEffectRipple::Reset()
170 mOvershootOverlay.SetProperty(Actor::Property::VISIBLE, false);
171 mOvershootOverlay.SetProperty(mEffectOvershootProperty, 0.f);
174 void ScrollOvershootEffectRipple::UpdatePropertyNotifications()
176 float absOvershoot = fabsf(mOvershoot);
178 Actor self = mAttachedScrollView.Self();
179 // update overshoot increase notify
180 if(mOvershootIncreaseNotification)
182 self.RemovePropertyNotification(mOvershootIncreaseNotification);
183 mOvershootIncreaseNotification.Reset();
185 if(absOvershoot < MAX_OVERSHOOT_NOTIFY_AMOUNT)
187 float increaseStep = absOvershoot + OVERSHOOT_NOTIFY_STEP;
188 if(increaseStep > MAX_OVERSHOOT_NOTIFY_AMOUNT)
190 increaseStep = MAX_OVERSHOOT_NOTIFY_AMOUNT;
192 mOvershootIncreaseNotification = self.AddPropertyNotification(mOvershootProperty, OutsideCondition(-increaseStep, increaseStep));
193 mOvershootIncreaseNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_TRUE);
194 mOvershootIncreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
197 // update overshoot decrease notify
198 if(mOvershootDecreaseNotification)
200 self.RemovePropertyNotification(mOvershootDecreaseNotification);
201 mOvershootDecreaseNotification.Reset();
203 if(absOvershoot > MIN_OVERSHOOT_NOTIFY_AMOUNT)
205 float reduceStep = absOvershoot - OVERSHOOT_NOTIFY_STEP;
206 if(reduceStep < MIN_OVERSHOOT_NOTIFY_AMOUNT)
208 reduceStep = MIN_OVERSHOOT_NOTIFY_AMOUNT;
210 mOvershootDecreaseNotification = self.AddPropertyNotification(mOvershootProperty, InsideCondition(-reduceStep, reduceStep));
211 mOvershootDecreaseNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_TRUE);
212 mOvershootDecreaseNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootNotification);
216 void ScrollOvershootEffectRipple::SetOvershootEffectColor(const Vector4& color)
218 if(mOvershootOverlay)
220 mOvershootOverlay.SetProperty(Actor::Property::COLOR, color);
224 void ScrollOvershootEffectRipple::UpdateVisibility(bool visible)
226 mOvershootOverlay.SetProperty(Actor::Property::VISIBLE, visible);
227 // make sure overshoot image is correctly placed
230 Actor self = mAttachedScrollView.Self();
231 if(mOvershoot > 0.0f)
233 // positive overshoot
234 const Vector3 size = mOvershootOverlay.GetCurrentProperty<Vector3>(Actor::Property::SIZE);
235 Vector3 relativeOffset;
236 const Vector3 parentSize = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE);
239 mOvershootOverlay.SetProperty(Actor::Property::ORIENTATION, Quaternion(Quaternion(Radian(0.0f), Vector3::ZAXIS)));
240 mOvershootOverlay.SetProperty(Actor::Property::SIZE, Vector3(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth));
244 mOvershootOverlay.SetProperty(Actor::Property::ORIENTATION, Quaternion(Quaternion(Radian(1.5f * Math::PI), Vector3::ZAXIS)));
245 mOvershootOverlay.SetProperty(Actor::Property::SIZE, Vector3(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth));
246 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
248 mOvershootOverlay.SetProperty(Actor::Property::POSITION, relativeOffset * parentSize);
252 // negative overshoot
253 const Vector3 size = mOvershootOverlay.GetCurrentProperty<Vector3>(Actor::Property::SIZE);
254 Vector3 relativeOffset;
255 const Vector3 parentSize = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE);
258 mOvershootOverlay.SetProperty(Actor::Property::ORIENTATION, Quaternion(Quaternion(Radian(Math::PI), Vector3::ZAXIS)));
259 mOvershootOverlay.SetProperty(Actor::Property::SIZE, Vector3(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth));
260 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
264 mOvershootOverlay.SetProperty(Actor::Property::ORIENTATION, Quaternion(Quaternion(Radian(0.5f * Math::PI), Vector3::ZAXIS)));
265 mOvershootOverlay.SetProperty(Actor::Property::SIZE, Vector3(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth));
266 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
268 mOvershootOverlay.SetProperty(Actor::Property::POSITION, relativeOffset * parentSize);
273 void ScrollOvershootEffectRipple::OnOvershootNotification(PropertyNotification& source)
275 Actor self = mAttachedScrollView.Self();
276 mOvershoot = self.GetCurrentProperty<float>(mOvershootProperty);
277 SetOvershoot(mOvershoot, false);
278 UpdatePropertyNotifications();
281 void ScrollOvershootEffectRipple::SetOvershoot(float amount, bool animate)
283 float absAmount = fabsf(amount);
284 bool animatingOn = absAmount > Math::MACHINE_EPSILON_0;
285 if((animatingOn && (mAnimationStateFlags & AnimatingIn)))
287 // trying to do what we are already doing
288 if(mAnimationStateFlags & AnimateBack)
290 mAnimationStateFlags &= ~AnimateBack;
294 if((!animatingOn && (mAnimationStateFlags & AnimatingOut)))
296 // trying to do what we are already doing
299 if(!animatingOn && (mAnimationStateFlags & AnimatingIn))
301 // dont interrupt while animating on
302 mAnimationStateFlags |= AnimateBack;
306 if(absAmount > Math::MACHINE_EPSILON_1)
308 UpdateVisibility(true);
311 float overshootAnimationSpeed = mAttachedScrollView.Self().GetProperty<float>(Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED);
313 if(animate && overshootAnimationSpeed > Math::MACHINE_EPSILON_0)
315 float currentOvershoot = fabsf(mOvershootOverlay.GetProperty(mEffectOvershootProperty).Get<float>());
316 float duration = mOvershootOverlay.GetCurrentProperty<Vector3>(Actor::Property::SIZE).height * (animatingOn ? (1.0f - currentOvershoot) : currentOvershoot) / overshootAnimationSpeed;
318 if(duration > Math::MACHINE_EPSILON_0)
320 if(mScrollOvershootAnimation)
322 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
323 mScrollOvershootAnimation.Stop();
324 mScrollOvershootAnimation.Reset();
326 mScrollOvershootAnimation = Animation::New(duration);
327 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
328 mScrollOvershootAnimation.AnimateTo(Property(mOvershootOverlay, mEffectOvershootProperty), amount, TimePeriod(duration));
329 mScrollOvershootAnimation.Play();
330 mAnimationStateFlags = animatingOn ? AnimatingIn : AnimatingOut;
335 mOvershootOverlay.SetProperty(mEffectOvershootProperty, amount);
339 void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation)
341 bool animateOff = false;
342 if(mAnimationStateFlags & AnimatingOut)
344 // should now be offscreen
345 mOvershootOverlay.SetProperty(Actor::Property::VISIBLE, false);
347 if((mAnimationStateFlags & AnimateBack))
351 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished);
352 mScrollOvershootAnimation.Stop();
353 mScrollOvershootAnimation.Reset();
354 mAnimationStateFlags = 0;
357 SetOvershoot(0.0f, true);
361 ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New(bool vertical, Scrollable& scrollable)
363 return new ScrollOvershootEffectRipple(vertical, scrollable);
366 } // namespace Internal
368 } // namespace Toolkit