Mercurial > projects > dynamin
annotate dynamin/gui/windows_window.d @ 106:acdbb30fee7e
Port to D2.
Most of the effort was dealing with immutable and const.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 17 Dec 2012 23:41:50 -0600 |
parents | 97997a544ac0 |
children |
rev | line source |
---|---|
0 | 1 |
2 /* | |
103
73060bc3f004
Change license to Boost 1.0 and MPL 2.0.
Jordan Miner <jminer7@gmail.com>
parents:
75
diff
changeset
|
3 * Copyright Jordan Miner |
0 | 4 * |
103
73060bc3f004
Change license to Boost 1.0 and MPL 2.0.
Jordan Miner <jminer7@gmail.com>
parents:
75
diff
changeset
|
5 * This Source Code Form is subject to the terms of the Mozilla Public |
73060bc3f004
Change license to Boost 1.0 and MPL 2.0.
Jordan Miner <jminer7@gmail.com>
parents:
75
diff
changeset
|
6 * License, v. 2.0. If a copy of the MPL was not distributed with this |
73060bc3f004
Change license to Boost 1.0 and MPL 2.0.
Jordan Miner <jminer7@gmail.com>
parents:
75
diff
changeset
|
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
0 | 8 * |
9 */ | |
10 | |
11 module dynamin.gui.windows_window; | |
12 | |
13 public import dynamin.c.windows; | |
14 public import dynamin.c.cairo; | |
15 public import dynamin.c.cairo_win32; | |
16 public import dynamin.all_core; | |
17 public import dynamin.all_gui; | |
18 public import dynamin.gui.window; | |
19 public import dynamin.gui.key; | |
20 public import dynamin.all_painting; | |
21 public import tango.io.Stdout; | |
12
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
22 public import tango.core.sync.Semaphore; |
0 | 23 |
24 /// | |
25 enum WindowsVersion { | |
26 /// | |
17
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
27 Windows95, /// |
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
28 Windows98, /// |
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
29 WindowsMe, /// |
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
30 Windows2000, /// |
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
31 WindowsXP, /// |
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
32 WindowsVista,/// |
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
33 Windows7 |
0 | 34 } |
35 /** | |
36 * Returns true if the version of Windows that is runninng now is the | |
37 * specified version or newer. | |
38 */ | |
39 bool checkWindowsVersion(WindowsVersion ver) { | |
40 // Windows Server "Longhorn" is 6.0 | |
41 // Windows Vista is 6.0 | |
42 // Windows Server 2003 is 5.2 | |
43 // Windows XP is 5.1 | |
44 // Windows Me is 4.90 | |
45 // Windows 98 is 4.10 | |
46 // Windows 95 is 4.0 | |
47 // Windows NT is 4.0 | |
48 OSVERSIONINFO info; | |
49 info.dwOSVersionInfoSize = OSVERSIONINFO.sizeof; | |
50 GetVersionEx(&info); | |
51 DWORD major, minor; | |
106 | 52 final switch(ver) { |
0 | 53 case WindowsVersion.Windows95: major = 4; minor = 0; break; |
54 case WindowsVersion.Windows98: major = 4; minor = 10; break; | |
55 case WindowsVersion.WindowsMe: major = 4; minor = 90; break; | |
56 case WindowsVersion.Windows2000: major = 5; minor = 0; break; | |
57 case WindowsVersion.WindowsXP: major = 5; minor = 1; break; | |
58 case WindowsVersion.WindowsVista: major = 6; minor = 0; break; | |
17
ef81af74a306
Add Windows 7 to version checking
Jordan Miner <jminer7@gmail.com>
parents:
12
diff
changeset
|
59 case WindowsVersion.Windows7: major = 6; minor = 1; break; |
0 | 60 } |
61 return info.dwMajorVersion > major || | |
62 (info.dwMajorVersion == major && info.dwMinorVersion >= minor); | |
63 } | |
64 /* unittest { | |
65 Stdout.format("Windows95 or newer: {}", checkWindowsVersion(WindowsVersion.Windows95)).newline; | |
66 Stdout.format("Windows98 or newer: {}", checkWindowsVersion(WindowsVersion.Windows98)).newline; | |
67 Stdout.format("WindowsMe or newer: {}", checkWindowsVersion(WindowsVersion.WindowsMe)).newline; | |
68 Stdout.format("Windows2000 or newer: {}", checkWindowsVersion(WindowsVersion.Windows2000)).newline; | |
69 Stdout.format("WindowsXP or newer: {}", checkWindowsVersion(WindowsVersion.WindowsXP)).newline; | |
70 } */ | |
71 | |
72 // TODO: the way I have stored references using SetProp() will not work | |
73 // if/when D gets a copying collector. I will need to store a key with | |
74 // SetProp() and use that key to store the reference in either an | |
75 // array or a hashtable. | |
76 Window[HWND] windows; | |
77 void setControl(HWND hwnd, Window win) { | |
78 if(win is null) | |
79 windows.remove(hwnd); | |
80 else | |
81 windows[hwnd] = win; | |
82 } | |
83 /** | |
84 * Returns: the Dynamin NativeControl that wraps the specified handle | |
85 */ | |
86 // TODO: change return type to NativeControl | |
87 Window getControl(HWND hwnd) { | |
88 assert(IsWindow(hwnd), "Invalid HWND"); | |
89 auto tmp = hwnd in windows; | |
90 return tmp is null ? null : *tmp; | |
91 } | |
92 | |
93 template ApplicationBackend() { | |
94 void backend_run(Window w) { | |
95 bool isWindowVisible() { | |
96 if(w is null) return true; | |
97 return w.visible; | |
98 } | |
99 MSG msg; | |
100 BOOL ret; | |
101 while(isWindowVisible() && (ret = GetMessage(&msg, null, 0, 0)) != 0) { | |
102 if(ret == -1) | |
103 Stdout("GetMessage() failed!").newline; | |
104 TranslateMessage(&msg); | |
105 DispatchMessage(&msg); | |
106 } | |
107 } | |
12
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
108 void backend_invoke(void delegate() dg) { |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
109 PostMessage(msgWnd, WM_USER + 7, |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
110 cast(word)dg.ptr, cast(word)dg.funcptr); |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
111 } |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
112 void backend_invokeNow(void delegate() dg) { |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
113 SendMessage(msgWnd, WM_USER + 7, |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
114 cast(word)dg.ptr, cast(word)dg.funcptr); |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
115 } |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
116 |
0 | 117 } |
118 /* | |
119 * The reason backends use the backend_ prefix and: | |
120 * mixin Backend(); | |
121 * instead of using just the method name and | |
122 * mixin Backend() backend; | |
123 * is that D mistakenly calls the method in the class, rather than | |
124 * the one mixed-in...causing infinite recursion/stack overflow | |
125 */ | |
126 //{{{ WindowBackend | |
127 template WindowBackend() { | |
128 HWND _handle; | |
129 bool backend_handleCreated() { return _handle !is null; } | |
130 //WS_CAPTION == WS_BORDER | WS_DLGFRAME; | |
131 void backend_recreateHandle() { | |
132 LONG style, exStyle; | |
133 backend_getWindowStyles(style, exStyle); | |
134 style &= ~WS_VISIBLE; // don't create visible | |
135 // TODO: set the owner with CreateWindowEx | |
136 HWND newHandle = CreateWindowEx(exStyle, "DynaminWindow", _text.toWcharPtr(), | |
137 style, cast(int)x, cast(int)y, cast(int)width, cast(int)height, | |
138 null, null, GetModuleHandle(null), null); | |
139 if(!newHandle) | |
140 Stdout("CreateWindowEx() failed").newline; | |
141 setControl(newHandle, this); | |
142 | |
143 // Windows does not completely obey the styles given in CreateWindowEx() | |
144 SetWindowLong(newHandle, GWL_STYLE, style); | |
145 SetWindowLong(newHandle, GWL_EXSTYLE, exStyle); | |
146 SetWindowPos(newHandle, null, 0, 0, 0, 0, | |
147 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); | |
148 | |
149 if(handleCreated) { | |
150 // TODO: move native children to new window? | |
151 | |
152 // set z-order to right above old window | |
153 // SetWindowPos() puts the window above the specified | |
154 // window in the z-order | |
155 SetWindowPos(newHandle, _handle, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); | |
156 if(IsWindowVisible(_handle)) | |
157 ShowWindow(newHandle, SW_SHOWNA); | |
158 DestroyWindow(_handle); | |
159 } | |
160 _handle = newHandle; | |
161 backend_nativeToBorderSize(); | |
162 } | |
163 extern(C) static void freeDC(void* hdc) { | |
164 ReleaseDC(null, hdc); | |
165 } | |
166 Graphics backend_quickCreateGraphics() { | |
167 HDC hdc = GetDC(handle); | |
168 cairo_surface_t* surface = cairo_win32_surface_create(hdc); | |
169 cairo_surface_set_user_data(surface, cast(cairo_user_data_key_t*)1, | |
170 hdc, &freeDC); | |
171 cairo_t* cr = cairo_create(surface); | |
172 cairo_surface_destroy(surface); | |
173 cairo_translate(cr, -borderSize.left, -borderSize.top); | |
174 auto g = new Graphics(cr); | |
175 cairo_destroy(cr); | |
176 return g; | |
177 } | |
178 void backend_visible(bool b) { | |
55
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
179 if(b) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
180 // visible has been set to true by now...use state() to show window |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
181 backend_state = _state; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
182 else |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
183 //if not created, create the handle by calling handle() |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
184 ShowWindow(handle, SW_HIDE); |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
185 } |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
186 void backend_state(WindowState s) { |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
187 if(!visible) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
188 return; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
189 //if not created, create the handle by calling handle() |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
190 if(s == WindowState.Normal) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
191 ShowWindow(handle, SW_RESTORE); |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
192 else if(s == WindowState.Minimized) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
193 ShowWindow(handle, SW_MINIMIZE); |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
194 else if(s == WindowState.Maximized) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
195 ShowWindow(handle, SW_MAXIMIZE); |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
196 } |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
197 void backend_activate() { |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
198 SetForegroundWindow(_handle); |
0 | 199 } |
200 void backend_borderStyle(WindowBorderStyle border) { | |
201 backend_updateWindowStyles(); | |
202 } | |
23
d55b5b998412
Implement built-in mouse cursors with X.
Jordan Miner <jminer7@gmail.com>
parents:
17
diff
changeset
|
203 void backend_setCurrentCursor(Cursor cur) { |
d55b5b998412
Implement built-in mouse cursors with X.
Jordan Miner <jminer7@gmail.com>
parents:
17
diff
changeset
|
204 SetCursor(cur.handle); |
d55b5b998412
Implement built-in mouse cursors with X.
Jordan Miner <jminer7@gmail.com>
parents:
17
diff
changeset
|
205 } |
0 | 206 void backend_repaint(Rect rect) { |
207 RECT wrect; | |
208 wrect.left = cast(int)(rect.x-_borderSize.left); | |
209 wrect.top = cast(int)(rect.y-_borderSize.top); | |
210 wrect.right = wrect.left+cast(int)rect.width; | |
211 wrect.bottom = wrect.top+cast(int)rect.height; | |
212 InvalidateRect(handle, &wrect, false); | |
213 } | |
214 void backend_resizable(bool b) { | |
215 backend_updateWindowStyles(); | |
216 } | |
217 void backend_contentMinSizeChanged() { | |
218 } | |
219 void backend_contentMaxSizeChanged() { | |
220 backend_updateWindowStyles(); | |
221 } | |
222 void backend_location(Point pt) { | |
223 SetWindowPos(handle, null, | |
224 cast(int)pt.x, cast(int)pt.y, 0, 0, | |
225 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); | |
226 } | |
227 void backend_size(Size size) { | |
228 SetWindowPos(handle, null, | |
229 0, 0, cast(int)size.width, cast(int)size.height, | |
230 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); | |
231 } | |
232 void backend_text(string str) { | |
233 SetWindowText(handle, str.toWcharPtr()); | |
234 } | |
235 //{{{ backend specific | |
236 | |
237 void backend_nativeToLocationSize() { | |
238 RECT winRect; | |
239 GetWindowRect(handle, &winRect); | |
240 _location.x = winRect.left; | |
241 _location.y = winRect.top; | |
242 _size.width = winRect.right-winRect.left; | |
243 _size.height = winRect.bottom-winRect.top; | |
244 } | |
245 package void backend_nativeToBorderSize() { | |
246 RECT clientRect, winRect; | |
247 POINT clientLoc; | |
248 GetClientRect(handle, &clientRect); | |
249 GetWindowRect(handle, &winRect); | |
250 ClientToScreen(handle, &clientLoc); | |
251 _borderSize.left = clientLoc.x-winRect.left; | |
252 _borderSize.top = clientLoc.y-winRect.top; | |
253 _borderSize.right = winRect.right-clientLoc.x-clientRect.right; | |
254 _borderSize.bottom = winRect.bottom-clientLoc.y-clientRect.bottom; | |
255 backend_nativeToLocationSize(); | |
256 } | |
257 void backend_getWindowStyles(out LONG style, out LONG exStyle) { | |
258 if(handleCreated) { | |
259 style = GetWindowLong(handle, GWL_STYLE); | |
260 exStyle = GetWindowLong(handle, GWL_EXSTYLE); | |
261 } | |
262 void SetIf(LONG s, bool b) { | |
263 // if condition satisfied, add style, otherwise clear style | |
264 b ? (style |= s) : (style &= ~s); | |
265 } | |
266 SetIf(WS_DLGFRAME, borderStyle != WindowBorderStyle.None); | |
267 SetIf(WS_BORDER, borderStyle != WindowBorderStyle.None); | |
268 SetIf(WS_THICKFRAME, | |
269 resizable && borderStyle != WindowBorderStyle.None); | |
270 SetIf(WS_MINIMIZEBOX, borderStyle == WindowBorderStyle.Normal); | |
271 SetIf(WS_MAXIMIZEBOX, | |
272 borderStyle == WindowBorderStyle.Normal && resizable && | |
273 content.maxWidth == 0 && content.maxHeight == 0); | |
274 SetIf(WS_SYSMENU, borderStyle != WindowBorderStyle.None); | |
275 if(borderStyle == WindowBorderStyle.Tool) | |
276 exStyle |= WS_EX_TOOLWINDOW; | |
277 else | |
278 exStyle &= ~WS_EX_TOOLWINDOW; | |
279 } | |
280 void backend_updateWindowStyles() { | |
281 if(!handleCreated) | |
282 return; | |
283 LONG style, exStyle; | |
284 backend_getWindowStyles(style, exStyle); | |
285 SetWindowLong(handle, GWL_STYLE, style); | |
286 SetWindowLong(handle, GWL_EXSTYLE, exStyle); | |
287 SetWindowPos(handle, null, 0, 0, 0, 0, | |
288 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); | |
289 } | |
290 //--FixedToorWindow-- | |
291 //Style: 0x16c80000 | |
292 //ExStyle: 0x10180 | |
293 //--SizableToorWindow-- | |
294 //Style: 0x16cc0000 | |
295 //ExStyle: 0x10180 | |
296 //--FixedDialog-- | |
297 //Style: 0x16c80000 | |
298 //ExStyle: 0x10101 | |
299 //--None-- | |
300 //Style: 0x16010000 | |
301 //ExStyle: 0x10000 | |
302 //--FixedSingle-- | |
303 //Style: 0x16c80000 | |
304 //ExStyle: 0x10100 | |
305 //--Sizable-- | |
306 //Style: 0x16cc0000 | |
307 //ExStyle: 0x10100 | |
308 //}}} | |
309 } | |
310 //}}} | |
311 | |
312 //{{{ Ux class | |
313 class Ux { | |
314 static: | |
315 private: | |
316 HMODULE uxLib = null; | |
317 // TODO: these are the wrong calling convention!! | |
318 extern(Windows) { | |
319 BOOL function() _IsAppThemed; | |
320 BOOL function() _IsThemeActive; | |
321 HTHEME function(HWND hwnd, LPCWSTR pszClassList) _OpenThemeData; | |
322 HRESULT function(HTHEME hTheme) _CloseThemeData; | |
323 HRESULT function(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, | |
324 RECT* pRect, RECT* pClipRect) _DrawThemeBackground; | |
325 } | |
326 static this() { | |
327 uxLib = LoadLibrary("uxtheme"); | |
328 if(uxLib) { | |
329 _IsAppThemed = cast(typeof(_IsAppThemed)) | |
330 GetProcAddress(uxLib, "IsAppThemed"); | |
331 _IsThemeActive = cast(typeof(_IsThemeActive)) | |
332 GetProcAddress(uxLib, "IsThemeActive"); | |
333 _OpenThemeData = cast(typeof(_OpenThemeData)) | |
334 GetProcAddress(uxLib, "OpenThemeData"); | |
335 _CloseThemeData = cast(typeof(_CloseThemeData)) | |
336 GetProcAddress(uxLib, "CloseThemeData"); | |
337 _DrawThemeBackground = cast(typeof(_DrawThemeBackground)) | |
338 GetProcAddress(uxLib, "DrawThemeBackground"); | |
339 } | |
340 } | |
106 | 341 HTHEME[mstring] cache; |
0 | 342 // opens an HTHEME for the specified controlName and caches it |
343 // next time, just returns the HTHEME from the cache | |
344 HTHEME getHTHEME(string controlName) { | |
345 HTHEME* hthemePtr = controlName in cache; | |
346 HTHEME htheme = controlName in cache; | |
347 if(hthemePtr) { | |
348 htheme = *hthemePtr; | |
349 } else { | |
350 htheme = _OpenThemeData(null, controlName.toWcharPtr()); | |
351 if(!htheme) { | |
352 if(_IsThemeActive()) | |
353 throw new Exception("invalid uxtheme controlName"); | |
354 else | |
355 throw new Exception("no theme active"); | |
356 } | |
357 cache[controlName] = htheme; | |
358 } | |
359 return htheme; | |
360 } | |
361 // This is called when the WM_THEMECHANGED message is sent | |
362 package void themeChanged() { | |
363 foreach(htheme; cache.values) | |
364 _CloseThemeData(htheme); | |
365 cache = null; | |
366 updateThemeActive = true; | |
367 } | |
368 bool themeActive; | |
369 bool updateThemeActive = true; | |
370 public: | |
371 // cache this value, as this function was showing up in profiles | |
372 bool isThemeActive() { | |
373 if(updateThemeActive) { | |
374 themeActive = uxLib && _IsThemeActive() && _IsAppThemed(); | |
375 updateThemeActive = false; | |
376 } | |
377 return themeActive; | |
378 } | |
379 // draw directly onto the HDC with the uxTheme API if the following | |
380 // three conditions are met: | |
381 // - the Graphics must be drawing to an HDC (duh) | |
382 // - there cannot be a scale or rotation...translation can be handled | |
383 // - the clip must be a pixel-aligned rectangle | |
384 bool drawBackground(Graphics g, Rect rect, string controlName, int part, int state) { | |
385 if(!uxLib) | |
386 throw new Exception("UxPaintBackground(): uxtheme library not found!"); | |
387 HTHEME htheme = getHTHEME(controlName); | |
388 static if(true) { | |
389 | |
390 HDC hdc = cairo_win32_surface_get_dc(cairo_get_target(g.handle)); | |
391 //HDC hdc = null; | |
392 bool isMatrixTranslationOnly() { | |
393 cairo_matrix_t matrix; | |
394 cairo_get_matrix(g.handle, &matrix); | |
395 return matrix.xx == 1 && matrix.xy == 0 && | |
396 matrix.xy == 0 && matrix.yy == 1; | |
397 } | |
398 bool isClipIntegerRect(cairo_rectangle_list_t* list) { | |
399 return list.status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE; | |
400 } | |
401 RECT locRect; | |
402 auto list = cairo_copy_clip_rectangle_list(g.handle); | |
403 scope(exit) cairo_rectangle_list_destroy(list); | |
404 if(hdc && isMatrixTranslationOnly() && isClipIntegerRect(list)) { | |
405 double x = rect.x, y = rect.y; | |
406 double right = rect.right, bottom = rect.bottom; | |
407 cairo_user_to_device(g.handle, &x, &y); | |
408 cairo_user_to_device(g.handle, &right, &bottom); | |
409 locRect.left = cast(int)x; | |
410 locRect.top = cast(int)y; | |
411 locRect.right = cast(int)right; | |
412 locRect.bottom = cast(int)bottom; | |
413 cairo_surface_flush(cairo_get_target(g.handle)); | |
414 | |
415 RECT clip; | |
416 for(int i = 0; i < list.num_rectangles; ++i) { | |
417 cairo_user_to_device(g.handle, &(list.rectangles[i].x), &(list.rectangles[i].y)); | |
418 clip.left = cast(int)list.rectangles[i].x; | |
419 clip.top = cast(int)list.rectangles[i].y; | |
420 clip.right = clip.left + cast(int)list.rectangles[i].width; | |
421 clip.bottom = clip.top + cast(int)list.rectangles[i].height; | |
422 _DrawThemeBackground(htheme, hdc, part, state, &locRect, &clip); | |
423 } | |
424 | |
425 cairo_surface_mark_dirty(cairo_get_target(g.handle)); | |
426 } else { | |
427 assert(0, "So far, visual styles are supported only with no rotation, no scaling, a rectangular clip, and HDC surfaces."); | |
428 static if(0) { | |
429 BITMAPINFO bmi; | |
430 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
431 bmi.bmiHeader.biWidth = width; | |
432 bmi.bmiHeader.biHeight = -height; // top-down DIB | |
433 bmi.bmiHeader.biPlanes = 1; | |
434 bmi.bmiHeader.biBitCount = 32; | |
435 bmi.bmiHeader.biCompression = BI_RGB; | |
436 //bmi.bmiHeader.biSizeImage = width * height * 4; | |
437 hbmpBlack = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, | |
438 &bmpBits, NULL, 0); | |
439 hbmpWhite = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, | |
440 &bmpBits, NULL, 0); | |
441 //draw on a black background and on a white background | |
442 //calculate alpha and colors and draw that image with cairo | |
443 // DO TESTS WITH ROTATION AND SCALING | |
444 _DrawThemeBackground(htheme, hdc, part, state, null/*change*/, null); | |
445 DeleteObject(hbmpBlack); | |
446 DeleteObject(hbmpWhite); | |
447 } | |
448 } | |
449 | |
450 } | |
451 return false; | |
452 } | |
453 } | |
454 //}}} | |
455 | |
456 //{{{ module constructor and destructor | |
457 HWND msgWnd; | |
458 static this() { | |
459 assert(cairo_version() >= CAIRO_VERSION_ENCODE(1, 4, 0), | |
460 "cairo version 1.4.0 or newer is required"); | |
461 | |
462 /* create window classes */ | |
463 WNDCLASSEX wc; | |
464 wc.cbSize = wc.sizeof; | |
465 wc.style = 0; | |
466 wc.hInstance = GetModuleHandle(null); | |
467 | |
468 wc.lpfnWndProc = &dynaminMsgWindowProc; | |
469 wc.lpszClassName = "DynaminMsgWindow"; | |
470 if(!RegisterClassExW(&wc)) | |
471 Stdout("RegisterClassEx() failed registering class 'DynaminMsgWindow'").newline; | |
472 | |
473 wc.lpfnWndProc = &dynaminWindowProc; | |
474 //wc.hbrBackground = cast(HBRUSH)16; | |
475 | |
476 wc.lpszClassName = "DynaminWindow"; | |
477 if(!RegisterClassEx(&wc)) | |
478 Stdout("RegisterClassEx() failed registering class 'DynaminWindow'").newline; | |
479 | |
480 wc.style = CS_SAVEBITS; | |
481 wc.lpszClassName = "DynaminPopup"; | |
482 if(!RegisterClassExW(&wc)) | |
483 Stdout("RegisterClassEx() failed registering class 'DynaminPopup'").newline; | |
484 | |
485 | |
486 msgWnd = CreateWindowEx(0, "DynaminMsgWindow", "", | |
487 0, | |
488 0, 0, 0, 0, | |
489 null, null, GetModuleHandle(null), null); | |
490 if(!msgWnd) | |
491 Stdout("CreateWindowEx() failed").newline; | |
492 | |
493 /* initialize COM */ | |
494 auto ret = CoInitializeEx(null, COINIT_APARTMENTTHREADED); | |
495 if(ret != S_OK && ret != S_FALSE) | |
496 Stdout("Failed to initialize COM").newline; | |
497 | |
498 } | |
499 static ~this() { | |
500 CoUninitialize(); | |
501 } | |
502 //}}} | |
503 | |
504 //{{{ dynaminWindowProc() | |
505 extern(Windows) | |
506 LRESULT dynaminWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { | |
507 //used in WM_MOVING | |
508 static int dragX, dragY; //the cursor location in window coordinates when the drag was started | |
509 static bool trackingMouseLeave = false; | |
510 auto c = getControl(hwnd); | |
511 //{{{ helper functions | |
512 void createMouseEvent(MouseButton button, void delegate(MouseEventArgs args) func) { | |
513 scope args = new MouseEventArgs( | |
514 cast(short)LOWORD(lParam)+c.borderSize.left, | |
515 cast(short)HIWORD(lParam)+c.borderSize.top, button); | |
516 func(args); | |
517 } | |
106 | 518 void snapSide(ref int sideToSnap, float side1, float side2) { |
0 | 519 if(sideToSnap >= side1-c.snapDistance && sideToSnap <= side1+c.snapDistance) |
520 sideToSnap = cast(int)side1; | |
521 if(sideToSnap >= side2-c.snapDistance && sideToSnap <= side2+c.snapDistance) | |
522 sideToSnap = cast(int)side2; | |
523 } | |
106 | 524 void delegate(Rect snapRect) emptyFunc = (Rect snapRect) { }; |
0 | 525 // used to snap vertical sides, left and right |
106 | 526 void snapVSide(ref int side, RECT* rect, void delegate(Rect snapRect) func = emptyFunc) { |
0 | 527 if(c.snapRects is null) |
528 return; | |
529 foreach(snapRect; c.snapRects) { | |
530 if(rect.bottom >= snapRect.y && rect.top <= snapRect.bottom) { | |
531 snapSide(side, snapRect.x, snapRect.right); | |
532 func(snapRect); | |
533 } | |
534 } | |
535 } | |
536 // used to snap horizontal sides, top and bottom | |
106 | 537 void snapHSide(ref int side, RECT* rect, void delegate(Rect snapRect) func = emptyFunc) { |
0 | 538 if(c.snapRects is null) |
539 return; | |
540 foreach(snapRect; c.snapRects) { | |
541 if(rect.right >= snapRect.x && rect.left <= snapRect.right) { | |
542 snapSide(side, snapRect.y, snapRect.bottom); | |
543 func(snapRect); | |
544 } | |
545 } | |
546 } | |
547 MouseButton getFromXBUTTONX() { | |
548 switch(HIWORD(wParam)) { | |
549 case XBUTTON1: return MouseButton.XButton1; | |
550 case XBUTTON2: return MouseButton.XButton2; | |
551 default: return MouseButton.None; | |
552 } | |
553 } | |
72
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
554 bool isKeyDown(int vk) { |
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
555 return cast(bool)HIWORD(GetKeyState(vk)); |
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
556 } |
0 | 557 //}}} |
558 switch(uMsg) { | |
559 case WM_ENTERSIZEMOVE: //when the user starts moving or resizing the window | |
560 //{{{ | |
75
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
561 DWORD cur = GetMessagePos(); |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
562 short curX = cur & 0xFFFF; |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
563 short curY = cur >> 16; |
0 | 564 RECT rect; |
565 GetWindowRect(hwnd, &rect); | |
75
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
566 dragX = curX-rect.left; |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
567 dragY = curY-rect.top; |
0 | 568 return 0; |
569 //}}} | |
570 case WM_MOVING: | |
571 //{{{ | |
572 if(c.snapRects is null || c.snapRects.length == 0) | |
573 break; | |
574 RECT* rect = cast(RECT*)lParam; | |
575 int rectWidth = rect.right-rect.left; | |
576 int rectHeight = rect.bottom-rect.top; | |
75
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
577 DWORD cur = GetMessagePos(); |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
578 short curX = cur & 0xFFFF; |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
579 short curY = cur >> 16; |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
580 rect.left = curX-dragX; |
d5928da5a1f0
Change from using GetCursorPos() to GetMessagePos() in a couple places.
Jordan Miner <jminer7@gmail.com>
parents:
72
diff
changeset
|
581 rect.top = curY-dragY; |
0 | 582 void updateRightAndBottom() { |
583 rect.right = rect.left+rectWidth; | |
584 rect.bottom = rect.top+rectHeight; | |
585 } | |
586 updateRightAndBottom(); | |
587 snapVSide(rect.left, rect, (Rect snapRect) { | |
588 snapSide(rect.left, snapRect.x-rectWidth, snapRect.right-rectWidth); | |
589 }); | |
590 updateRightAndBottom(); | |
591 snapHSide(rect.top, rect, (Rect snapRect) { | |
592 snapSide(rect.top, snapRect.y-rectHeight, snapRect.bottom-rectHeight); | |
593 }); | |
594 updateRightAndBottom(); | |
595 snapVSide(rect.left, rect, (Rect snapRect) { | |
596 snapSide(rect.left, snapRect.x-rectWidth, snapRect.right-rectWidth); | |
597 }); | |
598 updateRightAndBottom(); | |
599 return true; | |
600 //}}} | |
601 case WM_SIZING: | |
602 //{{{ | |
603 Size minSize = c.content.minSize+c.borderSize; | |
604 Size maxSize = c.content.maxSize+c.borderSize; | |
605 RECT* rect = cast(RECT*)lParam; | |
606 switch(wParam) { | |
607 case WMSZ_TOPLEFT: | |
608 snapHSide(rect.top, rect); | |
609 goto case WMSZ_LEFT; | |
610 case WMSZ_BOTTOMLEFT: | |
611 snapHSide(rect.bottom, rect); | |
612 case WMSZ_LEFT: | |
613 snapVSide(rect.left, rect); | |
614 // adjust left according to min and max | |
615 if(c.content.maxWidth != 0) | |
616 rect.left = max(rect.left, rect.right-cast(int)maxSize.width); | |
617 if(c.content.minWidth != 0) | |
618 rect.left = min(rect.left, rect.right-cast(int)minSize.width); | |
619 break; | |
620 case WMSZ_TOPRIGHT: | |
621 snapHSide(rect.top, rect); | |
622 goto case WMSZ_RIGHT; | |
623 case WMSZ_BOTTOMRIGHT: | |
624 snapHSide(rect.bottom, rect); | |
625 case WMSZ_RIGHT: | |
626 snapVSide(rect.right, rect); | |
627 // adjust right according to min and max | |
628 if(c.content.maxWidth != 0) | |
629 rect.right = min(rect.right, rect.left+cast(int)maxSize.width); | |
630 if(c.content.minWidth != 0) | |
631 rect.right = max(rect.right, rect.left+cast(int)minSize.width); | |
632 break; | |
633 default: break; | |
634 } | |
635 switch(wParam) { | |
636 case WMSZ_TOPLEFT: //already snapped left above | |
637 case WMSZ_TOPRIGHT: //already snapped right above | |
638 case WMSZ_TOP: | |
639 snapHSide(rect.top, rect); | |
640 // adjust top according to min and max | |
641 if(c.content.maxHeight != 0) | |
642 rect.top = max(rect.top, rect.bottom-cast(int)maxSize.height); | |
643 if(c.content.minHeight != 0) | |
644 rect.top = min(rect.top, rect.bottom-cast(int)minSize.height); | |
645 break; | |
646 case WMSZ_BOTTOMLEFT: //already snapped left above | |
647 case WMSZ_BOTTOMRIGHT: //already snapped right above | |
648 case WMSZ_BOTTOM: | |
649 snapHSide(rect.bottom, rect); | |
650 // adjust bottom according to min and max | |
651 if(c.content.maxHeight != 0) | |
652 rect.bottom = min(rect.bottom, rect.top+cast(int)maxSize.height); | |
653 if(c.content.minHeight != 0) | |
654 rect.bottom = max(rect.bottom, rect.top+cast(int)minSize.height); | |
655 break; | |
656 default: break; | |
657 } | |
658 return true; | |
659 //}}} | |
660 case WM_MOVE: | |
661 RECT rect; | |
662 GetWindowRect(hwnd, &rect); | |
663 c._location = Point(rect.left, rect.top); | |
664 scope args = new EventArgs(); | |
665 c.moved(args); | |
666 return 0; | |
667 case WM_SIZE: | |
55
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
668 if(wParam == SIZE_RESTORED) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
669 c._state = WindowState.Normal; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
670 else if(wParam == SIZE_MINIMIZED) { |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
671 c._state = WindowState.Minimized; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
672 break; // don't update size if minimized (would be wierd size) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
673 } else if(wParam == SIZE_MAXIMIZED) |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
674 c._state = WindowState.Maximized; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
675 |
0 | 676 RECT rect; |
677 GetWindowRect(hwnd, &rect); | |
678 c._size = Size(rect.right-rect.left, rect.bottom-rect.top); | |
679 c.backend_nativeToBorderSize(); | |
680 scope args = new EventArgs(); | |
681 c.resized(args); | |
682 return 0; | |
55
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
683 case WM_ACTIVATE: |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
684 scope e = new EventArgs; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
685 if(LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
686 c._active = true; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
687 c.activated(e); |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
688 } else if(LOWORD(wParam) == WA_INACTIVE) { |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
689 c._active = false; |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
690 c.deactivated(e); |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
691 } |
c138461bf845
Add focusing and other changes that are related
Jordan Miner <jminer7@gmail.com>
parents:
31
diff
changeset
|
692 return 0; |
0 | 693 case WM_MOUSEMOVE: |
694 if(!trackingMouseLeave) { | |
695 TRACKMOUSEEVENT tme; | |
696 tme.cbSize = TRACKMOUSEEVENT.sizeof; | |
697 tme.dwFlags = TME_LEAVE; | |
698 tme.hWndTrack = hwnd; | |
699 tme.dwHoverTime = 0; | |
700 TrackMouseEvent(&tme); | |
701 trackingMouseLeave = true; | |
702 } | |
703 auto pt = Point(cast(short)LOWORD(lParam)+c.borderSize.left, cast(short)HIWORD(lParam)+c.borderSize.top); | |
704 Control captor = getCaptorControl(); | |
705 if(captor) | |
706 pt = c.contentToContent(pt, captor); | |
707 else | |
708 captor = c; | |
28
00efca2c1545
Rename variable to be consistent.
Jordan Miner <jminer7@gmail.com>
parents:
23
diff
changeset
|
709 scope args = new MouseEventArgs( |
0 | 710 pt.x, pt.y, MouseButton.None); |
711 if(wParam & | |
712 (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | | |
713 MK_XBUTTON1 | MK_XBUTTON2)) { | |
28
00efca2c1545
Rename variable to be consistent.
Jordan Miner <jminer7@gmail.com>
parents:
23
diff
changeset
|
714 captor.mouseDragged(args); |
0 | 715 } else { |
28
00efca2c1545
Rename variable to be consistent.
Jordan Miner <jminer7@gmail.com>
parents:
23
diff
changeset
|
716 captor.mouseMoved(args); |
0 | 717 } |
718 return 0; | |
719 case WM_MOUSELEAVE: | |
720 trackingMouseLeave = false; | |
721 setHotControl(null); | |
722 return 0; | |
723 case WM_LBUTTONDOWN: | |
724 SetCapture(hwnd); | |
725 createMouseEvent(MouseButton.Left, (MouseEventArgs args) { | |
726 c.mouseDown(args); | |
727 }); | |
728 return 0; | |
729 case WM_MBUTTONDOWN: | |
730 SetCapture(hwnd); | |
731 createMouseEvent(MouseButton.Middle, (MouseEventArgs args) { | |
732 c.mouseDown(args); | |
733 }); | |
734 return 0; | |
735 case WM_RBUTTONDOWN: | |
736 SetCapture(hwnd); | |
737 createMouseEvent(MouseButton.Right, (MouseEventArgs args) { | |
738 c.mouseDown(args); | |
739 }); | |
740 return 0; | |
741 case WM_XBUTTONDOWN: | |
742 SetCapture(hwnd); | |
743 auto button = getFromXBUTTONX(); | |
744 if(!button) break; | |
745 createMouseEvent(button, (MouseEventArgs args) { | |
746 c.mouseDown(args); | |
747 }); | |
748 return true; | |
749 case WM_LBUTTONUP: | |
750 ReleaseCapture(); | |
751 createMouseEvent(MouseButton.Left, (MouseEventArgs args) { | |
752 c.mouseUp(args); | |
753 }); | |
754 return 0; | |
755 case WM_MBUTTONUP: | |
756 ReleaseCapture(); | |
757 createMouseEvent(MouseButton.Middle, (MouseEventArgs args) { | |
758 c.mouseUp(args); | |
759 }); | |
760 return 0; | |
761 case WM_RBUTTONUP: | |
762 ReleaseCapture(); | |
763 createMouseEvent(MouseButton.Right, (MouseEventArgs args) { | |
764 c.mouseUp(args); | |
765 }); | |
766 return 0; | |
767 case WM_XBUTTONUP: | |
768 ReleaseCapture(); | |
769 auto button = getFromXBUTTONX(); | |
770 if(!button) break; | |
771 createMouseEvent(button, (MouseEventArgs args) { | |
772 c.mouseUp(args); | |
773 }); | |
774 return true; | |
775 case WM_MOUSEWHEEL: | |
776 int scrollLines; | |
777 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0); | |
31
2a194d52fdb5
Implement MouseTurnedEventArgs.scrollScreen.
Jordan Miner <jminer7@gmail.com>
parents:
30
diff
changeset
|
778 bool sScreen = (scrollLines == 0xFFFFFFFF); |
2a194d52fdb5
Implement MouseTurnedEventArgs.scrollScreen.
Jordan Miner <jminer7@gmail.com>
parents:
30
diff
changeset
|
779 if(sScreen) |
0 | 780 scrollLines = 3; |
781 int delta = -cast(short)HIWORD(wParam); | |
782 auto screenPt = Point(LOWORD(lParam), HIWORD(lParam)); | |
783 auto des = c.getDescendantAtPoint(c.screenToContent(screenPt)); | |
31
2a194d52fdb5
Implement MouseTurnedEventArgs.scrollScreen.
Jordan Miner <jminer7@gmail.com>
parents:
30
diff
changeset
|
784 scope args = new MouseTurnedEventArgs(delta*scrollLines/120.0, sScreen); |
29
e6843df719a8
Use scope when creating EventArgs to avoid unnecessary heap usage.
Jordan Miner <jminer7@gmail.com>
parents:
28
diff
changeset
|
785 des.mouseTurned(args); |
0 | 786 return 0; |
787 case WM_SYSKEYDOWN: | |
788 //Stdout.format("WM_SYSKEYDOWN: {:x}", cast(int)wParam).newline; | |
789 if(wParam == 0x79) return 0; | |
790 break; | |
791 case WM_KEYDOWN: | |
792 //Stdout.format("WM_KEYDOWN: {:x}", cast(int)wParam).newline; | |
793 Control focused = c.focusedControl ? c.focusedControl : c; | |
72
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
794 scope args = new KeyEventArgs(VKToKey(wParam), |
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
795 cast(bool)(lParam & (1 << 30)), isKeyDown(VK_SHIFT), |
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
796 isKeyDown(VK_CONTROL), isKeyDown(VK_MENU) ); |
29
e6843df719a8
Use scope when creating EventArgs to avoid unnecessary heap usage.
Jordan Miner <jminer7@gmail.com>
parents:
28
diff
changeset
|
797 focused.keyDown(args); |
0 | 798 return 0; |
799 case WM_SYSKEYUP: | |
800 //Stdout.format("WM_SYSKEYUP: {:x}", cast(int)wParam).newline; | |
801 if(wParam == 0x79) return 0; | |
802 break; | |
803 case WM_KEYUP: | |
804 //Stdout.format("WM_KEYUP: {:x}", cast(int)wParam).newline; | |
805 Control focused = c.focusedControl ? c.focusedControl : c; | |
72
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
806 scope args = new KeyEventArgs( VKToKey(wParam), false, |
8dac206ea523
Add shift/control/altDown to KeyEventArgs.
Jordan Miner <jminer7@gmail.com>
parents:
55
diff
changeset
|
807 isKeyDown(VK_SHIFT), isKeyDown(VK_CONTROL), isKeyDown(VK_MENU) ); |
29
e6843df719a8
Use scope when creating EventArgs to avoid unnecessary heap usage.
Jordan Miner <jminer7@gmail.com>
parents:
28
diff
changeset
|
808 focused.keyUp(args); |
0 | 809 return 0; |
810 case WM_CHAR: | |
811 // DO NOT use the repeat count from the lParam to send multiple events | |
812 // I hate when programs do that | |
813 | |
814 //stop backspace and escape and shift+enter | |
815 if(wParam == 0x08 || wParam == 0x1B || wParam == 0x0A) | |
816 break; | |
817 // don't process characters typed while control is down | |
818 if(HIBYTE(GetKeyState(VK_CONTROL))) | |
819 break; | |
820 if(wParam == 0x0D) // change \r to \n | |
821 wParam = 0x0A; | |
822 bool repeat; | |
823 repeat = cast(bool)(lParam & (1 << 30)); | |
824 Control focused = c.focusedControl ? c.focusedControl : c; | |
29
e6843df719a8
Use scope when creating EventArgs to avoid unnecessary heap usage.
Jordan Miner <jminer7@gmail.com>
parents:
28
diff
changeset
|
825 scope args = new KeyTypedEventArgs(cast(dchar)wParam, repeat); |
e6843df719a8
Use scope when creating EventArgs to avoid unnecessary heap usage.
Jordan Miner <jminer7@gmail.com>
parents:
28
diff
changeset
|
826 focused.keyTyped(args); |
0 | 827 return 0; |
828 case WM_PRINT: | |
829 paintToHDC(cast(HDC)wParam, getControl(hwnd), null); | |
830 return 0; | |
831 case WM_PAINT: | |
832 PAINTSTRUCT ps; | |
833 BeginPaint(hwnd, &ps); | |
834 RECT* clip = &ps.rcPaint; | |
835 | |
836 HDC hdcBuffer = CreateCompatibleDC(ps.hdc); | |
837 HBITMAP hbmpBuffer = CreateCompatibleBitmap(ps.hdc, | |
838 clip.right-clip.left, clip.bottom-clip.top); | |
839 HBITMAP hbmpDefault = SelectObject(hdcBuffer, hbmpBuffer); | |
840 | |
841 paintToHDC(hdcBuffer, getControl(hwnd), clip); | |
842 | |
843 BitBlt(ps.hdc, clip.left, clip.top, clip.right-clip.left, clip.bottom-clip.top, | |
844 hdcBuffer, 0, 0, SRCCOPY); | |
845 SelectObject(hdcBuffer, hbmpDefault); | |
846 DeleteDC(hdcBuffer); | |
847 DeleteObject(hbmpBuffer); | |
848 | |
849 EndPaint(hwnd, &ps); | |
850 return 0; | |
851 case WM_CLOSE: | |
852 c.visible = false; | |
853 //DestroyWindow(hwnd); | |
854 //PostQuitMessage(0); | |
855 return 0; | |
856 case WM_DESTROY: | |
857 return 0; | |
858 default: | |
859 break; | |
860 } | |
861 return DefWindowProc(hwnd, uMsg, wParam, lParam); | |
862 } | |
863 //}}} | |
864 | |
865 //{{{ paintToHDC() | |
866 void paintToHDC(HDC hdc, Window w, RECT* clip) { | |
867 cairo_surface_t* surface = cairo_win32_surface_create(hdc); | |
868 cairo_t* cr = cairo_create(surface); | |
869 cairo_surface_destroy(surface); | |
870 | |
871 if(clip) { | |
872 cairo_translate(cr, | |
873 -clip.left-w.borderSize.left, -clip.top-w.borderSize.top); | |
874 | |
875 //cairo_rectangle(cr, clip.left, clip.top, clip.right-clip.left, clip.bottom-clip.top); | |
876 } | |
877 //if(w.Opaque) { | |
878 //cairo_set_source_rgb(cr, w.content.backColor.R/255.0, w.content.backColor.G/255.0, w.content.backColor.B/255.0); | |
879 //cairo_paint(cr); | |
880 //} | |
881 | |
882 //cairo_set_source_rgb(cr, .3, .3, .3); | |
883 //cairo_paint(cr); | |
884 | |
885 //cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); | |
886 //cairo_paint(cr); | |
887 //cairo_set_operator(cr, CAIRO_OPERATOR_OVER); | |
888 | |
889 cairo_set_source_rgb(cr, 0, 0, 0); | |
890 cairo_set_line_width(cr, 1.0); | |
891 | |
892 //HBITMAP hbmp = LoadImage(null, cast(wchar*)cast(ushort)OIC_WARNING, | |
893 // IMAGE_ICON, 0, 0, LR_SHARED); | |
894 //if(hbmp == null) | |
895 // Stdout.format("LoadImage failed. GetLastError()={}", GetLastError()).newline; | |
896 | |
897 //auto imgSurface = cairo_image_surface_create_for_data(); | |
898 //cairo_set_source_surface(cr, imgSurface, 50, 50); | |
899 //cairo_paint(cr); | |
900 //cairo_surface_destroy(imgSurface); | |
901 | |
902 auto g = new Graphics(cr); | |
903 scope args = new PaintingEventArgs(g); | |
904 w.painting(args); | |
905 delete g; | |
906 cairo_destroy(cr); | |
907 } | |
908 //}}} | |
909 | |
910 //{{{ VKToKey() | |
911 Key VKToKey(int code) { | |
912 switch(code) { | |
913 case VK_F1: return Key.F1; | |
914 case VK_F2: return Key.F2; | |
915 case VK_F3: return Key.F3; | |
916 case VK_F4: return Key.F4; | |
917 case VK_F5: return Key.F5; | |
918 case VK_F6: return Key.F6; | |
919 case VK_F7: return Key.F7; | |
920 case VK_F8: return Key.F8; | |
921 case VK_F9: return Key.F9; | |
922 case VK_F10: return Key.F10; | |
923 case VK_F11: return Key.F11; | |
924 case VK_F12: return Key.F12; | |
925 | |
926 case VK_ESCAPE: return Key.Escape; | |
927 case VK_TAB: return Key.Tab; | |
928 case VK_BACK: return Key.Backspace; | |
929 case VK_RETURN: return Key.Enter; | |
930 case VK_SPACE: return Key.Space; | |
931 | |
932 case VK_LEFT: return Key.Left; | |
933 case VK_RIGHT: return Key.Right; | |
934 case VK_UP: return Key.Up; | |
935 case VK_DOWN: return Key.Down; | |
936 | |
937 case VK_INSERT: return Key.Insert; | |
938 case VK_DELETE: return Key.Delete; | |
939 case VK_HOME: return Key.Home; | |
940 case VK_END: return Key.End; | |
941 case VK_PRIOR: return Key.PageUp; | |
942 case VK_NEXT: return Key.PageDown; | |
943 | |
944 case VK_SNAPSHOT: return Key.PrintScreen; | |
945 case VK_PAUSE: return Key.Pause; | |
946 | |
947 case VK_CAPITAL: return Key.CapsLock; | |
948 case VK_NUMLOCK: return Key.NumLock; | |
949 case VK_SCROLL: return Key.ScrollLock; | |
950 | |
951 case VK_NUMPAD0: return Key.NumPad0; | |
952 case VK_NUMPAD1: return Key.NumPad1; | |
953 case VK_NUMPAD2: return Key.NumPad2; | |
954 case VK_NUMPAD3: return Key.NumPad3; | |
955 case VK_NUMPAD4: return Key.NumPad4; | |
956 case VK_NUMPAD5: return Key.NumPad5; | |
957 case VK_NUMPAD6: return Key.NumPad6; | |
958 case VK_NUMPAD7: return Key.NumPad7; | |
959 case VK_NUMPAD8: return Key.NumPad8; | |
960 case VK_NUMPAD9: return Key.NumPad9; | |
961 case VK_DIVIDE: return Key.NumPadDivide; | |
962 case VK_MULTIPLY: return Key.NumPadMultiply; | |
963 case VK_SUBTRACT: return Key.NumPadSubtract; | |
964 case VK_ADD: return Key.NumPadAdd; | |
965 case VK_DECIMAL: return Key.NumPadDecimal; | |
966 | |
967 case VK_OEM_3: return Key.Backquote; | |
968 case VK_OEM_MINUS: return Key.Minus; | |
969 case VK_OEM_PLUS: return Key.Equals; | |
970 case VK_OEM_4: return Key.OpenBracket; | |
971 case VK_OEM_6: return Key.CloseBracket; | |
972 case VK_OEM_5: return Key.Backslash; | |
973 case VK_OEM_1: return Key.Semicolon; | |
974 case VK_OEM_7: return Key.Quote; | |
975 case VK_OEM_COMMA: return Key.Comma; | |
976 case VK_OEM_PERIOD: return Key.Period; | |
977 case VK_OEM_2: return Key.Slash; | |
978 | |
979 //case VK_APPS: return Key.Menu; | |
980 | |
981 case VK_SHIFT: return Key.Shift; | |
982 case VK_CONTROL: return Key.Control; | |
983 case VK_MENU: return Key.Alt; | |
984 | |
985 //case VK_: return Key.; | |
986 default: | |
987 if(code >= 0x30 && code <= 0x39) // Key.D0 - Key.D9 | |
988 return cast(Key)code; | |
989 if(code >= 0x41 && code <= 0x5A) // Key.A - Key.Z | |
990 return cast(Key)code; | |
991 return cast(Key)0; | |
992 } | |
993 } | |
994 //}}} | |
995 | |
996 // Use the msgWnd for the following: | |
997 // Timers | |
998 // Clipboard.DataChanged event | |
999 // Clipboard? | |
1000 // Taskbar created event using RegisterWindowMessage("TaskbarCreated") | |
1001 // different settings changed events? | |
1002 // screen saver enabled? | |
1003 extern(Windows) | |
1004 LRESULT dynaminMsgWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { | |
1005 switch(uMsg) { | |
1006 case WM_THEMECHANGED: | |
1007 Ux.themeChanged(); | |
1008 return 0; | |
1009 case WM_POWERBROADCAST: | |
1010 if(wParam == PBT_APMRESUMESUSPEND || wParam == PBT_APMRESUMECRITICAL) | |
1011 Environment.backend_increaseTimerRes(); | |
1012 return 0; | |
12
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
1013 case WM_USER + 7: |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
1014 void delegate() dg; |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
1015 dg.ptr = cast(void*)wParam; |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
1016 dg.funcptr = cast(void function())lParam; |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
1017 dg(); |
7a7e5f9bd1ae
Implement invoke() and invokeNow() on Windows.
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
1018 return 0; |
0 | 1019 case WM_TIMER: |
1020 case WM_CHANGECBCHAIN: | |
1021 case WM_DRAWCLIPBOARD: | |
1022 case WM_TIMECHANGE: //?? | |
1023 default: | |
1024 break; | |
1025 } | |
1026 return DefWindowProc(hwnd, uMsg, wParam, lParam); | |
1027 } | |
1028 | |
105
97997a544ac0
Add a couple TODOs and clean up a logging statement.
Jordan Miner <jminer7@gmail.com>
parents:
103
diff
changeset
|
1029 // TODO: backend tests |
97997a544ac0
Add a couple TODOs and clean up a logging statement.
Jordan Miner <jminer7@gmail.com>
parents:
103
diff
changeset
|
1030 // test that setting window.content.size doesn't include borders (I just saw this bug) |
97997a544ac0
Add a couple TODOs and clean up a logging statement.
Jordan Miner <jminer7@gmail.com>
parents:
103
diff
changeset
|
1031 |