Skip to content
This repository has been archived by the owner on Jul 23, 2020. It is now read-only.

Commit

Permalink
Implement basic support for QNativeGesture
Browse files Browse the repository at this point in the history
We install a handler for GtkGestureZoom and GtkGestureRotate, and map
these into QNativeGesture events. This seems to work about as well as
I'd expect; about on the level of GTK+ applications anyway.
  • Loading branch information
rburchell committed Oct 10, 2017
1 parent 1e6222f commit d16d14f
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/qgtkwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,22 @@ void QGtkWindow::create(Qt::WindowType windowType)
g_signal_connect(m_content.get(), "leave-notify-event", G_CALLBACK(leave_content_notify_cb), this);
gtk_widget_set_can_focus(m_content.get(), true);

m_zoomGesture = gtk_gesture_zoom_new(m_content.get());
gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_zoomGesture.get()), GTK_PHASE_CAPTURE);
g_signal_connect(m_zoomGesture.get(), "scale-changed", G_CALLBACK(QGtkWindow::zoom_cb), this);
g_signal_connect(m_zoomGesture.get(), "begin", G_CALLBACK(QGtkWindow::begin_zoom_cb), this);
g_signal_connect(m_zoomGesture.get(), "cancel", G_CALLBACK(QGtkWindow::cancel_zoom_cb), this);
g_signal_connect(m_zoomGesture.get(), "end", G_CALLBACK(QGtkWindow::end_zoom_cb), this);

m_rotateGesture = gtk_gesture_rotate_new(m_content.get());
gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_rotateGesture.get()), GTK_PHASE_CAPTURE);
g_signal_connect(m_rotateGesture.get(), "angle-changed", G_CALLBACK(QGtkWindow::rotate_cb), this);
g_signal_connect(m_rotateGesture.get(), "begin", G_CALLBACK(QGtkWindow::begin_rotate_cb), this);
g_signal_connect(m_rotateGesture.get(), "cancel", G_CALLBACK(QGtkWindow::cancel_rotate_cb), this);
g_signal_connect(m_rotateGesture.get(), "end", G_CALLBACK(QGtkWindow::end_rotate_cb), this);

gtk_gesture_group(m_zoomGesture.get(), m_rotateGesture.get());

m_touchDevice = new QTouchDevice;
m_touchDevice->setType(QTouchDevice::TouchScreen); // ### use GdkDevice or not?
m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::MouseEmulation);
Expand Down
26 changes: 26 additions & 0 deletions src/qgtkwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ class QGtkWindow : public QPlatformWindow
QGtkRefPtr<GtkMenuBar> gtkMenuBar() const;
QGtkRefPtr<GtkWidget> gtkWindow() const;

void beginZoom(QPointF &contentPoint, guint32 ts);
void zoom(QPointF &contentPoint, double scale, guint32 ts);
void endZoom(QPointF &contentPoint, guint32 ts);

void beginRotate(QPointF &contentPoint, guint32 ts);
void rotate(QPointF &contentPoint, double angle, double angle_delta, guint32 ts);
void endRotate(QPointF &contentPoint, guint32 ts);

private:
void maybeForceTransientParent(Qt::WindowType windowType);
void reallyForceTransientFor(QWindow *transientParent);
Expand All @@ -142,6 +150,24 @@ class QGtkWindow : public QPlatformWindow

static void drawCallback(GtkWidget *, cairo_t *cr, gpointer platformWindow);
static gboolean windowTickCallback(GtkWidget*, GdkFrameClock *, gpointer platformWindow);

static void zoom_cb(GtkGestureZoom *pt, gdouble scale, gpointer platformWindow);
static void begin_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);
static void end_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);
static void cancel_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);

static void rotate_cb(GtkGestureRotate *pt, gdouble angle, gdouble angle_delta, gpointer platformWindow);
static void begin_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);
static void end_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);
static void cancel_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow);

QGtkRefPtr<GtkGesture> m_zoomGesture;
QGtkRefPtr<GtkGesture> m_rotateGesture;
int m_activeNativeGestures = 0;
bool m_initialZoomSet = false;
double m_initialZoom = 0;
bool m_initialRotateSet = false;
double m_initialRotate = 0;
};

class QGtkCourierObject : public QObject
Expand Down
199 changes: 199 additions & 0 deletions src/qgtkwindow_gesture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/****************************************************************************
**
** Copyright (C) 2017 Crimson AS <[email protected]>
** Contact: https://www.crimson.no
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/

#include "qgtkwindow.h"

#include <math.h> // M_PI

#include <QtCore/qloggingcategory.h>

Q_LOGGING_CATEGORY(lcGesture, "qt.qpa.gtk.gesture");


static void populateTsAndPoint(GtkGesture *gesture, guint32 &ts, QPointF &contentPoint)
{
gdouble x;
gdouble y;

GdkEventSequence *seq = gtk_gesture_get_last_updated_sequence(gesture);
gtk_gesture_get_point(gesture, seq, &x, &y);
contentPoint = QPointF(x, y);
const GdkEvent *ev = gtk_gesture_get_last_event(gesture, seq);
ts = gdk_event_get_time(ev);
}

void QGtkWindow::zoom_cb(GtkGestureZoom *pt, gdouble scale, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);
GdkEventSequence *seq = gtk_gesture_get_last_updated_sequence(GTK_GESTURE(pt));
gtk_gesture_set_sequence_state(GTK_GESTURE(pt), seq, GTK_EVENT_SEQUENCE_CLAIMED); // ### not really sure this should be done here, but crashes in begin

pw->zoom(contentPoint, scale, ts);
}

void QGtkWindow::begin_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);

qCDebug(lcGesture) << "Begin zoom " << pw->window() << ts << contentPoint;
pw->beginZoom(contentPoint, ts);
}

void QGtkWindow::end_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);

qCDebug(lcGesture) << "End zoom " << pw->window() << ts << contentPoint;
pw->endZoom(contentPoint, ts);
}

void QGtkWindow::cancel_zoom_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);

qCDebug(lcGesture) << "Cancel zoom " << pw->window() << ts << contentPoint;
pw->endZoom(contentPoint, ts);
}

void QGtkWindow::beginZoom(QPointF &contentPoint, guint32 ts)
{
m_initialZoomSet = false;
if (m_activeNativeGestures++ == 0) {
qCDebug(lcGesture) << "Started native gesture sequence (due to zoom)";
QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::BeginNativeGesture, contentPoint, contentPoint);
}
}

void QGtkWindow::zoom(QPointF &contentPoint, double scale, guint32 ts)
{
if (scale == 0.0) {
return; // insane
}

if (!m_initialZoomSet) {
m_initialZoomSet = true;
m_initialZoom = scale;
}
double modScale = (scale - m_initialZoom) / m_initialZoom;
m_initialZoom = scale;
QWindowSystemInterface::handleGestureEventWithRealValue(window(), ts, Qt::ZoomNativeGesture, modScale, contentPoint, contentPoint);
}

void QGtkWindow::endZoom(QPointF &contentPoint, guint32 ts)
{
if (--m_activeNativeGestures == 0) {
qCDebug(lcGesture) << "Ended native gesture sequence (due to zoom)";
QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::EndNativeGesture, contentPoint, contentPoint);
}
}

void QGtkWindow::rotate_cb(GtkGestureRotate *pt, gdouble angle, gdouble angle_delta, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);
GdkEventSequence *seq = gtk_gesture_get_last_updated_sequence(GTK_GESTURE(pt));
gtk_gesture_set_sequence_state(GTK_GESTURE(pt), seq, GTK_EVENT_SEQUENCE_CLAIMED); // ### not really sure this should be done here, but crashes in begin

pw->rotate(contentPoint, angle, angle_delta, ts);
}

void QGtkWindow::begin_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);

qCDebug(lcGesture) << "Begin rotate " << pw->window() << ts << contentPoint;
pw->beginRotate(contentPoint, ts);
}

void QGtkWindow::end_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);

qCDebug(lcGesture) << "End rotate " << pw->window() << ts << contentPoint;
pw->endRotate(contentPoint, ts);
}

void QGtkWindow::cancel_rotate_cb(GtkGesture *pt, GdkEventSequence*, gpointer platformWindow)
{
QGtkWindow *pw = static_cast<QGtkWindow*>(platformWindow);
guint32 ts;
QPointF contentPoint;
populateTsAndPoint(GTK_GESTURE(pt), ts, contentPoint);

qCDebug(lcGesture) << "Cancel rotate " << pw->window() << ts << contentPoint;
pw->endRotate(contentPoint, ts);
}

void QGtkWindow::beginRotate(QPointF &contentPoint, guint32 ts)
{
m_initialRotateSet = false;
if (m_activeNativeGestures++ == 0) {
qCDebug(lcGesture) << "Started native gesture sequence (due to rotate)";
QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::BeginNativeGesture, contentPoint, contentPoint);
}
}

void QGtkWindow::rotate(QPointF &contentPoint, double angle, double angle_delta, guint32 ts)
{
Q_UNUSED(angle_delta)
angle = -angle;
if (!m_initialRotateSet) {
m_initialRotateSet = true;
m_initialRotate = angle * 180 / M_PI;
}
double degrees = m_initialRotate - (angle * 180 / M_PI);
m_initialRotate = angle * 180 / M_PI;
QWindowSystemInterface::handleGestureEventWithRealValue(window(), ts, Qt::RotateNativeGesture, degrees, contentPoint, contentPoint);
}

void QGtkWindow::endRotate(QPointF &contentPoint, guint32 ts)
{
if (--m_activeNativeGestures == 0) {
qCDebug(lcGesture) << "Ended native gesture sequence (due to rotate)";
QWindowSystemInterface::handleGestureEvent(window(), ts, Qt::EndNativeGesture, contentPoint, contentPoint);
}
}

1 change: 1 addition & 0 deletions src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ SOURCES = main.cpp \
qgtkwindow_mouse.cpp \
qgtkwindow_touch.cpp \
qgtkwindow_render.cpp \
qgtkwindow_gesture.cpp \
qgtktheme.cpp \
qgtksystemtrayicon.cpp \
qgtkmenubar.cpp \
Expand Down

0 comments on commit d16d14f

Please sign in to comment.