Mercurial > fife-parpg
comparison clients/editor/plugins/ObjectSelector.py @ 255:51cc05d862f2
Merged editor_rewrite branch to trunk.
This contains changes that may break compatibility against existing clients.
For a list of changes that may affect your client, see: http://wiki.fifengine.de/Changes_to_pychan_and_FIFE_in_editor_rewrite_branch
author | cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Mon, 08 Jun 2009 16:00:02 +0000 |
parents | |
children | 6add14ebe9f5 |
comparison
equal
deleted
inserted
replaced
254:10b5f7f36dd4 | 255:51cc05d862f2 |
---|---|
1 # coding: utf-8 | |
2 | |
3 import pychan | |
4 from pychan import widgets, tools, attrs, internal | |
5 from pychan.tools import callbackWithArguments | |
6 import scripts | |
7 import scripts.plugin as plugin | |
8 from scripts.events import * | |
9 from scripts.gui.action import Action | |
10 import fife | |
11 from fife import Color | |
12 | |
13 # TODO: | |
14 # - Better event handling | |
15 | |
16 _DEFAULT_BASE_COLOR = internal.DEFAULT_STYLE['default']['base_color'] | |
17 _DEFAULT_SELECTION_COLOR = internal.DEFAULT_STYLE['default']['selection_color'] | |
18 _DEFAULT_COLOR_STEP = Color(10, 10, 10) | |
19 | |
20 class ObjectIcon(widgets.VBox): | |
21 """ The ObjectIcon is used to represent the object in the object selector. | |
22 """ | |
23 ATTRIBUTES = widgets.VBox.ATTRIBUTES + [ attrs.Attr("text"), attrs.Attr("image"), attrs.BoolAttr("selected") ] | |
24 | |
25 def __init__(self,callback,**kwargs): | |
26 super(ObjectIcon,self).__init__(**kwargs) | |
27 | |
28 self.callback = callback | |
29 | |
30 self.capture(self._mouseEntered, "mouseEntered") | |
31 self.capture(self._mouseExited, "mouseExited") | |
32 self.capture(self._mouseClicked, "mouseClicked") | |
33 | |
34 vbox = widgets.VBox(padding=3) | |
35 | |
36 # Icon | |
37 self.icon = widgets.Icon(**kwargs) | |
38 self.addChild(self.icon) | |
39 | |
40 # Label | |
41 hbox = widgets.HBox(padding=1) | |
42 self.addChild(hbox) | |
43 self.label = widgets.Label(**kwargs) | |
44 hbox.addChild(self.label) | |
45 | |
46 def _setText(self, text): | |
47 self.label.text = text | |
48 | |
49 def _getText(self): | |
50 return self.label.text | |
51 text = property(_getText, _setText) | |
52 | |
53 def _setImage(self, image): | |
54 self.icon.image = image | |
55 | |
56 def _getImage(self): | |
57 return self.icon.image | |
58 image = property(_getImage, _setImage) | |
59 | |
60 def _setSelected(self, enabled): | |
61 if isinstance(self.parent, ObjectIconList): | |
62 if enabled == True: | |
63 self.parent.selected_item = self | |
64 else: | |
65 if self.selected: | |
66 self.parent.selected_item = None | |
67 | |
68 if self.selected: | |
69 self.base_color = _DEFAULT_SELECTION_COLOR | |
70 else: | |
71 self.base_color = _DEFAULT_BASE_COLOR | |
72 | |
73 def _isSelected(self): | |
74 if isinstance(self.parent, ObjectIconList): | |
75 return self == self.parent.selected_item | |
76 return False | |
77 selected = property(_isSelected, _setSelected) | |
78 | |
79 #--- Event handling ---# | |
80 def _mouseEntered(self, event): | |
81 self.base_color += _DEFAULT_COLOR_STEP | |
82 | |
83 def _mouseExited(self, event): | |
84 self.base_color -= _DEFAULT_COLOR_STEP | |
85 | |
86 def _mouseClicked(self, event): | |
87 self.selected = True | |
88 self.callback() | |
89 | |
90 class ObjectIconList(widgets.VBox): | |
91 ATTRIBUTES = widgets.VBox.ATTRIBUTES | |
92 | |
93 def __init__(self,**kwargs): | |
94 super(ObjectIconList, self).__init__(max_size=(5000,500000), **kwargs) | |
95 self.base_color = self.background_color | |
96 | |
97 self.capture(self._keyPressed, "keyPressed") | |
98 #self.capture(self._keyPressed, "keyReleased") | |
99 self._selectedItem = None | |
100 self.is_focusable = True | |
101 | |
102 def _keyPressed(self, event): | |
103 print "KeyEvent", event | |
104 | |
105 def clear(self): | |
106 for c in reversed(self.children): | |
107 self.removeChild(c) | |
108 | |
109 def _setSelectedItem(self, item): | |
110 if isinstance(item, ObjectIcon) or item is None: | |
111 if self._selectedItem is not None: | |
112 tmp = self._selectedItem | |
113 self._selectedItem = item | |
114 tmp.selected = False | |
115 else: | |
116 self._selectedItem = item | |
117 | |
118 def _getSelectedItem(self): | |
119 return self._selectedItem | |
120 selected_item = property(_getSelectedItem, _setSelectedItem) | |
121 | |
122 class ObjectSelector(plugin.Plugin): | |
123 """The ObjectSelector class offers a gui Widget that let's you select the object you | |
124 wish to use to in the editor. | |
125 @param engine: fife instance | |
126 @param map: fife.Map instance containing your map | |
127 @param selectNotify: callback function used to tell the editor you selected an object. | |
128 """ | |
129 def __init__(self): | |
130 self.editor = None | |
131 self.engine = None | |
132 self.mode = 'list' # Other mode is 'preview' | |
133 | |
134 self._enabled = False | |
135 | |
136 def enable(self): | |
137 if self._enabled is True: | |
138 return | |
139 | |
140 self.editor = scripts.editor.getEditor() | |
141 self.engine = self.editor.getEngine() | |
142 | |
143 self._showAction = Action(u"Object selector", checkable=True) | |
144 scripts.gui.action.activated.connect(self.toggle, sender=self._showAction) | |
145 | |
146 self.editor._toolsMenu.addAction(self._showAction) | |
147 | |
148 events.postMapShown.connect(self.update_namespace) | |
149 events.onObjectSelected.connect(self.setPreview) | |
150 | |
151 self.buildGui() | |
152 | |
153 def disable(self): | |
154 if self._enabled is False: | |
155 return | |
156 | |
157 self.gui.hide() | |
158 self.removeAllChildren() | |
159 | |
160 events.postMapShown.disconnect(self.update_namespace) | |
161 events.onObjectSelected.disconnect(self.setPreview) | |
162 | |
163 self.editor._toolsMenu.removeAction(self._showAction) | |
164 | |
165 def isEnabled(self): | |
166 return self._enabled; | |
167 | |
168 def getName(self): | |
169 return "Object selector" | |
170 | |
171 | |
172 def buildGui(self): | |
173 self.gui = pychan.loadXML('gui/objectselector.xml') | |
174 | |
175 # Add search field | |
176 self._searchfield = self.gui.findChild(name="searchField") | |
177 self._searchfield.capture(self._search) | |
178 self._searchfield.capture(self._search, "keyPressed") | |
179 self.gui.findChild(name="searchButton").capture(self._search) | |
180 | |
181 # Add the drop down with list of namespaces | |
182 self.namespaces = self.gui.findChild(name="namespaceDropdown") | |
183 self.namespaces.items = self.engine.getModel().getNamespaces() | |
184 self.namespaces.selected = 0 | |
185 | |
186 # TODO: Replace with SelectionEvent, once pychan supports it | |
187 self.namespaces.capture(self.update_namespace, "action") | |
188 self.namespaces.capture(self.update_namespace, "mouseWheelMovedUp") | |
189 self.namespaces.capture(self.update_namespace, "mouseWheelMovedDown") | |
190 self.namespaces.capture(self.update_namespace, "keyReleased") | |
191 | |
192 # Object list | |
193 self.mainScrollArea = self.gui.findChild(name="mainScrollArea") | |
194 self.objects = None | |
195 if self.mode == 'list': | |
196 self.setTextList() | |
197 else: # Assuming self.mode is 'preview' | |
198 self.setImageList() | |
199 | |
200 # Action buttons | |
201 self.gui.findChild(name="toggleModeButton").capture(self.toggleMode) | |
202 self.gui.findChild(name="closeButton").capture(self.hide) | |
203 | |
204 # Preview area | |
205 self.gui.findChild(name="previewScrollArea").background_color = self.gui.base_color | |
206 self.preview = self.gui.findChild(name="previewIcon") | |
207 | |
208 | |
209 def toggleMode(self): | |
210 if self.mode == 'list': | |
211 self.setImageList() | |
212 self.mode = 'preview' | |
213 elif self.mode == 'preview': | |
214 self.setTextList() | |
215 self.mode = 'list' | |
216 self.update() | |
217 | |
218 | |
219 def setImageList(self): | |
220 """Sets the mainScrollArea to contain a Vbox that can be used to fill in | |
221 preview Images""" | |
222 if self.objects is not None: | |
223 self.mainScrollArea.removeChild(self.objects) | |
224 self.objects = ObjectIconList(name='list', size=(200,1000)) | |
225 self.objects.base_color = self.mainScrollArea.background_color | |
226 self.mainScrollArea.addChild(self.objects) | |
227 | |
228 def setTextList(self): | |
229 """Sets the mainScrollArea to contain a List that can be used to fill in | |
230 Object names/paths""" | |
231 if self.objects is not None: | |
232 self.mainScrollArea.removeChild(self.objects) | |
233 self.objects = widgets.ListBox(name='list') | |
234 self.objects.capture(self.listEntrySelected) | |
235 self.mainScrollArea.addChild(self.objects) | |
236 | |
237 def _search(self): | |
238 self.search(self._searchfield.text) | |
239 | |
240 def search(self, str): | |
241 results = [] | |
242 | |
243 # Format search terms | |
244 terms = [term.lower() for term in str.split()] | |
245 | |
246 # Search | |
247 if len(terms) > 0: | |
248 namespaces = self.engine.getModel().getNamespaces() | |
249 for namesp in namespaces: | |
250 objects = self.engine.getModel().getObjects(namesp) | |
251 for obj in objects: | |
252 doAppend = True | |
253 for term in terms: | |
254 if obj.getId().lower().find(term) < 0: | |
255 doAppend = False | |
256 break | |
257 if doAppend: | |
258 results.append(obj) | |
259 else: | |
260 results = None | |
261 | |
262 if self.mode == 'list': | |
263 self.fillTextList(results) | |
264 elif self.mode == 'preview': | |
265 self.fillPreviewList(results) | |
266 | |
267 def fillTextList(self, objects=None): | |
268 if objects is None: | |
269 if self.namespaces.selected_item is None: | |
270 return | |
271 objects = self.engine.getModel().getObjects(self.namespaces.selected_item) | |
272 | |
273 class _ListItem: | |
274 def __init__( self, name, namespace ): | |
275 self.name = name | |
276 self.namespace = namespace | |
277 def __str__( self ): | |
278 return self.name | |
279 | |
280 if self.namespaces.selected_item: | |
281 self.objects.items = [_ListItem(obj.getId(), obj.getNamespace()) for obj in objects] | |
282 if not self.objects.selected_item: | |
283 self.objects.selected = 0 | |
284 self.listEntrySelected() | |
285 | |
286 def listEntrySelected(self): | |
287 """This function is used as callback for the TextList.""" | |
288 if self.objects.selected_item: | |
289 object_id = self.objects.selected_item.name | |
290 namespace = self.objects.selected_item.namespace | |
291 obj = self.engine.getModel().getObject(object_id, namespace) | |
292 self.objectSelected(obj) | |
293 | |
294 def fillPreviewList(self, objects=None): | |
295 self.objects.clear() | |
296 | |
297 if objects is None: | |
298 if self.namespaces.selected_item is None: | |
299 return | |
300 objects = self.engine.getModel().getObjects(self.namespaces.selected_item) | |
301 | |
302 for obj in objects: | |
303 image = self._getImage(obj) | |
304 if image is None: | |
305 print 'No image available for selected object' | |
306 image = "" | |
307 | |
308 callback = tools.callbackWithArguments(self.objectSelected, obj) | |
309 icon = ObjectIcon(callback=callback, image=image, text=unicode(obj.getId())) | |
310 self.objects.addChild(icon) | |
311 | |
312 if len(objects)>0: | |
313 objects[0].selected = True | |
314 self.objectSelected(objects[0]) | |
315 | |
316 | |
317 def objectSelected(self, obj): | |
318 """This is used as callback function to notify the editor that a new object has | |
319 been selected. | |
320 @param obj: fife.Object instance""" | |
321 | |
322 self.setPreview(obj) | |
323 | |
324 self.gui.adaptLayout() | |
325 events.onObjectSelected.send(sender=self, object=obj) | |
326 | |
327 self.objects.adaptLayout() | |
328 self.gui.adaptLayout() | |
329 | |
330 # Set preview image | |
331 def setPreview(self, object): | |
332 self.preview.image = self._getImage(object) | |
333 height = self.preview.image.getHeight(); | |
334 if height > 200: height = 200 | |
335 self.preview.parent.max_height = height | |
336 | |
337 def update_namespace(self): | |
338 | |
339 self.namespaces.items = self.engine.getModel().getNamespaces() | |
340 if not self.namespaces.selected_item: | |
341 self.namespaces.selected = 0 | |
342 if self.mode == 'list': | |
343 self.setTextList() | |
344 elif self.mode == 'preview': | |
345 self.setImageList() | |
346 self.update() | |
347 | |
348 def update(self): | |
349 if self.mode == 'list': | |
350 self.fillTextList() | |
351 elif self.mode == 'preview': | |
352 self.fillPreviewList() | |
353 | |
354 self.gui.adaptLayout() | |
355 | |
356 def _getImage(self, obj): | |
357 """ Returns an image for the given object. | |
358 @param: fife.Object for which an image is to be returned | |
359 @return: fife.GuiImage""" | |
360 visual = None | |
361 try: | |
362 visual = obj.get2dGfxVisual() | |
363 except: | |
364 print 'Visual Selection created for type without a visual?' | |
365 raise | |
366 | |
367 # Try to find a usable image | |
368 index = visual.getStaticImageIndexByAngle(0) | |
369 image = None | |
370 # if no static image available, try default action | |
371 if index == -1: | |
372 action = obj.getDefaultAction() | |
373 if action: | |
374 animation_id = action.get2dGfxVisual().getAnimationIndexByAngle(0) | |
375 animation = self.engine.getAnimationPool().getAnimation(animation_id) | |
376 image = animation.getFrameByTimestamp(0) | |
377 index = image.getPoolId() | |
378 | |
379 # Construct the new GuiImage that is to be returned | |
380 if index != -1: | |
381 image = fife.GuiImage(index, self.engine.getImagePool()) | |
382 | |
383 return image | |
384 | |
385 | |
386 def show(self): | |
387 self.update_namespace() | |
388 self.gui.show() | |
389 self._showAction.setChecked(True) | |
390 | |
391 def hide(self): | |
392 self.gui.setDocked(False) | |
393 self.gui.hide() | |
394 self._showAction.setChecked(False) | |
395 | |
396 def toggle(self): | |
397 if self.gui.isVisible() or self.gui.isDocked(): | |
398 self.hide() | |
399 else: | |
400 self.show() |