# HG changeset patch # User phoku@33b003aa-7bff-0310-803a-e67f0ece8222 # Date 1237032809 0 # Node ID 54bfd1015b35d793453d947ed07a824bee52c76e # Parent 5816ab527da8410b2a33578f6155cc4bab501b9a * PyChan event handling rework (part I) ** Unified listeners ** ...hopefully more robust attach/detach code. * Added compat, layout and also the new autopsition feature. * Documentation * Minor style fixes in core. diff -r 5816ab527da8 -r 54bfd1015b35 clients/pychan_demo/pychan_test.py --- a/clients/pychan_demo/pychan_test.py Sat Mar 14 12:03:56 2009 +0000 +++ b/clients/pychan_demo/pychan_test.py Sat Mar 14 12:13:29 2009 +0000 @@ -38,7 +38,7 @@ def testTimer(): import timer - timer.init( pychan.manager.engine.getTimeManager() ) + timer.init( pychan.manager.hook.engine.getTimeManager() ) def spam(): print "SPAM SPAM" return 1 diff -r 5816ab527da8 -r 54bfd1015b35 doc/dependencies/dirdeps.dot --- a/doc/dependencies/dirdeps.dot Sat Mar 14 12:03:56 2009 +0000 +++ b/doc/dependencies/dirdeps.dot Sat Mar 14 12:13:29 2009 +0000 @@ -190,6 +190,7 @@ "model/structures" -> "util/math" "model/structures" -> "util/resource" "model/structures" -> "util/structures" + "model/structures" -> "util/time" "pathfinder" -> "model/metamodel" "pathfinder" -> "model/structures" "pathfinder" -> "util/base" @@ -263,6 +264,7 @@ "view/renderers" -> "util/log" "view/renderers" -> "util/math" "view/renderers" -> "util/structures" + "view/renderers" -> "util/time" "view/renderers" -> "video" "view/renderers" -> "video/fonts" "view/renderers" -> "video/sdl" diff -r 5816ab527da8 -r 54bfd1015b35 doc/dependencies/filedeps.dot --- a/doc/dependencies/filedeps.dot Sat Mar 14 12:03:56 2009 +0000 +++ b/doc/dependencies/filedeps.dot Sat Mar 14 12:13:29 2009 +0000 @@ -165,6 +165,7 @@ "engine/core/gui/widgets/clicklabel.cpp" -> "util/base/exception.h" "engine/core/gui/widgets/clicklabel.cpp" -> "video/image.h" "engine/core/gui/widgets/icon2.cpp" -> "icon2.hpp" + "engine/core/gui/widgets/togglebutton.cpp" -> "togglebutton.h" "engine/core/gui/widgets/twobutton.cpp" -> "twobutton.h" "engine/core/loaders/native/audio_loaders/ogg_loader.cpp" -> "audio/soundclip.h" "engine/core/loaders/native/audio_loaders/ogg_loader.cpp" -> "ogg_loader.h" @@ -248,6 +249,7 @@ "engine/core/model/structures/instance.cpp" -> "util/base/exception.h" "engine/core/model/structures/instance.cpp" -> "util/log/logger.h" "engine/core/model/structures/instance.cpp" -> "util/math/fife_math.h" + "engine/core/model/structures/instance.cpp" -> "util/time/timemanager.h" "engine/core/model/structures/instance.h" -> "location.h" "engine/core/model/structures/instance.h" -> "model/metamodel/abstractvisual.h" "engine/core/model/structures/instance.h" -> "model/metamodel/object.h" @@ -636,6 +638,7 @@ "engine/core/view/renderers/genericrenderer.cpp" -> "model/structures/location.h" "engine/core/view/renderers/genericrenderer.cpp" -> "util/log/logger.h" "engine/core/view/renderers/genericrenderer.cpp" -> "util/math/fife_math.h" + "engine/core/view/renderers/genericrenderer.cpp" -> "util/time/timemanager.h" "engine/core/view/renderers/genericrenderer.cpp" -> "video/animation.h" "engine/core/view/renderers/genericrenderer.cpp" -> "video/animationpool.h" "engine/core/view/renderers/genericrenderer.cpp" -> "video/fonts/abstractfont.h" diff -r 5816ab527da8 -r 54bfd1015b35 engine/core/gui/guilistener.i --- a/engine/core/gui/guilistener.i Sat Mar 14 12:03:56 2009 +0000 +++ b/engine/core/gui/guilistener.i Sat Mar 14 12:13:29 2009 +0000 @@ -22,36 +22,17 @@ %module fife %{ #include -#include "gui/guilistener.h" +#include +#include %} namespace gcn { - %nodefaultctor; + %feature("director") MouseListener; class MouseListener { public: - }; - - class KeyListener { - public: - }; + virtual ~MouseListener(); - class ActionListener { - public: - }; - %clearnodefaultctor; -} - -namespace FIFE { - - %feature("director") GUIEventListener; - class GUIEventListener : - public gcn::MouseListener, public gcn::KeyListener, public gcn::ActionListener { - public: - GUIEventListener(); - virtual ~GUIEventListener(); - - /* Mouse events */ virtual void mouseEntered(gcn::MouseEvent& mouseEvent); virtual void mouseExited(gcn::MouseEvent& mouseEvent); virtual void mousePressed(gcn::MouseEvent& mouseEvent); @@ -61,9 +42,27 @@ virtual void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent); virtual void mouseMoved(gcn::MouseEvent& mouseEvent); virtual void mouseDragged(gcn::MouseEvent& mouseEvent); - - virtual void action(const gcn::ActionEvent& actionEvent); - + protected: + MouseListener() { } }; + %feature("director") MouseListener; + class KeyListener { + public: + virtual ~KeyListener() { } + + virtual void keyPressed(gcn::KeyEvent& keyEvent) { } + virtual void keyReleased(gcn::KeyEvent& keyEvent) { } + protected: + KeyListener() { } + }; + + %feature("director") ActionListener; + class ActionListener { + public: + virtual ~ActionListener() { } + virtual void action(const gcn::ActionEvent& actionEvent) = 0; + }; } + + diff -r 5816ab527da8 -r 54bfd1015b35 engine/core/util/base/resourceclass.cpp --- a/engine/core/util/base/resourceclass.cpp Sat Mar 14 12:03:56 2009 +0000 +++ b/engine/core/util/base/resourceclass.cpp Sat Mar 14 12:13:29 2009 +0000 @@ -56,7 +56,6 @@ void ResourceClass::setResourceLocation(const ResourceLocation& location) { delete m_location; - m_location = NULL; m_location = location.clone(); } diff -r 5816ab527da8 -r 54bfd1015b35 engine/core/util/resource/pool.h --- a/engine/core/util/resource/pool.h Sat Mar 14 12:03:56 2009 +0000 +++ b/engine/core/util/resource/pool.h Sat Mar 14 12:13:29 2009 +0000 @@ -130,7 +130,6 @@ */ virtual void reset(); - protected: private: class PoolEntry { public: diff -r 5816ab527da8 -r 54bfd1015b35 engine/core/video/animation.cpp --- a/engine/core/video/animation.cpp Sat Mar 14 12:03:56 2009 +0000 +++ b/engine/core/video/animation.cpp Sat Mar 14 12:13:29 2009 +0000 @@ -46,16 +46,9 @@ } Animation::~Animation() { -// std::vector::const_iterator i(m_frames.begin()); -// while (i != m_frames.end()) { -// i->img->decRef(); -// i++; -// } + // note: we don't need to free the images, as they are handled via + // smart references. } - -// void Animation::addFrame(Image* image, unsigned int duration) { -// addFrame(ResourcePtr(image),duration); -// } void Animation::addFrame(ResourcePtr image, unsigned int duration) { FrameInfo info; diff -r 5816ab527da8 -r 54bfd1015b35 engine/extensions/pychan/__init__.py --- a/engine/extensions/pychan/__init__.py Sat Mar 14 12:03:56 2009 +0000 +++ b/engine/extensions/pychan/__init__.py Sat Mar 14 12:13:29 2009 +0000 @@ -10,7 +10,7 @@ -------- - Simpler Interface - Very Basic XML Format support - - Very Basic Layout Engine + - Basic Layout Engine - Pseudo-Synchronous Dialogs. - Automagic background tiling (WIP) - Basic Styling support. @@ -24,8 +24,6 @@ - Documentation ( Allways not enough :-( ) - Handle Image Fonts - Move Font config files to XML, too ... - - Add support for fixed size 'Spacers' - - Add messageBox(text) - Implement real Menus - Implement StackWidget @@ -37,9 +35,9 @@ BUGS ---- - Focus problems with Widget.execute. - - Layout Bugs where max_size of a parent widget get's ignored. - Font.glyph_spacing is rendered incorrectly. - - It just looks ugly. + - Is this a bug? At least inconvenient. MouseEntered events are not distributed for freshly shown widget. + - It just looks bad. Problems -------- @@ -154,6 +152,8 @@ } } +A new style is added to pychan with L{internal.Manager.addStyle}. + The font is set via a string identifier pulled from a font definition in a PyChan configuration file. You have to load these by calling L{loadFonts} in your startup code:: @@ -225,8 +225,6 @@ 'manager' ] -import fife, pythonize - from widgets import * from exceptions import * @@ -244,9 +242,11 @@ @param engine: The FIFE engine object. """ - from manager import Manager + from compat import _munge_engine_hook + from internal import Manager global manager - manager = Manager(engine,debug) + + manager = Manager(_munge_engine_hook(engine),debug) # XML Loader @@ -326,6 +326,9 @@ def _createSpacer(self,cls,name,attrs): obj = cls(parent=self.root) + for k,v in attrs.items(): + self._setAttr(obj,k,v) + if hasattr(self.root,'add'): self.root.addSpacer(obj) else: diff -r 5816ab527da8 -r 54bfd1015b35 engine/extensions/pychan/autoposition.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/extensions/pychan/autoposition.py Sat Mar 14 12:13:29 2009 +0000 @@ -0,0 +1,121 @@ +# coding: utf-8 + +""" +Automatic widget positioning +============================ + +You can use the C{position_techinque} attribute +on top level widgets which can also be set from xml. + +For direct use call L{placeWidget}. +""" + +from internal import screen_width, screen_height +from exceptions import PyChanException + +EXPLICIT = "explicit" +AUTOMATIC = "automatic" + +TOP = "top" +LEFT = "left" +RIGHT = "right" +CENTER = "center" +BOTTOM = "bottom" + + +def _splicePosition(p): + if "+" in p: + technique,delta = p.split("+") + elif "-" in p: + technique,delta = p.split("-") + delta = '-' + delta + else: + technique,delta = p,0 + delta = int(delta) + return technique,delta + +def _parsePosition(position): + try: + if position == AUTOMATIC: + position = "center+0:center+0" + + x_pos,y_pos = position.split(":") + x_pos, x_delta = _splicePosition(x_pos) + y_pos, y_delta = _splicePosition(y_pos) + + if x_pos not in [EXPLICIT,LEFT,CENTER,RIGHT]: + raise + if y_pos not in [EXPLICIT,TOP,CENTER,BOTTOM]: + raise + except: + raise PyChanException("Malformed position definition: " + repr(position)) + return x_pos,x_delta,y_pos,y_delta + +def placeWidget(widget,position): + """ + Place a widget according to a string defining relative coordinates to screen borders. + + The position definition has to be of the form: C{":"} + + C{} may be one of: + - left + - right + - center + - explicit + + C{} may be one of: + - top + - bottom + - center + - explicit + + C{explicit} means that the widgets x or y position will not be touched. The rest should be + self explanatory. + + C{} and C{} must be of the form: +pixel_number or -pixel_number. Or comletely + omitted. Note that the sign has to be there for for positive deltas, too. + + For brevity two shortcuts exist: + - "explict" -> "explict:explict" + - "automatic" -> "center:center" + + A few examples:: + "right-20:top" + "center:top+10" + "center:center" + + @param widget: The PyChan widget. + @param position: A position definition. + + If the position cannot be parsed a L{PyChanException} is thrown. + + """ + if position == EXPLICIT: + return + x_pos,x_delta,y_pos,y_delta = _parsePosition(position) + + x,y = widget.position + w,h = widget.size + + if x_pos == CENTER: + x = (screen_width()-w)/2 + x_delta + + if y_pos == CENTER: + y = (screen_height()-h)/2 + y_delta + + if x_pos == LEFT: + x = x_delta + + if y_pos == TOP: + y = y_delta + + if x_pos == RIGHT: + x = screen_width() - w + x_delta + + if y_pos == BOTTOM: + y = screen_height() - h + y_delta + + widget.position = x,y + + + diff -r 5816ab527da8 -r 54bfd1015b35 engine/extensions/pychan/compat.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/extensions/pychan/compat.py Sat Mar 14 12:13:29 2009 +0000 @@ -0,0 +1,87 @@ +# coding: utf-8 + +in_fife = None +guichan = None + +def _import_guichan(): + global in_fife + + err_fife = "" + try: + import fife + in_fife = True + return fife + except ImportError, e: + err_fife = str(e) + + try: + import guichan + in_fife = False + return guichan + except ImportError, e: + import traceback + traceback.print_exc() + raise ImportError("Couldn't import neither fife nor guichan: fife:'%s' guichan:'%s'" % (err_fife,str(e))) +guichan = _import_guichan() + + + +def _munge_engine_hook(engine): + engine.translate_mouse_event = getattr(engine,'translate_mouse_event',lambda x : x ) + engine.translate_key_event = getattr(engine,'translate_key_event',lambda x : x ) + + if not in_fife: + return engine + if not isinstance(engine,fife.Engine): + return engine + + guimanager = engine.getGuiManager() + + def _fife_load_image(filename): + index = engine.imagePool.addResourceFromFile(filename) + return guichan.GuiImage(index,engine.getImagePool()) + + class hook: + pass + hook = hook() + + hook.add_widget = guimanager.add + hook.remove_widget = guimanager.remove + hook.default_font = engine.getDefaultFont() + hook.load_image = _fife_load_image + hook.translate_mouse_event = guimanager.translateMouseEvent + hook.translate_key_event = guimanager.translateKeyEvent + + hook.screen_width = engine.getRenderBackend().getScreenWidth() + hook.screen_height = engine.getRenderBackend().getScreenHeight() + + hook.engine = engine + return hook + + +class _multilistener(guichan.ActionListener,guichan.MouseListener,guichan.KeyListener): + def __init__(self): + guichan.ActionListener.__init__(self) + guichan.MouseListener.__init__(self) + guichan.KeyListener.__init__(self) + + +class _point(object): + def __init__(self,x=0,y=0): + self.x=0 + self.y=0 + +if in_fife: + fife = guichan + guichan.ActionListener._ActionListener_init__ = lambda x : x + #guichan.MouseListener.__init__ = lambda x : x + guichan.KeyListener.__init__ = lambda x : x +else: + guichan.Point = _point + guichan.ScrollArea.SHOW_AUTO = guichan.ScrollArea.ShowAuto + guichan.ScrollArea.SHOW_NEVER = guichan.ScrollArea.ShowNever + guichan.ScrollArea.SHOW_ALWAYS = guichan.ScrollArea.ShowAlways + +assert isinstance(_multilistener(),guichan.ActionListener) +assert isinstance(_multilistener(),guichan.MouseListener) +assert isinstance(_multilistener(),guichan.KeyListener) diff -r 5816ab527da8 -r 54bfd1015b35 engine/extensions/pychan/dialogs.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/extensions/pychan/dialogs.py Sat Mar 14 12:13:29 2009 +0000 @@ -0,0 +1,197 @@ +# coding: utf-8 + +from pychan import loadXML +import pychan.tools +import widgets +from internal import get_manager, screen_width, screen_height +from StringIO import StringIO + +OK,YES,NO,CANCEL = True,True,False,None + +def print_event(**kwargs): + print kwargs + +class XMLDialog(object): + def __init__(self, xml, ok_field = None, cancel_field = None,initial_data={},data={}): + self.gui = loadXML(xml) + self.ok_field = ok_field + self.cancel_field = cancel_field + self.initial_data= initial_data + self.data= data + self.max_size=None + self.min_size=None + self.gui.capture(print_event,"mouseEntered") + + def execute(self): + self.gui.distributeInitialData(self.initial_data) + self.gui.distributeData(self.data) + + screen_w, screen_h = screen_width(), screen_height() + if self.max_size is None: + self.max_size = screen_w/2, screen_h/3 + if self.min_size is None: + self.min_size = screen_w/2, screen_h/4 + self.gui.max_size = self.max_size + self.gui.min_size = self.min_size + + resultMap = {} + if self.gui.findChild(name="okButton"): + resultMap["okButton"] = OK + + if self.gui.findChild(name="cancelButton"): + resultMap["cancelButton"] = CANCEL + + if self.gui.findChild(name="yesButton"): + resultMap["noButton"] = NO + + if self.gui.findChild(name="yesButton"): + resultMap["yesButton"] = YES + + ok = self.gui.execute(resultMap) + if ok: + return self.getOkResult() + return self.getCancelResult() + + def getOkResult(self): + if self.ok_field: + return self.gui.collectData(self.ok_field) + return True + + def getCancelResult(self): + if self.cancel_field: + return self.gui.collectData(self.cancel_field) + return False + +MESSAGE_BOX_XML = """\ + + + + +