Mercurial > projects > dynamin
annotate dynamin/gui/window.d @ 1:3ab1e9bfb88c
Fix a rename of gui.backend -> gui_backend I missed.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 15 Jun 2009 22:14:21 -0500 |
parents | aa4efef0f0b1 |
children | 4029d5af7542 |
rev | line source |
---|---|
0 | 1 // Written in the D programming language |
2 // www.digitalmars.com/d/ | |
3 | |
4 /* | |
5 * The contents of this file are subject to the Mozilla Public License Version | |
6 * 1.1 (the "License"); you may not use this file except in compliance with | |
7 * the License. You may obtain a copy of the License at | |
8 * http://www.mozilla.org/MPL/ | |
9 * | |
10 * Software distributed under the License is distributed on an "AS IS" basis, | |
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
12 * for the specific language governing rights and limitations under the | |
13 * License. | |
14 * | |
15 * The Original Code is the Dynamin library. | |
16 * | |
17 * The Initial Developer of the Original Code is Jordan Miner. | |
18 * Portions created by the Initial Developer are Copyright (C) 2006-2009 | |
19 * the Initial Developer. All Rights Reserved. | |
20 * | |
21 * Contributor(s): | |
22 * Jordan Miner <jminer7@gmail.com> | |
23 * | |
24 */ | |
25 | |
26 module dynamin.gui.window; | |
27 | |
28 import dynamin.c.cairo; | |
29 import dynamin.all_core; | |
30 import dynamin.all_painting; | |
31 import dynamin.all_gui; | |
32 import dynamin.gui.control; | |
1
3ab1e9bfb88c
Fix a rename of gui.backend -> gui_backend I missed.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
33 import dynamin.gui_backend; |
0 | 34 import dynamin.gui.container; |
35 import dynamin.gui.events; | |
36 import tango.io.Stdout; | |
37 import tango.core.Exception; | |
38 import tango.core.Thread; | |
39 | |
40 static class Application { | |
41 static: | |
42 mixin ApplicationBackend; | |
43 package Thread eventThread; | |
44 void run(Window w = null) { | |
45 Window.hasProcessedEvents = true; | |
46 | |
47 auto thread = Thread.getThis(); | |
48 assert(eventThread is null || eventThread is thread, | |
49 "Application.run called from two different threads"); | |
50 eventThread = thread; | |
51 | |
52 backend_run(w); | |
53 } | |
54 //void invoke(void delegate() dg) { | |
55 // | |
56 //} | |
57 //void invokeNow(void delegate() dg) | |
58 } | |
59 | |
60 enum DialogResult { | |
61 /// | |
62 OK, | |
63 /// | |
64 Yes, | |
65 /// | |
66 No, | |
67 /// | |
68 Cancel, | |
69 /// | |
70 Custom | |
71 } | |
72 | |
73 /// | |
74 enum Position { | |
75 /// Specifies being at the top-left corner. | |
76 TopLeft, | |
77 /// Specifies being centered between the top-left corner and the top-right corner. | |
78 Top, | |
79 /// Specifies being at the top-right corner. | |
80 TopRight, | |
81 /// Specifies being centered between the top-left corner and the bottom-left corner. | |
82 Left, | |
83 /// Specifies being centered between all corners. | |
84 Center, | |
85 /// Specifies being centered between the top-right corner and the bottom-right corner. | |
86 Right, | |
87 /// Specifies being at the bottom-left corner. | |
88 BottomLeft, | |
89 /// Specifies being centered between the bottom-left corner and the bottom-right corner. | |
90 Bottom, | |
91 /// Specifies being at the bottom-right corner. | |
92 BottomRight | |
93 } | |
94 | |
95 /** | |
96 * The different types of borders that a window may have. | |
97 * These do not affect whether the window is resizable-- | |
98 * use Window.Resizable for that. | |
99 */ | |
100 enum WindowBorderStyle { | |
101 /** Specifies that the window has no border around the content area. */ | |
102 None, | |
103 /** | |
104 * Specifies that the window has a normal border with a title bar, icon, | |
105 * and minimize button. | |
106 */ | |
107 Normal, | |
108 /** Specifies that the window has a normal border without a minimize button. */ | |
109 Dialog, | |
110 /** Specifies that the window has the border of a floating tool box. */ | |
111 Tool | |
112 } | |
113 | |
114 alias List!(Control) ControlList; | |
115 //Frames and Dialogs are identical except that Dialogs do not have minimize and | |
116 //maximize buttons, are not shown on the taskbar, and can be modal. | |
117 /** | |
118 * A window is a top level control that has no parent. Its location is relative | |
119 * to the top-left corner of the screen. | |
120 * A window can have no border, the border of a normal window, or the border | |
121 * of a tool window. | |
122 * | |
123 * The appearance of a window with Windows Classic: | |
124 * | |
125 * $(IMAGE ../web/example_window.png) | |
126 */ | |
127 class Window : Container { | |
128 private static hasProcessedEvents = false; | |
129 ~this() { // this should be a static ~this, but I get a circular dep error | |
130 if(!hasProcessedEvents) { | |
131 Stdout("Warning: a window was created, but Application.run"); | |
132 Stdout(" was not called to process events").newline; | |
133 } | |
134 } | |
135 protected: | |
136 mixin WindowBackend; | |
137 BorderSize _borderSize; | |
138 Window _owner; | |
139 WindowBorderStyle _borderStyle; | |
140 bool _resizable = true; | |
141 Panel _content; | |
142 Control _focusedControl; | |
143 package Control focusedControl() { return _focusedControl; } | |
144 package void focusedControl(Control c) { | |
145 _focusedControl = c; | |
146 } | |
147 override void dispatchPainting(PaintingEventArgs e) { | |
148 Theme.current.Window_paint(this, e.graphics); | |
149 super.dispatchPainting(e); | |
150 } | |
151 public: | |
152 this() { | |
153 _children = new ControlList(); | |
154 content = new Panel; | |
155 | |
156 _visible = false; | |
157 _minSize = Size(0, 0); | |
158 _maxSize = Size(0, 0); | |
159 _borderStyle = WindowBorderStyle.Normal; | |
160 recreateHandle(); | |
161 } | |
162 this(string text) { | |
163 this(); | |
164 this.text = text; | |
165 } | |
166 | |
167 void content(Panel panel) { | |
168 if(panel is null) | |
169 throw new IllegalArgumentException("content must not be null"); | |
170 // TODO: remove handlers | |
171 super.remove(panel); | |
172 super.add(_content = panel); | |
173 _content.resized += &whenContentResized; | |
174 _content.minSizeChanged += &whenContentMinSizeChanged; | |
175 _content.maxSizeChanged += &whenContentMaxSizeChanged; | |
176 | |
177 auto best = _content.bestSize; | |
178 _content.minSize = best; | |
179 _content.maxSize = Size(_content.elasticX ? 0 : best.width, | |
180 _content.elasticY ? 0 : best.height); | |
181 resizable = _content.maxSize != best; // avoid calling elasticX/Y again | |
182 _content.size = best; | |
183 | |
184 } | |
185 bool ignoreResize; | |
186 void whenContentResized(EventArgs e) { | |
187 if(ignoreResize) | |
188 return; | |
189 ignoreResize = true; | |
190 size = _content.size + _borderSize; | |
191 ignoreResize = false; | |
192 } | |
193 void whenContentMinSizeChanged(EventArgs e) { | |
194 if(!handleCreated) | |
195 return; | |
196 backend_contentMinSizeChanged; | |
197 } | |
198 void whenContentMaxSizeChanged(EventArgs e) { | |
199 if(!handleCreated) | |
200 return; | |
201 backend_contentMaxSizeChanged; | |
202 } | |
203 override void whenResized(EventArgs e) { | |
204 if(ignoreResize) | |
205 return; | |
206 _content._location = Point(_borderSize.left, _borderSize.top); | |
207 ignoreResize = true; | |
208 _content.size = _size-_borderSize; | |
209 ignoreResize = false; | |
210 } | |
211 Panel content() { | |
212 return _content; | |
213 } | |
214 /** | |
215 * If the handle has not yet been created, calling this will cause it to be. | |
216 * Under the Windows backend, returns a HWND. | |
217 * Under the X backend, returns a Window. | |
218 * Returns: the backend specific native handle. | |
219 */ | |
220 typeof(_handle) handle() { | |
221 if(!handleCreated) | |
222 recreateHandle(); | |
223 assert(Thread.getThis() is Application.eventThread || | |
224 Application.eventThread is null, | |
225 "controls must be accessed and changed only on the event thread"); | |
226 return _handle; | |
227 } | |
228 bool handleCreated() { return backend_handleCreated; } | |
229 void recreateHandle() { | |
230 backend_recreateHandle(); | |
231 } | |
232 override protected Graphics quickCreateGraphics() { | |
233 if(!handleCreated) | |
234 return null; | |
235 return backend_quickCreateGraphics(); | |
236 } | |
237 override bool onScreen() { | |
238 return true; | |
239 } | |
240 override Point screenLocation() { | |
241 return location; | |
242 } | |
243 override Point contentToScreen(Point pt) { | |
244 return pt + location; | |
245 } | |
246 override Point screenToContent(Point pt) { | |
247 return pt - location; | |
248 } | |
249 override bool topLevel() { return true; } | |
250 override Container parent() { return null; } | |
251 // TODO: because you should always be able to click a window from the taskbar, | |
252 // then show it on taskbar if window has an owner, but don't if it does not | |
253 void owner(Window w) { | |
254 _owner = w; | |
255 if(!handleCreated) | |
256 return; | |
257 recreateHandle(); | |
258 } | |
259 Window owner() { return _owner; } | |
260 alias Control.visible visible; | |
261 void visible(bool b) { | |
262 _visible = b; | |
263 backend_visible = b; | |
264 } | |
265 /** | |
266 * Gets or sets what border this window will have around its contents. | |
267 * The default is WindowBorderStyle.Normal. | |
268 */ | |
269 WindowBorderStyle borderStyle() { return _borderStyle; } | |
270 /// ditto | |
271 void borderStyle(WindowBorderStyle border) { | |
272 if(border > WindowBorderStyle.Tool) | |
273 throw new IllegalArgumentException("Window.borderStyle(): invalid border style"); | |
274 _borderStyle = border; | |
275 backend_borderStyle = border; | |
276 } | |
277 alias Control.repaint repaint; | |
278 void repaint(Rect rect) { | |
279 if(!handleCreated) | |
280 return; | |
281 backend_repaint(rect); | |
282 } | |
283 /** | |
284 * An array of rectangles in screen coordinates that the window will be | |
285 * snapped to. | |
286 */ | |
287 Rect[] snapRects = null; | |
288 /** | |
289 * Convenience method that sets SnapRects to an array | |
290 * with just the specified Rect. | |
291 */ | |
292 void snapRect(Rect rect) { | |
293 snapRects = [rect]; | |
294 } | |
295 /** | |
296 * The SnapDistance specifies how close a window has to be to a | |
297 * snap rectangle for the window to snap to it. The default is 10 pixels. | |
298 */ | |
299 uint snapDistance = 10; | |
300 /** | |
301 * Gets or sets whether this window can be resized by the user. | |
302 * The default is true. | |
303 */ | |
304 bool resizable() { return _resizable; } | |
305 /// ditto | |
306 void resizable(bool b) { // TODO: set based upon whether content is elastic? | |
307 _resizable = b; | |
308 if(!handleCreated) | |
309 return; | |
310 backend_resizable = b; | |
311 } | |
312 // TODO: 1.0 MinSize -> contentMinSize MaxSize -> contentMaxSize | |
313 alias Control.location location; | |
314 void location(Point pt) { | |
315 super.location(pt); | |
316 if(!handleCreated) | |
317 return; | |
318 backend_location = pt; | |
319 } | |
320 alias Control.size size; | |
321 void size(Size size) { | |
322 super.size(size); | |
323 _content.size = size - _borderSize; | |
324 if(!handleCreated) | |
325 return; | |
326 backend_size = size; | |
327 } | |
328 /** | |
329 * Gets the size of the border/frame around this window. | |
330 */ | |
331 BorderSize borderSize() { | |
332 return _borderSize; | |
333 } | |
334 alias Control.text text; | |
335 void text(string str) { | |
336 super.text(str); | |
337 if(!handleCreated) | |
338 return; | |
339 backend_text = str; | |
340 } | |
341 /** | |
342 * Moves this window to the specified position relative to | |
343 * the specified control. If no control is specified, the | |
344 * window is positioned relative to the screen. | |
345 */ | |
346 void position(Position pos, Control c = null) { | |
347 Rect rect; | |
348 if(c && c.onScreen) { | |
349 rect = c.screenLocation + c.size; | |
350 } else { | |
351 rect = desktopRect; | |
352 } | |
353 Point newLoc = Point(); | |
354 switch(pos) { | |
355 case Position.TopLeft: | |
356 case Position.Left: | |
357 case Position.BottomLeft: | |
358 newLoc.x = rect.x; | |
359 break; | |
360 case Position.Top: | |
361 case Position.Center: | |
362 case Position.Bottom: | |
363 newLoc.x = rect.x + (rect.width - width)/2; | |
364 break; | |
365 case Position.TopRight: | |
366 case Position.Right: | |
367 case Position.BottomRight: | |
368 newLoc.x = rect.x + rect.width - width; | |
369 break; | |
370 } | |
371 switch(pos) { | |
372 case Position.TopLeft: | |
373 case Position.Top: | |
374 case Position.TopRight: | |
375 newLoc.y = rect.y; | |
376 break; | |
377 case Position.Left: | |
378 case Position.Center: | |
379 case Position.Right: | |
380 newLoc.y = rect.y + (rect.height - height)/2; | |
381 break; | |
382 case Position.BottomLeft: | |
383 case Position.Bottom: | |
384 case Position.BottomRight: | |
385 newLoc.y = rect.y + rect.height - height; | |
386 break; | |
387 } | |
388 location = newLoc; | |
389 } | |
390 } | |
391 |