bugfix.
[minwii.git] / src / pgu / gui / container.py
1 """
2 """
3 import pygame
4 from pygame.locals import *
5
6 from const import *
7 import widget, surface
8 import pguglobals
9
10 class Container(widget.Widget):
11 """The base container widget, can be used as a template as well as stand alone.
12
13 <pre>Container()</pre>
14 """
15 def __init__(self,**params):
16 widget.Widget.__init__(self,**params)
17 self.myfocus = None
18 self.mywindow = None
19 self.myhover = None
20 #self.background = 0
21 self.widgets = []
22 self.windows = []
23 self.toupdate = {}
24 self.topaint = {}
25
26 def update(self,s):
27 updates = []
28
29 if self.myfocus: self.toupdate[self.myfocus] = self.myfocus
30
31 for w in self.topaint:
32 if w is self.mywindow:
33 continue
34 else:
35 sub = surface.subsurface(s,w.rect)
36 #if (hasattr(w, "_container_bkgr")):
37 # sub.blit(w._container_bkgr,(0,0))
38 w.paint(sub)
39 updates.append(pygame.rect.Rect(w.rect))
40
41 for w in self.toupdate:
42 if w is self.mywindow:
43 continue
44 else:
45 us = w.update(surface.subsurface(s,w.rect))
46 if us:
47 for u in us:
48 updates.append(pygame.rect.Rect(u.x + w.rect.x,u.y+w.rect.y,u.w,u.h))
49
50 for w in self.topaint:
51 if w is self.mywindow:
52 w.paint(self.top_surface(s,w))
53 updates.append(pygame.rect.Rect(w.rect))
54 else:
55 continue
56
57 for w in self.toupdate:
58 if w is self.mywindow:
59 us = w.update(self.top_surface(s,w))
60 else:
61 continue
62 if us:
63 for u in us:
64 updates.append(pygame.rect.Rect(u.x + w.rect.x,u.y+w.rect.y,u.w,u.h))
65
66 self.topaint = {}
67 self.toupdate = {}
68
69 return updates
70
71 def repaint(self,w=None):
72 if not w:
73 return widget.Widget.repaint(self)
74 self.topaint[w] = w
75 self.reupdate()
76
77 def reupdate(self,w=None):
78 if not w:
79 return widget.Widget.reupdate(self)
80 self.toupdate[w] = w
81 self.reupdate()
82
83 def paint(self,s):
84 self.toupdate = {}
85 self.topaint = {}
86 for w in self.widgets:
87 try:
88 sub = surface.subsurface(s, w.rect)
89 except:
90 print 'container.paint(): %s not inside %s' % (
91 w.__class__.__name__,self.__class__.__name__)
92 print s.get_width(), s.get_height(), w.rect
93 print ""
94 else:
95 # if (not hasattr(w,'_container_bkgr') or
96 # w._container_bkgr.get_size() != sub.get_size()):
97 # #w._container_bkgr.get_width() == sub.get_width() and
98 # #w._container_bkgr.get_height() == sub.get_height())):
99 # w._container_bkgr = sub.copy()
100 # w._container_bkgr.fill((0,0,0,0))
101 # w._container_bkgr.blit(sub,(0,0))
102 w.paint(sub)
103
104 for w in self.windows:
105 w.paint(self.top_surface(s,w))
106
107 def top_surface(self,s,w):
108 x,y = s.get_abs_offset()
109 s = s.get_abs_parent()
110 return surface.subsurface(s,(x+w.rect.x,y+w.rect.y,w.rect.w,w.rect.h))
111
112 def event(self,e):
113 used = False
114
115 if self.mywindow and e.type == MOUSEBUTTONDOWN:
116 w = self.mywindow
117 if self.myfocus is w:
118 if not w.rect.collidepoint(e.pos): self.blur(w)
119 if not self.myfocus:
120 if w.rect.collidepoint(e.pos): self.focus(w)
121
122 if not self.mywindow:
123 #### by Gal Koren
124 ##
125 ## if e.type == FOCUS:
126 if e.type == FOCUS and not self.myfocus:
127 #self.first()
128 pass
129 elif e.type == EXIT:
130 if self.myhover: self.exit(self.myhover)
131 elif e.type == BLUR:
132 if self.myfocus: self.blur(self.myfocus)
133 elif e.type == MOUSEBUTTONDOWN:
134 h = None
135 for w in self.widgets:
136 if not w.disabled: #focusable not considered, since that is only for tabs
137 if w.rect.collidepoint(e.pos):
138 h = w
139 if self.myfocus is not w: self.focus(w)
140 if not h and self.myfocus:
141 self.blur(self.myfocus)
142 elif e.type == MOUSEMOTION:
143 if 1 in e.buttons:
144 if self.myfocus: ws = [self.myfocus]
145 else: ws = []
146 else: ws = self.widgets
147
148 h = None
149 for w in ws:
150 if w.rect.collidepoint(e.pos):
151 h = w
152 if self.myhover is not w: self.enter(w)
153 if not h and self.myhover:
154 self.exit(self.myhover)
155 w = self.myhover
156
157 if w and w is not self.myfocus:
158 sub = pygame.event.Event(e.type,{
159 'buttons':e.buttons,
160 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y),
161 'rel':e.rel})
162 used = w._event(sub)
163
164 w = self.myfocus
165 if w:
166 sub = e
167
168 if e.type == MOUSEBUTTONUP or e.type == MOUSEBUTTONDOWN:
169 sub = pygame.event.Event(e.type,{
170 'button':e.button,
171 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y)})
172 used = w._event(sub)
173 elif e.type == CLICK and self.myhover is w:
174 sub = pygame.event.Event(e.type,{
175 'button':e.button,
176 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y)})
177 used = w._event(sub)
178 elif e.type == CLICK: #a dead click
179 pass
180 elif e.type == MOUSEMOTION:
181 sub = pygame.event.Event(e.type,{
182 'buttons':e.buttons,
183 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y),
184 'rel':e.rel})
185 used = w._event(sub)
186 else:
187 used = w._event(sub)
188
189 if not used:
190 if e.type is KEYDOWN:
191 if e.key is K_TAB and self.myfocus:
192 if (e.mod&KMOD_SHIFT) == 0:
193 self.myfocus.next()
194 else:
195 self.myfocus.previous()
196 return True
197 elif e.key == K_UP:
198 self._move_focus(0,-1)
199 return True
200 elif e.key == K_RIGHT:
201 self._move_focus(1,0)
202 return True
203 elif e.key == K_DOWN:
204 self._move_focus(0,1)
205 return True
206 elif e.key == K_LEFT:
207 self._move_focus(-1,0)
208 return True
209 return used
210
211 def _move_focus(self,dx_,dy_):
212 myfocus = self.myfocus
213 if not self.myfocus: return
214
215 widgets = self._get_widgets(pguglobals.app)
216 #if myfocus not in widgets: return
217 #widgets.remove(myfocus)
218 if myfocus in widgets:
219 widgets.remove(myfocus)
220 rect = myfocus.get_abs_rect()
221 fx,fy = rect.centerx,rect.centery
222
223 def sign(v):
224 if v < 0: return -1
225 if v > 0: return 1
226 return 0
227
228 dist = []
229 for w in widgets:
230 wrect = w.get_abs_rect()
231 wx,wy = wrect.centerx,wrect.centery
232 dx,dy = wx-fx,wy-fy
233 if dx_ > 0 and wrect.left < rect.right: continue
234 if dx_ < 0 and wrect.right > rect.left: continue
235 if dy_ > 0 and wrect.top < rect.bottom: continue
236 if dy_ < 0 and wrect.bottom > rect.top: continue
237 dist.append((dx*dx+dy*dy,w))
238 if not len(dist): return
239 dist.sort()
240 d,w = dist.pop(0)
241 w.focus()
242
243 def _get_widgets(self,c):
244 widgets = []
245 if c.mywindow:
246 widgets.extend(self._get_widgets(c.mywindow))
247 else:
248 for w in c.widgets:
249 if isinstance(w,Container):
250 widgets.extend(self._get_widgets(w))
251 elif not w.disabled and w.focusable:
252 widgets.append(w)
253 return widgets
254
255 def remove(self,w):
256 """Remove a widget from the container.
257
258 <pre>Container.remove(w)</pre>
259 """
260 self.blur(w)
261 self.widgets.remove(w)
262 #self.repaint()
263 self.chsize()
264
265 def add(self,w,x,y):
266 """Add a widget to the container.
267
268 <pre>Container.add(w,x,y)</pre>
269
270 <dl>
271 <dt>x, y<dd>position of the widget
272 </dl>
273 """
274 w.style.x = x
275 w.style.y = y
276 w.container = self
277 #NOTE: this might fix it, sort of...
278 #but the thing is, we don't really want to resize
279 #something if it is going to get resized again later
280 #for no reason...
281 #w.rect.x,w.rect.y = w.style.x,w.style.y
282 #w.rect.w, w.rect.h = w.resize()
283 self.widgets.append(w)
284 self.chsize()
285
286 def open(self,w=None,x=None,y=None):
287 if (not w):
288 w = self
289
290 if (x != None):
291 # The position is relative to this container
292 rect = self.get_abs_rect()
293 pos = (rect.x + x, rect.y + y)
294 else:
295 pos = None
296 # Have the application open the window
297 pguglobals.app.open(w, pos)
298
299 def focus(self,w=None):
300 widget.Widget.focus(self) ### by Gal koren
301 # if not w:
302 # return widget.Widget.focus(self)
303 if not w: return
304 if self.myfocus: self.blur(self.myfocus)
305 if self.myhover is not w: self.enter(w)
306 self.myfocus = w
307 w._event(pygame.event.Event(FOCUS))
308
309 #print self.myfocus,self.myfocus.__class__.__name__
310
311 def blur(self,w=None):
312 if not w:
313 return widget.Widget.blur(self)
314 if self.myfocus is w:
315 if self.myhover is w: self.exit(w)
316 self.myfocus = None
317 w._event(pygame.event.Event(BLUR))
318
319 def enter(self,w):
320 if self.myhover: self.exit(self.myhover)
321 self.myhover = w
322 w._event(pygame.event.Event(ENTER))
323
324 def exit(self,w):
325 if self.myhover and self.myhover is w:
326 self.myhover = None
327 w._event(pygame.event.Event(EXIT))
328
329
330 # def first(self):
331 # for w in self.widgets:
332 # if w.focusable:
333 # self.focus(w)
334 # return
335 # if self.container: self.container.next(self)
336
337 # def next(self,w):
338 # if w not in self.widgets: return #HACK: maybe. this happens in windows for some reason...
339 #
340 # for w in self.widgets[self.widgets.index(w)+1:]:
341 # if w.focusable:
342 # self.focus(w)
343 # return
344 # if self.container: return self.container.next(self)
345
346
347 def _next(self,orig=None):
348 start = 0
349 if orig in self.widgets: start = self.widgets.index(orig)+1
350 for w in self.widgets[start:]:
351 if not w.disabled and w.focusable:
352 if isinstance(w,Container):
353 if w._next():
354 return True
355 else:
356 self.focus(w)
357 return True
358 return False
359
360 def _previous(self,orig=None):
361 end = len(self.widgets)
362 if orig in self.widgets: end = self.widgets.index(orig)
363 ws = self.widgets[:end]
364 ws.reverse()
365 for w in ws:
366 if not w.disabled and w.focusable:
367 if isinstance(w,Container):
368 if w._previous():
369 return True
370 else:
371 self.focus(w)
372 return True
373 return False
374
375 def next(self,w=None):
376 if w != None and w not in self.widgets: return #HACK: maybe. this happens in windows for some reason...
377
378 if self._next(w): return True
379 if self.container: return self.container.next(self)
380
381
382 def previous(self,w=None):
383 if w != None and w not in self.widgets: return #HACK: maybe. this happens in windows for some reason...
384
385 if self._previous(w): return True
386 if self.container: return self.container.previous(self)
387
388 def resize(self,width=None,height=None):
389 #r = self.rect
390 #r.w,r.h = 0,0
391 ww,hh = 0,0
392 if self.style.width: ww = self.style.width
393 if self.style.height: hh = self.style.height
394
395 for w in self.widgets:
396 #w.rect.w,w.rect.h = 0,0
397 w.rect.x,w.rect.y = w.style.x,w.style.y
398 w.rect.w, w.rect.h = w.resize()
399 #w._resize()
400
401 ww = max(ww,w.rect.right)
402 hh = max(hh,w.rect.bottom)
403 return ww,hh
404
405 # Returns the widget with the given name
406 def find(self, name):
407 for w in self.widgets:
408 if (w.name == name):
409 return w
410 elif (isinstance(w, Container)):
411 tmp = w.find(name)
412 if (tmp): return tmp
413 return None
414