# HG changeset patch # User Jeremy Thurgood # Date 1329226588 -7200 # Node ID c8b683dd56d3eb6fc32a7a109810b42e26e9b824 # Parent d6ded808cc339696bf8f0f7daa5c14a3763e2a27 Better modal message and result handling. diff -r d6ded808cc33 -r c8b683dd56d3 gamelib/custom_widgets.py --- 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): diff -r d6ded808cc33 -r c8b683dd56d3 pyntnclick/cursor.py --- 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 diff -r d6ded808cc33 -r c8b683dd56d3 pyntnclick/gamescreen.py --- 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""" diff -r d6ded808cc33 -r c8b683dd56d3 pyntnclick/state.py --- 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: diff -r d6ded808cc33 -r c8b683dd56d3 pyntnclick/widgets/base.py --- 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 diff -r d6ded808cc33 -r c8b683dd56d3 pyntnclick/widgets/text.py --- 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