ScrollAnimatorGtk.cpp [plain text]
#include "config.h"
#include "ScrollAnimatorGtk.h"
#include "ScrollAnimationSmooth.h"
#include "ScrollableArea.h"
#include "ScrollbarTheme.h"
#include <wtf/CurrentTime.h>
namespace WebCore {
static const double overflowScrollbarsAnimationDuration = 1;
static const double overflowScrollbarsAnimationHideDelay = 2;
std::unique_ptr<ScrollAnimator> ScrollAnimator::create(ScrollableArea& scrollableArea)
{
return std::make_unique<ScrollAnimatorGtk>(scrollableArea);
}
ScrollAnimatorGtk::ScrollAnimatorGtk(ScrollableArea& scrollableArea)
: ScrollAnimator(scrollableArea)
, m_overlayScrollbarAnimationTimer(*this, &ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired)
{
#if ENABLE(SMOOTH_SCROLLING)
if (scrollableArea.scrollAnimatorEnabled())
ensureSmoothScrollingAnimation();
#endif
}
ScrollAnimatorGtk::~ScrollAnimatorGtk()
{
}
#if ENABLE(SMOOTH_SCROLLING)
void ScrollAnimatorGtk::ensureSmoothScrollingAnimation()
{
if (m_animation)
return;
m_animation = std::make_unique<ScrollAnimationSmooth>(m_scrollableArea, m_currentPosition, [this](FloatPoint&& position) {
FloatSize delta = position - m_currentPosition;
m_currentPosition = WTFMove(position);
notifyPositionChanged(delta);
});
}
bool ScrollAnimatorGtk::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
{
if (!m_scrollableArea.scrollAnimatorEnabled() || granularity == ScrollByPrecisePixel)
return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
ensureSmoothScrollingAnimation();
return m_animation->scroll(orientation, granularity, step, multiplier);
}
void ScrollAnimatorGtk::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
{
FloatPoint position = ScrollableArea::scrollPositionFromOffset(offset, toFloatSize(m_scrollableArea.scrollOrigin()));
if (m_animation)
m_animation->setCurrentPosition(position);
FloatSize delta = position - m_currentPosition;
m_currentPosition = position;
notifyPositionChanged(delta);
}
void ScrollAnimatorGtk::willEndLiveResize()
{
if (m_animation)
m_animation->updateVisibleLengths();
}
#endif
void ScrollAnimatorGtk::didAddVerticalScrollbar(Scrollbar* scrollbar)
{
#if ENABLE(SMOOTH_SCROLLING)
if (m_animation)
m_animation->updateVisibleLengths();
#endif
if (!scrollbar->isOverlayScrollbar())
return;
m_verticalOverlayScrollbar = scrollbar;
if (!m_horizontalOverlayScrollbar)
m_overlayScrollbarAnimationCurrent = 1;
m_verticalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
hideOverlayScrollbars();
}
void ScrollAnimatorGtk::didAddHorizontalScrollbar(Scrollbar* scrollbar)
{
#if ENABLE(SMOOTH_SCROLLING)
if (m_animation)
m_animation->updateVisibleLengths();
#endif
if (!scrollbar->isOverlayScrollbar())
return;
m_horizontalOverlayScrollbar = scrollbar;
if (!m_verticalOverlayScrollbar)
m_overlayScrollbarAnimationCurrent = 1;
m_horizontalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
hideOverlayScrollbars();
}
void ScrollAnimatorGtk::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
{
if (m_verticalOverlayScrollbar != scrollbar)
return;
m_verticalOverlayScrollbar = nullptr;
if (!m_horizontalOverlayScrollbar)
m_overlayScrollbarAnimationCurrent = 0;
}
void ScrollAnimatorGtk::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
{
if (m_horizontalOverlayScrollbar != scrollbar)
return;
m_horizontalOverlayScrollbar = nullptr;
if (!m_verticalOverlayScrollbar)
m_overlayScrollbarAnimationCurrent = 0;
}
void ScrollAnimatorGtk::updateOverlayScrollbarsOpacity()
{
if (m_verticalOverlayScrollbar && m_overlayScrollbarAnimationCurrent != m_verticalOverlayScrollbar->opacity()) {
m_verticalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
if (m_verticalOverlayScrollbar->hoveredPart() == NoPart)
m_verticalOverlayScrollbar->invalidate();
}
if (m_horizontalOverlayScrollbar && m_overlayScrollbarAnimationCurrent != m_horizontalOverlayScrollbar->opacity()) {
m_horizontalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
if (m_horizontalOverlayScrollbar->hoveredPart() == NoPart)
m_horizontalOverlayScrollbar->invalidate();
}
}
static inline double easeOutCubic(double t)
{
double p = t - 1;
return p * p * p + 1;
}
void ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired()
{
if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
return;
if (m_overlayScrollbarsLocked)
return;
double currentTime = monotonicallyIncreasingTime();
double progress = 1;
if (currentTime < m_overlayScrollbarAnimationEndTime)
progress = (currentTime - m_overlayScrollbarAnimationStartTime) / (m_overlayScrollbarAnimationEndTime - m_overlayScrollbarAnimationStartTime);
progress = m_overlayScrollbarAnimationSource + (easeOutCubic(progress) * (m_overlayScrollbarAnimationTarget - m_overlayScrollbarAnimationSource));
if (progress != m_overlayScrollbarAnimationCurrent) {
m_overlayScrollbarAnimationCurrent = progress;
updateOverlayScrollbarsOpacity();
}
if (m_overlayScrollbarAnimationCurrent != m_overlayScrollbarAnimationTarget) {
static const double frameRate = 60;
static const double tickTime = 1 / frameRate;
static const double minimumTimerInterval = .001;
double deltaToNextFrame = std::max(tickTime - (monotonicallyIncreasingTime() - currentTime), minimumTimerInterval);
m_overlayScrollbarAnimationTimer.startOneShot(deltaToNextFrame);
} else
hideOverlayScrollbars();
}
void ScrollAnimatorGtk::showOverlayScrollbars()
{
if (m_overlayScrollbarsLocked)
return;
if (m_overlayScrollbarAnimationTimer.isActive() && m_overlayScrollbarAnimationTarget == 1)
return;
m_overlayScrollbarAnimationTimer.stop();
if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
return;
m_overlayScrollbarAnimationSource = m_overlayScrollbarAnimationCurrent;
m_overlayScrollbarAnimationTarget = 1;
if (m_overlayScrollbarAnimationTarget != m_overlayScrollbarAnimationCurrent) {
m_overlayScrollbarAnimationStartTime = monotonicallyIncreasingTime();
m_overlayScrollbarAnimationEndTime = m_overlayScrollbarAnimationStartTime + overflowScrollbarsAnimationDuration;
m_overlayScrollbarAnimationTimer.startOneShot(0);
} else
hideOverlayScrollbars();
}
void ScrollAnimatorGtk::hideOverlayScrollbars()
{
if (m_overlayScrollbarAnimationTimer.isActive() && !m_overlayScrollbarAnimationTarget)
return;
m_overlayScrollbarAnimationTimer.stop();
if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
return;
m_overlayScrollbarAnimationSource = m_overlayScrollbarAnimationCurrent;
m_overlayScrollbarAnimationTarget = 0;
if (m_overlayScrollbarAnimationTarget == m_overlayScrollbarAnimationCurrent)
return;
m_overlayScrollbarAnimationStartTime = monotonicallyIncreasingTime() + overflowScrollbarsAnimationHideDelay;
m_overlayScrollbarAnimationEndTime = m_overlayScrollbarAnimationStartTime + overflowScrollbarsAnimationDuration + overflowScrollbarsAnimationHideDelay;
m_overlayScrollbarAnimationTimer.startOneShot(overflowScrollbarsAnimationHideDelay);
}
void ScrollAnimatorGtk::mouseEnteredContentArea()
{
showOverlayScrollbars();
}
void ScrollAnimatorGtk::mouseExitedContentArea()
{
hideOverlayScrollbars();
}
void ScrollAnimatorGtk::mouseMovedInContentArea()
{
showOverlayScrollbars();
}
void ScrollAnimatorGtk::contentAreaDidShow()
{
showOverlayScrollbars();
}
void ScrollAnimatorGtk::contentAreaDidHide()
{
if (m_overlayScrollbarsLocked)
return;
m_overlayScrollbarAnimationTimer.stop();
if (m_overlayScrollbarAnimationCurrent) {
m_overlayScrollbarAnimationCurrent = 0;
updateOverlayScrollbarsOpacity();
}
}
void ScrollAnimatorGtk::notifyContentAreaScrolled(const FloatSize&)
{
showOverlayScrollbars();
}
void ScrollAnimatorGtk::lockOverlayScrollbarStateToHidden(bool shouldLockState)
{
if (m_overlayScrollbarsLocked == shouldLockState)
return;
m_overlayScrollbarsLocked = shouldLockState;
if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
return;
if (m_overlayScrollbarsLocked) {
m_overlayScrollbarAnimationTimer.stop();
if (m_horizontalOverlayScrollbar)
m_horizontalOverlayScrollbar->setOpacity(0);
if (m_verticalOverlayScrollbar)
m_verticalOverlayScrollbar->setOpacity(0);
} else {
if (m_overlayScrollbarAnimationCurrent == 1)
updateOverlayScrollbarsOpacity();
else
showOverlayScrollbars();
}
}
}