changeset 693:c8b683dd56d3 pyntnclick

Better modal message and result handling.
author Jeremy Thurgood <firxen@gmail.com>
date Tue, 14 Feb 2012 15:36:28 +0200
parents d6ded808cc33
children 58d78038a197
files gamelib/custom_widgets.py pyntnclick/cursor.py pyntnclick/gamescreen.py pyntnclick/state.py pyntnclick/widgets/base.py pyntnclick/widgets/text.py
diffstat 6 files changed, 87 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/gamelib/custom_widgets.py	Tue Feb 14 13:39:05 2012 +0200
+++ b/gamelib/custom_widgets.py	Tue Feb 14 15:36:28 2012 +0200
@@ -1,10 +1,10 @@
 """Custom widgets for Suspened Sentence"""
 
 import pygame
-from pyntnclick.widgets.text import ModalWrappedTextLabel
+from pyntnclick.widgets.text import WrappedTextLabel
 
 
-class JimLabel(ModalWrappedTextLabel):
+class JimLabel(WrappedTextLabel):
     """Custom widget for JIM's speech"""
 
     def __init__(self, gd, mesg):
--- a/pyntnclick/cursor.py	Tue Feb 14 13:39:05 2012 +0200
+++ b/pyntnclick/cursor.py	Tue Feb 14 15:36:28 2012 +0200
@@ -42,6 +42,7 @@
         self.rect.top = pos[1] - self.pointer_y
 
     def set_highlight(self, enable):
+        # XXX: Use image transforms and such here.
         if enable != self.highlighted:
             #XXX: Do we need this? self.load()
             self.highlighted = enable
--- a/pyntnclick/gamescreen.py	Tue Feb 14 13:39:05 2012 +0200
+++ b/pyntnclick/gamescreen.py	Tue Feb 14 15:36:28 2012 +0200
@@ -9,9 +9,9 @@
 
 from pyntnclick.cursor import CursorScreen
 from pyntnclick.engine import Screen
-from pyntnclick.state import handle_result
-from pyntnclick.widgets.base import Container, ModalStackContainer
-from pyntnclick.widgets.text import TextButton, ModalWrappedTextLabel
+from pyntnclick.widgets.base import (
+    Container, ModalStackContainer, ModalWrapper)
+from pyntnclick.widgets.text import TextButton, WrappedTextLabel
 from pyntnclick.widgets.imagebutton import ImageButtonWidget
 
 # XXX: Need a way to get at the constants.
@@ -51,7 +51,7 @@
             self.parent.select(None)
         elif self.item.is_interactive(self.parent.game.tool):
             result = self.item.interact(self.parent.game.tool)
-            handle_result(result, self.parent.scene_widget)
+            self.parent.screen.handle_result(result)
         else:
             self.parent.select(self.item)
 
@@ -126,9 +126,6 @@
         else:
             self.down_button.enable()
 
-    def draw(self, surface):
-        super(InventoryView, self).draw(surface)
-
     @property
     def slot_items(self):
         return self.game.inventory[self.inv_offset:][:len(self.slots)]
@@ -153,7 +150,6 @@
         self.game = screen.game
         self.add_callback(MOUSEBUTTONDOWN, self.mouse_down)
         self.add_callback(MOUSEMOTION, self.mouse_move)
-        self._message_queue = []
         self.is_detail = is_detail
         if is_detail:
             self.close_button = TextButton((0, 0), self.gd, "Close")
@@ -163,22 +159,19 @@
 
     def draw(self, surface):
         self.scene.draw(surface.subsurface(self.rect))
-        if self.parent.is_top(self):
-            self.scene.draw_description(surface)
-        super(SceneWidget, self).draw(surface)
         if self.is_detail:
             border = self.rect.inflate(self.DETAIL_BORDER, self.DETAIL_BORDER)
             pygame.draw.rect(
                 surface, self.DETAIL_BORDER_COLOR, border, self.DETAIL_BORDER)
+        if self.parent.is_top(self):
+            self.scene.draw_description(surface)
+        super(SceneWidget, self).draw(surface)
 
     @property
     def highlight_cursor(self):
         return (self.scene.current_thing
                 and self.scene.current_thing.is_interactive())
 
-    def queue_widget(self, widget):
-        self._message_queue.append(widget)
-
     def mouse_down(self, event, widget):
         self.mouse_move(event, widget)
         if event.button != 1:  # We have a right/middle click
@@ -186,21 +179,9 @@
         else:
             pos = self.global_to_local(event.pos)
             result = self.scene.interact(self.game.tool, pos)
-            handle_result(result, self)
+            self.screen.handle_result(result)
 
     def animate(self):
-        # XXX: if self.game.animate():
-            # queue a redraw
-        #    self.invalidate()
-        # We do this here so we can get enter and leave events regardless
-        # of what happens
-        # result = self.game.check_enter_leave(self.screen)
-        # handle_result(result, self)
-        if self._message_queue:
-            # Only add a message if we're at the top
-            if self.screen.screen_modal.is_top(self.screen.inner_container):
-                widget = self._message_queue.pop(0)
-                self.screen.screen_modal.add(widget)
         self.scene.animate()
 
     def mouse_move(self, event, widget):
@@ -208,24 +189,6 @@
         self.scene.mouse_move(pos)
         self.game.old_pos = event.pos
 
-    def show_message(self, message):
-        # Display the message as a modal dialog
-        # XXX: MessageDialog(self.screen, message, 60, style=style).present()
-        # queue a redraw to show updated state
-        # XXX: self.invalidate()
-        # The cursor could have gone anywhere
-        # XXX: if self.subwidgets:
-        #    self.subwidgets[0]._mouse_move(mouse.get_pos())
-        # else:
-        #    self._mouse_move(mouse.get_pos())
-        rect = Rect((0, 0), (1, 1))
-        widget = ModalWrappedTextLabel(rect, self.gd, message,
-                max_width=self.gd.constants.screen[0] - 100)
-        widget.rect.center = self.rect.center
-        # We abuse animate so we can queue multiple results
-        # according
-        self.queue_widget(widget)
-
     def close(self, event, widget):
         self.screen.close_detail(self)
 
@@ -286,6 +249,7 @@
         self.container.add_callback(KEYDOWN, self.key_pressed)
 
     def _clear_all(self):
+        self._message_queue = []
         for widget in self.container.children[:]:
             self.container.remove(widget)
 
@@ -341,14 +305,14 @@
             rect = Rect((0, 0), scene.get_detail_size())
             rect.center = self.scene_modal.rect.center
 
-        scene_widget = self.scene_modal.add(
-            SceneWidget(rect, self.gd, scene, self, detail))
+        self.scene_modal.add(SceneWidget(rect, self.gd, scene, self, detail))
+        self.handle_result(scene.enter())
 
-        handle_result(scene.enter(), scene_widget)
-
-    def close_detail(self, detail):
+    def close_detail(self, detail=None):
+        if detail is None:
+            detail = self.scene_modal.top
         self.scene_modal.remove(detail)
-        detail.scene.leave()
+        self.handle_result(detail.scene.leave())
 
     def animate(self):
         """Animate the scene widgets"""
@@ -359,6 +323,36 @@
         if event.key == K_ESCAPE:
             self.change_screen('menu')
 
+    def end_game(self):
+        self.change_screen('end')
+
+    def show_queued_widget(self):
+        if self._message_queue:
+            # Only add a message if there isn't already one up
+            if self.screen_modal.is_top(self.inner_container):
+                widget = self._message_queue.pop(0)
+                self.screen_modal.add(
+                    ModalWrapper(widget, self.show_queued_widget))
+
+    def queue_widget(self, widget):
+        self._message_queue.append(widget)
+        self.show_queued_widget()
+
+    def show_message(self, message):
+        rect = Rect((0, 0), (1, 1))
+        max_width = self.gd.constants.screen[0] - 100
+        widget = WrappedTextLabel(rect, self.gd, message, max_width=max_width)
+        widget.rect.center = self.container.rect.center
+        self.queue_widget(widget)
+
+    def handle_result(self, resultset):
+        """Handle dealing with result or result sequences"""
+        if resultset:
+            if hasattr(resultset, 'process'):
+                resultset = [resultset]
+            for result in resultset:
+                result.process(self)
+
 
 class DefEndScreen(Screen):
     """A placeholder 'Game Over' screen so people can get started easily"""
--- a/pyntnclick/state.py	Tue Feb 14 13:39:05 2012 +0200
+++ b/pyntnclick/state.py	Tue Feb 14 15:36:28 2012 +0200
@@ -28,34 +28,22 @@
         self.close_detail = close_detail
         self.end_game = end_game
 
-    def play_sound(self, scene_widget):
+    def play_sound(self, screen):
         if self.soundfile:
-            sound = scene_widget.game.gd.sound.get_sound(self.soundfile)
+            sound = screen.gd.sound.get_sound(self.soundfile)
             sound.play()
 
-    def process(self, scene_widget):
+    def process(self, screen):
         """Helper function to do the right thing with a result object"""
-        self.play_sound(scene_widget)
+        self.play_sound(screen)
         if self.widget:
-            scene_widget.queue_widget(self.widget)
+            screen.queue_widget(self.widget)
         if self.message:
-            scene_widget.show_message(self.message)
+            screen.show_message(self.message)
         if self.detail_view:
-            scene_widget.game.show_detail(self.detail_view)
+            screen.game.show_detail(self.detail_view)
         if self.end_game:
-            scene_widget.end_game()
-
-
-def handle_result(result, scene_widget):
-    """Handle dealing with result or result sequences"""
-    if result:
-        if hasattr(result, 'process'):
-            result.process(scene_widget)
-        else:
-            for res in result:
-                if res:
-                    # List may contain None's
-                    res.process(scene_widget)
+            screen.end_game()
 
 
 class GameState(dict):
@@ -188,6 +176,7 @@
     def cancel_doodah(self, screen):
         if self.tool:
             self.set_tool(None)
+        
         # XXX: Needs to be tweaked to work with the new
         # scene stuff
         #elif self.current_detail:
--- a/pyntnclick/widgets/base.py	Tue Feb 14 13:39:05 2012 +0200
+++ b/pyntnclick/widgets/base.py	Tue Feb 14 15:36:28 2012 +0200
@@ -143,20 +143,27 @@
             obscure_color = gd.constants.modal_obscure_color
         self.obscure_color = convert_color(obscure_color)
 
+    @property
+    def top(self):
+        if self.children:
+            return self.children[-1]
+        return None
+
     def event(self, ev):
         """Only the topmost child gets events.
         """
-        self.mouseover_widget = self.children[-1].mouseover_widget
-        if self.children[-1].event(ev):
-            return True
+        self.mouseover_widget = self
+        if self.top:
+            self.mouseover_widget = self.top.mouseover_widget
+            if self.top.event(ev):
+                return True
 
+        # We skip Container's event() method and hop straight to its parent's.
         if super(Container, self).event(ev):
             return True
 
     def is_top(self, widget):
-        if self.children:
-            return self.children[-1] is widget
-        return False
+        return self.top is widget
 
     def draw(self, surface):
         obscure = pygame.Surface(self.rect.size, SRCALPHA)
@@ -205,3 +212,20 @@
     if isinstance(color, basestring):
         return pygame.Color(color)
     return pygame.Color(*color)
+
+
+class ModalWrapper(Container):
+    "A wrapper around a widget that removes itself when a mouse click occurs"
+
+    def __init__(self, widget, close_callback=None):
+        super(ModalWrapper, self).__init__(widget.rect, widget.gd)
+        self.close_callback = close_callback
+        self.add(widget)
+        widget.add_callback(MOUSEBUTTONDOWN, self.close)
+
+    def close(self, ev, widget):
+        if self.parent:
+            self.parent.remove(self)
+            if self.close_callback:
+                self.close_callback()
+            return True
--- a/pyntnclick/widgets/text.py	Tue Feb 14 13:39:05 2012 +0200
+++ b/pyntnclick/widgets/text.py	Tue Feb 14 15:36:28 2012 +0200
@@ -2,7 +2,6 @@
 
 import pygame
 from pygame.constants import SRCALPHA
-from pygame.locals import MOUSEBUTTONDOWN
 
 from pyntnclick.widgets.base import Widget, Button, convert_color
 
@@ -158,16 +157,3 @@
                                (line_surf.get_rect().size))
             self.surface.blit(line_surf, rect)
             height += line_surf.get_rect().height
-
-
-class ModalWrappedTextLabel(WrappedTextLabel):
-    """A WrappedTextLabel that removes itself when a mouse
-       click occurs"""
-    def __init__(self, rect, gd, *args, **kwargs):
-        super(ModalWrappedTextLabel, self).__init__(rect, gd, *args, **kwargs)
-        self.add_callback(MOUSEBUTTONDOWN, self.close)
-
-    def close(self, ev, widget):
-        if self.parent:
-            self.parent.remove(self)
-            return True