Mercurial > fife-parpg
diff engine/extensions/pychan/events.py @ 157:bb9902910067
input_rework merged!
Bad features:
* Broken DND for zero-projekt.
* Design short-comings.
author | phoku@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Tue, 14 Oct 2008 07:41:48 +0000 |
parents | |
children | 5b04a7d3ded6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/extensions/pychan/events.py Tue Oct 14 07:41:48 2008 +0000 @@ -0,0 +1,204 @@ +#coding: utf-8 + +""" +PyChan event handling +===================== + +Users shouldn't need to use this module directly. +L{widgets.Widget.capture} and L{widgets.Widget.mapEvents} provide +a convenient API to capture events. + +Nevertheless to understand how its supposed to work +take a look at L{EventMapper} and L{EventListener} + +Available Events +---------------- + +""" + +import fife +import exceptions +import manager +import tools + +EVENTS = [ + "mouseEntered", + "mouseExited", + "mousePressed", + "mouseReleased", + "mouseClicked", + "mouseMoved", + "mouseDragged", + "action", + "keyPressed", + "keyReleased", +] + +# Add the EVENTS to the docs. +__doc__ += "".join([" - %s\n" % event for event in EVENTS]) + +MOUSE_EVENT, KEY_EVENT, ACTION_EVENT = range(3) +def getEventType(name): + if "mouse" in name: + return MOUSE_EVENT + if "key" in name: + return MOUSE_EVENT + return ACTION_EVENT + + +CALLBACK_NONE_MESSAGE = """\ +You passed None as parameter to %s.capture, which would normally remove a mapped event. +But there was no event mapped. Did you accidently call a function instead of passing it? +""" + +class EventListener(fife.GUIEventListener): + """ + Redirector for event callbacks. + Use *only* from L{EventMapper}. + + This class uses the SWIG director feature - overriden + virtual methods are called from C++ to - listen to + Guichan events. + + When the module is first loaded the event handler + methods are auto-generated from the list L{EVENTS}. + This is effectively the same code as:: + def mouseEntered(self,event): + self._redirectEvent("mouseEntered",event) + + This way L{EVENTS} and the actually receivable events + are forced to be in sync. + """ + def __init__(self,debug=True): + super(EventListener,self).__init__() + self.events = {} + self.indent = 0 + self.debug = debug + self.guimanager = manager.Manager.manager.guimanager + + def _redirectEvent(self,name,event): + self.indent += 4 + event = self.translateEvent(getEventType(name), event) + if name in self.events: + if self.debug: print "-"*self.indent, name + self.events[name]( event ) + self.indent -= 4 + + def translateEvent(self,event_type,event): + if event_type == MOUSE_EVENT: + return self.guimanager.translateMouseEvent(event) + if event_type == KEY_EVENT: + return self.guimanager.translateKeyEvent(event) + return event + +def _redirect(name): + def redirectorFunc(self,event): + self._redirectEvent(name,event) + return redirectorFunc + +for event_name in EVENTS: + setattr(EventListener,event_name,_redirect(event_name)) + +class EventMapper(object): + """ + Handles events and callbacks for L{widgets.Widget} + and derived classes. + + Every PyChan widget has an L{EventMapper} instance + as attribute *event_mapper*. + + This instance handles all necessary house-keeping. + Such an event mapper can be either *attached* or + *detached*. In its attached state an L{EventListener} + is added to the Guichan widget and will redirect + the events to the callbacks. + + In its detached state no events are received from the + real Guichan widget. + + The event mapper starts in the detached state. + When a new event is captured the mapper attaches itself + automatically. The widget doesn't need to handle that. + """ + def __init__(self,widget): + super(EventMapper,self).__init__() + self.widget = widget + self.listener = EventListener() + self.is_attached = False + self.debug = manager.Manager.manager.debug + + def __del__(self): + self.detach() + def __repr__(self): + return "EventMapper(%s)" % repr(self.widget) + + def attach(self): + """ + Start receiving events. + No need to call this manually. + """ + + if self.is_attached: + return + if not self.listener.events: + return + if self.debug: print "Attach:",self + self.widget.real_widget.addMouseListener( self.listener ) + self.widget.real_widget.addActionListener( self.listener ) + self.is_attached = True + + def detach(self): + """ + Stop receiving events. + No need to call this manually. + """ + + if not self.is_attached: + return + if self.debug: print "Detach:",self + self.widget.real_widget.removeMouseListener( self.listener ) + self.widget.real_widget.removeActionListener( self.listener ) + self.is_attached = False + + def capture(self,event_name,callback): + if event_name not in EVENTS: + raise exceptions.RuntimeError("Unknown eventname: " + event_name) + + if callback is None and not self.isCaptured(event_name): + if self.debug: + print CALLBACK_NONE_MESSAGE % str(self.widget) + return + + if callback is None: + del self.listener.events[event_name] + if not self.listener.events: + self.detach() + return + + if not callable(callback): + raise RuntimeError("An event callback must be either a callable or None - not %s" % repr(callback)) + + def captured_f(event): + tools.applyOnlySuitable(callback,event=event,widget=self.widget) + + self.listener.events[event_name] = captured_f + self.attach() + + def isCaptured(self,event_name): + return event_name in self.listener.events + + def getCapturedEvents(self): + return self.listener.events.keys() + + +def splitEventDescriptor(name): + """ Utility function to split "widgetName/eventName" descriptions into tuples. """ + L = name.split("/") + if len(L) == 1: + L = L[0],"action" + if len(L) != 2: + raise exceptions.RuntimeError("Invalid widgetname / eventname combination: " + name) + if L[1] not in EVENTS: + raise exceptions.RuntimeError("Unknown event name: " + name) + return L +