https://bugs.webkit.org/show_bug.cgi?id=155750
Patch by Adrien Plazas <aplazas@igalia.com> on 2017-06-09
Reviewed by Carlos Garcia Campos.
Patch by Adrien Plazas and Yusuke Suzuki.
Source/WebCore:
Add the ScrollAnimationKinetic scrolling animation allowing to perform momentum scrolling; it is based on GTK+'s
GtkKineticScrolling type.
Add the notion of phase, momentum phase and swipe velocity to PlatformWheelEvent.
Depending on whether the scrolling ended normally or triggered a swipe, the scroll animator will either compute
the swipe velocity from the previous scrolling events or use the one from the swipe gesture to initiate the
momentum scrolling.
* PlatformGTK.cmake:
* platform/PlatformWheelEvent.h:
(WebCore::PlatformWheelEvent::setHasPreciseScrollingDeltas):
(WebCore::PlatformWheelEvent::phase):
(WebCore::PlatformWheelEvent::momentumPhase):
(WebCore::PlatformWheelEvent::isTransitioningToMomentumScroll):
* platform/ScrollAnimation.h:
(WebCore::ScrollAnimation::scroll):
(WebCore::ScrollAnimation::updateVisibleLengths):
(WebCore::ScrollAnimation::setCurrentPosition):
* platform/ScrollAnimationKinetic.cpp: Added.
(WebCore::ScrollAnimationKinetic::PerAxisData::PerAxisData):
(WebCore::ScrollAnimationKinetic::PerAxisData::animateScroll):
(WebCore::ScrollAnimationKinetic::ScrollAnimationKinetic):
(WebCore::ScrollAnimationKinetic::~ScrollAnimationKinetic):
(WebCore::ScrollAnimationKinetic::stop):
(WebCore::ScrollAnimationKinetic::start):
(WebCore::ScrollAnimationKinetic::animationTimerFired):
* platform/ScrollAnimationKinetic.h: Copied from Source/WebCore/platform/ScrollAnimation.h.
* platform/gtk/PlatformWheelEventGtk.cpp:
(WebCore::PlatformWheelEvent::PlatformWheelEvent):
(WebCore::PlatformWheelEvent::swipeVelocity):
* platform/gtk/ScrollAnimatorGtk.cpp:
(WebCore::ScrollAnimatorGtk::ScrollAnimatorGtk):
(WebCore::ScrollAnimatorGtk::ensureSmoothScrollingAnimation):
(WebCore::ScrollAnimatorGtk::scroll):
(WebCore::ScrollAnimatorGtk::scrollToOffsetWithoutAnimation):
(WebCore::ScrollAnimatorGtk::computeVelocity):
(WebCore::ScrollAnimatorGtk::handleWheelEvent):
(WebCore::ScrollAnimatorGtk::willEndLiveResize):
(WebCore::ScrollAnimatorGtk::updatePosition):
(WebCore::ScrollAnimatorGtk::didAddVerticalScrollbar):
(WebCore::ScrollAnimatorGtk::didAddHorizontalScrollbar):
* platform/gtk/ScrollAnimatorGtk.h:
Source/WebKit2:
Add the notion of phase and momentum phase to WebWheelEvent.
Make WebWheelEvent manage the 'is_stop' attribute of GdkEventScroll to create the corresponding WebWheelEvent
with the correct phases and deltas.
Make GestureController manage swipes to create the corresponding WebWheelEvent with the correct phases and
deltas.
* Shared/NativeWebWheelEvent.h:
* Shared/WebEvent.h:
* Shared/WebEventConversion.cpp:
(WebKit::WebKit2PlatformWheelEvent::WebKit2PlatformWheelEvent):
* Shared/WebWheelEvent.cpp:
(WebKit::WebWheelEvent::WebWheelEvent):
(WebKit::WebWheelEvent::encode):
(WebKit::WebWheelEvent::decode):
* Shared/gtk/NativeWebWheelEventGtk.cpp:
(WebKit::NativeWebWheelEvent::NativeWebWheelEvent):
* Shared/gtk/WebEventFactory.cpp:
(WebKit::WebEventFactory::createWebWheelEvent):
* Shared/gtk/WebEventFactory.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::handleWheelEvent):
(WebKit::WebPageProxy::shouldProcessWheelEventNow):
* UIProcess/WebPageProxy.h:
* UIProcess/gtk/GestureController.cpp:
(WebKit::GestureController::GestureController):
(WebKit::GestureController::handleEvent):
(WebKit::GestureController::isProcessingGestures):
(WebKit::createScrollEvent):
(WebKit::GestureController::DragGesture::startDrag):
(WebKit::GestureController::DragGesture::handleDrag):
(WebKit::GestureController::DragGesture::begin):
(WebKit::GestureController::DragGesture::end):
(WebKit::GestureController::SwipeGesture::startMomentumScroll):
(WebKit::GestureController::SwipeGesture::swipe):
(WebKit::GestureController::SwipeGesture::SwipeGesture):
* UIProcess/gtk/GestureController.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@217971
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2017-06-09 Adrien Plazas <aplazas@igalia.com>
+
+ [GTK] Add kinetic scrolling
+ https://bugs.webkit.org/show_bug.cgi?id=155750
+
+ Reviewed by Carlos Garcia Campos.
+
+ Patch by Adrien Plazas and Yusuke Suzuki.
+
+ Add the ScrollAnimationKinetic scrolling animation allowing to perform momentum scrolling; it is based on GTK+'s
+ GtkKineticScrolling type.
+
+ Add the notion of phase, momentum phase and swipe velocity to PlatformWheelEvent.
+
+ Depending on whether the scrolling ended normally or triggered a swipe, the scroll animator will either compute
+ the swipe velocity from the previous scrolling events or use the one from the swipe gesture to initiate the
+ momentum scrolling.
+
+ * PlatformGTK.cmake:
+ * platform/PlatformWheelEvent.h:
+ (WebCore::PlatformWheelEvent::setHasPreciseScrollingDeltas):
+ (WebCore::PlatformWheelEvent::phase):
+ (WebCore::PlatformWheelEvent::momentumPhase):
+ (WebCore::PlatformWheelEvent::isTransitioningToMomentumScroll):
+ * platform/ScrollAnimation.h:
+ (WebCore::ScrollAnimation::scroll):
+ (WebCore::ScrollAnimation::updateVisibleLengths):
+ (WebCore::ScrollAnimation::setCurrentPosition):
+ * platform/ScrollAnimationKinetic.cpp: Added.
+ (WebCore::ScrollAnimationKinetic::PerAxisData::PerAxisData):
+ (WebCore::ScrollAnimationKinetic::PerAxisData::animateScroll):
+ (WebCore::ScrollAnimationKinetic::ScrollAnimationKinetic):
+ (WebCore::ScrollAnimationKinetic::~ScrollAnimationKinetic):
+ (WebCore::ScrollAnimationKinetic::stop):
+ (WebCore::ScrollAnimationKinetic::start):
+ (WebCore::ScrollAnimationKinetic::animationTimerFired):
+ * platform/ScrollAnimationKinetic.h: Copied from Source/WebCore/platform/ScrollAnimation.h.
+ * platform/gtk/PlatformWheelEventGtk.cpp:
+ (WebCore::PlatformWheelEvent::PlatformWheelEvent):
+ (WebCore::PlatformWheelEvent::swipeVelocity):
+ * platform/gtk/ScrollAnimatorGtk.cpp:
+ (WebCore::ScrollAnimatorGtk::ScrollAnimatorGtk):
+ (WebCore::ScrollAnimatorGtk::ensureSmoothScrollingAnimation):
+ (WebCore::ScrollAnimatorGtk::scroll):
+ (WebCore::ScrollAnimatorGtk::scrollToOffsetWithoutAnimation):
+ (WebCore::ScrollAnimatorGtk::computeVelocity):
+ (WebCore::ScrollAnimatorGtk::handleWheelEvent):
+ (WebCore::ScrollAnimatorGtk::willEndLiveResize):
+ (WebCore::ScrollAnimatorGtk::updatePosition):
+ (WebCore::ScrollAnimatorGtk::didAddVerticalScrollbar):
+ (WebCore::ScrollAnimatorGtk::didAddHorizontalScrollbar):
+ * platform/gtk/ScrollAnimatorGtk.h:
+
2017-06-09 Zan Dobersek <zdobersek@igalia.com>
[GCrypt] ECDSA signing results can be smaller than the EC key size
page/linux/ResourceUsageThreadLinux.cpp
platform/KillRingNone.cpp
+ platform/ScrollAnimationKinetic.cpp
platform/StaticPasteboard.cpp
platform/UserAgentQuirks.cpp
ScrollByPixelWheelEvent,
};
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
enum PlatformWheelEventPhase : uint8_t {
PlatformWheelEventPhaseNone = 0,
#if PLATFORM(GTK)
explicit PlatformWheelEvent(GdkEventScroll*);
+ FloatPoint swipeVelocity() const;
#endif
#if PLATFORM(COCOA)
bool hasPreciseScrollingDeltas() const { return m_hasPreciseScrollingDeltas; }
void setHasPreciseScrollingDeltas(bool hasPreciseScrollingDeltas) { m_hasPreciseScrollingDeltas = hasPreciseScrollingDeltas; }
- PlatformWheelEventPhase phase() const { return m_phase; }
- PlatformWheelEventPhase momentumPhase() const { return m_momentumPhase; }
unsigned scrollCount() const { return m_scrollCount; }
float unacceleratedScrollingDeltaX() const { return m_unacceleratedScrollingDeltaX; }
float unacceleratedScrollingDeltaY() const { return m_unacceleratedScrollingDeltaY; }
bool shouldConsiderLatching() const;
bool shouldResetLatching() const;
bool isEndOfMomentumScroll() const;
- bool isEndOfNonMomentumScroll() const;
- bool isTransitioningToMomentumScroll() const;
#else
bool useLatchedEventElement() const { return false; }
#endif
+#if PLATFORM(COCOA) || PLATFORM(GTK)
+ PlatformWheelEventPhase phase() const { return m_phase; }
+ PlatformWheelEventPhase momentumPhase() const { return m_momentumPhase; }
+ bool isEndOfNonMomentumScroll() const;
+ bool isTransitioningToMomentumScroll() const;
+#endif
+
#if PLATFORM(WIN)
PlatformWheelEvent(HWND, WPARAM, LPARAM, bool isMouseHWheel);
PlatformWheelEvent(HWND, const FloatSize& delta, const FloatPoint& location);
// Scrolling velocity in pixels per second.
FloatSize m_scrollingVelocity;
-#if PLATFORM(COCOA)
- bool m_hasPreciseScrollingDeltas { false };
+#if PLATFORM(COCOA) || PLATFORM(GTK)
PlatformWheelEventPhase m_phase { PlatformWheelEventPhaseNone };
PlatformWheelEventPhase m_momentumPhase { PlatformWheelEventPhaseNone };
+#endif
+#if PLATFORM(COCOA)
+ bool m_hasPreciseScrollingDeltas { false };
unsigned m_scrollCount { 0 };
float m_unacceleratedScrollingDeltaX { 0 };
float m_unacceleratedScrollingDeltaY { 0 };
return m_phase == PlatformWheelEventPhaseNone && m_momentumPhase == PlatformWheelEventPhaseEnded;
}
+#endif
+
+#if PLATFORM(COCOA) || PLATFORM(GTK)
+
inline bool PlatformWheelEvent::isEndOfNonMomentumScroll() const
{
return m_phase == PlatformWheelEventPhaseEnded && m_momentumPhase == PlatformWheelEventPhaseNone;
{
return m_phase == PlatformWheelEventPhaseNone && m_momentumPhase == PlatformWheelEventPhaseBegan;
}
-
#endif
} // namespace WebCore
class ScrollAnimation {
public:
virtual ~ScrollAnimation() { };
- virtual bool scroll(ScrollbarOrientation, ScrollGranularity, float step, float multiplier) = 0;
+ virtual bool scroll(ScrollbarOrientation, ScrollGranularity, float /* step */, float /* multiplier */) { return true; };
virtual void stop() = 0;
- virtual void updateVisibleLengths() = 0;
- virtual void setCurrentPosition(const FloatPoint&) = 0;
+ virtual void updateVisibleLengths() { };
+ virtual void setCurrentPosition(const FloatPoint&) { };
virtual void serviceAnimation() { };
protected:
--- /dev/null
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ScrollAnimationKinetic.h"
+
+#include "ScrollableArea.h"
+#include <wtf/CurrentTime.h>
+
+/*
+ * PerAxisData is a port of GtkKineticScrolling as of GTK+ 3.20,
+ * mimicking its API and its behavior.
+ *
+ * All our curves are second degree linear differential equations, and
+ * so they can always be written as linear combinations of 2 base
+ * solutions. coef1 and coef2 are the coefficients to these two base
+ * solutions, and are computed from the initial position and velocity.
+ *
+ * In the case of simple deceleration, the differential equation is
+ *
+ * y'' = -my'
+ *
+ * With m the resistence factor. For this we use the following 2
+ * base solutions:
+ *
+ * f1(x) = 1
+ * f2(x) = exp(-mx)
+ *
+ * In the case of overshoot, the differential equation is
+ *
+ * y'' = -my' - ky
+ *
+ * With m the resistance, and k the spring stiffness constant. We let
+ * k = m^2 / 4, so that the system is critically damped (ie, returns to its
+ * equilibrium position as quickly as possible, without oscillating), and offset
+ * the whole thing, such that the equilibrium position is at 0. This gives the
+ * base solutions
+ *
+ * f1(x) = exp(-mx / 2)
+ * f2(x) = t exp(-mx / 2)
+ */
+
+static const double decelFriction = 4;
+static const double frameRate = 60;
+static const Seconds tickTime = 1_s / frameRate;
+static const Seconds minimumTimerInterval { 1_ms };
+
+namespace WebCore {
+
+ScrollAnimationKinetic::PerAxisData::PerAxisData(double lower, double upper, double initialPosition, double initialVelocity)
+ : m_lower(lower)
+ , m_upper(upper)
+ , m_coef1(initialVelocity / decelFriction + initialPosition)
+ , m_coef2(-initialVelocity / decelFriction)
+ , m_position(clampTo(initialPosition, lower, upper))
+ , m_velocity(initialPosition < lower || initialPosition > upper ? 0 : initialVelocity)
+{
+}
+
+bool ScrollAnimationKinetic::PerAxisData::animateScroll(Seconds timeDelta)
+{
+ m_elapsedTime += timeDelta;
+
+ double exponentialPart = exp(-decelFriction * m_elapsedTime.value());
+ m_position = m_coef1 + m_coef2 * exponentialPart;
+ m_velocity = -decelFriction * m_coef2 * exponentialPart;
+
+ if (m_position < m_lower) {
+ m_position = m_lower;
+ m_velocity = 0;
+ } else if (m_position > m_upper) {
+ m_position = m_upper;
+ m_velocity = 0;
+ } else if (fabs(m_velocity) < 1) {
+ m_position = round(m_position);
+ m_velocity = 0;
+ }
+
+ return m_velocity;
+}
+
+ScrollAnimationKinetic::ScrollAnimationKinetic(ScrollableArea& scrollableArea, std::function<void(FloatPoint&&)>&& notifyPositionChangedFunction)
+ : ScrollAnimation(scrollableArea)
+ , m_notifyPositionChangedFunction(WTFMove(notifyPositionChangedFunction))
+ , m_animationTimer(*this, &ScrollAnimationKinetic::animationTimerFired)
+{
+}
+
+ScrollAnimationKinetic::~ScrollAnimationKinetic()
+{
+}
+
+void ScrollAnimationKinetic::stop()
+{
+ m_animationTimer.stop();
+ m_horizontalData = std::nullopt;
+ m_verticalData = std::nullopt;
+}
+
+void ScrollAnimationKinetic::start(const FloatPoint& initialPosition, const FloatPoint& velocity, bool mayHScroll, bool mayVScroll)
+{
+ stop();
+
+ m_position = initialPosition;
+
+ if (!velocity.x() && !velocity.y())
+ return;
+
+ if (mayHScroll) {
+ m_horizontalData = PerAxisData(m_scrollableArea.minimumScrollPosition().x(),
+ m_scrollableArea.maximumScrollPosition().x(),
+ initialPosition.x(), velocity.x());
+ }
+ if (mayVScroll) {
+ m_verticalData = PerAxisData(m_scrollableArea.minimumScrollPosition().y(),
+ m_scrollableArea.maximumScrollPosition().y(),
+ initialPosition.y(), velocity.y());
+ }
+
+ m_startTime = MonotonicTime::now() - tickTime / 2.;
+ animationTimerFired();
+}
+
+void ScrollAnimationKinetic::animationTimerFired()
+{
+ MonotonicTime currentTime = MonotonicTime::now();
+ Seconds deltaToNextFrame = 1_s * ceil((currentTime - m_startTime).value() * frameRate) / frameRate - (currentTime - m_startTime);
+
+ if (m_horizontalData && !m_horizontalData.value().animateScroll(deltaToNextFrame))
+ m_horizontalData = std::nullopt;
+
+ if (m_verticalData && !m_verticalData.value().animateScroll(deltaToNextFrame))
+ m_verticalData = std::nullopt;
+
+ // If one of the axes didn't finish its animation we must continue it.
+ if (m_horizontalData || m_verticalData)
+ m_animationTimer.startOneShot(std::max(minimumTimerInterval, deltaToNextFrame));
+
+ double x = m_horizontalData ? m_horizontalData.value().position() : m_position.x();
+ double y = m_verticalData ? m_verticalData.value().position() : m_position.y();
+ m_position = FloatPoint(x, y);
+ m_notifyPositionChangedFunction(FloatPoint(m_position));
+}
+
+} // namespace WebCore
--- /dev/null
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "FloatPoint.h"
+#include "ScrollAnimation.h"
+#include "Timer.h"
+
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+class ScrollableArea;
+
+class ScrollAnimationKinetic final: public ScrollAnimation {
+private:
+ class PerAxisData {
+ public:
+ PerAxisData(double lower, double upper, double initialPosition, double initialVelocity);
+
+ double position() { return m_position; }
+
+ bool animateScroll(Seconds timeDelta);
+
+ private:
+ double m_lower { 0 };
+ double m_upper { 0 };
+
+ double m_coef1 { 0 };
+ double m_coef2 { 0 };
+
+ Seconds m_elapsedTime;
+ double m_position { 0 };
+ double m_velocity { 0 };
+ };
+
+public:
+ ScrollAnimationKinetic(ScrollableArea&, std::function<void(FloatPoint&&)>&& notifyPositionChangedFunction);
+ virtual ~ScrollAnimationKinetic();
+
+ void start(const FloatPoint& initialPosition, const FloatPoint& velocity, bool mayHScroll, bool mayVScroll);
+
+private:
+ void stop() override;
+ void animationTimerFired();
+
+ std::function<void(FloatPoint&&)> m_notifyPositionChangedFunction;
+
+ std::optional<PerAxisData> m_horizontalData;
+ std::optional<PerAxisData> m_verticalData;
+
+ MonotonicTime m_startTime;
+ Timer m_animationTimer;
+ FloatPoint m_position;
+};
+
+} // namespace WebCore
#include "config.h"
#include "PlatformWheelEvent.h"
+#include "FloatPoint.h"
#include "PlatformKeyboardEvent.h"
#include "Scrollbar.h"
#include <gdk/gdk.h>
m_wheelTicksX = m_deltaX;
m_wheelTicksY = m_deltaY;
+#ifndef GTK_API_VERSION_2
+#if GTK_CHECK_VERSION(3, 20, 0)
+ m_phase = event->is_stop ?
+ PlatformWheelEventPhaseEnded :
+ PlatformWheelEventPhaseChanged;
+#else
+ m_phase = event->direction == GDK_SCROLL_SMOOTH && !m_deltaX && !m_deltaY ?
+ PlatformWheelEventPhaseEnded :
+ PlatformWheelEventPhaseChanged;
+#endif
+#else
+ m_phase = PlatformWheelEventPhaseChanged;
+#endif // GTK_API_VERSION_2
+
m_position = IntPoint(static_cast<int>(event->x), static_cast<int>(event->y));
m_globalPosition = IntPoint(static_cast<int>(event->x_root), static_cast<int>(event->y_root));
m_granularity = ScrollByPixelWheelEvent;
m_deltaY *= static_cast<float>(Scrollbar::pixelsPerLineStep());
}
+FloatPoint PlatformWheelEvent::swipeVelocity() const
+{
+ // The swiping velocity is stored in the deltas of the event declaring it.
+ return isTransitioningToMomentumScroll() ? FloatPoint(m_wheelTicksX, m_wheelTicksY) : FloatPoint();
+}
+
}
#include "config.h"
#include "ScrollAnimatorGtk.h"
+#include "ScrollAnimationKinetic.h"
#include "ScrollAnimationSmooth.h"
#include "ScrollableArea.h"
#include "ScrollbarTheme.h"
static const Seconds overflowScrollbarsAnimationDuration { 1_s };
static const Seconds overflowScrollbarsAnimationHideDelay { 2_s };
+static const Seconds scrollCaptureThreshold { 150_ms };
std::unique_ptr<ScrollAnimator> ScrollAnimator::create(ScrollableArea& scrollableArea)
{
: ScrollAnimator(scrollableArea)
, m_overlayScrollbarAnimationTimer(*this, &ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired)
{
+ m_kineticAnimation = std::make_unique<ScrollAnimationKinetic>(m_scrollableArea, [this](FloatPoint&& position) {
+#if ENABLE(SMOOTH_SCROLLING)
+ if (m_smoothAnimation)
+ m_smoothAnimation->setCurrentPosition(position);
+#endif
+ updatePosition(WTFMove(position));
+ });
+
#if ENABLE(SMOOTH_SCROLLING)
if (scrollableArea.scrollAnimatorEnabled())
ensureSmoothScrollingAnimation();
#if ENABLE(SMOOTH_SCROLLING)
void ScrollAnimatorGtk::ensureSmoothScrollingAnimation()
{
- if (m_animation)
+ if (m_smoothAnimation)
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);
+ m_smoothAnimation = std::make_unique<ScrollAnimationSmooth>(m_scrollableArea, m_currentPosition, [this](FloatPoint&& position) {
+ updatePosition(WTFMove(position));
});
}
+#endif
+#if ENABLE(SMOOTH_SCROLLING)
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);
+ return m_smoothAnimation->scroll(orientation, granularity, step, multiplier);
}
+#endif
void ScrollAnimatorGtk::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
{
FloatPoint position = ScrollableArea::scrollPositionFromOffset(offset, toFloatSize(m_scrollableArea.scrollOrigin()));
- if (m_animation)
- m_animation->setCurrentPosition(position);
+ m_kineticAnimation->stop();
+ m_scrollHistory.clear();
- FloatSize delta = position - m_currentPosition;
- m_currentPosition = position;
- notifyPositionChanged(delta);
+#if ENABLE(SMOOTH_SCROLLING)
+ if (m_smoothAnimation)
+ m_smoothAnimation->setCurrentPosition(position);
+#endif
+
+ updatePosition(WTFMove(position));
}
-void ScrollAnimatorGtk::willEndLiveResize()
+FloatPoint ScrollAnimatorGtk::computeVelocity()
{
- if (m_animation)
- m_animation->updateVisibleLengths();
+ if (m_scrollHistory.isEmpty())
+ return { };
+
+ double first = m_scrollHistory[0].timestamp();
+ double last = m_scrollHistory.rbegin()->timestamp();
+
+ if (last == first)
+ return { };
+
+ FloatPoint accumDelta;
+ for (const auto& scrollEvent : m_scrollHistory)
+ accumDelta += FloatPoint(scrollEvent.deltaX(), scrollEvent.deltaY());
+
+ m_scrollHistory.clear();
+
+ return FloatPoint(accumDelta.x() * -1000 / (last - first), accumDelta.y() * -1000 / (last - first));
}
+
+bool ScrollAnimatorGtk::handleWheelEvent(const PlatformWheelEvent& event)
+{
+ m_kineticAnimation->stop();
+
+ m_scrollHistory.removeAllMatching([&event] (PlatformWheelEvent& otherEvent) -> bool {
+ return Seconds::fromMilliseconds(event.timestamp() - otherEvent.timestamp()) > scrollCaptureThreshold;
+ });
+
+ if (event.isEndOfNonMomentumScroll()) {
+ // We don't need to add the event to the history as its delta will be (0, 0).
+ static_cast<ScrollAnimationKinetic*>(m_kineticAnimation.get())->start(m_currentPosition, computeVelocity(), m_scrollableArea.horizontalScrollbar(), m_scrollableArea.verticalScrollbar());
+ return true;
+ }
+ if (event.isTransitioningToMomentumScroll()) {
+ m_scrollHistory.clear();
+ static_cast<ScrollAnimationKinetic*>(m_kineticAnimation.get())->start(m_currentPosition, event.swipeVelocity(), m_scrollableArea.horizontalScrollbar(), m_scrollableArea.verticalScrollbar());
+ return true;
+ }
+
+ m_scrollHistory.append(event);
+
+ return ScrollAnimator::handleWheelEvent(event);
+}
+
+void ScrollAnimatorGtk::willEndLiveResize()
+{
+ m_kineticAnimation->updateVisibleLengths();
+
+#if ENABLE(SMOOTH_SCROLLING)
+ if (m_smoothAnimation)
+ m_smoothAnimation->updateVisibleLengths();
#endif
+}
+
+void ScrollAnimatorGtk::updatePosition(FloatPoint&& position)
+{
+ FloatSize delta = position - m_currentPosition;
+ m_currentPosition = WTFMove(position);
+ notifyPositionChanged(delta);
+}
void ScrollAnimatorGtk::didAddVerticalScrollbar(Scrollbar* scrollbar)
{
+ m_kineticAnimation->updateVisibleLengths();
+
#if ENABLE(SMOOTH_SCROLLING)
- if (m_animation)
- m_animation->updateVisibleLengths();
+ if (m_smoothAnimation)
+ m_smoothAnimation->updateVisibleLengths();
#endif
if (!scrollbar->isOverlayScrollbar())
return;
void ScrollAnimatorGtk::didAddHorizontalScrollbar(Scrollbar* scrollbar)
{
+ m_kineticAnimation->updateVisibleLengths();
+
#if ENABLE(SMOOTH_SCROLLING)
- if (m_animation)
- m_animation->updateVisibleLengths();
+ if (m_smoothAnimation)
+ m_smoothAnimation->updateVisibleLengths();
#endif
if (!scrollbar->isOverlayScrollbar())
return;
private:
#if ENABLE(SMOOTH_SCROLLING)
bool scroll(ScrollbarOrientation, ScrollGranularity, float step, float multiplier) override;
+#endif
void scrollToOffsetWithoutAnimation(const FloatPoint&) override;
void willEndLiveResize() override;
-#endif
+
+ bool handleWheelEvent(const PlatformWheelEvent&) override;
void didAddVerticalScrollbar(Scrollbar*) override;
void didAddHorizontalScrollbar(Scrollbar*) override;
void notifyContentAreaScrolled(const FloatSize& delta) override;
void lockOverlayScrollbarStateToHidden(bool) override;
+ void updatePosition(FloatPoint&&);
+
void overlayScrollbarAnimationTimerFired();
void showOverlayScrollbars();
void hideOverlayScrollbars();
void updateOverlayScrollbarsOpacity();
+ FloatPoint computeVelocity();
+
#if ENABLE(SMOOTH_SCROLLING)
void ensureSmoothScrollingAnimation();
- std::unique_ptr<ScrollAnimation> m_animation;
+ std::unique_ptr<ScrollAnimation> m_smoothAnimation;
#endif
+ std::unique_ptr<ScrollAnimation> m_kineticAnimation;
+ Vector<PlatformWheelEvent> m_scrollHistory;
Scrollbar* m_horizontalOverlayScrollbar { nullptr };
Scrollbar* m_verticalOverlayScrollbar { nullptr };
bool m_overlayScrollbarsLocked { false };
+2017-06-09 Adrien Plazas <aplazas@igalia.com>
+
+ [GTK] Add kinetic scrolling
+ https://bugs.webkit.org/show_bug.cgi?id=155750
+
+ Reviewed by Carlos Garcia Campos.
+
+ Patch by Adrien Plazas and Yusuke Suzuki.
+
+ Add the notion of phase and momentum phase to WebWheelEvent.
+
+ Make WebWheelEvent manage the 'is_stop' attribute of GdkEventScroll to create the corresponding WebWheelEvent
+ with the correct phases and deltas.
+
+ Make GestureController manage swipes to create the corresponding WebWheelEvent with the correct phases and
+ deltas.
+
+ * Shared/NativeWebWheelEvent.h:
+ * Shared/WebEvent.h:
+ * Shared/WebEventConversion.cpp:
+ (WebKit::WebKit2PlatformWheelEvent::WebKit2PlatformWheelEvent):
+ * Shared/WebWheelEvent.cpp:
+ (WebKit::WebWheelEvent::WebWheelEvent):
+ (WebKit::WebWheelEvent::encode):
+ (WebKit::WebWheelEvent::decode):
+ * Shared/gtk/NativeWebWheelEventGtk.cpp:
+ (WebKit::NativeWebWheelEvent::NativeWebWheelEvent):
+ * Shared/gtk/WebEventFactory.cpp:
+ (WebKit::WebEventFactory::createWebWheelEvent):
+ * Shared/gtk/WebEventFactory.h:
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handleWheelEvent):
+ (WebKit::WebPageProxy::shouldProcessWheelEventNow):
+ * UIProcess/WebPageProxy.h:
+ * UIProcess/gtk/GestureController.cpp:
+ (WebKit::GestureController::GestureController):
+ (WebKit::GestureController::handleEvent):
+ (WebKit::GestureController::isProcessingGestures):
+ (WebKit::createScrollEvent):
+ (WebKit::GestureController::DragGesture::startDrag):
+ (WebKit::GestureController::DragGesture::handleDrag):
+ (WebKit::GestureController::DragGesture::begin):
+ (WebKit::GestureController::DragGesture::end):
+ (WebKit::GestureController::SwipeGesture::startMomentumScroll):
+ (WebKit::GestureController::SwipeGesture::swipe):
+ (WebKit::GestureController::SwipeGesture::SwipeGesture):
+ * UIProcess/gtk/GestureController.h:
+
2017-06-09 Jer Noble <jer.noble@apple.com>
Crash in -[WKWebView _initializeWithConfiguration:]
#elif PLATFORM(GTK)
NativeWebWheelEvent(const NativeWebWheelEvent&);
NativeWebWheelEvent(GdkEvent*);
+ NativeWebWheelEvent(GdkEvent*, WebWheelEvent::Phase, WebWheelEvent::Phase momentumPhase);
#elif PLATFORM(WPE)
NativeWebWheelEvent(struct wpe_input_axis_event*, float deviceScaleFactor);
#endif
ScrollByPixelWheelEvent
};
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
enum Phase {
PhaseNone = 0,
PhaseBegan = 1 << 0,
WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Granularity, Modifiers, double timestamp);
#if PLATFORM(COCOA)
WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Granularity, bool directionInvertedFromDevice, Phase, Phase momentumPhase, bool hasPreciseScrollingDeltas, uint32_t scrollCount, const WebCore::FloatSize& unacceleratedScrollingDelta, Modifiers, double timestamp);
+#elif PLATFORM(GTK)
+ WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Phase, Phase momentumPhase, Granularity, Modifiers, double timestamp);
#endif
const WebCore::IntPoint position() const { return m_position; }
const WebCore::FloatSize wheelTicks() const { return m_wheelTicks; }
Granularity granularity() const { return static_cast<Granularity>(m_granularity); }
bool directionInvertedFromDevice() const { return m_directionInvertedFromDevice; }
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
Phase phase() const { return static_cast<Phase>(m_phase); }
Phase momentumPhase() const { return static_cast<Phase>(m_momentumPhase); }
+#endif
+#if PLATFORM(COCOA)
bool hasPreciseScrollingDeltas() const { return m_hasPreciseScrollingDeltas; }
uint32_t scrollCount() const { return m_scrollCount; }
const WebCore::FloatSize& unacceleratedScrollingDelta() const { return m_unacceleratedScrollingDelta; }
WebCore::FloatSize m_wheelTicks;
uint32_t m_granularity; // Granularity
bool m_directionInvertedFromDevice;
+#if PLATFORM(COCOA) || PLATFORM(GTK)
+ uint32_t m_phase { Phase::PhaseNone };
+ uint32_t m_momentumPhase { Phase::PhaseNone };
+#endif
#if PLATFORM(COCOA)
- uint32_t m_phase; // Phase
- uint32_t m_momentumPhase; // Phase
bool m_hasPreciseScrollingDeltas;
uint32_t m_scrollCount;
WebCore::FloatSize m_unacceleratedScrollingDelta;
m_wheelTicksY = webEvent.wheelTicks().height();
m_granularity = (webEvent.granularity() == WebWheelEvent::ScrollByPageWheelEvent) ? WebCore::ScrollByPageWheelEvent : WebCore::ScrollByPixelWheelEvent;
m_directionInvertedFromDevice = webEvent.directionInvertedFromDevice();
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
m_phase = static_cast<WebCore::PlatformWheelEventPhase>(webEvent.phase());
m_momentumPhase = static_cast<WebCore::PlatformWheelEventPhase>(webEvent.momentumPhase());
+#endif
+#if PLATFORM(COCOA)
m_hasPreciseScrollingDeltas = webEvent.hasPreciseScrollingDeltas();
m_scrollCount = webEvent.scrollCount();
m_unacceleratedScrollingDeltaX = webEvent.unacceleratedScrollingDelta().width();
{
ASSERT(isWheelEventType(type));
}
+#elif PLATFORM(GTK)
+WebWheelEvent::WebWheelEvent(Type type, const IntPoint& position, const IntPoint& globalPosition, const FloatSize& delta, const FloatSize& wheelTicks, Phase phase, Phase momentumPhase, Granularity granularity, Modifiers modifiers, double timestamp)
+ : WebEvent(type, modifiers, timestamp)
+ , m_position(position)
+ , m_globalPosition(globalPosition)
+ , m_delta(delta)
+ , m_wheelTicks(wheelTicks)
+ , m_granularity(granularity)
+ , m_directionInvertedFromDevice(false)
+ , m_phase(phase)
+ , m_momentumPhase(momentumPhase)
+{
+ ASSERT(isWheelEventType(type));
+}
#endif
void WebWheelEvent::encode(IPC::Encoder& encoder) const
encoder << m_wheelTicks;
encoder << m_granularity;
encoder << m_directionInvertedFromDevice;
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
encoder << m_phase;
encoder << m_momentumPhase;
+#endif
+#if PLATFORM(COCOA)
encoder << m_hasPreciseScrollingDeltas;
encoder << m_scrollCount;
encoder << m_unacceleratedScrollingDelta;
return false;
if (!decoder.decode(t.m_directionInvertedFromDevice))
return false;
-#if PLATFORM(COCOA)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
if (!decoder.decode(t.m_phase))
return false;
if (!decoder.decode(t.m_momentumPhase))
return false;
+#endif
+#if PLATFORM(COCOA)
if (!decoder.decode(t.m_hasPreciseScrollingDeltas))
return false;
if (!decoder.decode(t.m_scrollCount))
{
}
+NativeWebWheelEvent::NativeWebWheelEvent(GdkEvent* event, WebWheelEvent::Phase phase, WebWheelEvent::Phase momentumPhase)
+ : WebWheelEvent(WebEventFactory::createWebWheelEvent(event, phase, momentumPhase))
+ , m_nativeEvent(gdk_event_copy(event))
+{
+}
+
NativeWebWheelEvent::NativeWebWheelEvent(const NativeWebWheelEvent& event)
- : WebWheelEvent(WebEventFactory::createWebWheelEvent(event.nativeEvent()))
+ : WebWheelEvent(WebEventFactory::createWebWheelEvent(event.nativeEvent(), event.phase(), event.momentumPhase()))
, m_nativeEvent(gdk_event_copy(event.nativeEvent()))
{
}
WebWheelEvent WebEventFactory::createWebWheelEvent(const GdkEvent* event)
{
+#ifndef GTK_API_VERSION_2
+#if GTK_CHECK_VERSION(3, 20, 0)
+ WebWheelEvent::Phase phase = gdk_event_is_scroll_stop_event(event) ?
+ WebWheelEvent::Phase::PhaseEnded :
+ WebWheelEvent::Phase::PhaseChanged;
+#else
+ double deltaX, deltaY;
+ gdk_event_get_scroll_deltas(event, &deltaX, &deltaY);
+ WebWheelEvent::Phase phase = event->scroll.direction == GDK_SCROLL_SMOOTH && !deltaX && !deltaY ?
+ WebWheelEvent::Phase::PhaseEnded :
+ WebWheelEvent::Phase::PhaseChanged;
+#endif
+#else
+ WebWheelEvent::Phase phase = WebWheelEvent::Phase::PhaseChanged;
+#endif // GTK_API_VERSION_2
+
+ return createWebWheelEvent(event, phase, WebWheelEvent::Phase::PhaseNone);
+}
+
+WebWheelEvent WebEventFactory::createWebWheelEvent(const GdkEvent* event, WebWheelEvent::Phase phase, WebWheelEvent::Phase momentumPhase)
+{
double x, y, xRoot, yRoot;
gdk_event_get_coords(event, &x, &y);
gdk_event_get_root_coords(event, &xRoot, &yRoot);
FloatSize delta(wheelTicks.width() * step, wheelTicks.height() * step);
return WebWheelEvent(WebEvent::Wheel,
- IntPoint(x, y),
- IntPoint(xRoot, yRoot),
- delta,
- wheelTicks,
- WebWheelEvent::ScrollByPixelWheelEvent,
- modifiersForEvent(event),
- gdk_event_get_time(event));
+ IntPoint(x, y),
+ IntPoint(xRoot, yRoot),
+ delta,
+ wheelTicks,
+ phase,
+ momentumPhase,
+ WebWheelEvent::ScrollByPixelWheelEvent,
+ modifiersForEvent(event),
+ gdk_event_get_time(event));
}
WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(const GdkEvent* event, const WebCore::CompositionResults& compositionResults, Vector<String>&& commands)
public:
static WebMouseEvent createWebMouseEvent(const GdkEvent*, int);
static WebWheelEvent createWebWheelEvent(const GdkEvent*);
+ static WebWheelEvent createWebWheelEvent(const GdkEvent*, WebWheelEvent::Phase, WebWheelEvent::Phase momentumPhase);
static WebKeyboardEvent createWebKeyboardEvent(const GdkEvent*, const WebCore::CompositionResults&, Vector<String>&& commands);
#if ENABLE(TOUCH_EVENTS)
static WebTouchEvent createWebTouchEvent(const GdkEvent*, Vector<WebPlatformTouchPoint>&&);
if (!m_currentlyProcessedWheelEvents.isEmpty()) {
m_wheelEventQueue.append(event);
- if (m_wheelEventQueue.size() < wheelEventQueueSizeThreshold)
+ if (!shouldProcessWheelEventNow(event))
return;
// The queue has too many wheel events, so push a new event.
}
m_process->isResponsive(nullptr);
}
+bool WebPageProxy::shouldProcessWheelEventNow(const WebWheelEvent& event) const
+{
+#if PLATFORM(GTK)
+ // Don't queue events representing a non-trivial scrolling phase to
+ // avoid having them trapped in the queue, potentially preventing a
+ // scrolling session to beginning or end correctly.
+ // This is only needed by platforms whose WebWheelEvent has this phase
+ // information (Cocoa and GTK+) but Cocoa was fine without it.
+ if (event.phase() == WebWheelEvent::Phase::PhaseNone
+ || event.phase() == WebWheelEvent::Phase::PhaseChanged
+ || event.momentumPhase() == WebWheelEvent::Phase::PhaseNone
+ || event.momentumPhase() == WebWheelEvent::Phase::PhaseChanged)
+ return true;
+#else
+ UNUSED_PARAM(event);
+#endif
+ if (m_wheelEventQueue.size() >= wheelEventQueueSizeThreshold)
+ return true;
+ return false;
+}
+
void WebPageProxy::handleKeyboardEvent(const NativeWebKeyboardEvent& event)
{
if (!isValid())
void processNextQueuedWheelEvent();
void sendWheelEvent(const WebWheelEvent&);
+ bool shouldProcessWheelEventNow(const WebWheelEvent&) const;
#if ENABLE(TOUCH_EVENTS)
void updateTouchEventTracking(const WebTouchEvent&);
GestureController::GestureController(WebPageProxy& page)
: m_dragGesture(page)
+ , m_swipeGesture(page)
, m_zoomGesture(page)
{
}
{
bool wasProcessingGestures = isProcessingGestures();
m_dragGesture.handleEvent(event);
+ m_swipeGesture.handleEvent(event);
m_zoomGesture.handleEvent(event);
return event->type == GDK_TOUCH_END ? wasProcessingGestures : isProcessingGestures();
}
bool GestureController::isProcessingGestures() const
{
- return m_dragGesture.isActive() || m_zoomGesture.isActive();
+ return m_dragGesture.isActive() || m_swipeGesture.isActive() || m_zoomGesture.isActive();
}
GestureController::Gesture::Gesture(GtkGesture* gesture, WebPageProxy& page)
gtk_event_controller_handle_event(GTK_EVENT_CONTROLLER(m_gesture.get()), event);
}
-void GestureController::DragGesture::handleDrag(const GdkEvent* event, double x, double y)
+static GUniquePtr<GdkEvent> createScrollEvent(const GdkEvent* event, double x, double y, double deltaX, double deltaY, gboolean isStop)
{
- ASSERT(m_inDrag);
GUniquePtr<GdkEvent> scrollEvent(gdk_event_new(GDK_SCROLL));
scrollEvent->scroll.time = event->touch.time;
- scrollEvent->scroll.x = m_start.x();
- scrollEvent->scroll.y = m_start.y();
+ scrollEvent->scroll.x = x;
+ scrollEvent->scroll.y = y;
scrollEvent->scroll.x_root = event->touch.x_root;
scrollEvent->scroll.y_root = event->touch.y_root;
scrollEvent->scroll.direction = GDK_SCROLL_SMOOTH;
- scrollEvent->scroll.delta_x = (m_offset.x() - x) / Scrollbar::pixelsPerLineStep();
- scrollEvent->scroll.delta_y = (m_offset.y() - y) / Scrollbar::pixelsPerLineStep();
+ scrollEvent->scroll.delta_x = deltaX;
+ scrollEvent->scroll.delta_y = deltaY;
scrollEvent->scroll.state = event->touch.state;
- m_page.handleWheelEvent(NativeWebWheelEvent(scrollEvent.get()));
+#if GTK_CHECK_VERSION(3, 20, 0)
+ scrollEvent->scroll.is_stop = isStop;
+#endif
+ return scrollEvent;
+}
+
+void GestureController::DragGesture::startDrag(const GdkEvent* event)
+{
+ ASSERT(!m_inDrag);
+ GUniquePtr<GdkEvent> scrollEvent = createScrollEvent(event, m_start.x(), m_start.y(), 0, 0, FALSE);
+ m_page.handleWheelEvent(NativeWebWheelEvent(scrollEvent.get(), WebWheelEvent::Phase::PhaseBegan, WebWheelEvent::Phase::PhaseNone));
+}
+
+void GestureController::DragGesture::handleDrag(const GdkEvent* event, double x, double y)
+{
+ ASSERT(m_inDrag);
+ GUniquePtr<GdkEvent> scrollEvent = createScrollEvent(event,
+ m_start.x(), m_start.y(),
+ (m_offset.x() - x) / Scrollbar::pixelsPerLineStep(),
+ (m_offset.y() - y) / Scrollbar::pixelsPerLineStep(),
+ FALSE);
+ m_page.handleWheelEvent(NativeWebWheelEvent(scrollEvent.get(), WebWheelEvent::Phase::PhaseChanged, WebWheelEvent::Phase::PhaseNone));
}
void GestureController::DragGesture::handleTap(const GdkEvent* event)
unsigned delay;
g_object_get(gtk_widget_get_settings(widget), "gtk-long-press-time", &delay, nullptr);
dragGesture->m_longPressTimeout.startOneShot(1_ms * delay);
+ dragGesture->startDrag(gtk_gesture_get_last_event(gesture, sequence));
}
void GestureController::DragGesture::update(DragGesture* dragGesture, double x, double y, GtkGesture* gesture)
void GestureController::DragGesture::end(DragGesture* dragGesture, GdkEventSequence* sequence, GtkGesture* gesture)
{
dragGesture->m_longPressTimeout.stop();
+ if (!gtk_gesture_handles_sequence(gesture, sequence)) {
+ gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
if (!dragGesture->m_inDrag) {
dragGesture->handleTap(gtk_gesture_get_last_event(gesture, sequence));
gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED);
- } else if (!gtk_gesture_handles_sequence(gesture, sequence))
- gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED);
+ }
}
void GestureController::DragGesture::longPressFired()
g_signal_connect_swapped(m_gesture.get(), "end", G_CALLBACK(end), this);
}
+void GestureController::SwipeGesture::startMomentumScroll(const GdkEvent* event, double velocityX, double velocityY)
+{
+ GUniquePtr<GdkEvent> scrollEvent = createScrollEvent(event, event->touch.x, event->touch.y, velocityX, velocityY, TRUE);
+ m_page.handleWheelEvent(NativeWebWheelEvent(scrollEvent.get(), WebWheelEvent::Phase::PhaseNone, WebWheelEvent::Phase::PhaseBegan));
+}
+
+void GestureController::SwipeGesture::swipe(SwipeGesture* swipeGesture, double velocityX, double velocityY, GtkGesture* gesture)
+{
+ GdkEventSequence* sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture));
+ if (!gtk_gesture_handles_sequence(gesture, sequence))
+ return;
+
+ gtk_gesture_set_sequence_state(gesture, sequence, GTK_EVENT_SEQUENCE_CLAIMED);
+
+ swipeGesture->startMomentumScroll(gtk_gesture_get_last_event(gesture, sequence), velocityX, velocityY);
+}
+
+GestureController::SwipeGesture::SwipeGesture(WebPageProxy& page)
+ : Gesture(gtk_gesture_swipe_new(page.viewWidget()), page)
+{
+ gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(m_gesture.get()), TRUE);
+ g_signal_connect_swapped(m_gesture.get(), "swipe", G_CALLBACK(swipe), this);
+}
+
IntPoint GestureController::ZoomGesture::center() const
{
double x, y;
DragGesture(WebPageProxy&);
private:
+ // Notify that a drag started, allowing to stop kinetic deceleration.
+ void startDrag(const GdkEvent*);
void handleDrag(const GdkEvent*, double x, double y);
void handleTap(const GdkEvent*);
void longPressFired();
bool m_inDrag;
};
+ class SwipeGesture final : public Gesture {
+ public:
+ SwipeGesture(WebPageProxy&);
+
+ private:
+ void startMomentumScroll(const GdkEvent*, double velocityX, double velocityY);
+
+ static void swipe(SwipeGesture*, double velocityX, double velocityY, GtkGesture*);
+ };
+
class ZoomGesture final : public Gesture {
public:
ZoomGesture(WebPageProxy&);
};
DragGesture m_dragGesture;
+ SwipeGesture m_swipeGesture;
ZoomGesture m_zoomGesture;
};