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