Mercurial > fife-parpg
comparison 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 |
comparison
equal
deleted
inserted
replaced
156:376b8afc9a18 | 157:bb9902910067 |
---|---|
1 #coding: utf-8 | |
2 | |
3 """ | |
4 PyChan event handling | |
5 ===================== | |
6 | |
7 Users shouldn't need to use this module directly. | |
8 L{widgets.Widget.capture} and L{widgets.Widget.mapEvents} provide | |
9 a convenient API to capture events. | |
10 | |
11 Nevertheless to understand how its supposed to work | |
12 take a look at L{EventMapper} and L{EventListener} | |
13 | |
14 Available Events | |
15 ---------------- | |
16 | |
17 """ | |
18 | |
19 import fife | |
20 import exceptions | |
21 import manager | |
22 import tools | |
23 | |
24 EVENTS = [ | |
25 "mouseEntered", | |
26 "mouseExited", | |
27 "mousePressed", | |
28 "mouseReleased", | |
29 "mouseClicked", | |
30 "mouseMoved", | |
31 "mouseDragged", | |
32 "action", | |
33 "keyPressed", | |
34 "keyReleased", | |
35 ] | |
36 | |
37 # Add the EVENTS to the docs. | |
38 __doc__ += "".join([" - %s\n" % event for event in EVENTS]) | |
39 | |
40 MOUSE_EVENT, KEY_EVENT, ACTION_EVENT = range(3) | |
41 def getEventType(name): | |
42 if "mouse" in name: | |
43 return MOUSE_EVENT | |
44 if "key" in name: | |
45 return MOUSE_EVENT | |
46 return ACTION_EVENT | |
47 | |
48 | |
49 CALLBACK_NONE_MESSAGE = """\ | |
50 You passed None as parameter to %s.capture, which would normally remove a mapped event. | |
51 But there was no event mapped. Did you accidently call a function instead of passing it? | |
52 """ | |
53 | |
54 class EventListener(fife.GUIEventListener): | |
55 """ | |
56 Redirector for event callbacks. | |
57 Use *only* from L{EventMapper}. | |
58 | |
59 This class uses the SWIG director feature - overriden | |
60 virtual methods are called from C++ to - listen to | |
61 Guichan events. | |
62 | |
63 When the module is first loaded the event handler | |
64 methods are auto-generated from the list L{EVENTS}. | |
65 This is effectively the same code as:: | |
66 def mouseEntered(self,event): | |
67 self._redirectEvent("mouseEntered",event) | |
68 | |
69 This way L{EVENTS} and the actually receivable events | |
70 are forced to be in sync. | |
71 """ | |
72 def __init__(self,debug=True): | |
73 super(EventListener,self).__init__() | |
74 self.events = {} | |
75 self.indent = 0 | |
76 self.debug = debug | |
77 self.guimanager = manager.Manager.manager.guimanager | |
78 | |
79 def _redirectEvent(self,name,event): | |
80 self.indent += 4 | |
81 event = self.translateEvent(getEventType(name), event) | |
82 if name in self.events: | |
83 if self.debug: print "-"*self.indent, name | |
84 self.events[name]( event ) | |
85 self.indent -= 4 | |
86 | |
87 def translateEvent(self,event_type,event): | |
88 if event_type == MOUSE_EVENT: | |
89 return self.guimanager.translateMouseEvent(event) | |
90 if event_type == KEY_EVENT: | |
91 return self.guimanager.translateKeyEvent(event) | |
92 return event | |
93 | |
94 def _redirect(name): | |
95 def redirectorFunc(self,event): | |
96 self._redirectEvent(name,event) | |
97 return redirectorFunc | |
98 | |
99 for event_name in EVENTS: | |
100 setattr(EventListener,event_name,_redirect(event_name)) | |
101 | |
102 class EventMapper(object): | |
103 """ | |
104 Handles events and callbacks for L{widgets.Widget} | |
105 and derived classes. | |
106 | |
107 Every PyChan widget has an L{EventMapper} instance | |
108 as attribute *event_mapper*. | |
109 | |
110 This instance handles all necessary house-keeping. | |
111 Such an event mapper can be either *attached* or | |
112 *detached*. In its attached state an L{EventListener} | |
113 is added to the Guichan widget and will redirect | |
114 the events to the callbacks. | |
115 | |
116 In its detached state no events are received from the | |
117 real Guichan widget. | |
118 | |
119 The event mapper starts in the detached state. | |
120 When a new event is captured the mapper attaches itself | |
121 automatically. The widget doesn't need to handle that. | |
122 """ | |
123 def __init__(self,widget): | |
124 super(EventMapper,self).__init__() | |
125 self.widget = widget | |
126 self.listener = EventListener() | |
127 self.is_attached = False | |
128 self.debug = manager.Manager.manager.debug | |
129 | |
130 def __del__(self): | |
131 self.detach() | |
132 def __repr__(self): | |
133 return "EventMapper(%s)" % repr(self.widget) | |
134 | |
135 def attach(self): | |
136 """ | |
137 Start receiving events. | |
138 No need to call this manually. | |
139 """ | |
140 | |
141 if self.is_attached: | |
142 return | |
143 if not self.listener.events: | |
144 return | |
145 if self.debug: print "Attach:",self | |
146 self.widget.real_widget.addMouseListener( self.listener ) | |
147 self.widget.real_widget.addActionListener( self.listener ) | |
148 self.is_attached = True | |
149 | |
150 def detach(self): | |
151 """ | |
152 Stop receiving events. | |
153 No need to call this manually. | |
154 """ | |
155 | |
156 if not self.is_attached: | |
157 return | |
158 if self.debug: print "Detach:",self | |
159 self.widget.real_widget.removeMouseListener( self.listener ) | |
160 self.widget.real_widget.removeActionListener( self.listener ) | |
161 self.is_attached = False | |
162 | |
163 def capture(self,event_name,callback): | |
164 if event_name not in EVENTS: | |
165 raise exceptions.RuntimeError("Unknown eventname: " + event_name) | |
166 | |
167 if callback is None and not self.isCaptured(event_name): | |
168 if self.debug: | |
169 print CALLBACK_NONE_MESSAGE % str(self.widget) | |
170 return | |
171 | |
172 if callback is None: | |
173 del self.listener.events[event_name] | |
174 if not self.listener.events: | |
175 self.detach() | |
176 return | |
177 | |
178 if not callable(callback): | |
179 raise RuntimeError("An event callback must be either a callable or None - not %s" % repr(callback)) | |
180 | |
181 def captured_f(event): | |
182 tools.applyOnlySuitable(callback,event=event,widget=self.widget) | |
183 | |
184 self.listener.events[event_name] = captured_f | |
185 self.attach() | |
186 | |
187 def isCaptured(self,event_name): | |
188 return event_name in self.listener.events | |
189 | |
190 def getCapturedEvents(self): | |
191 return self.listener.events.keys() | |
192 | |
193 | |
194 def splitEventDescriptor(name): | |
195 """ Utility function to split "widgetName/eventName" descriptions into tuples. """ | |
196 L = name.split("/") | |
197 if len(L) == 1: | |
198 L = L[0],"action" | |
199 if len(L) != 2: | |
200 raise exceptions.RuntimeError("Invalid widgetname / eventname combination: " + name) | |
201 if L[1] not in EVENTS: | |
202 raise exceptions.RuntimeError("Unknown event name: " + name) | |
203 return L | |
204 |