blob: 3707be08f3f6f3a7bcee311eecdf85c56a5e9e31 [file]
#include "rive/constraints/scrolling/scroll_bar_constraint.hpp"
#include "rive/constraints/scrolling/scroll_constraint.hpp"
#include "rive/constraints/scrolling/scroll_constraint_proxy.hpp"
#include "rive/constraints/scrolling/scroll_physics.hpp"
#include "rive/math/vec2d.hpp"
using namespace rive;
bool ViewportDraggableProxy::drag(Vec2D mousePosition, float timeStamp)
{
if (!m_constraint->interactive())
{
return false;
}
auto deltaPosition = mousePosition - m_lastPosition;
if (!m_isDragging)
{
switch (m_constraint->direction())
{
case DraggableConstraintDirection::vertical:
{
if (std::abs(deltaPosition.y) > m_constraint->threshold())
{
m_isDragging = true;
}
else
{
return false;
}
}
break;
case DraggableConstraintDirection::horizontal:
{
if (std::abs(deltaPosition.x) > m_constraint->threshold())
{
m_isDragging = true;
}
else
{
return false;
}
}
break;
case DraggableConstraintDirection::all:
{
if (deltaPosition.length() > m_constraint->threshold())
{
m_isDragging = true;
}
else
{
return false;
}
}
break;
}
}
m_constraint->dragView(deltaPosition, timeStamp);
m_lastPosition = mousePosition;
return true;
}
bool ViewportDraggableProxy::startDrag(Vec2D mousePosition, float timeStamp)
{
if (!m_constraint->interactive())
{
return false;
}
m_isDragging = false;
m_constraint->initPhysics();
m_lastPosition = mousePosition;
return true;
}
bool ViewportDraggableProxy::endDrag(Vec2D mousePosition, float timeStamp)
{
if (!m_constraint->interactive())
{
return false;
}
m_constraint->runPhysics();
return true;
}
bool ViewportDraggableProxy::wantsScroll(const ScrollEvent& event)
{
// A zero-delta event can't say which axis the gesture is on, so every
// branch tests the delta and it declines; the first directional one picks.
if (event.phase == ScrollPhase::inertiaCancel)
{
// Only matters to a view already coasting, and only one the wheel
// drives, as scroll would decline it anyway.
return m_constraint->wheelEnabled() &&
(m_constraint->isScrolling() ||
(m_constraint->physics() != nullptr &&
m_constraint->physics()->isRunning()));
}
if (event.precise)
{
// An elastic view stretches at its edge instead of chaining.
return m_constraint->canConsume(event.delta) ||
m_constraint->canStretch(event.delta);
}
return m_constraint->canConsume(event.delta);
}
bool ViewportDraggableProxy::acceptsScroll()
{
return m_constraint->wheelEnabled();
}
void ViewportDraggableProxy::cancelScroll()
{
m_constraint->stopPhysics();
m_constraint->clearVelocity();
m_constraint->endScrollGesture();
}
bool ViewportDraggableProxy::isScrollGestureActive()
{
return m_constraint->isScrolling();
}
bool ViewportDraggableProxy::scroll(const ScrollEvent& event, float timeStamp)
{
if (!m_constraint->wheelEnabled())
{
return false;
}
switch (event.phase)
{
case ScrollPhase::inertiaCancel:
m_constraint->stopPhysics();
m_constraint->clearVelocity();
m_constraint->endScrollGesture();
return true;
case ScrollPhase::begin:
m_constraint->beginScrollGesture();
m_constraint->primePhysics();
// The first update measures from here, not from the reset clock.
if (m_constraint->physics() != nullptr)
{
m_constraint->physics()->markTime(timeStamp);
}
return true;
case ScrollPhase::update:
if (event.precise)
{
// Re-prime for a new gesture, or to take the view back from a
// running simulation before it overwrites this drag.
bool started = m_constraint->beginScrollGesture();
bool running = m_constraint->physics() != nullptr &&
m_constraint->physics()->isRunning();
bool primed = started || running;
if (primed)
{
m_constraint->primePhysics();
}
else
{
// A settle can strip the helpers mid-gesture.
primed = m_constraint->ensurePhysicsPrimed();
}
// A primed clock has no earlier sample, so measuring this
// delta against it would divide by microseconds.
m_constraint->dragView(event.delta, timeStamp, !primed);
}
else
{
// A detent cancels inertia rather than being overwritten by
// it on the next advance.
if (m_constraint->physics() != nullptr &&
m_constraint->physics()->isRunning())
{
m_constraint->stopPhysics();
}
m_constraint->beginScrollGesture();
m_constraint->scrollBy(event.delta);
}
return true;
case ScrollPhase::momentum:
{
bool settling = m_constraint->physics() != nullptr &&
m_constraint->physics()->isRunning();
if (m_constraint->isOverscrolled() || settling)
{
// Inertia at the edge bounces now rather than stretching for
// the whole tail. Stays live so the latch holds.
if (!settling)
{
m_constraint->primePhysics();
m_constraint->startPhysics();
}
m_constraint->markScrollActivity();
return true;
}
// Platform momentum lands as-is; flinging on top would double it.
m_constraint->beginScrollGesture();
if (m_constraint->canStretch(event.delta) &&
m_constraint->canConsume(event.delta))
{
// The coast is tracked like a finger, so the delta that
// reaches an end stretches the band and then releases at
// the coast's speed, exactly as a drag does. A view already
// at its end stays put. A primed clock has no earlier
// sample to measure against.
bool primed = m_constraint->ensurePhysicsPrimed();
m_constraint->dragView(event.delta, timeStamp, !primed);
// Landing exactly on the end counts: the next delta could
// not move it, and the release carries it past from there.
if (m_constraint->isOverscrolled() ||
!m_constraint->canConsume(event.delta))
{
m_constraint->startPhysics();
}
}
else
{
m_constraint->scrollBy(event.delta);
}
return true;
}
case ScrollPhase::end:
m_constraint->startPhysics();
m_constraint->endScrollGesture();
return true;
}
return false;
}