From 0c565d7fbec93463b050083bbc8fdd63fcfdb687 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Feb 2014 19:15:41 +0530 Subject: [PATCH] Implement Tap And Hold --- .../progress_indicator/QProgressIndicator.cpp | 18 +++++++++ .../progress_indicator/QProgressIndicator.h | 1 + .../progress_indicator/QProgressIndicator.sip | 2 + src/calibre/gui2/viewer/documentview.py | 10 +++++ src/calibre/gui2/viewer/gestures.py | 40 ++++++++++++++++--- 5 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp b/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp index da9bdab45f..90ff986447 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp @@ -172,3 +172,21 @@ void set_no_activate_on_click(QWidget *widget) { widget->setStyle(new NoActivateStyle); } +class TouchMenuStyle: public QProxyStyle { + private: + int extra_margin; + + public: + TouchMenuStyle(int margin) : extra_margin(margin) {} + QSize sizeFromContents ( ContentsType type, const QStyleOption * option, const QSize & contentsSize, const QWidget * widget = 0 ) const { + QSize ans = QProxyStyle::sizeFromContents(type, option, contentsSize, widget); + if (type == QStyle::CT_MenuItem) { + ans.setHeight(ans.height() + extra_margin); // Make the menu items more easily touchable + } + return ans; + } +}; + +void set_touch_menu_style(QWidget *widget, int margin) { + widget->setStyle(new TouchMenuStyle(margin)); +} diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.h b/src/calibre/gui2/progress_indicator/QProgressIndicator.h index 424cfe7d5a..43c10a6189 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.h +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.h @@ -103,3 +103,4 @@ int load_style(QString &path, QString &name); bool do_notify(QObject *receiver, QEvent *event); void set_no_activate_on_click(QWidget *widget); +void set_touch_menu_style(QWidget *widget, int margin=20); diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.sip b/src/calibre/gui2/progress_indicator/QProgressIndicator.sip index ce9f3854a7..a4caa35020 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.sip +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.sip @@ -10,6 +10,7 @@ int load_style(QString &path, QString &name); bool do_notify(QObject *receiver, QEvent *event); void set_no_activate_on_click(QWidget *widget); +void set_touch_menu_style(QWidget *widget, int margin); %End class QProgressIndicator : QWidget { @@ -62,3 +63,4 @@ int load_style(QString &path, QString &name); bool do_notify(QObject *receiver, QEvent *event); void set_no_activate_on_click(QWidget *widget); +void set_touch_menu_style(QWidget *widget, int margin=20); diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 7343bb10f3..4b7c804ac9 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -705,6 +705,16 @@ class DocumentView(QWebView): # {{{ for plugin in self.document.all_viewer_plugins: plugin.customize_context_menu(menu, ev, r) + if ev.reason() == ev.Other: + # Triggered by a touch event + from calibre.constants import plugins + pi = plugins['progress_indicator'][0] + for x in (menu, self.goto_location_menu): + if hasattr(pi, 'set_touch_menu_style'): + pi.set_touch_menu_style(x) + else: + self.goto_location_menu.setStyle(self.style()) + self.context_menu = menu menu.exec_(ev.globalPos()) def inspect(self): diff --git a/src/calibre/gui2/viewer/gestures.py b/src/calibre/gui2/viewer/gestures.py index 053d724b21..af869832fa 100644 --- a/src/calibre/gui2/viewer/gestures.py +++ b/src/calibre/gui2/viewer/gestures.py @@ -7,7 +7,10 @@ __license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal ' import time, ctypes, sys -from PyQt4.Qt import QObject, QPointF, pyqtSignal, QEvent, QApplication +from functools import partial +from PyQt4.Qt import ( + QObject, QPointF, pyqtSignal, QEvent, QApplication, QMouseEvent, Qt, + QContextMenuEvent) from calibre.constants import iswindows @@ -64,10 +67,12 @@ class State(QObject): tapped = pyqtSignal(object) swiped = pyqtSignal(object) + tap_hold_started = pyqtSignal(object) tap_hold_updated = pyqtSignal(object) + swipe_hold_started = pyqtSignal(object) swipe_hold_updated = pyqtSignal(object) - tap_and_hold_finished = pyqtSignal(object) - swipe_and_hold_finished = pyqtSignal(object) + tap_hold_finished = pyqtSignal(object) + swipe_hold_finished = pyqtSignal(object) def __init__(self): QObject.__init__(self) @@ -127,10 +132,12 @@ class State(QObject): self.hold_started = True self.possible_gestures = {SwipeAndHold} self.hold_data = (now, st) + self.swipe_hold_started.emit(tp) else: self.possible_gestures = {TapAndHold} self.hold_started = True self.hold_data = now + self.tap_hold_started.emit(tp) def finalize(self): if Tap in self.possible_gestures: @@ -151,11 +158,11 @@ class State(QObject): if TapAndHold in self.possible_gestures: tp = next(self.touch_points.itervalues()) - self.tap_and_hold_finished.emit(tp) + self.tap_hold_finished.emit(tp) return if SwipeAndHold in self.possible_gestures: - self.swipe_and_hold_finished.emit(self.hold_data[1]) + self.swipe_hold_finished.emit(self.hold_data[1]) return @@ -166,6 +173,9 @@ class GestureHandler(QObject): self.state = State() self.state.swiped.connect(self.handle_swipe) self.state.tapped.connect(self.handle_tap) + self.state.tap_hold_started.connect(partial(self.handle_tap_hold, 'start')) + self.state.tap_hold_updated.connect(partial(self.handle_tap_hold, 'update')) + self.state.tap_hold_finished.connect(partial(self.handle_tap_hold, 'end')) self.evmap = {QEvent.TouchBegin: 'start', QEvent.TouchUpdate: 'update', QEvent.TouchEnd: 'end'} # Ignore fake mouse events generated by the window system from touch @@ -216,13 +226,33 @@ class GestureHandler(QObject): ev.accept() return True + def close_open_menu(self): + m = getattr(self.parent(), 'context_menu', None) + if m is not None and m.isVisible(): + m.close() + return True + def handle_swipe(self, direction): + if self.close_open_menu(): + return view = self.parent() func = {Left:'next_page', Right: 'previous_page', Up:'goto_previous_section', Down:'goto_next_section'}[direction] getattr(view, func)() def handle_tap(self, tp): + if self.close_open_menu(): + return view = self.parent() threshold = view.width() / 3.0 attr = 'previous' if tp.start_position.x() <= threshold else 'next' getattr(view, '%s_page'%attr)() + + def handle_tap_hold(self, action, tp): + etype = {'start':QEvent.MouseButtonPress, 'update':QEvent.MouseMove, 'end':QEvent.MouseButtonRelease}[action] + ev = QMouseEvent(etype, tp.current_position.toPoint(), tp.current_screen_position.toPoint(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) + QApplication.sendEvent(self.parent(), ev) + if action == 'end': + ev = QContextMenuEvent(QContextMenuEvent.Other, tp.current_position.toPoint(), tp.current_screen_position.toPoint()) + # We have to use post event otherwise the popup remains an alien widget and does not receive events + QApplication.postEvent(self.parent(), ev) +