Minor PEP8 fix
[doneit] / src / gtk_toolbox.py
index c9a6ccf..210ce71 100644 (file)
@@ -1,13 +1,6 @@
 #!/usr/bin/python
 
 """
-@todo Implement my own tap-n-hold
-       Constructor takes
-               a widget (Event Box)
-               Callback function with default to a "callback widget" that places the widget in the appropriate place
-               Change of system default (class variable) for hold time
-       Follows enable/disable pattern
-       Cares about mouse move for disqualifying a tap
 @todo Implement a custom label that encodes random info (Not set in stone, just throwing these out there)
        Background color based on Location
        Text intensity based on time estimate
@@ -22,6 +15,7 @@ import gobject
 import gtk
 
 import toolbox
+import coroutines
 
 
 @contextlib.contextmanager
@@ -33,6 +27,104 @@ def gtk_lock():
                gtk.gdk.threads_leave()
 
 
+class ContextHandler(object):
+
+       HOLD_TIMEOUT = 1000
+       MOVE_THRESHHOLD = 10
+
+       def __init__(self, actionWidget, activationTarget = coroutines.null_sink()):
+               self._actionWidget = actionWidget
+               self._activationTarget = activationTarget
+
+               self._actionPressId = None
+               self._actionReleaseId = None
+               self._motionNotifyId = None
+               self._popupMenuId = None
+               self._holdTimerId = None
+
+               self._respondOnRelease = False
+               self._startPosition = None
+
+       def enable(self):
+               self._actionPressId = self._actionWidget.connect("button-press-event", self._on_press)
+               self._actionReleaseId = self._actionWidget.connect("button-release-event", self._on_release)
+               self._motionNotifyId = self._actionWidget.connect("motion-notify-event", self._on_motion)
+               self._popupMenuId = self._actionWidget.connect("popup-menu", self._on_popup)
+
+       def disable(self):
+               self._clear()
+               self._actionWidget.disconnect(self._actionPressId)
+               self._actionWidget.disconnect(self._actionReleaseId)
+               self._actionWidget.disconnect(self._motionNotifyId)
+               self._actionWidget.disconnect(self._popupMenuId)
+
+       def _respond(self, position):
+               widgetPosition = 0, 0 # @todo Figure out how to get a widgets root position
+               responsePosition = (
+                       widgetPosition[0] + position[0],
+                       widgetPosition[1] + position[1],
+               )
+               self._activationTarget.send((self._actionWidget, responsePosition))
+
+       def _clear(self):
+               if self._holdTimerId is not None:
+                       gobject.source_remove(self._holdTimerId)
+               self._respondOnRelease = False
+               self._startPosition = None
+
+       def _is_cleared(self):
+               return self._startPosition is None
+
+       def _on_press(self, widget, event):
+               if not self._is_cleared():
+                       return
+
+               self._startPosition = event.get_coords()
+               if event.button == 1:
+                       # left click
+                       self._holdTimerId = gobject.timeout_add(self.HOLD_TIMEOUT, self._on_hold_timeout)
+               else:
+                       # any other click
+                       self._respondOnRelease = True
+
+       def _on_release(self, widget, event):
+               if self._is_cleared():
+                       return
+
+               if self._respondOnRelease:
+                       position = self._startPosition
+                       self._clear()
+                       self._respond(position)
+               else:
+                       self._clear()
+
+       def _on_hold_timeout(self):
+               assert not self._is_cleared()
+               gobject.source_remove(self._holdTimerId)
+               self._holdTimerId = None
+
+               position = self._startPosition
+               self._clear()
+               self._respond(position)
+
+       def _on_motion(self, widget, event):
+               if self._is_cleared():
+                       return
+               curPosition = event.get_coords()
+               dx, dy = (
+                       curPosition[1] - self._startPosition[1],
+                       curPosition[1] - self._startPosition[1],
+               )
+               delta = (dx ** 2 + dy ** 2) ** (0.5)
+               if self.MOVE_THRESHHOLD <= delta:
+                       self._clear()
+
+       def _on_popup(self, widget):
+               self._clear()
+               position = 0, 0
+               self._respond(position)
+
+
 class LoginWindow(object):
 
        def __init__(self, widgetTree):
@@ -403,3 +495,17 @@ class EditTaskDialog(object):
        def _on_cancel_clicked(self, *args):
                self._dialog.response(gtk.RESPONSE_CANCEL)
 
+
+if __name__ == "__main__":
+       if True:
+               win = gtk.Window()
+               win.set_title("Tap'N'Hold")
+               eventBox = gtk.EventBox()
+               win.add(eventBox)
+
+               context = ContextHandler(eventBox, coroutines.printer_sink())
+               context.enable()
+               win.connect("destroy", lambda w: gtk.main_quit())
+
+               win.show_all()
+               gtk.main()