blob: 5e2a17b8b23ae464b7658ae9db2156d3a5c048b7 [file] [log] [blame]
#include "rive/constraints/draggable_constraint.hpp"
#include "rive/constraints/scrolling/scroll_bar_constraint.hpp"
#include "rive/constraints/scrolling/scroll_bar_constraint_proxy.hpp"
#include "rive/constraints/transform_constraint.hpp"
#include "rive/core_context.hpp"
#include "rive/layout_component.hpp"
#include "rive/transform_component.hpp"
#include "rive/math/mat2d.hpp"
using namespace rive;
float ScrollBarConstraint::computedThumbWidth()
{
if (autoSize() && m_scrollConstraint != nullptr)
{
return track()->innerWidth() * m_scrollConstraint->visibleWidthRatio();
}
return thumb()->layoutWidth();
}
float ScrollBarConstraint::computedThumbHeight()
{
if (autoSize() && m_scrollConstraint != nullptr)
{
return track()->innerHeight() *
m_scrollConstraint->visibleHeightRatio();
}
return thumb()->layoutHeight();
}
std::vector<DraggableProxy*> ScrollBarConstraint::draggables()
{
std::vector<DraggableProxy*> items;
if (parent()->is<LayoutComponent>())
{
items.push_back(
new ThumbDraggableProxy(this,
parent()->as<LayoutComponent>()->proxy()));
}
if (parent()->parent() != nullptr &&
parent()->parent()->is<LayoutComponent>())
{
items.push_back(new TrackDraggableProxy(
this,
parent()->parent()->as<LayoutComponent>()->proxy()));
}
return items;
}
void ScrollBarConstraint::constrain(TransformComponent* component)
{
if (m_scrollConstraint == nullptr || track() == nullptr ||
thumb() == nullptr)
{
return;
}
float thumbOffsetX = 0;
float thumbOffsetY = 0;
if (constrainsHorizontal())
{
auto innerWidth = track()->innerWidth();
auto thumbWidth = computedThumbWidth();
auto maxThumbOffset = innerWidth - thumbWidth;
thumbOffsetX = (m_scrollConstraint->maxOffsetX() == 0)
? 0
: m_scrollConstraint->clampedOffsetX() /
m_scrollConstraint->maxOffsetX() *
maxThumbOffset;
if (thumbOffsetX < 0)
{
thumbWidth += thumbOffsetX;
thumbOffsetX = 0;
}
else if (thumbOffsetX > maxThumbOffset)
{
thumbWidth -= thumbOffsetX - maxThumbOffset;
thumbOffsetX = autoSize() ? thumbOffsetX : maxThumbOffset;
}
if (autoSize())
{
thumb()->forcedWidth(thumbWidth);
}
}
if (constrainsVertical())
{
auto innerHeight = track()->innerHeight();
auto thumbHeight = computedThumbHeight();
auto maxThumbOffset = innerHeight - thumbHeight;
thumbOffsetY = (m_scrollConstraint->maxOffsetY() == 0)
? 0
: m_scrollConstraint->clampedOffsetY() /
m_scrollConstraint->maxOffsetY() *
maxThumbOffset;
if (thumbOffsetY < 0)
{
thumbHeight += thumbOffsetY;
thumbOffsetY = 0;
}
else if (thumbOffsetY > maxThumbOffset)
{
thumbHeight -= thumbOffsetY - maxThumbOffset;
thumbOffsetY = autoSize() ? thumbOffsetY : maxThumbOffset;
}
if (autoSize())
{
thumb()->forcedHeight(thumbHeight);
}
}
auto targetTransform =
Mat2D::multiply(component->worldTransform(),
Mat2D::fromTranslate(thumbOffsetX, thumbOffsetY));
TransformConstraint::constrainWorld(component,
component->worldTransform(),
m_componentsA,
targetTransform,
m_componentsB,
strength());
}
void ScrollBarConstraint::buildDependencies()
{
m_scrollConstraint->addDependent(this);
Super::buildDependencies();
}
StatusCode ScrollBarConstraint::onAddedDirty(CoreContext* context)
{
StatusCode result = Super::onAddedDirty(context);
if (result != StatusCode::Ok)
{
return result;
}
auto coreObject = context->resolve(scrollConstraintId());
if (coreObject == nullptr || !coreObject->is<ScrollConstraint>())
{
return StatusCode::MissingObject;
}
m_scrollConstraint = static_cast<ScrollConstraint*>(coreObject);
return StatusCode::Ok;
}
void ScrollBarConstraint::hitTrack(Vec2D worldPosition)
{
if (m_scrollConstraint == nullptr || track() == nullptr)
{
return;
}
Mat2D inverseWorld;
if (!track()->worldTransform().invert(&inverseWorld))
{
return;
}
auto localPosition = inverseWorld * worldPosition;
if (constrainsHorizontal())
{
localPosition.x -= track()->paddingLeft();
auto innerWidth = track()->innerWidth();
auto thumbWidth = computedThumbWidth();
auto trackRange = innerWidth - thumbWidth;
auto maxOffsetX = m_scrollConstraint->maxOffsetX();
m_scrollConstraint->offsetX(
math::clamp(localPosition.x / trackRange * maxOffsetX,
maxOffsetX,
0));
}
if (constrainsVertical())
{
localPosition.y -= track()->paddingTop();
auto innerHeight = track()->innerHeight();
auto thumbHeight = computedThumbHeight();
auto trackRange = innerHeight - thumbHeight;
auto maxOffsetY = m_scrollConstraint->maxOffsetY();
m_scrollConstraint->offsetY(
math::clamp(localPosition.y / trackRange * maxOffsetY,
maxOffsetY,
0));
}
}
void ScrollBarConstraint::dragThumb(Vec2D delta)
{
if (m_scrollConstraint == nullptr || thumb() == nullptr ||
track() == nullptr)
{
return;
}
if (constrainsHorizontal())
{
auto innerWidth = track()->innerWidth();
auto thumbWidth = computedThumbWidth();
if (autoSize())
{
thumb()->forcedWidth(thumbWidth);
}
auto trackRange = innerWidth - thumbWidth;
auto maxOffsetX = m_scrollConstraint->maxOffsetX();
auto thumbOffset =
(m_scrollConstraint->offsetX() / maxOffsetX * trackRange) + delta.x;
m_scrollConstraint->offsetX(
math::clamp((thumbOffset / trackRange * maxOffsetX),
maxOffsetX,
0));
}
if (constrainsVertical())
{
auto innerHeight = track()->innerHeight();
auto thumbHeight = computedThumbHeight();
if (autoSize())
{
thumb()->forcedHeight(thumbHeight);
}
auto trackRange = innerHeight - thumbHeight;
auto maxOffsetY = m_scrollConstraint->maxOffsetY();
auto thumbOffset =
(m_scrollConstraint->offsetY() / maxOffsetY * trackRange) + delta.y;
m_scrollConstraint->offsetY(
math::clamp((thumbOffset / trackRange * maxOffsetY),
maxOffsetY,
0));
}
}
bool ScrollBarConstraint::validate(CoreContext* context)
{
auto coreObject = context->resolve(scrollConstraintId());
return coreObject != nullptr && coreObject->is<ScrollConstraint>();
}