Implement Tap And Hold

This commit is contained in:
Kovid Goyal 2014-02-10 19:15:41 +05:30
parent 72119dd2d4
commit 0c565d7fbe
5 changed files with 66 additions and 5 deletions

View File

@ -172,3 +172,21 @@ void set_no_activate_on_click(QWidget *widget) {
widget->setStyle(new NoActivateStyle); 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));
}

View File

@ -103,3 +103,4 @@ int load_style(QString &path, QString &name);
bool do_notify(QObject *receiver, QEvent *event); bool do_notify(QObject *receiver, QEvent *event);
void set_no_activate_on_click(QWidget *widget); void set_no_activate_on_click(QWidget *widget);
void set_touch_menu_style(QWidget *widget, int margin=20);

View File

@ -10,6 +10,7 @@
int load_style(QString &path, QString &name); int load_style(QString &path, QString &name);
bool do_notify(QObject *receiver, QEvent *event); bool do_notify(QObject *receiver, QEvent *event);
void set_no_activate_on_click(QWidget *widget); void set_no_activate_on_click(QWidget *widget);
void set_touch_menu_style(QWidget *widget, int margin);
%End %End
class QProgressIndicator : QWidget { class QProgressIndicator : QWidget {
@ -62,3 +63,4 @@ int load_style(QString &path, QString &name);
bool do_notify(QObject *receiver, QEvent *event); bool do_notify(QObject *receiver, QEvent *event);
void set_no_activate_on_click(QWidget *widget); void set_no_activate_on_click(QWidget *widget);
void set_touch_menu_style(QWidget *widget, int margin=20);

View File

@ -705,6 +705,16 @@ class DocumentView(QWebView): # {{{
for plugin in self.document.all_viewer_plugins: for plugin in self.document.all_viewer_plugins:
plugin.customize_context_menu(menu, ev, r) 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()) menu.exec_(ev.globalPos())
def inspect(self): def inspect(self):

View File

@ -7,7 +7,10 @@ __license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import time, ctypes, sys 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 from calibre.constants import iswindows
@ -64,10 +67,12 @@ class State(QObject):
tapped = pyqtSignal(object) tapped = pyqtSignal(object)
swiped = pyqtSignal(object) swiped = pyqtSignal(object)
tap_hold_started = pyqtSignal(object)
tap_hold_updated = pyqtSignal(object) tap_hold_updated = pyqtSignal(object)
swipe_hold_started = pyqtSignal(object)
swipe_hold_updated = pyqtSignal(object) swipe_hold_updated = pyqtSignal(object)
tap_and_hold_finished = pyqtSignal(object) tap_hold_finished = pyqtSignal(object)
swipe_and_hold_finished = pyqtSignal(object) swipe_hold_finished = pyqtSignal(object)
def __init__(self): def __init__(self):
QObject.__init__(self) QObject.__init__(self)
@ -127,10 +132,12 @@ class State(QObject):
self.hold_started = True self.hold_started = True
self.possible_gestures = {SwipeAndHold} self.possible_gestures = {SwipeAndHold}
self.hold_data = (now, st) self.hold_data = (now, st)
self.swipe_hold_started.emit(tp)
else: else:
self.possible_gestures = {TapAndHold} self.possible_gestures = {TapAndHold}
self.hold_started = True self.hold_started = True
self.hold_data = now self.hold_data = now
self.tap_hold_started.emit(tp)
def finalize(self): def finalize(self):
if Tap in self.possible_gestures: if Tap in self.possible_gestures:
@ -151,11 +158,11 @@ class State(QObject):
if TapAndHold in self.possible_gestures: if TapAndHold in self.possible_gestures:
tp = next(self.touch_points.itervalues()) tp = next(self.touch_points.itervalues())
self.tap_and_hold_finished.emit(tp) self.tap_hold_finished.emit(tp)
return return
if SwipeAndHold in self.possible_gestures: 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 return
@ -166,6 +173,9 @@ class GestureHandler(QObject):
self.state = State() self.state = State()
self.state.swiped.connect(self.handle_swipe) self.state.swiped.connect(self.handle_swipe)
self.state.tapped.connect(self.handle_tap) 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'} self.evmap = {QEvent.TouchBegin: 'start', QEvent.TouchUpdate: 'update', QEvent.TouchEnd: 'end'}
# Ignore fake mouse events generated by the window system from touch # Ignore fake mouse events generated by the window system from touch
@ -216,13 +226,33 @@ class GestureHandler(QObject):
ev.accept() ev.accept()
return True 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): def handle_swipe(self, direction):
if self.close_open_menu():
return
view = self.parent() view = self.parent()
func = {Left:'next_page', Right: 'previous_page', Up:'goto_previous_section', Down:'goto_next_section'}[direction] func = {Left:'next_page', Right: 'previous_page', Up:'goto_previous_section', Down:'goto_next_section'}[direction]
getattr(view, func)() getattr(view, func)()
def handle_tap(self, tp): def handle_tap(self, tp):
if self.close_open_menu():
return
view = self.parent() view = self.parent()
threshold = view.width() / 3.0 threshold = view.width() / 3.0
attr = 'previous' if tp.start_position.x() <= threshold else 'next' attr = 'previous' if tp.start_position.x() <= threshold else 'next'
getattr(view, '%s_page'%attr)() 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)