init.
[minwii.git] / src / pgu / gui / app.py
1 """
2 """
3 import pygame
4 from pygame.locals import *
5
6 import pguglobals
7 import container
8 from const import *
9
10 class App(container.Container):
11 """The top-level widget for an application.
12
13 <pre>App(theme=None)</pre>
14
15 <dl>
16 <dt>theme<dd>an instance of a Theme, optional as it will use the default Theme class.
17 </dl>
18
19 <strong>Basic Example</strong>
20 <code>
21 app = gui.App()
22 app.run(widget=widget,screen=screen)
23 </code>
24
25 <strong>Integrated Example</strong>
26 <code>
27 app = gui.App()
28 gui.init(widget=widget)
29 while 1:
30 for e in pygame.event.get():
31 app.event(e)
32 app.update(screen)
33 </code>
34
35
36
37 """
38 def __init__(self,theme=None,**params):
39 self.set_global_app()
40
41 if theme == None:
42 from theme import Theme
43 theme = Theme()
44 self.theme = theme
45
46 params['decorate'] = 'app'
47 container.Container.__init__(self,**params)
48 self._quit = False
49 self.widget = None
50 self._chsize = False
51 self._repaint = False
52
53 self.screen = None
54 self.container = None
55 self.events = []
56
57 def set_global_app(self):
58 # Keep a global reference to this application instance so that PGU
59 # components can easily find it.
60 pguglobals.app = self
61 # For backwards compatibility we keep a reference in the class
62 # itself too.
63 App.app = self
64
65 def resize(self):
66
67 screen = self.screen
68 w = self.widget
69 wsize = 0
70
71 #5 cases
72
73 #input screen is already set use its size
74 if screen:
75 self.screen = screen
76 width,height = screen.get_width(),screen.get_height()
77
78 #display.screen
79 elif pygame.display.get_surface():
80 screen = pygame.display.get_surface()
81 self.screen = screen
82 width,height = screen.get_width(),screen.get_height()
83
84 #app has width,height
85 elif self.style.width != 0 and self.style.height != 0:
86 screen = pygame.display.set_mode((self.style.width,self.style.height),SWSURFACE)
87 self.screen = screen
88 width,height = screen.get_width(),screen.get_height()
89
90 #widget has width,height, or its own size..
91 else:
92 wsize = 1
93 width,height = w.rect.w,w.rect.h = w.resize()
94 #w._resize()
95 screen = pygame.display.set_mode((width,height),SWSURFACE)
96 self.screen = screen
97
98 #use screen to set up size of this widget
99 self.style.width,self.style.height = width,height
100 self.rect.w,self.rect.h = width,height
101 self.rect.x,self.rect.y = 0,0
102
103 w.rect.x,w.rect.y = 0,0
104 w.rect.w,w.rect.h = w.resize(width,height)
105
106 for w in self.windows:
107 w.rect.w,w.rect.h = w.resize()
108
109 self._chsize = False
110
111
112 def init(self,widget=None,screen=None): #TODO widget= could conflict with module widget
113 """Initialize the application.
114
115 <pre>App.init(widget=None,screen=None)</pre>
116
117 <dl>
118 <dt>widget<dd>main widget
119 <dt>screen<dd>pygame.Surface to render to
120 </dl>
121 """
122
123 self.set_global_app()
124
125 if widget: self.widget = widget
126 if screen: self.screen = screen
127
128 self.resize()
129
130 w = self.widget
131
132 self.widgets = []
133 self.widgets.append(w)
134 w.container = self
135 self.focus(w)
136
137 pygame.key.set_repeat(500,30)
138
139 self._repaint = True
140 self._quit = False
141
142 self.send(INIT)
143
144 def event(self,e):
145 """Pass an event to the main widget.
146
147 <pre>App.event(e)</pre>
148
149 <dl>
150 <dt>e<dd>event
151 </dl>
152 """
153 self.set_global_app()
154
155 #NOTE: might want to deal with ACTIVEEVENT in the future.
156 self.send(e.type,e)
157 container.Container.event(self,e)
158 if e.type == MOUSEBUTTONUP:
159 if e.button not in (4,5): #ignore mouse wheel
160 sub = pygame.event.Event(CLICK,{
161 'button':e.button,
162 'pos':e.pos})
163 self.send(sub.type,sub)
164 container.Container.event(self,sub)
165
166
167 def loop(self):
168 self.set_global_app()
169
170 s = self.screen
171 for e in pygame.event.get():
172 if not (e.type == QUIT and self.mywindow):
173 self.event(e)
174 us = self.update(s)
175 pygame.display.update(us)
176
177
178 def paint(self,screen):
179 self.screen = screen
180 if self._chsize:
181 self.resize()
182 self._chsize = False
183 if hasattr(self,'background'):
184 self.background.paint(screen)
185 container.Container.paint(self,screen)
186
187 def update(self,screen):
188 """Update the screen.
189
190 <dl>
191 <dt>screen<dd>pygame surface
192 </dl>
193 """
194 self.screen = screen
195 if self._chsize:
196 self.resize()
197 self._chsize = False
198 if self._repaint:
199 self.paint(screen)
200 self._repaint = False
201 return [pygame.Rect(0,0,screen.get_width(),screen.get_height())]
202 else:
203 us = container.Container.update(self,screen)
204 return us
205
206 def run(self,widget=None,screen=None):
207 """Run an application.
208
209 <p>Automatically calls <tt>App.init</tt> and then forever loops <tt>App.event</tt> and <tt>App.update</tt></p>
210
211 <dl>
212 <dt>widget<dd>main widget
213 <dt>screen<dd>pygame.Surface to render to
214 </dl>
215 """
216 self.init(widget,screen)
217 while not self._quit:
218 self.loop()
219 pygame.time.wait(10)
220
221 def reupdate(self,w=None): pass
222 def repaint(self,w=None): self._repaint = True
223 def repaintall(self): self._repaint = True
224 def chsize(self):
225 self._chsize = True
226 self._repaint = True
227
228 def quit(self,value=None): self._quit = True
229
230 def open(self, w, pos=None):
231 w.container = self
232
233 if (w.rect.w == 0 or w.rect.h == 0):
234 w.rect.size = w.resize()
235
236 if (not pos):
237 # Auto-center the window
238 w.rect.center = self.rect.center
239 #w.rect.topleft = ((self.rect.w - w.rect.w)/2,
240 # (self.rect.h - w.rect.h)/2)
241 else:
242 # Show the window in a particular location
243 w.rect.topleft = pos
244
245 self.windows.append(w)
246 self.mywindow = w
247 self.focus(w)
248 self.repaint(w)
249 w.send(OPEN)
250
251 def close(self, w):
252 if self.myfocus is w: self.blur(w)
253
254 if w not in self.windows: return #no need to remove it twice! happens.
255
256 self.windows.remove(w)
257
258 self.mywindow = None
259 if self.windows:
260 self.mywindow = self.windows[-1]
261 self.focus(self.mywindow)
262
263 if not self.mywindow:
264 self.myfocus = self.widget #HACK: should be done fancier, i think..
265 if not self.myhover:
266 self.enter(self.widget)
267
268 self.repaintall()
269 w.send(CLOSE)
270
271
272 class Desktop(App):
273 """Create an App using the <tt>desktop</tt> theme class.
274
275 <pre>Desktop()</pre>
276 """
277 def __init__(self,**params):
278 params.setdefault('cls','desktop')
279 App.__init__(self,**params)