diff options
Diffstat (limited to 'pgu/gui/container.py')
-rw-r--r-- | pgu/gui/container.py | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/pgu/gui/container.py b/pgu/gui/container.py new file mode 100644 index 0000000..b3dd810 --- /dev/null +++ b/pgu/gui/container.py @@ -0,0 +1,455 @@ +""" +""" +import pygame +from pygame.locals import * + +from const import * +import widget, surface +import pguglobals + +class Container(widget.Widget): + """The base container widget, can be used as a template as well as stand alone. + + <pre>Container()</pre> + """ + def __init__(self,**params): + widget.Widget.__init__(self,**params) + self.myfocus = None + self.mywindow = None + self.myhover = None + #self.background = 0 + self.widgets = [] + self.windows = [] + self.toupdate = {} + self.topaint = {} + + def update(self,s): + updates = [] + + if self.myfocus: self.toupdate[self.myfocus] = self.myfocus + + for w in self.topaint: + if w is self.mywindow: + continue + else: + sub = surface.subsurface(s,w.rect) + sub.blit(w._container_bkgr,(0,0)) + w.paint(sub) + updates.append(pygame.rect.Rect(w.rect)) + + for w in self.toupdate: + if w is self.mywindow: + continue + else: + us = w.update(surface.subsurface(s,w.rect)) + if us: + for u in us: + updates.append(pygame.rect.Rect(u.x + w.rect.x,u.y+w.rect.y,u.w,u.h)) + + for w in self.topaint: + if w is self.mywindow: + w.paint(self.top_surface(s,w)) + updates.append(pygame.rect.Rect(w.rect)) + else: + continue + + for w in self.toupdate: + if w is self.mywindow: + us = w.update(self.top_surface(s,w)) + else: + continue + if us: + for u in us: + updates.append(pygame.rect.Rect(u.x + w.rect.x,u.y+w.rect.y,u.w,u.h)) + + self.topaint = {} + self.toupdate = {} + + return updates + + def repaint(self,w=None): + if not w: + return widget.Widget.repaint(self) + self.topaint[w] = w + self.reupdate() + + def reupdate(self,w=None): + if not w: + return widget.Widget.reupdate(self) + self.toupdate[w] = w + self.reupdate() + + def paint(self,s): + self.toupdate = {} + self.topaint = {} + for w in self.widgets: + try: + sub = surface.subsurface(s, w.rect) + except: + print 'container.paint(): %s not inside %s' % ( + w.__class__.__name__,self.__class__.__name__) + print s.get_width(), s.get_height(), w.rect + print "" + else: + if (not (hasattr(w,'_container_bkgr') and + w._container_bkgr.get_width() == sub.get_width() and + w._container_bkgr.get_height() == sub.get_height())): + w._container_bkgr = sub.copy() + w._container_bkgr.fill((0,0,0,0)) + w._container_bkgr.blit(sub,(0,0)) + + w.paint(sub) + + for w in self.windows: + print 'container: windows', len(self.windows), s, w.rect + w.paint(self.top_surface(s,w)) + + def top_surface(self,s,w): + x,y = s.get_abs_offset() + s = s.get_abs_parent() + return surface.subsurface(s,(x+w.rect.x,y+w.rect.y,w.rect.w,w.rect.h)) + + def event(self,e): + used = False + + if self.mywindow and e.type == MOUSEBUTTONDOWN: + w = self.mywindow + if self.myfocus is w: + if not w.rect.collidepoint(e.pos): self.blur(w) + if not self.myfocus: + if w.rect.collidepoint(e.pos): self.focus(w) + + if not self.mywindow: + #### by Gal Koren + ## + ## if e.type == FOCUS: + if e.type == FOCUS and not self.myfocus: + #self.first() + pass + elif e.type == EXIT: + if self.myhover: self.exit(self.myhover) + elif e.type == BLUR: + if self.myfocus: self.blur(self.myfocus) + elif e.type == MOUSEBUTTONDOWN: + h = None + for w in self.widgets: + if not w.disabled: #focusable not considered, since that is only for tabs + if w.rect.collidepoint(e.pos): + h = w + if self.myfocus is not w: self.focus(w) + if not h and self.myfocus: + self.blur(self.myfocus) + elif e.type == MOUSEMOTION: + if 1 in e.buttons: + if self.myfocus: ws = [self.myfocus] + else: ws = [] + else: ws = self.widgets + + h = None + for w in ws: + if w.rect.collidepoint(e.pos): + h = w + if self.myhover is not w: self.enter(w) + if not h and self.myhover: + self.exit(self.myhover) + w = self.myhover + + if w and w is not self.myfocus: + sub = pygame.event.Event(e.type,{ + 'buttons':e.buttons, + 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y), + 'rel':e.rel}) + used = w._event(sub) + + w = self.myfocus + if w: + sub = e + + if e.type == MOUSEBUTTONUP or e.type == MOUSEBUTTONDOWN: + sub = pygame.event.Event(e.type,{ + 'button':e.button, + 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y)}) + used = w._event(sub) + elif e.type == CLICK and self.myhover is w: + sub = pygame.event.Event(e.type,{ + 'button':e.button, + 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y)}) + used = w._event(sub) + elif e.type == CLICK: #a dead click + pass + elif e.type == MOUSEMOTION: + sub = pygame.event.Event(e.type,{ + 'buttons':e.buttons, + 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y), + 'rel':e.rel}) + used = w._event(sub) + else: + used = w._event(sub) + + if not used: + if e.type is KEYDOWN: + if e.key is K_TAB and self.myfocus: + if (e.mod&KMOD_SHIFT) == 0: + self.myfocus.next() + else: + self.myfocus.previous() + return True + elif e.key == K_UP: + self._move_focus(0,-1) + return True + elif e.key == K_RIGHT: + self._move_focus(1,0) + return True + elif e.key == K_DOWN: + self._move_focus(0,1) + return True + elif e.key == K_LEFT: + self._move_focus(-1,0) + return True + return used + + def _move_focus(self,dx_,dy_): + myfocus = self.myfocus + if not self.myfocus: return + + from pgu.gui import App + widgets = self._get_widgets(pguglobals.app) + #if myfocus not in widgets: return + #widgets.remove(myfocus) + if myfocus in widgets: + widgets.remove(myfocus) + rect = myfocus.get_abs_rect() + fx,fy = rect.centerx,rect.centery + + def sign(v): + if v < 0: return -1 + if v > 0: return 1 + return 0 + + dist = [] + for w in widgets: + wrect = w.get_abs_rect() + wx,wy = wrect.centerx,wrect.centery + dx,dy = wx-fx,wy-fy + if dx_ > 0 and wrect.left < rect.right: continue + if dx_ < 0 and wrect.right > rect.left: continue + if dy_ > 0 and wrect.top < rect.bottom: continue + if dy_ < 0 and wrect.bottom > rect.top: continue + dist.append((dx*dx+dy*dy,w)) + if not len(dist): return + dist.sort() + d,w = dist.pop(0) + w.focus() + + def _get_widgets(self,c): + widgets = [] + if c.mywindow: + widgets.extend(self._get_widgets(c.mywindow)) + else: + for w in c.widgets: + if isinstance(w,Container): + widgets.extend(self._get_widgets(w)) + elif not w.disabled and w.focusable: + widgets.append(w) + return widgets + + def remove(self,w): + """Remove a widget from the container. + + <pre>Container.remove(w)</pre> + """ + self.blur(w) + self.widgets.remove(w) + #self.repaint() + self.chsize() + + def add(self,w,x,y): + """Add a widget to the container. + + <pre>Container.add(w,x,y)</pre> + + <dl> + <dt>x, y<dd>position of the widget + </dl> + """ + w.style.x = x + w.style.y = y + w.container = self + #NOTE: this might fix it, sort of... + #but the thing is, we don't really want to resize + #something if it is going to get resized again later + #for no reason... + #w.rect.x,w.rect.y = w.style.x,w.style.y + #w.rect.w, w.rect.h = w.resize() + self.widgets.append(w) + self.chsize() + + def open(self,w=None,x=None,y=None): + from app import App #HACK: I import it here to prevent circular importing + if not w: + if (not hasattr(self,'container') or + not self.container) and self is not pguglobals.app: + self.container = pguglobals.app + #print 'top level open' + return widget.Widget.open(self) + + if self.container: + if x != None: return self.container.open(w,self.rect.x+x,self.rect.y+y) + return self.container.open(w) + + w.container = self + + if w.rect.w == 0 or w.rect.h == 0: #this might be okay, not sure if needed. + #_chsize = App.app._chsize #HACK: we don't want this resize to trigger a chsize. + w.rect.w,w.rect.h = w.resize() + #App.app._chsize = _chsize + + if x == None or y == None: #auto center the window + #w.style.x,w.style.y = 0,0 + w.rect.x = (self.rect.w-w.rect.w)/2 + w.rect.y = (self.rect.h-w.rect.h)/2 + #w.resize() + #w._resize(self.rect.w,self.rect.h) + else: #show it where we want it + w.rect.x = x + w.rect.y = y + #w._resize() + + + self.windows.append(w) + self.mywindow = w + self.focus(w) + self.repaint(w) + w.send(OPEN) + + def close(self,w=None): + if not w: + return widget.Widget.close(self) + + if self.container: #make sure we're in the App + return self.container.close(w) + + if self.myfocus is w: self.blur(w) + + if w not in self.windows: return #no need to remove it twice! happens. + + self.windows.remove(w) + + self.mywindow = None + if self.windows: + self.mywindow = self.windows[-1] + self.focus(self.mywindow) + + if not self.mywindow: + self.myfocus = self.widget #HACK: should be done fancier, i think.. + if not self.myhover: + self.enter(self.widget) + + self.repaintall() + w.send(CLOSE) + + def focus(self,w=None): + widget.Widget.focus(self) ### by Gal koren +# if not w: +# return widget.Widget.focus(self) + if not w: return + if self.myfocus: self.blur(self.myfocus) + if self.myhover is not w: self.enter(w) + self.myfocus = w + w._event(pygame.event.Event(FOCUS)) + + #print self.myfocus,self.myfocus.__class__.__name__ + + def blur(self,w=None): + if not w: + return widget.Widget.blur(self) + if self.myfocus is w: + if self.myhover is w: self.exit(w) + self.myfocus = None + w._event(pygame.event.Event(BLUR)) + + def enter(self,w): + if self.myhover: self.exit(self.myhover) + self.myhover = w + w._event(pygame.event.Event(ENTER)) + + def exit(self,w): + if self.myhover and self.myhover is w: + self.myhover = None + w._event(pygame.event.Event(EXIT)) + + +# def first(self): +# for w in self.widgets: +# if w.focusable: +# self.focus(w) +# return +# if self.container: self.container.next(self) + +# def next(self,w): +# if w not in self.widgets: return #HACK: maybe. this happens in windows for some reason... +# +# for w in self.widgets[self.widgets.index(w)+1:]: +# if w.focusable: +# self.focus(w) +# return +# if self.container: return self.container.next(self) + + + def _next(self,orig=None): + start = 0 + if orig in self.widgets: start = self.widgets.index(orig)+1 + for w in self.widgets[start:]: + if not w.disabled and w.focusable: + if isinstance(w,Container): + if w._next(): + return True + else: + self.focus(w) + return True + return False + + def _previous(self,orig=None): + end = len(self.widgets) + if orig in self.widgets: end = self.widgets.index(orig) + ws = self.widgets[:end] + ws.reverse() + for w in ws: + if not w.disabled and w.focusable: + if isinstance(w,Container): + if w._previous(): + return True + else: + self.focus(w) + return True + return False + + def next(self,w=None): + if w != None and w not in self.widgets: return #HACK: maybe. this happens in windows for some reason... + + if self._next(w): return True + if self.container: return self.container.next(self) + + + def previous(self,w=None): + if w != None and w not in self.widgets: return #HACK: maybe. this happens in windows for some reason... + + if self._previous(w): return True + if self.container: return self.container.previous(self) + + def resize(self,width=None,height=None): + #r = self.rect + #r.w,r.h = 0,0 + ww,hh = 0,0 + if self.style.width: ww = self.style.width + if self.style.height: hh = self.style.height + + for w in self.widgets: + #w.rect.w,w.rect.h = 0,0 + w.rect.x,w.rect.y = w.style.x,w.style.y + w.rect.w, w.rect.h = w.resize() + #w._resize() + + ww = max(ww,w.rect.right) + hh = max(hh,w.rect.bottom) + return ww,hh |