reflexion 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.doc/ext/reflex/application.cpp +37 -6
- data/.doc/ext/reflex/capture_event.cpp +1 -1
- data/.doc/ext/reflex/chase_constraint.cpp +1 -1
- data/.doc/ext/reflex/clipboard.cpp +1 -1
- data/.doc/ext/reflex/color_event.cpp +76 -0
- data/.doc/ext/reflex/color_picker.cpp +113 -0
- data/.doc/ext/reflex/constraint.cpp +1 -1
- data/.doc/ext/reflex/contact_event.cpp +1 -1
- data/.doc/ext/reflex/control_change_event.cpp +1 -1
- data/.doc/ext/reflex/device.cpp +1 -1
- data/.doc/ext/reflex/device_event.cpp +1 -1
- data/.doc/ext/reflex/draw_event.cpp +1 -1
- data/.doc/ext/reflex/ellipse_shape.cpp +1 -1
- data/.doc/ext/reflex/event.cpp +1 -1
- data/.doc/ext/reflex/file_dialog.cpp +181 -0
- data/.doc/ext/reflex/file_event.cpp +103 -0
- data/.doc/ext/reflex/filter.cpp +1 -1
- data/.doc/ext/reflex/focus_event.cpp +1 -1
- data/.doc/ext/reflex/frame_event.cpp +1 -1
- data/.doc/ext/reflex/gamepad.cpp +48 -0
- data/.doc/ext/reflex/image_view.cpp +1 -1
- data/.doc/ext/reflex/key_event.cpp +1 -274
- data/.doc/ext/reflex/line_shape.cpp +1 -1
- data/.doc/ext/reflex/link_constraint.cpp +1 -1
- data/.doc/ext/reflex/menu.cpp +6 -5
- data/.doc/ext/reflex/midi.cpp +9 -2
- data/.doc/ext/reflex/midi_event.cpp +1 -1
- data/.doc/ext/reflex/motion_event.cpp +1 -1
- data/.doc/ext/reflex/native.cpp +18 -6
- data/.doc/ext/reflex/note_event.cpp +1 -1
- data/.doc/ext/reflex/pin.cpp +1 -1
- data/.doc/ext/reflex/pointer.cpp +1 -1
- data/.doc/ext/reflex/pointer_event.cpp +2 -2
- data/.doc/ext/reflex/polygon_shape.cpp +1 -1
- data/.doc/ext/reflex/rect_shape.cpp +1 -1
- data/.doc/ext/reflex/reflex.cpp +566 -232
- data/.doc/ext/reflex/screen.cpp +27 -1
- data/.doc/ext/reflex/scroll_event.cpp +1 -1
- data/.doc/ext/reflex/selector.cpp +1 -1
- data/.doc/ext/reflex/shape.cpp +1 -1
- data/.doc/ext/reflex/snap_constraint.cpp +1 -1
- data/.doc/ext/reflex/style.cpp +1 -1
- data/.doc/ext/reflex/style_length.cpp +1 -1
- data/.doc/ext/reflex/text_event.cpp +104 -0
- data/.doc/ext/reflex/timer.cpp +1 -1
- data/.doc/ext/reflex/timer_event.cpp +1 -1
- data/.doc/ext/reflex/update_event.cpp +1 -1
- data/.doc/ext/reflex/view.cpp +110 -56
- data/.doc/ext/reflex/wheel_constraint.cpp +1 -1
- data/.doc/ext/reflex/wheel_event.cpp +1 -1
- data/.doc/ext/reflex/window.cpp +226 -17
- data/.github/workflows/release-gem.yml +2 -49
- data/.github/workflows/tag.yml +5 -28
- data/.github/workflows/test.yml +1 -37
- data/ChangeLog.md +54 -0
- data/Gemfile.lock +13 -8
- data/Rakefile +13 -2
- data/Reflex.podspec +2 -2
- data/VERSION +1 -1
- data/ext/reflex/application.cpp +41 -6
- data/ext/reflex/capture_event.cpp +1 -1
- data/ext/reflex/chase_constraint.cpp +1 -1
- data/ext/reflex/clipboard.cpp +1 -1
- data/ext/reflex/color_event.cpp +80 -0
- data/ext/reflex/color_picker.cpp +121 -0
- data/ext/reflex/constraint.cpp +1 -1
- data/ext/reflex/contact_event.cpp +1 -1
- data/ext/reflex/control_change_event.cpp +1 -1
- data/ext/reflex/device.cpp +1 -1
- data/ext/reflex/device_event.cpp +1 -1
- data/ext/reflex/draw_event.cpp +1 -1
- data/ext/reflex/ellipse_shape.cpp +1 -1
- data/ext/reflex/event.cpp +1 -1
- data/ext/reflex/extconf.rb +4 -2
- data/ext/reflex/file_dialog.cpp +194 -0
- data/ext/reflex/file_event.cpp +108 -0
- data/ext/reflex/filter.cpp +1 -1
- data/ext/reflex/focus_event.cpp +1 -1
- data/ext/reflex/frame_event.cpp +1 -1
- data/ext/reflex/gamepad.cpp +50 -0
- data/ext/reflex/image_view.cpp +1 -1
- data/ext/reflex/key_event.cpp +1 -275
- data/ext/reflex/line_shape.cpp +1 -1
- data/ext/reflex/link_constraint.cpp +1 -1
- data/ext/reflex/menu.cpp +8 -7
- data/ext/reflex/midi.cpp +11 -3
- data/ext/reflex/midi_event.cpp +1 -1
- data/ext/reflex/motion_event.cpp +1 -1
- data/ext/reflex/native.cpp +18 -6
- data/ext/reflex/note_event.cpp +1 -1
- data/ext/reflex/pin.cpp +1 -1
- data/ext/reflex/pointer.cpp +1 -1
- data/ext/reflex/pointer_event.cpp +2 -2
- data/ext/reflex/polygon_shape.cpp +1 -1
- data/ext/reflex/rect_shape.cpp +1 -1
- data/ext/reflex/reflex.cpp +568 -232
- data/ext/reflex/screen.cpp +31 -2
- data/ext/reflex/scroll_event.cpp +1 -1
- data/ext/reflex/selector.cpp +1 -1
- data/ext/reflex/selector.h +9 -9
- data/ext/reflex/shape.cpp +1 -1
- data/ext/reflex/snap_constraint.cpp +1 -1
- data/ext/reflex/style.cpp +1 -1
- data/ext/reflex/style_length.cpp +1 -1
- data/ext/reflex/text_event.cpp +111 -0
- data/ext/reflex/timer.cpp +1 -1
- data/ext/reflex/timer_event.cpp +1 -1
- data/ext/reflex/update_event.cpp +1 -1
- data/ext/reflex/view.cpp +121 -61
- data/ext/reflex/wheel_constraint.cpp +1 -1
- data/ext/reflex/wheel_event.cpp +1 -1
- data/ext/reflex/window.cpp +256 -25
- data/include/reflex/application.h +10 -0
- data/include/reflex/color_picker.h +65 -0
- data/include/reflex/event.h +178 -88
- data/include/reflex/file_dialog.h +73 -0
- data/include/reflex/gamepad.h +3 -0
- data/include/reflex/menu.h +3 -3
- data/include/reflex/midi.h +3 -0
- data/include/reflex/reflex.h +2 -0
- data/include/reflex/ruby/application.h +6 -1
- data/include/reflex/ruby/color_picker.h +89 -0
- data/include/reflex/ruby/event.h +62 -29
- data/include/reflex/ruby/file_dialog.h +89 -0
- data/include/reflex/ruby/gamepad.h +46 -0
- data/include/reflex/ruby/reflex.h +3 -0
- data/include/reflex/ruby/view.h +92 -44
- data/include/reflex/ruby/window.h +60 -1
- data/include/reflex/ruby.h +3 -0
- data/include/reflex/screen.h +11 -2
- data/include/reflex/view.h +28 -16
- data/include/reflex/window.h +40 -8
- data/include/reflex.h +3 -0
- data/lib/reflex/application.rb +5 -5
- data/lib/reflex/autoinit.rb +3 -1
- data/lib/reflex/button.rb +3 -3
- data/lib/reflex/color_event.rb +16 -0
- data/lib/reflex/color_picker.rb +45 -0
- data/lib/reflex/control_change_event.rb +17 -0
- data/lib/reflex/file_dialog.rb +70 -0
- data/lib/reflex/file_event.rb +37 -0
- data/lib/reflex/helper.rb +58 -7
- data/lib/reflex/key_event.rb +9 -4
- data/lib/reflex/menu.rb +17 -1
- data/lib/reflex/model.rb +19 -19
- data/lib/reflex/note_event.rb +1 -1
- data/lib/reflex/pointer.rb +14 -3
- data/lib/reflex/pointer_event.rb +12 -1
- data/lib/reflex/reflex.rb +5 -0
- data/lib/reflex/shape.rb +14 -14
- data/lib/reflex/style.rb +16 -16
- data/lib/reflex/text_event.rb +40 -0
- data/lib/reflex/timer.rb +3 -3
- data/lib/reflex/view.rb +29 -14
- data/lib/reflex/wheel_event.rb +12 -3
- data/lib/reflex/window.rb +25 -13
- data/lib/reflex.rb +8 -2
- data/reflex.gemspec +4 -4
- data/samples/ime.rb +100 -0
- data/samples/ios/hello/hello.xcodeproj/project.pbxproj +236 -224
- data/samples/menu.rb +4 -1
- data/samples/model.rb +3 -3
- data/samples/test/alert.rb +93 -0
- data/samples/test/always_on.rb +76 -0
- data/samples/test/app_background.rb +51 -0
- data/samples/test/boundary.rb +75 -0
- data/samples/test/callback_exception.rb +101 -0
- data/samples/test/cancel.rb +53 -0
- data/samples/test/color_picker.rb +63 -0
- data/samples/test/file_dialog.rb +102 -0
- data/samples/window_style.rb +156 -0
- data/src/application.cpp +159 -18
- data/src/application.h +92 -7
- data/src/color_picker.cpp +113 -0
- data/src/color_picker.h +36 -0
- data/src/event.cpp +410 -252
- data/src/event.h +12 -0
- data/src/file_dialog.cpp +163 -0
- data/src/file_dialog.h +52 -0
- data/src/gamepad.cpp +42 -5
- data/src/gamepad.h +4 -0
- data/src/ios/app_delegate.mm +5 -3
- data/src/ios/application.mm +24 -0
- data/src/ios/color_picker.mm +19 -0
- data/src/ios/file_dialog.mm +21 -0
- data/src/ios/gamepad.mm +4 -4
- data/src/ios/menu.mm +5 -0
- data/src/ios/reflex.mm +8 -0
- data/src/ios/screen.mm +26 -0
- data/src/ios/view_controller.mm +2 -30
- data/src/ios/window.mm +38 -9
- data/src/menu.cpp +66 -4
- data/src/menu.h +17 -1
- data/src/midi.cpp +61 -31
- data/src/osx/app_delegate.h +4 -0
- data/src/osx/app_delegate.mm +102 -22
- data/src/osx/application.mm +88 -6
- data/src/osx/color_picker.mm +112 -0
- data/src/osx/event.h +10 -0
- data/src/osx/event.mm +84 -30
- data/src/osx/file_dialog.mm +168 -0
- data/src/osx/gamepad.mm +2 -2
- data/src/osx/gamepad_hid.mm +1 -1
- data/src/osx/menu.mm +177 -33
- data/src/osx/native_window.h +16 -2
- data/src/osx/native_window.mm +114 -57
- data/src/osx/opengl_view.h +2 -1
- data/src/osx/opengl_view.mm +184 -43
- data/src/osx/reflex.mm +38 -0
- data/src/osx/screen.mm +36 -0
- data/src/osx/window.mm +106 -24
- data/src/pointer.cpp +13 -4
- data/src/pointer.h +4 -0
- data/src/rays.h +46 -0
- data/src/sdl/application.cpp +68 -8
- data/src/sdl/color_picker.cpp +18 -0
- data/src/sdl/event.cpp +24 -3
- data/src/sdl/event.h +12 -0
- data/src/sdl/file_dialog.cpp +20 -0
- data/src/sdl/gamepad.cpp +3 -7
- data/src/sdl/menu.cpp +5 -0
- data/src/sdl/reflex.cpp +20 -0
- data/src/sdl/screen.cpp +36 -0
- data/src/sdl/window.cpp +142 -29
- data/src/shape.cpp +15 -10
- data/src/view.cpp +287 -198
- data/src/view.h +4 -2
- data/src/vk.h +416 -0
- data/src/win32/application.cpp +162 -12
- data/src/win32/application.h +20 -0
- data/src/win32/color_picker.cpp +82 -0
- data/src/win32/event.cpp +114 -12
- data/src/win32/event.h +21 -4
- data/src/win32/file_dialog.cpp +226 -0
- data/src/win32/gamepad.cpp +9 -2
- data/src/win32/menu.cpp +191 -53
- data/src/win32/menu.h +9 -1
- data/src/win32/opengl.cpp +2 -9
- data/src/win32/reflex.cpp +44 -3
- data/src/win32/screen.cpp +164 -3
- data/src/win32/screen.h +8 -0
- data/src/win32/tray.cpp +191 -0
- data/src/win32/tray.h +22 -0
- data/src/win32/vk.cpp +11 -0
- data/src/win32/window.cpp +809 -148
- data/src/win32/window.h +6 -0
- data/src/window.cpp +718 -185
- data/src/window.h +66 -33
- data/src/world.cpp +9 -1
- data/test/helper.rb +17 -0
- data/test/test_application.rb +90 -1
- data/test/test_chase_constraint.rb +1 -3
- data/test/test_color_event.rb +36 -0
- data/test/test_color_picker.rb +62 -0
- data/test/test_constraint.rb +2 -4
- data/test/test_event.rb +1 -1
- data/test/test_file_dialog.rb +48 -0
- data/test/test_file_event.rb +43 -0
- data/test/test_has_text_preedit.rb +54 -0
- data/test/test_key_event.rb +20 -5
- data/test/test_link_constraint.rb +1 -3
- data/test/test_menu.rb +40 -10
- data/test/test_pointer.rb +33 -23
- data/test/test_pointer_event.rb +24 -9
- data/test/test_reflex.rb +36 -0
- data/test/test_screen.rb +24 -2
- data/test/test_snap_constraint.rb +1 -3
- data/test/test_text_event.rb +54 -0
- data/test/test_view.rb +41 -4
- data/test/test_wheel_constraint.rb +1 -3
- data/test/test_wheel_event.rb +13 -5
- data/test/test_window.rb +196 -44
- metadata +80 -10
- data/.github/workflows/utils.rb +0 -127
data/src/win32/window.cpp
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
#include "window.h"
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
#include <xot/windows.h>
|
|
5
|
+
#include <imm.h>
|
|
6
|
+
#include <dwmapi.h>
|
|
7
|
+
#include <shellapi.h>
|
|
8
|
+
|
|
4
9
|
#include <assert.h>
|
|
10
|
+
#include <limits.h>
|
|
11
|
+
#include <math.h>
|
|
5
12
|
#include <map>
|
|
6
13
|
#include <memory>
|
|
7
14
|
#include <xot/time.h>
|
|
8
|
-
#include <xot/windows.h>
|
|
9
15
|
#include <rays/rays.h>
|
|
10
16
|
#include "reflex/defs.h"
|
|
11
17
|
#include "reflex/exception.h"
|
|
12
18
|
#include "reflex/debug.h"
|
|
19
|
+
#include "../application.h"
|
|
13
20
|
#include "../view.h"
|
|
14
21
|
#include "event.h"
|
|
15
|
-
#include "
|
|
22
|
+
#include "application.h"
|
|
23
|
+
#include "menu.h"
|
|
16
24
|
#include "screen.h"
|
|
25
|
+
#include "gamepad.h"
|
|
17
26
|
#include "opengl.h"
|
|
18
|
-
#include "menu.h"
|
|
19
27
|
|
|
20
28
|
|
|
21
29
|
namespace Reflex
|
|
@@ -25,19 +33,26 @@ namespace Reflex
|
|
|
25
33
|
typedef std::map<int, String> PressingKeyMap;
|
|
26
34
|
|
|
27
35
|
|
|
28
|
-
static const
|
|
36
|
+
static const wchar_t* WINDOWCLASS = L"Reflex:WindowClass";
|
|
29
37
|
|
|
30
|
-
static const
|
|
38
|
+
static const wchar_t* USERDATA_PROP = L"Reflex:Window:HWND";
|
|
31
39
|
|
|
32
40
|
|
|
33
41
|
struct WindowData : public Window::Data
|
|
34
42
|
{
|
|
35
43
|
|
|
36
|
-
HWND hwnd
|
|
44
|
+
HWND hwnd = NULL;
|
|
45
|
+
|
|
46
|
+
bool need_rebind = false;
|
|
47
|
+
|
|
48
|
+
bool transparent = false;
|
|
37
49
|
|
|
38
|
-
bool
|
|
50
|
+
bool tracking_mouse = false;
|
|
39
51
|
|
|
40
|
-
|
|
52
|
+
// the first half of a surrogate pair, waiting for the second half
|
|
53
|
+
wchar_t pending_surrogate = 0;
|
|
54
|
+
|
|
55
|
+
HACCEL haccel = NULL;
|
|
41
56
|
|
|
42
57
|
OpenGLContext context;
|
|
43
58
|
|
|
@@ -45,6 +60,17 @@ namespace Reflex
|
|
|
45
60
|
|
|
46
61
|
mutable String title_tmp;
|
|
47
62
|
|
|
63
|
+
virtual ~WindowData ()
|
|
64
|
+
{
|
|
65
|
+
set_accelerator_table(NULL);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void set_accelerator_table (HACCEL haccel)
|
|
69
|
+
{
|
|
70
|
+
if (this->haccel) DestroyAcceleratorTable(this->haccel);
|
|
71
|
+
this->haccel = haccel;
|
|
72
|
+
}
|
|
73
|
+
|
|
48
74
|
bool is_valid () const
|
|
49
75
|
{
|
|
50
76
|
return hwnd && IsWindow(hwnd);
|
|
@@ -67,12 +93,20 @@ namespace Reflex
|
|
|
67
93
|
return get_data(const_cast<Window*>(window));
|
|
68
94
|
}
|
|
69
95
|
|
|
96
|
+
HWND
|
|
97
|
+
Window_get_hwnd (const Window* window)
|
|
98
|
+
{
|
|
99
|
+
if (!window) return NULL;
|
|
100
|
+
|
|
101
|
+
return get_data(window)->hwnd;
|
|
102
|
+
}
|
|
103
|
+
|
|
70
104
|
static bool
|
|
71
105
|
window_has_wndproc (HWND hwnd)
|
|
72
106
|
{
|
|
73
107
|
if (!hwnd) return false;
|
|
74
108
|
|
|
75
|
-
WNDPROC proc = (WNDPROC)
|
|
109
|
+
WNDPROC proc = (WNDPROC) GetWindowLongPtrW(hwnd, GWLP_WNDPROC);
|
|
76
110
|
if (proc == wndproc)
|
|
77
111
|
return true;
|
|
78
112
|
|
|
@@ -80,24 +114,42 @@ namespace Reflex
|
|
|
80
114
|
// it can not determine the hwnd is mine or not.
|
|
81
115
|
|
|
82
116
|
enum {BUFSIZE = 256};
|
|
83
|
-
|
|
117
|
+
wchar_t buf[BUFSIZE + 1];
|
|
84
118
|
if (
|
|
85
|
-
|
|
119
|
+
GetClassNameW(hwnd, &buf[0], BUFSIZE) == 0 &&
|
|
86
120
|
GetLastError() != 0)
|
|
87
121
|
{
|
|
88
122
|
return false;
|
|
89
123
|
}
|
|
90
124
|
|
|
91
|
-
return
|
|
125
|
+
return _wcsicmp(buf, WINDOWCLASS) == 0;
|
|
92
126
|
}
|
|
93
127
|
|
|
94
128
|
static Window*
|
|
95
129
|
get_window_from_hwnd (HWND hwnd)
|
|
96
130
|
{
|
|
97
131
|
if (window_has_wndproc(hwnd))
|
|
98
|
-
return (Window*)
|
|
132
|
+
return (Window*) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
|
99
133
|
else
|
|
100
|
-
return (Window*)
|
|
134
|
+
return (Window*) GetPropW(hwnd, USERDATA_PROP);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
bool
|
|
138
|
+
Window_translate_accelerator (MSG* msg)
|
|
139
|
+
{
|
|
140
|
+
if (!msg || !msg->hwnd) return false;
|
|
141
|
+
|
|
142
|
+
if (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYDOWN)
|
|
143
|
+
return false;
|
|
144
|
+
|
|
145
|
+
HWND hwnd = GetAncestor(msg->hwnd, GA_ROOT);
|
|
146
|
+
Window* win = get_window_from_hwnd(hwnd);
|
|
147
|
+
if (!win) return false;
|
|
148
|
+
|
|
149
|
+
HACCEL haccel = get_data(win)->haccel;
|
|
150
|
+
if (!haccel) return false;
|
|
151
|
+
|
|
152
|
+
return TranslateAcceleratorW(hwnd, haccel, msg) != 0;
|
|
101
153
|
}
|
|
102
154
|
|
|
103
155
|
static void
|
|
@@ -111,13 +163,13 @@ namespace Reflex
|
|
|
111
163
|
if (window_has_wndproc(hwnd))
|
|
112
164
|
{
|
|
113
165
|
SetLastError(0);
|
|
114
|
-
|
|
166
|
+
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) win);
|
|
115
167
|
if (GetLastError() != 0)
|
|
116
168
|
system_error(__FILE__, __LINE__);
|
|
117
169
|
}
|
|
118
170
|
else
|
|
119
171
|
{
|
|
120
|
-
if (!
|
|
172
|
+
if (!SetPropW(hwnd, USERDATA_PROP, (HANDLE) win))
|
|
121
173
|
system_error(__FILE__, __LINE__);
|
|
122
174
|
}
|
|
123
175
|
|
|
@@ -158,13 +210,13 @@ namespace Reflex
|
|
|
158
210
|
if (window_has_wndproc(self->hwnd))
|
|
159
211
|
{
|
|
160
212
|
SetLastError(0);
|
|
161
|
-
|
|
213
|
+
SetWindowLongPtrW(self->hwnd, GWLP_USERDATA, 0);
|
|
162
214
|
if (GetLastError() != 0)
|
|
163
215
|
system_error(__FILE__, __LINE__);
|
|
164
216
|
}
|
|
165
217
|
else
|
|
166
218
|
{
|
|
167
|
-
if (!
|
|
219
|
+
if (!RemovePropW(self->hwnd, USERDATA_PROP))
|
|
168
220
|
system_error(__FILE__, __LINE__);
|
|
169
221
|
}
|
|
170
222
|
|
|
@@ -173,8 +225,162 @@ namespace Reflex
|
|
|
173
225
|
|
|
174
226
|
win->release();
|
|
175
227
|
|
|
176
|
-
|
|
177
|
-
|
|
228
|
+
Application_quit_if_should();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static bool
|
|
232
|
+
has_style (HWND hwnd, DWORD style)
|
|
233
|
+
{
|
|
234
|
+
return ((DWORD) GetWindowLongPtrW(hwnd, GWL_STYLE) & style) == style;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static bool
|
|
238
|
+
calc_size (LRESULT* result, HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
239
|
+
{
|
|
240
|
+
if (
|
|
241
|
+
!wp || IsZoomed(hwnd) ||
|
|
242
|
+
has_style(hwnd, WS_CAPTION) || !has_style(hwnd, WS_THICKFRAME))
|
|
243
|
+
{
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// without a caption the sizing border is still drawn at the
|
|
248
|
+
// top of the window, so let the client area cover it
|
|
249
|
+
NCCALCSIZE_PARAMS* params = (NCCALCSIZE_PARAMS*) lp;
|
|
250
|
+
LONG top = params->rgrc[0].top;
|
|
251
|
+
*result = DefWindowProcW(hwnd, msg, wp, lp);
|
|
252
|
+
params->rgrc[0].top = top;
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
static int
|
|
257
|
+
border_size ()
|
|
258
|
+
{
|
|
259
|
+
return
|
|
260
|
+
GetSystemMetrics(SM_CYSIZEFRAME) +
|
|
261
|
+
GetSystemMetrics(SM_CXPADDEDBORDER);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static bool
|
|
265
|
+
hit_test (LRESULT* result, Window* win, HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
266
|
+
{
|
|
267
|
+
// the client area covers the sizing border, or there is no border at
|
|
268
|
+
// all, so an edge answers HTCLIENT and cannot resize. this gives the
|
|
269
|
+
// edges back.
|
|
270
|
+
if (has_style(hwnd, WS_CAPTION) || IsZoomed(hwnd))
|
|
271
|
+
return false;
|
|
272
|
+
|
|
273
|
+
LRESULT hit = DefWindowProcW(hwnd, msg, wp, lp);
|
|
274
|
+
|
|
275
|
+
// the sizing border of a window that cannot resize is only there for
|
|
276
|
+
// the shadow, so its edges answer as a plain border
|
|
277
|
+
if (!win || !win->has_flag(Window::FLAG_RESIZABLE))
|
|
278
|
+
{
|
|
279
|
+
*result = HTBORDER;
|
|
280
|
+
return HTSIZEFIRST <= hit && hit <= HTSIZELAST;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (hit != HTCLIENT)
|
|
284
|
+
return false;
|
|
285
|
+
|
|
286
|
+
RECT window;
|
|
287
|
+
if (!GetWindowRect(hwnd, &window))
|
|
288
|
+
system_error(__FILE__, __LINE__);
|
|
289
|
+
|
|
290
|
+
int size = border_size();
|
|
291
|
+
POINT pos = {GET_X_LPARAM(lp), GET_Y_LPARAM(lp)};
|
|
292
|
+
bool left = pos.x < window.left + size;
|
|
293
|
+
bool right = pos.x >= window.right - size;
|
|
294
|
+
bool top = pos.y < window.top + size;
|
|
295
|
+
bool bottom = pos.y >= window.bottom - size;
|
|
296
|
+
|
|
297
|
+
// a sizing border draws the other edges outside the client area
|
|
298
|
+
if (has_style(hwnd, WS_THICKFRAME) && !top)
|
|
299
|
+
return false;
|
|
300
|
+
|
|
301
|
+
int corner = size * 2;
|
|
302
|
+
bool near_l = pos.x < window.left + corner;
|
|
303
|
+
bool near_r = pos.x >= window.right - corner;
|
|
304
|
+
bool near_t = pos.y < window.top + corner;
|
|
305
|
+
bool near_b = pos.y >= window.bottom - corner;
|
|
306
|
+
|
|
307
|
+
if (top && near_l) *result = HTTOPLEFT;
|
|
308
|
+
else if (top && near_r) *result = HTTOPRIGHT;
|
|
309
|
+
else if (bottom && near_l) *result = HTBOTTOMLEFT;
|
|
310
|
+
else if (bottom && near_r) *result = HTBOTTOMRIGHT;
|
|
311
|
+
else if (left && near_t) *result = HTTOPLEFT;
|
|
312
|
+
else if (left && near_b) *result = HTBOTTOMLEFT;
|
|
313
|
+
else if (right && near_t) *result = HTTOPRIGHT;
|
|
314
|
+
else if (right && near_b) *result = HTBOTTOMRIGHT;
|
|
315
|
+
else if (top) *result = HTTOP;
|
|
316
|
+
else if (bottom) *result = HTBOTTOM;
|
|
317
|
+
else if (left) *result = HTLEFT;
|
|
318
|
+
else if (right) *result = HTRIGHT;
|
|
319
|
+
else return false;
|
|
320
|
+
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
static void
|
|
325
|
+
keep_caption_on_screen (HWND hwnd, RECT* rect)
|
|
326
|
+
{
|
|
327
|
+
if (!has_style(hwnd, WS_CAPTION)) return;
|
|
328
|
+
|
|
329
|
+
HMONITOR hmonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
|
|
330
|
+
if (!hmonitor) return;
|
|
331
|
+
|
|
332
|
+
MONITORINFO info = {sizeof(info)};
|
|
333
|
+
if (!GetMonitorInfoW(hmonitor, &info))
|
|
334
|
+
system_error(__FILE__, __LINE__);
|
|
335
|
+
|
|
336
|
+
LONG over = info.rcWork.top - rect->top;
|
|
337
|
+
if (over <= 0) return;
|
|
338
|
+
|
|
339
|
+
rect->top += over;
|
|
340
|
+
rect->bottom += over;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
static bool
|
|
344
|
+
window_pos_changing (
|
|
345
|
+
LRESULT* result, Window* win, HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
346
|
+
{
|
|
347
|
+
WINDOWPOS* pos = (WINDOWPOS*) lp;
|
|
348
|
+
|
|
349
|
+
if (win && win->has_flag(Window::FLAG_ALWAYS_ON_BOTTOM))
|
|
350
|
+
{
|
|
351
|
+
pos->hwndInsertAfter = HWND_BOTTOM;
|
|
352
|
+
pos->flags &= ~SWP_NOZORDER;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!(pos->flags & SWP_SHOWWINDOW) && !IsWindowVisible(hwnd))
|
|
356
|
+
{
|
|
357
|
+
// a hidden window gets the size it asks for, but windows widens
|
|
358
|
+
// a captioned one here, so take the width back
|
|
359
|
+
int cx = pos->cx;
|
|
360
|
+
*result = DefWindowProcW(hwnd, msg, wp, lp);
|
|
361
|
+
pos->cx = cx;
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (!(pos->flags & SWP_NOMOVE) && !IsIconic(hwnd) && !IsZoomed(hwnd))
|
|
366
|
+
{
|
|
367
|
+
// once on screen the caption has to stay where it can be grabbed
|
|
368
|
+
RECT rect = {pos->x, pos->y, pos->x + pos->cx, pos->y + pos->cy};
|
|
369
|
+
keep_caption_on_screen(hwnd, &rect);
|
|
370
|
+
pos->y = rect.top;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static void
|
|
377
|
+
update_accelerator_table (Window* win)
|
|
378
|
+
{
|
|
379
|
+
Menu* menu = win->menu();
|
|
380
|
+
if (!menu || !Menu_is_modified(menu))
|
|
381
|
+
return;
|
|
382
|
+
|
|
383
|
+
get_data(win)->set_accelerator_table(Menu_create_accelerator_table(menu));
|
|
178
384
|
}
|
|
179
385
|
|
|
180
386
|
void
|
|
@@ -182,11 +388,12 @@ namespace Reflex
|
|
|
182
388
|
{
|
|
183
389
|
WindowData* self = get_data(win);
|
|
184
390
|
|
|
391
|
+
update_accelerator_table(win);
|
|
185
392
|
Gamepad_poll();
|
|
186
393
|
|
|
187
394
|
Window_call_update_event(win);
|
|
188
395
|
|
|
189
|
-
if (self->
|
|
396
|
+
if (Xot::check_and_remove_flag(&self->flags, Window::Data::REDRAW))
|
|
190
397
|
{
|
|
191
398
|
#if 1
|
|
192
399
|
InvalidateRect(self->hwnd, NULL, FALSE);
|
|
@@ -194,8 +401,6 @@ namespace Reflex
|
|
|
194
401
|
RedrawWindow(
|
|
195
402
|
self->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | EDW_ALLCHILDREN);
|
|
196
403
|
#endif
|
|
197
|
-
|
|
198
|
-
self->redraw = false;
|
|
199
404
|
}
|
|
200
405
|
}
|
|
201
406
|
|
|
@@ -222,32 +427,51 @@ namespace Reflex
|
|
|
222
427
|
Window_call_draw_event(win, &e);
|
|
223
428
|
}
|
|
224
429
|
|
|
225
|
-
static
|
|
226
|
-
|
|
430
|
+
static String
|
|
431
|
+
get_chars (WindowData* self, UINT msg)
|
|
227
432
|
{
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
win->self->prev_size = b.size();
|
|
233
|
-
|
|
234
|
-
if (dpos == 0 && dsize == 0) return;
|
|
235
|
-
|
|
236
|
-
Reflex::FrameEvent e(b, dpos.x, dpos.y, 0, dsize.x, dsize.y, 0);
|
|
237
|
-
if (dpos != 0) win->on_move(&e);
|
|
238
|
-
if (dsize != 0)
|
|
433
|
+
MSG wmchar;
|
|
434
|
+
UINT filter = msg == WM_SYSKEYDOWN ? WM_SYSCHAR : WM_CHAR;
|
|
435
|
+
BOOL peeked = PeekMessageW(&wmchar, self->hwnd, filter, filter, PM_NOREMOVE);
|
|
436
|
+
if (peeked)
|
|
239
437
|
{
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
438
|
+
// a character outside the BMP is injected as one VK_PACKET per
|
|
439
|
+
// UTF-16 code unit, so the halves of the surrogate pair arrive in
|
|
440
|
+
// two key events and the first of them has to be held back until
|
|
441
|
+
// the second one completes the character.
|
|
442
|
+
wchar_t unit = (wchar_t) wmchar.wParam;
|
|
443
|
+
wchar_t pending = self->pending_surrogate;
|
|
444
|
+
self->pending_surrogate = 0;
|
|
445
|
+
|
|
446
|
+
if (IS_HIGH_SURROGATE(unit))
|
|
447
|
+
self->pending_surrogate = unit;
|
|
448
|
+
else if (IS_LOW_SURROGATE(unit))
|
|
449
|
+
{
|
|
450
|
+
if (pending)
|
|
451
|
+
{
|
|
452
|
+
wchar_t pair[] = {pending, unit};
|
|
453
|
+
return String(pair, 2);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else
|
|
457
|
+
return String(&unit, 1);
|
|
458
|
+
}
|
|
459
|
+
return "";
|
|
460
|
+
}
|
|
245
461
|
|
|
246
|
-
|
|
247
|
-
|
|
462
|
+
static bool
|
|
463
|
+
accepts_text_input (Window* win)
|
|
464
|
+
{
|
|
465
|
+
const View* focus = win->focus();
|
|
466
|
+
return focus && focus->accepts_text_input();
|
|
467
|
+
}
|
|
248
468
|
|
|
249
|
-
|
|
250
|
-
|
|
469
|
+
// macos never commits a control character, so it is not text here either
|
|
470
|
+
static bool
|
|
471
|
+
is_committable (const String& chars)
|
|
472
|
+
{
|
|
473
|
+
uchar c = chars.empty() ? 0 : (uchar) chars[0];
|
|
474
|
+
return c >= 0x20 && c != 0x7f;
|
|
251
475
|
}
|
|
252
476
|
|
|
253
477
|
static void
|
|
@@ -255,23 +479,26 @@ namespace Reflex
|
|
|
255
479
|
{
|
|
256
480
|
assert(*win);
|
|
257
481
|
|
|
258
|
-
|
|
482
|
+
if (wp == VK_PROCESSKEY) return;
|
|
259
483
|
|
|
260
|
-
|
|
261
|
-
UINT filter = msg == WM_SYSKEYDOWN ? WM_SYSCHAR : WM_CHAR;
|
|
262
|
-
BOOL peeked = PeekMessage(&wmchar, self->hwnd, filter, filter, PM_NOREMOVE);
|
|
484
|
+
WindowData* self = get_data(win);
|
|
263
485
|
|
|
264
|
-
String chars;
|
|
265
|
-
if (peeked) chars += (char) wmchar.wParam;
|
|
486
|
+
String chars = get_chars(self, msg);
|
|
266
487
|
|
|
267
488
|
NativeKeyEvent e(msg, wp, lp, chars);
|
|
268
|
-
|
|
269
489
|
self->pressing_keys.insert_or_assign(e.code(), chars);
|
|
490
|
+
|
|
270
491
|
#if 0
|
|
271
492
|
for (auto kv : self->pressing_keys)
|
|
272
493
|
doutln("0x%x : %s", kv.first, (const char*) kv.second);
|
|
273
494
|
#endif
|
|
274
495
|
|
|
496
|
+
if (accepts_text_input(win) && is_committable(chars))
|
|
497
|
+
{
|
|
498
|
+
TextEvent te(TextEvent::COMMIT, chars);
|
|
499
|
+
Window_call_text_event(win, &te);
|
|
500
|
+
}
|
|
501
|
+
|
|
275
502
|
Window_call_key_event(win, &e);
|
|
276
503
|
}
|
|
277
504
|
|
|
@@ -286,11 +513,73 @@ namespace Reflex
|
|
|
286
513
|
|
|
287
514
|
auto it = self->pressing_keys.find(e.code());
|
|
288
515
|
if (it != self->pressing_keys.end())
|
|
516
|
+
{
|
|
289
517
|
KeyEvent_set_chars(&e, it->second);
|
|
518
|
+
self->pressing_keys.erase(it);
|
|
519
|
+
}
|
|
290
520
|
|
|
291
521
|
Window_call_key_event(win, &e);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
static void
|
|
525
|
+
update_composition (Window* win, LPARAM lp)
|
|
526
|
+
{
|
|
527
|
+
assert(*win);
|
|
528
|
+
|
|
529
|
+
WindowData* self = get_data(win);
|
|
530
|
+
|
|
531
|
+
HIMC himc = ImmGetContext(self->hwnd);
|
|
532
|
+
if (!himc) return;
|
|
533
|
+
|
|
534
|
+
if (lp & GCS_RESULTSTR)
|
|
535
|
+
{
|
|
536
|
+
NativeTextEvent e(TextEvent::COMMIT, himc, GCS_RESULTSTR);
|
|
537
|
+
Window_call_text_event(win, &e, true);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (lp & GCS_COMPSTR)
|
|
541
|
+
{
|
|
542
|
+
NativeTextEvent e(TextEvent::PREEDIT, himc, GCS_COMPSTR);
|
|
543
|
+
Window_call_text_event(win, &e);
|
|
544
|
+
}
|
|
292
545
|
|
|
293
|
-
|
|
546
|
+
ImmReleaseContext(self->hwnd, himc);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
static void
|
|
550
|
+
clear_composition (Window* win)
|
|
551
|
+
{
|
|
552
|
+
assert(*win);
|
|
553
|
+
|
|
554
|
+
TextEvent e(TextEvent::PREEDIT, "");
|
|
555
|
+
Window_call_text_event(win, &e);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
static void
|
|
559
|
+
update_ime_position (Window* win)
|
|
560
|
+
{
|
|
561
|
+
assert(*win);
|
|
562
|
+
|
|
563
|
+
const View* focus = win->focus();
|
|
564
|
+
if (!focus) return;
|
|
565
|
+
|
|
566
|
+
WindowData* self = get_data(win);
|
|
567
|
+
|
|
568
|
+
HIMC himc = ImmGetContext(self->hwnd);
|
|
569
|
+
if (!himc) return;
|
|
570
|
+
|
|
571
|
+
Bounds b = focus->text_input_bounds();
|
|
572
|
+
POINT p1 = Window_to_native_coord(*win, focus->to_window(b.position()));
|
|
573
|
+
POINT p2 = Window_to_native_coord(*win, focus->to_window(b.position() + b.size()));
|
|
574
|
+
|
|
575
|
+
CANDIDATEFORM form = {0};
|
|
576
|
+
form.dwIndex = 0;
|
|
577
|
+
form.dwStyle = CFS_EXCLUDE;
|
|
578
|
+
form.ptCurrentPos = {(LONG) p1.x, (LONG) p2.y};
|
|
579
|
+
form.rcArea = {(LONG) p1.x, (LONG) p1.y, (LONG) p2.x, (LONG) p2.y};
|
|
580
|
+
ImmSetCandidateWindow(himc, &form);
|
|
581
|
+
|
|
582
|
+
ImmReleaseContext(self->hwnd, himc);
|
|
294
583
|
}
|
|
295
584
|
|
|
296
585
|
#ifndef MOUSEEVENTF_FROMTOUCH
|
|
@@ -303,6 +592,40 @@ namespace Reflex
|
|
|
303
592
|
return (GetMessageExtraInfo() & 0xffffff00) == MOUSEEVENTF_FROMTOUCH;
|
|
304
593
|
}
|
|
305
594
|
|
|
595
|
+
static void
|
|
596
|
+
capture_mouse_on_button_down (HWND hwnd, UINT msg)
|
|
597
|
+
{
|
|
598
|
+
switch (msg)
|
|
599
|
+
{
|
|
600
|
+
case WM_LBUTTONDOWN:
|
|
601
|
+
case WM_RBUTTONDOWN:
|
|
602
|
+
case WM_MBUTTONDOWN:
|
|
603
|
+
case WM_LBUTTONDBLCLK:
|
|
604
|
+
case WM_RBUTTONDBLCLK:
|
|
605
|
+
case WM_MBUTTONDBLCLK:
|
|
606
|
+
SetCapture(hwnd);
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
static void
|
|
612
|
+
release_mouse_on_button_up (HWND hwnd, UINT msg, WPARAM wp)
|
|
613
|
+
{
|
|
614
|
+
switch (msg)
|
|
615
|
+
{
|
|
616
|
+
case WM_LBUTTONUP:
|
|
617
|
+
case WM_RBUTTONUP:
|
|
618
|
+
case WM_MBUTTONUP:
|
|
619
|
+
if (
|
|
620
|
+
(wp & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 &&
|
|
621
|
+
hwnd == GetCapture())
|
|
622
|
+
{
|
|
623
|
+
ReleaseCapture();
|
|
624
|
+
}
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
306
629
|
static void
|
|
307
630
|
mouse (Window* win, UINT msg, WPARAM wp, LPARAM lp)
|
|
308
631
|
{
|
|
@@ -318,7 +641,7 @@ namespace Reflex
|
|
|
318
641
|
|
|
319
642
|
// Win32 has no mouse-enter message; the first move after (re)arming
|
|
320
643
|
// the leave tracking is when the mouse entered the window.
|
|
321
|
-
NativePointerEvent e(msg, wp, lp, Pointer::ENTER);
|
|
644
|
+
NativePointerEvent e(*win, msg, wp, lp, Pointer::ENTER);
|
|
322
645
|
Window_call_pointer_event(win, &e);
|
|
323
646
|
}
|
|
324
647
|
else if (msg == WM_MOUSELEAVE)
|
|
@@ -330,8 +653,15 @@ namespace Reflex
|
|
|
330
653
|
lp = MAKELPARAM(pt.x, pt.y);
|
|
331
654
|
}
|
|
332
655
|
|
|
333
|
-
|
|
656
|
+
// before the press is handled: a popup menu opened by it takes the
|
|
657
|
+
// capture away and eats the release, which would never come to let
|
|
658
|
+
// go of a capture taken after
|
|
659
|
+
capture_mouse_on_button_down(self->hwnd, msg);
|
|
660
|
+
|
|
661
|
+
NativePointerEvent e(*win, msg, wp, lp);
|
|
334
662
|
Window_call_pointer_event(win, &e);
|
|
663
|
+
|
|
664
|
+
release_mouse_on_button_up(self->hwnd, msg, wp);
|
|
335
665
|
}
|
|
336
666
|
|
|
337
667
|
static void
|
|
@@ -346,18 +676,18 @@ namespace Reflex
|
|
|
346
676
|
{
|
|
347
677
|
wp_y = wp;
|
|
348
678
|
MSG m;
|
|
349
|
-
if (
|
|
679
|
+
if (PeekMessageW(&m, self->hwnd, WM_MOUSEHWHEEL, WM_MOUSEHWHEEL, PM_REMOVE))
|
|
350
680
|
wp_x = m.wParam;
|
|
351
681
|
}
|
|
352
682
|
else
|
|
353
683
|
{
|
|
354
684
|
wp_x = wp;
|
|
355
685
|
MSG m;
|
|
356
|
-
if (
|
|
686
|
+
if (PeekMessageW(&m, self->hwnd, WM_MOUSEWHEEL, WM_MOUSEWHEEL, PM_REMOVE))
|
|
357
687
|
wp_y = m.wParam;
|
|
358
688
|
}
|
|
359
689
|
|
|
360
|
-
NativeWheelEvent e(wp_x, wp_y, lp);
|
|
690
|
+
NativeWheelEvent e(*win, wp_x, wp_y, lp);
|
|
361
691
|
Window_call_wheel_event(win, &e);
|
|
362
692
|
}
|
|
363
693
|
|
|
@@ -375,7 +705,7 @@ namespace Reflex
|
|
|
375
705
|
if (!GetTouchInputInfo(handle, size, &touches[0], sizeof(TOUCHINPUT)))
|
|
376
706
|
return;
|
|
377
707
|
|
|
378
|
-
NativePointerEvent e(
|
|
708
|
+
NativePointerEvent e(*win, &touches[0], size);
|
|
379
709
|
Window_call_pointer_event(win, &e);
|
|
380
710
|
|
|
381
711
|
CloseTouchInputHandle(handle);
|
|
@@ -385,12 +715,36 @@ namespace Reflex
|
|
|
385
715
|
window_proc (Window* win, HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
386
716
|
{
|
|
387
717
|
if (!win || !*win || hwnd != get_data(win)->hwnd)
|
|
388
|
-
return
|
|
718
|
+
return DefWindowProcW(hwnd, msg, wp, lp);
|
|
389
719
|
|
|
390
720
|
WindowData* self = get_data(win);
|
|
391
721
|
|
|
392
722
|
switch (msg)
|
|
393
723
|
{
|
|
724
|
+
case WM_NCCALCSIZE:
|
|
725
|
+
{
|
|
726
|
+
LRESULT result = 0;
|
|
727
|
+
if (calc_size(&result, hwnd, msg, wp, lp))
|
|
728
|
+
return result;
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
case WM_NCHITTEST:
|
|
733
|
+
{
|
|
734
|
+
LRESULT result = 0;
|
|
735
|
+
if (hit_test(&result, win, hwnd, msg, wp, lp))
|
|
736
|
+
return result;
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
case WM_WINDOWPOSCHANGING:
|
|
741
|
+
{
|
|
742
|
+
LRESULT result = 0;
|
|
743
|
+
if (window_pos_changing(&result, win, hwnd, msg, wp, lp))
|
|
744
|
+
return result;
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
|
|
394
748
|
case WM_ACTIVATE:
|
|
395
749
|
{
|
|
396
750
|
if (LOWORD(wp) == WA_INACTIVE)
|
|
@@ -405,10 +759,22 @@ namespace Reflex
|
|
|
405
759
|
|
|
406
760
|
case WM_CLOSE:
|
|
407
761
|
{
|
|
408
|
-
win
|
|
762
|
+
Window_call_close(win);
|
|
409
763
|
return 0;
|
|
410
764
|
}
|
|
411
765
|
|
|
766
|
+
case WM_COMMAND:
|
|
767
|
+
{
|
|
768
|
+
if (HIWORD(wp) == 1)
|
|
769
|
+
{
|
|
770
|
+
// menu items are reported by WM_MENUCOMMAND (MNS_NOTIFYBYPOS),
|
|
771
|
+
// so only accelerators (HIWORD == 1) arrive here
|
|
772
|
+
Menu_call_command_event(win->menu(), LOWORD(wp));
|
|
773
|
+
return 0;
|
|
774
|
+
}
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
|
|
412
778
|
case WM_MENUCOMMAND:
|
|
413
779
|
{
|
|
414
780
|
Menu_call_command_event((HMENU) lp, (uint) wp);
|
|
@@ -449,13 +815,13 @@ namespace Reflex
|
|
|
449
815
|
|
|
450
816
|
case WM_MOVE:
|
|
451
817
|
{
|
|
452
|
-
|
|
818
|
+
Window_call_frame_event(win);
|
|
453
819
|
break;
|
|
454
820
|
}
|
|
455
821
|
|
|
456
822
|
case WM_SIZE:
|
|
457
823
|
{
|
|
458
|
-
|
|
824
|
+
Window_call_frame_event(win);
|
|
459
825
|
break;
|
|
460
826
|
}
|
|
461
827
|
|
|
@@ -473,6 +839,31 @@ namespace Reflex
|
|
|
473
839
|
break;
|
|
474
840
|
}
|
|
475
841
|
|
|
842
|
+
case WM_IME_STARTCOMPOSITION:
|
|
843
|
+
{
|
|
844
|
+
if (!accepts_text_input(win)) break;
|
|
845
|
+
|
|
846
|
+
update_ime_position(win);
|
|
847
|
+
return 0;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
case WM_IME_ENDCOMPOSITION:
|
|
851
|
+
{
|
|
852
|
+
if (!accepts_text_input(win)) break;
|
|
853
|
+
|
|
854
|
+
clear_composition(win);
|
|
855
|
+
return 0;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
case WM_IME_COMPOSITION:
|
|
859
|
+
{
|
|
860
|
+
if (!accepts_text_input(win)) break;
|
|
861
|
+
|
|
862
|
+
update_composition(win, lp);
|
|
863
|
+
update_ime_position(win);
|
|
864
|
+
return 0;
|
|
865
|
+
}
|
|
866
|
+
|
|
476
867
|
case WM_LBUTTONDOWN:
|
|
477
868
|
case WM_RBUTTONDOWN:
|
|
478
869
|
case WM_MBUTTONDOWN:
|
|
@@ -496,6 +887,12 @@ namespace Reflex
|
|
|
496
887
|
break;
|
|
497
888
|
}
|
|
498
889
|
|
|
890
|
+
case WM_CAPTURECHANGED:
|
|
891
|
+
{
|
|
892
|
+
Window_cancel_active_pointers(win);
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
|
|
499
896
|
case WM_TOUCH:
|
|
500
897
|
{
|
|
501
898
|
touch(win, msg, wp, lp);
|
|
@@ -512,29 +909,51 @@ namespace Reflex
|
|
|
512
909
|
}
|
|
513
910
|
}
|
|
514
911
|
|
|
515
|
-
return
|
|
912
|
+
return DefWindowProcW(hwnd, msg, wp, lp);
|
|
516
913
|
}
|
|
517
914
|
|
|
518
915
|
static LRESULT CALLBACK
|
|
519
916
|
wndproc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
520
917
|
{
|
|
521
|
-
|
|
522
|
-
|
|
918
|
+
if (Application_has_exception())
|
|
919
|
+
return DefWindowProcW(hwnd, msg, wp, lp);
|
|
920
|
+
|
|
921
|
+
return Application_guard([&]() -> LRESULT
|
|
523
922
|
{
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
923
|
+
Window* win = NULL;
|
|
924
|
+
if (msg == WM_NCCREATE)
|
|
925
|
+
{
|
|
926
|
+
CREATESTRUCTW* cs = (CREATESTRUCTW*) lp;
|
|
927
|
+
win = (Window*) cs->lpCreateParams;
|
|
928
|
+
setup_window(win, hwnd);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if (!win) win = get_window_from_hwnd(hwnd);
|
|
932
|
+
if (win) rebind(win);
|
|
933
|
+
|
|
934
|
+
LRESULT ret = window_proc(win, hwnd, msg, wp, lp);
|
|
528
935
|
|
|
529
|
-
|
|
530
|
-
|
|
936
|
+
if (msg == WM_NCDESTROY)
|
|
937
|
+
cleanup_window(win);
|
|
531
938
|
|
|
532
|
-
|
|
939
|
+
return ret;
|
|
940
|
+
}, (LRESULT) 0);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
static void
|
|
944
|
+
load_app_icons (HICON* large, HICON* small)
|
|
945
|
+
{
|
|
946
|
+
// the icons Explorer shows for the host executable, if it has any;
|
|
947
|
+
// NULL leaves the window with the default icon
|
|
948
|
+
*large = *small = NULL;
|
|
533
949
|
|
|
534
|
-
|
|
535
|
-
|
|
950
|
+
wchar_t path[32768];
|
|
951
|
+
if (GetModuleFileNameW(NULL, path, sizeof(path) / sizeof(path[0])) == 0)
|
|
952
|
+
return;
|
|
536
953
|
|
|
537
|
-
|
|
954
|
+
UINT n = ExtractIconExW(path, 0, large, small, 1);
|
|
955
|
+
if (n == 0 || n == UINT_MAX)
|
|
956
|
+
*large = *small = NULL;
|
|
538
957
|
}
|
|
539
958
|
|
|
540
959
|
static void
|
|
@@ -543,22 +962,21 @@ namespace Reflex
|
|
|
543
962
|
static bool registered = false;
|
|
544
963
|
if (registered) return;
|
|
545
964
|
|
|
546
|
-
|
|
965
|
+
WNDCLASSEXW wc;
|
|
547
966
|
memset(&wc, 0, sizeof(wc));
|
|
548
|
-
|
|
549
967
|
wc.cbSize = sizeof(wc);
|
|
550
968
|
wc.lpszClassName = WINDOWCLASS;
|
|
551
969
|
wc.lpfnWndProc = (WNDPROC) wndproc;
|
|
552
970
|
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
|
|
553
|
-
wc.hInstance =
|
|
554
|
-
|
|
555
|
-
//wc.hIconSm = LoadIcon(wc.hInstance, IDI_APP_SMALL);
|
|
556
|
-
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
971
|
+
wc.hInstance = GetModuleHandleW(NULL);
|
|
972
|
+
wc.hCursor = LoadCursorW(NULL, (LPCWSTR) IDC_ARROW);
|
|
557
973
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
558
974
|
wc.lpszMenuName = NULL;
|
|
559
975
|
wc.cbWndExtra = sizeof(Window*);
|
|
560
976
|
|
|
561
|
-
|
|
977
|
+
load_app_icons(&wc.hIcon, &wc.hIconSm);
|
|
978
|
+
|
|
979
|
+
if (!RegisterClassExW(&wc))
|
|
562
980
|
system_error(__FILE__, __LINE__);
|
|
563
981
|
|
|
564
982
|
registered = true;
|
|
@@ -571,10 +989,10 @@ namespace Reflex
|
|
|
571
989
|
invalid_state_error(__FILE__, __LINE__);
|
|
572
990
|
|
|
573
991
|
register_windowclass();
|
|
574
|
-
HWND hwnd =
|
|
575
|
-
0, WINDOWCLASS, "",
|
|
992
|
+
HWND hwnd = CreateWindowExW(
|
|
993
|
+
0, WINDOWCLASS, L"",
|
|
576
994
|
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
|
577
|
-
0, 0, 0, 0, NULL, NULL,
|
|
995
|
+
0, 0, 0, 0, NULL, NULL, GetModuleHandleW(NULL), win);
|
|
578
996
|
if (!hwnd)
|
|
579
997
|
system_error(__FILE__, __LINE__);
|
|
580
998
|
|
|
@@ -596,9 +1014,12 @@ namespace Reflex
|
|
|
596
1014
|
Window_default_flags ()
|
|
597
1015
|
{
|
|
598
1016
|
return
|
|
599
|
-
Window::FLAG_CLOSABLE
|
|
600
|
-
Window::
|
|
601
|
-
Window::
|
|
1017
|
+
Window::FLAG_CLOSABLE |
|
|
1018
|
+
Window::FLAG_MINIMIZABLE |
|
|
1019
|
+
Window::FLAG_RESIZABLE |
|
|
1020
|
+
Window::FLAG_TITLEBAR_BUTTONS |
|
|
1021
|
+
Window::FLAG_TITLEBAR_BACKGROUND |
|
|
1022
|
+
Window::FLAG_SHADOW;
|
|
602
1023
|
}
|
|
603
1024
|
|
|
604
1025
|
void
|
|
@@ -617,9 +1038,15 @@ namespace Reflex
|
|
|
617
1038
|
|
|
618
1039
|
WindowData* self = get_data(window);
|
|
619
1040
|
|
|
1041
|
+
// the frame is passed so that WM_WINDOWPOSCHANGING can correct it
|
|
1042
|
+
RECT rect = {0};
|
|
1043
|
+
if (!GetWindowRect(self->hwnd, &rect))
|
|
1044
|
+
system_error(__FILE__, __LINE__);
|
|
1045
|
+
|
|
620
1046
|
SetWindowPos(
|
|
621
|
-
self->hwnd, HWND_TOP,
|
|
622
|
-
|
|
1047
|
+
self->hwnd, HWND_TOP,
|
|
1048
|
+
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
|
1049
|
+
SWP_SHOWWINDOW);
|
|
623
1050
|
UpdateWindow(self->hwnd);
|
|
624
1051
|
}
|
|
625
1052
|
|
|
@@ -654,7 +1081,7 @@ namespace Reflex
|
|
|
654
1081
|
|
|
655
1082
|
if (!*window) return;
|
|
656
1083
|
|
|
657
|
-
if (!
|
|
1084
|
+
if (!SetWindowTextW(get_data(window)->hwnd, String(title).to_wstr().c_str()))
|
|
658
1085
|
system_error(__FILE__, __LINE__);
|
|
659
1086
|
}
|
|
660
1087
|
|
|
@@ -665,75 +1092,80 @@ namespace Reflex
|
|
|
665
1092
|
|
|
666
1093
|
const WindowData* self = get_data(&window);
|
|
667
1094
|
|
|
668
|
-
int size =
|
|
1095
|
+
int size = GetWindowTextLengthW(self->hwnd);
|
|
669
1096
|
if (size <= 0) return "";
|
|
670
1097
|
|
|
671
|
-
std::unique_ptr<
|
|
672
|
-
if (
|
|
1098
|
+
std::unique_ptr<wchar_t[]> buf(new wchar_t[size + 1]);
|
|
1099
|
+
if (GetWindowTextW(self->hwnd, &buf[0], size + 1) != size)
|
|
673
1100
|
return "";
|
|
674
1101
|
|
|
675
|
-
self->title_tmp = &buf[0];
|
|
1102
|
+
self->title_tmp = String(&buf[0], size);
|
|
676
1103
|
return self->title_tmp.c_str();
|
|
677
1104
|
}
|
|
678
1105
|
|
|
679
|
-
static
|
|
680
|
-
|
|
681
|
-
HWND hwnd, coord* x, coord* y, coord* width, coord* height,
|
|
682
|
-
coord* nonclient_width = NULL, coord* nonclient_height = NULL)
|
|
1106
|
+
static RECT
|
|
1107
|
+
get_client_rect (HWND hwnd)
|
|
683
1108
|
{
|
|
684
|
-
if (!x && !y && !width && !height && !nonclient_width && !nonclient_height)
|
|
685
|
-
argument_error(__FILE__, __LINE__);
|
|
686
|
-
|
|
687
|
-
RECT window;
|
|
688
|
-
if (!GetWindowRect(hwnd, &window))
|
|
689
|
-
system_error(__FILE__, __LINE__);
|
|
690
|
-
|
|
691
1109
|
RECT client;
|
|
692
1110
|
if (!GetClientRect(hwnd, &client))
|
|
693
1111
|
system_error(__FILE__, __LINE__);
|
|
694
1112
|
|
|
695
|
-
|
|
696
|
-
if (
|
|
697
|
-
|
|
698
|
-
if (height) *height = client.bottom - client.top;
|
|
1113
|
+
POINT pos = {client.left, client.top};
|
|
1114
|
+
if (!ClientToScreen(hwnd, &pos))
|
|
1115
|
+
system_error(__FILE__, __LINE__);
|
|
699
1116
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
1117
|
+
return {
|
|
1118
|
+
pos.x,
|
|
1119
|
+
pos.y,
|
|
1120
|
+
pos.x + (client.right - client.left),
|
|
1121
|
+
pos.y + (client.bottom - client.top)
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
static void
|
|
1126
|
+
client_to_window_rect (HWND hwnd, RECT* rect)
|
|
1127
|
+
{
|
|
1128
|
+
DWORD style = (DWORD) GetWindowLongPtrW(hwnd, GWL_STYLE);
|
|
1129
|
+
DWORD exstyle = (DWORD) GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
|
|
1130
|
+
|
|
1131
|
+
LONG top = rect->top;
|
|
1132
|
+
if (!AdjustWindowRectEx(rect, style, GetMenu(hwnd) != NULL, exstyle))
|
|
1133
|
+
system_error(__FILE__, __LINE__);
|
|
1134
|
+
|
|
1135
|
+
// WM_NCCALCSIZE gives the top of the sizing border to the client area
|
|
1136
|
+
// when there is no caption, so it takes no room here either
|
|
1137
|
+
if (!has_style(hwnd, WS_CAPTION)) rect->top = top;
|
|
712
1138
|
}
|
|
713
1139
|
|
|
714
1140
|
void
|
|
715
|
-
Window_set_frame (Window* window, coord x, coord y, coord
|
|
1141
|
+
Window_set_frame (Window* window, coord x, coord y, coord w, coord h)
|
|
716
1142
|
{
|
|
717
1143
|
if (!*window) return;
|
|
718
1144
|
|
|
719
|
-
|
|
1145
|
+
HWND hwnd = get_data(window)->hwnd;
|
|
720
1146
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1147
|
+
RECT now = get_client_rect(hwnd);
|
|
1148
|
+
RECT rect = Screen_to_native_coord(x, y, w, h);
|
|
1149
|
+
UINT flags = 0;
|
|
724
1150
|
|
|
725
|
-
|
|
726
|
-
|
|
1151
|
+
if (rect.left == now.left && rect.top == now.top)
|
|
1152
|
+
flags |= SWP_NOMOVE;
|
|
727
1153
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1154
|
+
if (
|
|
1155
|
+
rect.right - rect.left == now.right - now.left &&
|
|
1156
|
+
rect.bottom - rect.top == now.bottom - now.top)
|
|
1157
|
+
{
|
|
1158
|
+
flags |= SWP_NOSIZE;
|
|
1159
|
+
}
|
|
731
1160
|
|
|
732
1161
|
if (flags == (SWP_NOMOVE | SWP_NOSIZE))
|
|
733
1162
|
return;
|
|
734
1163
|
|
|
1164
|
+
client_to_window_rect(hwnd, &rect);
|
|
1165
|
+
|
|
735
1166
|
if (!SetWindowPos(
|
|
736
|
-
|
|
1167
|
+
hwnd, NULL,
|
|
1168
|
+
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
|
737
1169
|
flags | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER))
|
|
738
1170
|
{
|
|
739
1171
|
system_error(__FILE__, __LINE__);
|
|
@@ -746,27 +1178,20 @@ namespace Reflex
|
|
|
746
1178
|
if (!window)
|
|
747
1179
|
invalid_state_error(__FILE__, __LINE__);
|
|
748
1180
|
|
|
749
|
-
|
|
750
|
-
get_window_bounds(get_data(&window)->hwnd, &x, &y, &w, &h);
|
|
751
|
-
return Bounds(x, y, w, h);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
HWND
|
|
755
|
-
Window_get_hwnd (const Window* window)
|
|
756
|
-
{
|
|
757
|
-
if (!window) return NULL;
|
|
758
|
-
|
|
759
|
-
return get_data(window)->hwnd;
|
|
1181
|
+
return Screen_from_native_coord(get_client_rect(get_data(&window)->hwnd));
|
|
760
1182
|
}
|
|
761
1183
|
|
|
762
1184
|
void
|
|
763
1185
|
Window_set_menu (Window* window, Menu* menu)
|
|
764
1186
|
{
|
|
765
|
-
|
|
1187
|
+
WindowData* self = get_data(window);
|
|
1188
|
+
HWND hwnd = self->hwnd;
|
|
766
1189
|
if (!hwnd) return;
|
|
767
1190
|
|
|
768
|
-
SetMenu(hwnd, menu ? Menu_get_hmenu(menu) : NULL);
|
|
1191
|
+
SetMenu(hwnd, menu ? Menu_get_hmenu(menu, true) : NULL);
|
|
769
1192
|
DrawMenuBar(hwnd);
|
|
1193
|
+
|
|
1194
|
+
self->set_accelerator_table(menu ? Menu_create_accelerator_table(menu) : NULL);
|
|
770
1195
|
}
|
|
771
1196
|
|
|
772
1197
|
Screen
|
|
@@ -779,20 +1204,256 @@ namespace Reflex
|
|
|
779
1204
|
return s;
|
|
780
1205
|
}
|
|
781
1206
|
|
|
1207
|
+
static void
|
|
1208
|
+
set_background_transparent (HWND hwnd, bool transparent)
|
|
1209
|
+
{
|
|
1210
|
+
std::shared_ptr<HRGN__> region;
|
|
1211
|
+
if (transparent)
|
|
1212
|
+
{
|
|
1213
|
+
region.reset(CreateRectRgn(0, 0, -1, -1), DeleteObject);// no blur
|
|
1214
|
+
if (!region)
|
|
1215
|
+
system_error(__FILE__, __LINE__);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
DWM_BLURBEHIND blur = {0};
|
|
1219
|
+
blur.dwFlags = DWM_BB_ENABLE | (transparent ? DWM_BB_BLURREGION : 0);
|
|
1220
|
+
blur.fEnable = transparent ? TRUE : FALSE;
|
|
1221
|
+
blur.hRgnBlur = region.get();
|
|
1222
|
+
HRESULT result = DwmEnableBlurBehindWindow(hwnd, &blur);
|
|
1223
|
+
if (FAILED(result))
|
|
1224
|
+
{
|
|
1225
|
+
system_error(
|
|
1226
|
+
__FILE__, __LINE__,
|
|
1227
|
+
"DwmEnableBlurBehindWindow failed (0x%08x)", (uint) result);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
static DWORD
|
|
1232
|
+
make_window_style (uint flags, DWORD style)
|
|
1233
|
+
{
|
|
1234
|
+
using namespace Xot;
|
|
1235
|
+
|
|
1236
|
+
bool caption = has_flag(flags, Window::FLAG_TITLEBAR_BACKGROUND);
|
|
1237
|
+
|
|
1238
|
+
// the sizing border follows resizable, but a captionless window keeps
|
|
1239
|
+
// it for the shadow and leaves the resizing to the hit test
|
|
1240
|
+
bool sizing_border = caption
|
|
1241
|
+
? has_flag(flags, Window::FLAG_RESIZABLE)
|
|
1242
|
+
: has_flag(flags, Window::FLAG_SHADOW);
|
|
1243
|
+
|
|
1244
|
+
update_flag(&style, WS_SYSMENU, has_flag(flags, Window::FLAG_TITLEBAR_BUTTONS));
|
|
1245
|
+
update_flag(&style, WS_CAPTION, caption);
|
|
1246
|
+
update_flag(&style, WS_MINIMIZEBOX, has_flag(flags, Window::FLAG_MINIMIZABLE));
|
|
1247
|
+
update_flag(&style, WS_MAXIMIZEBOX, has_flag(flags, Window::FLAG_RESIZABLE));
|
|
1248
|
+
update_flag(&style, WS_THICKFRAME, sizing_border);
|
|
1249
|
+
|
|
1250
|
+
return style;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
static void
|
|
1254
|
+
set_window_style (HWND hwnd, DWORD style)
|
|
1255
|
+
{
|
|
1256
|
+
RECT client = {0};
|
|
1257
|
+
bool normal = !IsIconic(hwnd) && !IsZoomed(hwnd) && GetClientRect(hwnd, &client);
|
|
1258
|
+
|
|
1259
|
+
POINT pos = {0, 0};
|
|
1260
|
+
if (normal && !ClientToScreen(hwnd, &pos))
|
|
1261
|
+
system_error(__FILE__, __LINE__);
|
|
1262
|
+
|
|
1263
|
+
RECT rect = {
|
|
1264
|
+
pos.x, pos.y,
|
|
1265
|
+
pos.x + (client.right - client.left),
|
|
1266
|
+
pos.y + (client.bottom - client.top)
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
SetLastError(0);
|
|
1270
|
+
SetWindowLongPtrW(hwnd, GWL_STYLE, style);
|
|
1271
|
+
if (GetLastError() != 0)
|
|
1272
|
+
system_error(__FILE__, __LINE__);
|
|
1273
|
+
|
|
1274
|
+
UINT posflags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
|
|
1275
|
+
if (normal)
|
|
1276
|
+
client_to_window_rect(hwnd, &rect);
|
|
1277
|
+
else
|
|
1278
|
+
posflags |= SWP_NOMOVE | SWP_NOSIZE;
|
|
1279
|
+
|
|
1280
|
+
if (!SetWindowPos(
|
|
1281
|
+
hwnd, NULL,
|
|
1282
|
+
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
|
1283
|
+
posflags))
|
|
1284
|
+
{
|
|
1285
|
+
system_error(__FILE__, __LINE__);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
static void
|
|
1290
|
+
set_closable (HWND hwnd, bool closable)
|
|
1291
|
+
{
|
|
1292
|
+
// the close button follows the state of the close menu item
|
|
1293
|
+
HMENU menu = GetSystemMenu(hwnd, FALSE);
|
|
1294
|
+
if (!menu) return;
|
|
1295
|
+
|
|
1296
|
+
EnableMenuItem(
|
|
1297
|
+
menu, SC_CLOSE, MF_BYCOMMAND | (closable ? MF_ENABLED : MF_GRAYED));
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
static void
|
|
1301
|
+
set_unlisted (HWND hwnd, bool state)
|
|
1302
|
+
{
|
|
1303
|
+
DWORD current = (DWORD) GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
|
|
1304
|
+
DWORD exstyle = state
|
|
1305
|
+
? current | WS_EX_TOOLWINDOW
|
|
1306
|
+
: current & ~WS_EX_TOOLWINDOW;
|
|
1307
|
+
if (exstyle == current) return;
|
|
1308
|
+
|
|
1309
|
+
SetLastError(0);
|
|
1310
|
+
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exstyle);
|
|
1311
|
+
if (GetLastError() != 0)
|
|
1312
|
+
system_error(__FILE__, __LINE__);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
782
1315
|
void
|
|
783
1316
|
Window_set_flags (Window* window, uint flags)
|
|
784
1317
|
{
|
|
1318
|
+
if (
|
|
1319
|
+
Xot::has_flag(flags, Window::FLAG_TITLEBAR_BUTTONS) &&
|
|
1320
|
+
!Xot::has_flag(flags, Window::FLAG_TITLEBAR_BACKGROUND))
|
|
1321
|
+
{
|
|
1322
|
+
argument_error(
|
|
1323
|
+
__FILE__, __LINE__, "FLAG_TITLEBAR_BUTTONS needs FLAG_TITLEBAR_BACKGROUND");
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
if (
|
|
1327
|
+
Xot::has_flag(flags, Window::FLAG_TITLEBAR_BACKGROUND) &&
|
|
1328
|
+
!Xot::has_flag(flags, Window::FLAG_SHADOW))
|
|
1329
|
+
{
|
|
1330
|
+
argument_error(
|
|
1331
|
+
__FILE__, __LINE__, "FLAG_TITLEBAR_BACKGROUND needs FLAG_SHADOW");
|
|
1332
|
+
}
|
|
1333
|
+
|
|
785
1334
|
if (Xot::has_flag(flags, Window::FLAG_PORTRAIT))
|
|
786
1335
|
argument_error(__FILE__, __LINE__, "FLAG_PORTRAIT is not supported");
|
|
787
1336
|
|
|
788
1337
|
if (Xot::has_flag(flags, Window::FLAG_LANDSCAPE))
|
|
789
1338
|
argument_error(__FILE__, __LINE__, "FLAG_LANDSCAPE is not supported");
|
|
1339
|
+
|
|
1340
|
+
if (!*window)
|
|
1341
|
+
invalid_state_error(__FILE__, __LINE__);
|
|
1342
|
+
|
|
1343
|
+
WindowData* self = get_data(window);
|
|
1344
|
+
|
|
1345
|
+
bool transparent = Xot::has_flag(flags, Window::FLAG_TRANSPARENT);
|
|
1346
|
+
if (transparent != self->transparent)
|
|
1347
|
+
{
|
|
1348
|
+
set_background_transparent(self->hwnd, transparent);
|
|
1349
|
+
self->transparent = transparent;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
DWORD current = (DWORD) GetWindowLongPtrW(self->hwnd, GWL_STYLE);
|
|
1353
|
+
DWORD style = make_window_style(flags, current);
|
|
1354
|
+
if (style != current)
|
|
1355
|
+
set_window_style(self->hwnd, style);
|
|
1356
|
+
|
|
1357
|
+
set_closable(self->hwnd, Xot::has_flag(flags, Window::FLAG_CLOSABLE));
|
|
1358
|
+
|
|
1359
|
+
set_unlisted(self->hwnd, Xot::has_flag(flags, Window::FLAG_UNLISTED));
|
|
1360
|
+
|
|
1361
|
+
HWND after = HWND_NOTOPMOST;
|
|
1362
|
+
if (Xot::has_flag(flags, Window::FLAG_ALWAYS_ON_TOP))
|
|
1363
|
+
after = HWND_TOPMOST;
|
|
1364
|
+
else if (Xot::has_flag(flags, Window::FLAG_ALWAYS_ON_BOTTOM))
|
|
1365
|
+
after = HWND_BOTTOM;
|
|
1366
|
+
if (!SetWindowPos(
|
|
1367
|
+
self->hwnd, after, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE))
|
|
1368
|
+
{
|
|
1369
|
+
system_error(__FILE__, __LINE__);
|
|
1370
|
+
}
|
|
790
1371
|
}
|
|
791
1372
|
|
|
792
1373
|
float
|
|
793
1374
|
Window_get_pixel_density (const Window& window)
|
|
794
1375
|
{
|
|
795
|
-
|
|
1376
|
+
HWND hwnd = get_data(&window)->hwnd;
|
|
1377
|
+
return Screen_get_pixel_density(
|
|
1378
|
+
hwnd ? MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) : NULL);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
Point
|
|
1382
|
+
Window_from_native_coord (const Window& window, coord x, coord y)
|
|
1383
|
+
{
|
|
1384
|
+
float density = Window_get_pixel_density(window);
|
|
1385
|
+
return Point(x / density, y / density);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
POINT
|
|
1389
|
+
Window_to_native_coord (const Window& window, const Point& point)
|
|
1390
|
+
{
|
|
1391
|
+
float density = Window_get_pixel_density(window);
|
|
1392
|
+
return {
|
|
1393
|
+
(LONG) lround(point.x * density),
|
|
1394
|
+
(LONG) lround(point.y * density)
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
void
|
|
1399
|
+
Window_set_pointer_through (Window* window, bool through)
|
|
1400
|
+
{
|
|
1401
|
+
static const DWORD THROUGH = WS_EX_TRANSPARENT | WS_EX_LAYERED;
|
|
1402
|
+
|
|
1403
|
+
HWND hwnd = get_data(window)->hwnd;
|
|
1404
|
+
DWORD current = (DWORD) GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
|
|
1405
|
+
DWORD exstyle = through ? current | THROUGH : current & ~THROUGH;
|
|
1406
|
+
if (exstyle == current) return;
|
|
1407
|
+
|
|
1408
|
+
SetLastError(0);
|
|
1409
|
+
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exstyle);
|
|
1410
|
+
if (GetLastError() != 0)
|
|
1411
|
+
system_error(__FILE__, __LINE__);
|
|
1412
|
+
|
|
1413
|
+
if (through && !SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA))
|
|
1414
|
+
system_error(__FILE__, __LINE__);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
static bool
|
|
1418
|
+
is_covered_by_another_window (HWND hwnd, POINT pos)
|
|
1419
|
+
{
|
|
1420
|
+
// only a window that takes mouse events counts as covering. the hit
|
|
1421
|
+
// test skips a window letting pointers through, so the hit can be one
|
|
1422
|
+
// below hwnd itself; it covers hwnd when it is among the windows above
|
|
1423
|
+
|
|
1424
|
+
HWND hit = WindowFromPoint(pos);
|
|
1425
|
+
if (hit) hit = GetAncestor(hit, GA_ROOT);
|
|
1426
|
+
if (!hit || hit == hwnd)
|
|
1427
|
+
return false;
|
|
1428
|
+
|
|
1429
|
+
for (HWND h = GetWindow(hwnd, GW_HWNDPREV); h; h = GetWindow(h, GW_HWNDPREV))
|
|
1430
|
+
{
|
|
1431
|
+
if (h == hit)
|
|
1432
|
+
return true;
|
|
1433
|
+
}
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
bool
|
|
1438
|
+
Window_is_pointer_over_and_uncovered (Point* position, const Window& window)
|
|
1439
|
+
{
|
|
1440
|
+
POINT pos;
|
|
1441
|
+
if (!GetCursorPos(&pos))
|
|
1442
|
+
return false;
|
|
1443
|
+
|
|
1444
|
+
HWND hwnd = get_data(&window)->hwnd;
|
|
1445
|
+
RECT rect;
|
|
1446
|
+
if (!GetWindowRect(hwnd, &rect))
|
|
1447
|
+
return false;
|
|
1448
|
+
if (!PtInRect(&rect, pos))
|
|
1449
|
+
return false;
|
|
1450
|
+
if (is_covered_by_another_window(hwnd, pos))
|
|
1451
|
+
return false;
|
|
1452
|
+
if (!ScreenToClient(hwnd, &pos))
|
|
1453
|
+
return false;
|
|
1454
|
+
|
|
1455
|
+
*position = Window_from_native_coord(window, pos.x, pos.y);
|
|
1456
|
+
return true;
|
|
796
1457
|
}
|
|
797
1458
|
|
|
798
1459
|
|