From 0a2e9bb4091e3dc76f3c4df34869e2143d75acbb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 4 May 2010 22:30:24 -0500 Subject: [PATCH] Breaking free the navigation code so it can be reused in the radioview --- src/presenter.py | 212 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 139 insertions(+), 73 deletions(-) diff --git a/src/presenter.py b/src/presenter.py index 2d8f153..949a295 100644 --- a/src/presenter.py +++ b/src/presenter.py @@ -1,5 +1,6 @@ import logging +import gobject import pango import cairo import gtk @@ -10,10 +11,113 @@ import util.misc as misc_utils _moduleLogger = logging.getLogger(__name__) -class StreamPresenter(object): +class NavigationBox(gobject.GObject): + + __gsignals__ = { + 'action' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + 'navigating' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + } MINIMUM_MOVEMENT = 20 + _NO_POSITION = -1, -1 + + def __init__(self): + gobject.GObject.__init__(self) + self._eventBox = gtk.EventBox() + self._eventBox.connect("button_press_event", self._on_button_press) + self._eventBox.connect("button_release_event", self._on_button_release) + self._eventBox.connect("motion_notify_event", self._on_motion_notify) + + self._isPortrait = True + self._clickPosition = self._NO_POSITION + + @property + def toplevel(self): + return self._eventBox + + def set_orientation(self, orientation): + if orientation == gtk.ORIENTATION_VERTICAL: + self._isPortrait = True + elif orientation == gtk.ORIENTATION_HORIZONTAL: + self._isPortrait = False + else: + raise NotImplementedError(orientation) + + def is_active(self): + return self._clickPosition != self._NO_POSITION + + def get_state(self, newCoord): + if self._clickPosition == self._NO_POSITION: + return "" + + if self._isPortrait: + delta = ( + newCoord[0] - self._clickPosition[0], + - (newCoord[1] - self._clickPosition[1]) + ) + else: + delta = ( + newCoord[1] - self._clickPosition[1], + - (newCoord[0] - self._clickPosition[0]) + ) + absDelta = (abs(delta[0]), abs(delta[1])) + if max(*absDelta) < self.MINIMUM_MOVEMENT: + return "clicking" + + if absDelta[0] < absDelta[1]: + if 0 < delta[1]: + return "up" + else: + return "down" + else: + if 0 < delta[0]: + return "right" + else: + return "left" + + @misc_utils.log_exception(_moduleLogger) + def _on_button_press(self, widget, event): + if self._clickPosition != self._NO_POSITION: + _moduleLogger.debug("Ignoring double click") + self._clickPosition = event.get_coords() + + self.emit("navigating", "clicking") + + @misc_utils.log_exception(_moduleLogger) + def _on_button_release(self, widget, event): + assert self._clickPosition != self._NO_POSITION + try: + mousePosition = event.get_coords() + state = self.get_state(mousePosition) + assert state + self.emit("action", state) + finally: + self._clickPosition = self._NO_POSITION + + @misc_utils.log_exception(_moduleLogger) + def _on_motion_notify(self, widget, event): + if self._clickPosition == self._NO_POSITION: + return + + mousePosition = event.get_coords() + newState = self.get_state(mousePosition) + self.emit("navigating", newState) + + +gobject.type_register(NavigationBox) + + +class StreamPresenter(object): + BUTTON_STATE_PLAY = "play" BUTTON_STATE_PAUSE = "pause" BUTTON_STATE_NEXT = "next" @@ -21,8 +125,6 @@ class StreamPresenter(object): BUTTON_STATE_UP = "up" BUTTON_STATE_CANCEL = "cancel" - _NO_POSITION = -1, -1 - _STATE_TO_IMAGE = { BUTTON_STATE_PLAY: "play.png", BUTTON_STATE_PAUSE: "pause.png", @@ -40,16 +142,14 @@ class StreamPresenter(object): self._image = gtk.DrawingArea() self._image.connect("expose_event", self._on_expose) - self._imageEvents = gtk.EventBox() - self._imageEvents.connect("motion_notify_event", self._on_motion_notify) - self._imageEvents.connect("button_press_event", self._on_button_press) - self._imageEvents.connect("button_release_event", self._on_button_release) - self._imageEvents.add(self._image) + self._imageNav = NavigationBox() + self._imageNav.toplevel.add(self._image) + self._imageNav.connect("navigating", self._on_navigating) + self._imageNav.connect("action", self._on_nav_action) self._isPortrait = True self._canNavigate = True - self._clickPosition = self._NO_POSITION self._potentialButtonState = self.BUTTON_STATE_PLAY self._currentButtonState = self.BUTTON_STATE_PLAY @@ -68,9 +168,11 @@ class StreamPresenter(object): @property def toplevel(self): - return self._imageEvents + return self._imageNav.toplevel def set_orientation(self, orientation): + self._imageNav.set_orientation(orientation) + if orientation == gtk.ORIENTATION_VERTICAL: self._isPortrait = True elif orientation == gtk.ORIENTATION_HORIZONTAL: @@ -96,7 +198,7 @@ class StreamPresenter(object): if newState != self._currentButtonState: self._currentButtonState = newState - if self._clickPosition == self._NO_POSITION: + if not self._imageNav.is_active(): cairoContext = self._image.window.cairo_create() if not self._isPortrait: cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) @@ -116,7 +218,7 @@ class StreamPresenter(object): if newPotState != self._potentialButtonState: self._potentialButtonState = newPotState - if self._clickPosition == self._NO_POSITION: + if not self._imageNav.is_active(): cairoContext = self._image.window.cairo_create() if not self._isPortrait: cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) @@ -134,7 +236,7 @@ class StreamPresenter(object): imagePath = self._store.STORE_LOOKUP[self._player.background] self._backgroundImage = self._store.get_surface_from_store(imagePath) - if self._clickPosition == self._NO_POSITION: + if not self._imageNav.get_state(): cairoContext = self._image.window.cairo_create() if not self._isPortrait: cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) @@ -146,91 +248,55 @@ class StreamPresenter(object): self._draw_presenter(cairoContext, self._potentialButtonState) @misc_utils.log_exception(_moduleLogger) - def _on_button_press(self, widget, event): - self._clickPosition = event.get_coords() - if self._currentButtonState == self.BUTTON_STATE_PLAY: - newState = self.BUTTON_STATE_PAUSE - else: - newState = self.BUTTON_STATE_PLAY - self._potentialButtonState = newState + def _on_navigating(self, widget, navState): + buttonState = self._translate_state(navState) + self._potentialButtonState = buttonState cairoContext = self._image.window.cairo_create() if not self._isPortrait: cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) self._draw_state(cairoContext, self._potentialButtonState) @misc_utils.log_exception(_moduleLogger) - def _on_button_release(self, widget, event): + def _on_nav_action(self, widget, navState): try: - mousePosition = event.get_coords() - newState = self._calculate_state(mousePosition) - if newState == self.BUTTON_STATE_PLAY: + buttonState = self._translate_state(navState) + if buttonState == self.BUTTON_STATE_PLAY: self._player.play() - elif newState == self.BUTTON_STATE_PAUSE: + elif buttonState == self.BUTTON_STATE_PAUSE: self._player.pause() - elif newState == self.BUTTON_STATE_NEXT: + elif buttonState == self.BUTTON_STATE_NEXT: self._player.next() - elif newState == self.BUTTON_STATE_BACK: + elif buttonState == self.BUTTON_STATE_BACK: self._player.back() - elif newState == self.BUTTON_STATE_UP: + elif buttonState == self.BUTTON_STATE_UP: raise NotImplementedError("Drag-down not implemented yet") - elif newState == self.BUTTON_STATE_CANCEL: + elif buttonState == self.BUTTON_STATE_CANCEL: pass finally: if self._player.state == "play": - newState = self.BUTTON_STATE_PLAY + buttonState = self.BUTTON_STATE_PLAY else: - newState = self.BUTTON_STATE_PAUSE - self._potentialButtonState = newState + buttonState = self.BUTTON_STATE_PAUSE + self._potentialButtonState = buttonState cairoContext = self._image.window.cairo_create() if not self._isPortrait: cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) self._draw_state(cairoContext, self._potentialButtonState) - self._clickPosition = self._NO_POSITION - @misc_utils.log_exception(_moduleLogger) - def _on_motion_notify(self, widget, event): - if self._clickPosition == self._NO_POSITION: - return - - mousePosition = event.get_coords() - newState = self._calculate_state(mousePosition) - if newState != self._potentialButtonState: - self._potentialButtonState = newState - cairoContext = self._image.window.cairo_create() - if not self._isPortrait: - cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) - self._draw_state(cairoContext, self._potentialButtonState) - - def _calculate_state(self, newCoord): - assert self._clickPosition != self._NO_POSITION - - if self._isPortrait: - delta = ( - newCoord[0] - self._clickPosition[0], - - (newCoord[1] - self._clickPosition[1]) - ) - else: - delta = ( - newCoord[1] - self._clickPosition[1], - - (newCoord[0] - self._clickPosition[0]) - ) - absDelta = (abs(delta[0]), abs(delta[1])) - if max(*absDelta) < self.MINIMUM_MOVEMENT or not self._canNavigate: + def _translate_state(self, navState): + if navState == "clicking" or not self._canNavigate: if self._currentButtonState == self.BUTTON_STATE_PLAY: return self.BUTTON_STATE_PAUSE else: return self.BUTTON_STATE_PLAY - - if absDelta[0] < absDelta[1]: - if 0 < delta[1]: - return self.BUTTON_STATE_CANCEL - else: - return self.BUTTON_STATE_UP - else: - if 0 < delta[0]: - return self.BUTTON_STATE_BACK - else: - return self.BUTTON_STATE_NEXT + elif navState == "down": + return self.BUTTON_STATE_UP + elif navState == "up": + return self.BUTTON_STATE_CANCEL + elif navState == "left": + return self.BUTTON_STATE_NEXT + elif navState == "right": + return self.BUTTON_STATE_BACK @misc_utils.log_exception(_moduleLogger) def _on_expose(self, widget, event): -- 1.7.9.5