X-Git-Url: http://git.maemo.org/git/?p=watersofshiloah;a=blobdiff_plain;f=src%2Fpresenter.py;h=45df0793e45959e7448e1eac4519d68b1e6676a7;hp=335d118d21e03c289b055af99456ba7793ee9827;hb=b1e464affa3d62cd135faf66b8ba3a8edeead743;hpb=ed27fe5f073929a7b3de7ea3ef2966f0cac8c72d diff --git a/src/presenter.py b/src/presenter.py index 335d118..45df079 100644 --- a/src/presenter.py +++ b/src/presenter.py @@ -1,7 +1,7 @@ import logging +import gobject import pango -import cairo import gtk import util.misc as misc_utils @@ -10,65 +10,38 @@ import util.misc as misc_utils _moduleLogger = logging.getLogger(__name__) -class StreamPresenter(object): +class NavigationBox(gobject.GObject): - MINIMUM_MOVEMENT = 20 + __gsignals__ = { + 'action' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + 'navigating' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, ), + ), + } - BUTTON_STATE_PLAY = "play" - BUTTON_STATE_PAUSE = "pause" - BUTTON_STATE_NEXT = "next" - BUTTON_STATE_BACK = "back" - BUTTON_STATE_UP = "up" - BUTTON_STATE_CANCEL = "cancel" + MINIMUM_MOVEMENT = 32 _NO_POSITION = -1, -1 - _STATE_TO_IMAGE = { - BUTTON_STATE_PLAY: "play.png", - BUTTON_STATE_PAUSE: "pause.png", - BUTTON_STATE_NEXT: "next.png", - BUTTON_STATE_BACK: "prev.png", - } - - def __init__(self, player, store): - self._store = store - - self._player = player - self._player.connect("state-change", self._on_player_state_change) - self._player.connect("navigate-change", self._on_player_nav_change) - self._player.connect("title-change", self._on_player_title_change) - - 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) + 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._canNavigate = True self._clickPosition = self._NO_POSITION - self._potentialButtonState = self.BUTTON_STATE_PLAY - self._currentButtonState = self.BUTTON_STATE_PLAY - - imagePath = self._store.STORE_LOOKUP[self._player.background] - self._backgroundImage = self._store.get_surface_from_store(imagePath) - imagePath = self._STATE_TO_IMAGE[self._currentButtonState] - self._buttonImage = self._store.get_surface_from_store(imagePath) - - if self._isPortrait: - backWidth = self._backgroundImage.get_width() - backHeight = self._backgroundImage.get_height() - else: - backHeight = self._backgroundImage.get_width() - backWidth = self._backgroundImage.get_height() - self._image.set_size_request(backWidth, backHeight) @property def toplevel(self): - return self._imageEvents + return self._eventBox def set_orientation(self, orientation): if orientation == gtk.ORIENTATION_VERTICAL: @@ -78,114 +51,50 @@ class StreamPresenter(object): else: raise NotImplementedError(orientation) - cairoContext = self._image.window.cairo_create() - if not self._isPortrait: - cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) - self._draw_presenter(cairoContext, self._currentButtonState) + def is_active(self): + return self._clickPosition != self._NO_POSITION - @misc_utils.log_exception(_moduleLogger) - def _on_player_state_change(self, player, newState): - if newState == "play": - newState = self.BUTTON_STATE_PLAY - elif newState == "pause": - newState = self.BUTTON_STATE_PAUSE - elif newState == "stop": - newState = self.BUTTON_STATE_PAUSE - else: - newState = self._currentButtonState - - if newState != self._currentButtonState: - self._currentButtonState = newState - if self._clickPosition == self._NO_POSITION: - 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._currentButtonState) - - @misc_utils.log_exception(_moduleLogger) - def _on_player_nav_change(self, player, newState): - canNavigate = self._player.can_navigate - newPotState = self._potentialButtonState - if self._canNavigate != canNavigate: - self._canNavigate = canNavigate - if self._potentialButtonState in (self.BUTTON_STATE_NEXT, self.BUTTON_STATE_BACK): - if self._currentButtonState == self.BUTTON_STATE_PLAY: - newPotState = self.BUTTON_STATE_PAUSE - else: - newPotState = self.BUTTON_STATE_PLAY - - if newPotState != self._potentialButtonState: - self._potentialButtonState = newPotState - if self._clickPosition == self._NO_POSITION: - 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 get_state(self, newCoord): + if self._clickPosition == self._NO_POSITION: + return "" - @misc_utils.log_exception(_moduleLogger) - def _on_player_title_change(self, player, newState): - if self._isPortrait: - backWidth = self._backgroundImage.get_width() - backHeight = self._backgroundImage.get_height() - else: - backHeight = self._backgroundImage.get_width() - backWidth = self._backgroundImage.get_height() - self._image.set_size_request(backWidth, backHeight) + delta = ( + newCoord[0] - self._clickPosition[0], + - (newCoord[1] - self._clickPosition[1]) + ) + absDelta = (abs(delta[0]), abs(delta[1])) + if max(*absDelta) < self.MINIMUM_MOVEMENT: + return "clicking" - imagePath = self._store.STORE_LOOKUP[self._player.background] - self._backgroundImage = self._store.get_surface_from_store(imagePath) - if self._clickPosition == self._NO_POSITION: - cairoContext = self._image.window.cairo_create() - if not self._isPortrait: - cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) - self._draw_presenter(cairoContext, self._currentButtonState) + if absDelta[0] < absDelta[1]: + if 0 < delta[1]: + return "up" + else: + return "down" else: - cairoContext = self._image.window.cairo_create() - if not self._isPortrait: - cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) - self._draw_presenter(cairoContext, self._potentialButtonState) + 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() - if self._currentButtonState == self.BUTTON_STATE_PLAY: - newState = self.BUTTON_STATE_PAUSE - else: - newState = self.BUTTON_STATE_PLAY - 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) + + 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() - newState = self._calculate_state(mousePosition) - if newState == self.BUTTON_STATE_PLAY: - self._player.play() - elif newState == self.BUTTON_STATE_PAUSE: - self._player.pause() - elif newState == self.BUTTON_STATE_NEXT: - self._player.next() - elif newState == self.BUTTON_STATE_BACK: - self._player.back() - elif newState == self.BUTTON_STATE_UP: - raise NotImplementedError("Drag-down not implemented yet") - elif newState == self.BUTTON_STATE_CANCEL: - pass + state = self.get_state(mousePosition) + assert state finally: - if self._player.state == "play": - newState = self.BUTTON_STATE_PLAY - else: - newState = self.BUTTON_STATE_PAUSE - 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) self._clickPosition = self._NO_POSITION + self.emit("action", state) @misc_utils.log_exception(_moduleLogger) def _on_motion_notify(self, widget, event): @@ -193,58 +102,73 @@ class StreamPresenter(object): 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 + newState = self.get_state(mousePosition) + self.emit("navigating", newState) - 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: - 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 +gobject.type_register(NavigationBox) + + +class StreamPresenter(object): + + def __init__(self, store): + self._store = store + + self._image = gtk.DrawingArea() + self._image.connect("expose_event", self._on_expose) + + self._isPortrait = True + + self._backgroundImage = None + self._title = "" + self._subtitle = "" + self._buttonImage = None + self._imageName = "" + self._dims = 0, 0 + + @property + def toplevel(self): + return self._image + + def set_orientation(self, orientation): + if orientation == gtk.ORIENTATION_VERTICAL: + self._isPortrait = True + elif orientation == gtk.ORIENTATION_HORIZONTAL: + self._isPortrait = False else: - if 0 < delta[0]: - return self.BUTTON_STATE_BACK - else: - return self.BUTTON_STATE_NEXT + raise NotImplementedError(orientation) + + self._image.queue_draw() + + def set_state(self, stateImage): + if stateImage == self._imageName: + return + self._imageName = stateImage + self._buttonImage = self._store.get_surface_from_store(stateImage) + + self._image.queue_draw() + + def set_context(self, backgroundImage, title, subtitle): + self._backgroundImage = self._store.get_surface_from_store(backgroundImage) + self._title = title + self._subtitle = subtitle + + backWidth = self._backgroundImage.get_width() + backHeight = self._backgroundImage.get_height() + self._image.set_size_request(backWidth, backHeight) + + self._image.queue_draw() @misc_utils.log_exception(_moduleLogger) def _on_expose(self, widget, event): - self._potentialButtonState = self._player.state cairoContext = self._image.window.cairo_create() - if not self._isPortrait: - cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0)) - self._draw_presenter(cairoContext, self._player.state) + self._draw_presenter(cairoContext) - def _draw_presenter(self, cairoContext, state): - assert state in (self._currentButtonState, self._potentialButtonState) + def _draw_presenter(self, cairoContext): + rect = self._image.get_allocation() + self._dims = rect.width, rect.height # Blank things - rect = self._image.get_allocation() cairoContext.rectangle( 0, 0, @@ -253,49 +177,84 @@ class StreamPresenter(object): ) cairoContext.set_source_rgb(0, 0, 0) cairoContext.fill() - cairoContext.paint() # Draw Background - cairoContext.set_source_surface( - self._backgroundImage, - 0, - 0, - ) - cairoContext.paint() + if self._backgroundImage is not None: + cairoContext.set_source_surface( + self._backgroundImage, + 0, + 0, + ) + cairoContext.paint() - # title - if self._player.title: - _moduleLogger.info("Displaying text") - backWidth = self._backgroundImage.get_width() - backHeight = self._backgroundImage.get_height() + pangoContext = self._image.create_pango_context() - pangoContext = self._image.create_pango_context() - textLayout = pango.Layout(pangoContext) - textLayout.set_markup(self._player.title) + titleLayout = pango.Layout(pangoContext) + titleLayout.set_markup("%s" % self._subtitle) + textWidth, textHeight = titleLayout.get_pixel_size() + subtitleTextX = self._dims[0] / 2 - textWidth / 2 + subtitleTextY = self._dims[1] - textHeight - self._buttonImage.get_height() + 10 - textWidth, textHeight = textLayout.get_pixel_size() - textX = backWidth / 2 - textWidth / 2 - textY = backHeight - textHeight - self._buttonImage.get_height() + subtitleLayout = pango.Layout(pangoContext) + subtitleLayout.set_markup("%s" % self._title) + textWidth, textHeight = subtitleLayout.get_pixel_size() + textX = self._dims[0] / 2 - textWidth / 2 + textY = subtitleTextY - textHeight - cairoContext.move_to(textX, textY) - cairoContext.set_source_rgb(0, 0, 0) - cairoContext.show_layout(textLayout) + xPadding = min((self._dims[0] - textWidth) / 2 - 5, 5) + yPadding = 5 + startContent = xPadding, textY - yPadding + endContent = self._dims[0] - xPadding, self._dims[1] - yPadding - self._draw_state(cairoContext, state) + # Control background + cairoContext.rectangle( + startContent[0], + startContent[1], + endContent[0] - startContent[0], + endContent[1] - startContent[1], + ) + cairoContext.set_source_rgba(0.9, 0.9, 0.9, 0.75) + cairoContext.fill() - def _draw_state(self, cairoContext, state): - assert state in (self._currentButtonState, self._potentialButtonState) - if state == self.BUTTON_STATE_CANCEL: - state = self._currentButtonState + # title + if self._title or self._subtitle: + cairoContext.move_to(subtitleTextX, subtitleTextY) + cairoContext.set_source_rgb(0, 0, 0) + cairoContext.show_layout(titleLayout) - backWidth = self._backgroundImage.get_width() - backHeight = self._backgroundImage.get_height() + cairoContext.move_to(textX, textY) + cairoContext.set_source_rgb(0, 0, 0) + cairoContext.show_layout(subtitleLayout) - imagePath = self._STATE_TO_IMAGE[state] - self._buttonImage = self._store.get_surface_from_store(imagePath) + self._draw_state(cairoContext) + + def _draw_state(self, cairoContext): + if self._backgroundImage is None or self._buttonImage is None: + return cairoContext.set_source_surface( self._buttonImage, - backWidth / 2 - self._buttonImage.get_width() / 2, - backHeight - self._buttonImage.get_height() + 5, + self._dims[0] / 2 - self._buttonImage.get_width() / 2, + self._dims[1] - self._buttonImage.get_height() + 5, ) cairoContext.paint() + + +class StreamMiniPresenter(object): + + def __init__(self, store): + self._store = store + + self._button = gtk.Image() + + @property + def toplevel(self): + return self._button + + def set_orientation(self, orientation): + pass + + def set_state(self, stateImage): + self._store.set_image_from_store(self._button, stateImage) + + def set_context(self, backgroundImage, title, subtitle): + pass