fzeet 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/Command.rbw +9 -11
- data/examples/Control/Button.rbw +19 -0
- data/examples/Dialog.rbw +43 -0
- data/examples/DialogApplication.rbw +22 -0
- data/examples/Inheritance.rbw +44 -0
- data/examples/Inheritance1.rbw +43 -0
- data/examples/LifeCycle.rbw +1 -1
- data/examples/LifeCycle2.rbw +1 -1
- data/examples/LifeCycleNC.rbw +1 -1
- data/examples/Menu/ContextMenu.rbw +15 -0
- data/examples/Menu/Menu.rbw +31 -22
- data/examples/Menu/MenuPARGB32.rbw +22 -0
- data/examples/MinimalOpts.rbw +7 -0
- data/examples/Scribble.rbw +26 -0
- data/examples/res/edit-copy.bmp +0 -0
- data/examples/res/edit-cut.bmp +0 -0
- data/examples/res/edit-paste.bmp +0 -0
- data/lib/fzeet/Accelerator.rb +1 -1
- data/lib/fzeet/Application.rb +24 -5
- data/lib/fzeet/Control.rb +32 -3
- data/lib/fzeet/Menu.rb +37 -1
- data/lib/fzeet/Window.rb +177 -18
- data/lib/fzeet/WindowMethods.rb +41 -7
- data/lib/fzeet/common.rb +214 -6
- data/lib/fzeet/windows/comctl.rb +59 -0
- data/lib/fzeet/windows/common.rb +25 -1
- data/lib/fzeet/windows/gdi.rb +3 -0
- data/lib/fzeet/windows/user.rb +252 -34
- data/lib/fzeet/windows/userctl.rb +68 -0
- data/lib/fzeet/windows/usermsg.rb +265 -0
- data/lib/fzeet/windows.rb +4 -0
- data/lib/fzeet.rb +0 -2
- metadata +19 -4
data/lib/fzeet/Window.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
require_relative 'WindowMethods'
|
2
2
|
|
3
3
|
module Fzeet
|
4
|
-
class
|
4
|
+
class BasicWindow < Handle
|
5
5
|
include WindowMethods
|
6
6
|
|
7
|
+
Prefix = {
|
8
|
+
xstyle: [:ws_ex_],
|
9
|
+
style: [:ws_, :ds_],
|
10
|
+
message: [:wm_],
|
11
|
+
notification: []
|
12
|
+
}
|
13
|
+
|
7
14
|
WindowProc = FFI::Function.new(:long, [:pointer, :uint, :uint, :long], convention: :stdcall) { |hwnd, uMsg, wParam, lParam|
|
8
15
|
begin
|
9
16
|
if uMsg == Windows::WM_NCCREATE
|
@@ -16,9 +23,13 @@ module Fzeet
|
|
16
23
|
|
17
24
|
result = @@instances[hwnd.to_i].windowProc(hwnd, uMsg, wParam, lParam) if @@instances[hwnd.to_i]
|
18
25
|
rescue
|
19
|
-
Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, icon: :error
|
26
|
+
answer = Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, buttons: [:abort, :retry, :ignore], icon: :error
|
27
|
+
|
28
|
+
Application.quit if answer.abort?
|
20
29
|
ensure
|
21
30
|
if uMsg == Windows::WM_NCDESTROY
|
31
|
+
@@instances[hwnd.to_i].dialog = false
|
32
|
+
|
22
33
|
@@instances.delete(hwnd.to_i)
|
23
34
|
end
|
24
35
|
end
|
@@ -26,18 +37,38 @@ module Fzeet
|
|
26
37
|
result || Windows.DefWindowProc(hwnd, uMsg, wParam, lParam)
|
27
38
|
}
|
28
39
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
wc[:lpszClassName] = className = FFI::MemoryPointer.from_string('Fzeet.Window')
|
40
|
+
DialogProc = FFI::Function.new(:int, [:pointer, :uint, :uint, :long], convention: :stdcall) { |hwnd, uMsg, wParam, lParam|
|
41
|
+
begin
|
42
|
+
if uMsg == Windows::WM_INITDIALOG
|
43
|
+
@@instances[hwnd.to_i] = ObjectSpace._id2ref(lParam)
|
44
|
+
|
45
|
+
@@instances[hwnd.to_i].instance_variable_set(:@handle, hwnd)
|
46
|
+
end
|
37
47
|
|
38
|
-
|
48
|
+
result = @@instances[hwnd.to_i].windowProc(hwnd, uMsg, wParam, lParam) if @@instances[hwnd.to_i]
|
49
|
+
rescue
|
50
|
+
answer = Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, buttons: [:abort, :retry, :ignore], icon: :error
|
51
|
+
|
52
|
+
Application.quit if answer.abort?
|
53
|
+
ensure
|
54
|
+
if uMsg == Windows::WM_NCDESTROY
|
55
|
+
@@instances[hwnd.to_i].dialog = false
|
56
|
+
|
57
|
+
@@instances.delete(hwnd.to_i)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
(result.nil?) ? 0 : 1
|
39
62
|
}
|
40
63
|
|
64
|
+
WindowClass = Fzeet::WindowClass.new(nil, 'Fzeet.BasicWindow')
|
65
|
+
|
66
|
+
def self.[](name, opts = {})
|
67
|
+
Class.new(self) { |klass|
|
68
|
+
klass.const_set(:WindowClass, Fzeet::WindowClass.new(self::WindowClass, name, opts))
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
41
72
|
def self.crackMessage(hwnd, uMsg, wParam, lParam)
|
42
73
|
window = @@instances[hwnd.to_i]
|
43
74
|
|
@@ -46,7 +77,7 @@ module Fzeet
|
|
46
77
|
uMsg: uMsg,
|
47
78
|
wParam: wParam,
|
48
79
|
lParam: lParam,
|
49
|
-
result: 0,
|
80
|
+
result: (window.kind_of?(Dialog)) ? 1 : 0,
|
50
81
|
window: window,
|
51
82
|
sender: window
|
52
83
|
}
|
@@ -60,6 +91,24 @@ module Fzeet
|
|
60
91
|
when Windows::WM_COMMAND
|
61
92
|
args[:command], args[:notification], args[:hctl] = Windows.LOWORD(wParam), Windows.HIWORD(wParam), FFI::Pointer.new(lParam)
|
62
93
|
args[:sender] = @@instances[args[:hctl].to_i] unless args[:hctl].null?
|
94
|
+
when Windows::WM_NOTIFY
|
95
|
+
args[:nmh] = Windows::NMHDR.new(FFI::Pointer.new(lParam))
|
96
|
+
args[:command], args[:notification], args[:hctl] = args[:nmh][:idFrom], args[:nmh][:code], args[:nmh][:hwndFrom]
|
97
|
+
(args[:sender] = @@instances[args[:hctl].to_i]).class.crackNotification(args)
|
98
|
+
when \
|
99
|
+
Windows::WM_MOUSEMOVE,
|
100
|
+
Windows::WM_LBUTTONDOWN, Windows::WM_LBUTTONUP, Windows::WM_LBUTTONDBLCLK,
|
101
|
+
Windows::WM_RBUTTONDOWN, Windows::WM_RBUTTONUP, Windows::WM_RBUTTONDBLCLK,
|
102
|
+
Windows::WM_MBUTTONDOWN, Windows::WM_MBUTTONUP, Windows::WM_MBUTTONDBLCLK,
|
103
|
+
Windows::WM_CONTEXTMENU
|
104
|
+
|
105
|
+
args[:x], args[:y] = Windows.GET_X_LPARAM(lParam), Windows.GET_Y_LPARAM(lParam)
|
106
|
+
|
107
|
+
if uMsg == Windows::WM_CONTEXTMENU && args[:x] == -1
|
108
|
+
Windows.DetonateLastError(0, :GetCursorPos, pt = Point.new)
|
109
|
+
|
110
|
+
args[:keyboard], args[:x], args[:y] = true, pt[:x], pt[:y]
|
111
|
+
end
|
63
112
|
end
|
64
113
|
|
65
114
|
args
|
@@ -69,7 +118,7 @@ module Fzeet
|
|
69
118
|
_opts = {
|
70
119
|
xstyle: [],
|
71
120
|
caption: Application.name,
|
72
|
-
style: [
|
121
|
+
style: [],
|
73
122
|
x: Windows::CW_USEDEFAULT,
|
74
123
|
y: Windows::CW_USEDEFAULT,
|
75
124
|
width: Windows::CW_USEDEFAULT,
|
@@ -82,17 +131,18 @@ module Fzeet
|
|
82
131
|
|
83
132
|
yield self, _opts if block_given?
|
84
133
|
|
85
|
-
_opts[:xstyle] = _opts[:xstyle]
|
134
|
+
_opts[:xstyle] = Fzeet.flags(_opts[:xstyle], *self.class::Prefix[:xstyle])
|
86
135
|
_opts[:caption] = _opts[:caption].to_s
|
87
|
-
_opts[:style] = _opts[:style]
|
136
|
+
_opts[:style] = Fzeet.flags(_opts[:style], *self.class::Prefix[:style])
|
88
137
|
|
89
138
|
@messages ||= {}
|
90
139
|
@commands ||= {}
|
140
|
+
@notifies ||= {}
|
91
141
|
|
92
142
|
@processed = [0, 0]
|
93
143
|
|
94
144
|
@handle = Windows.CreateWindowEx(
|
95
|
-
_opts[:xstyle],
|
145
|
+
_opts[:xstyle], self.class::WindowClass.name, _opts[:caption], _opts[:style],
|
96
146
|
_opts[:x], _opts[:y], _opts[:width], _opts[:height],
|
97
147
|
_opts[:parent] && _opts[:parent].handle, nil, Windows.GetModuleHandle(nil), FFI::Pointer.new(object_id)
|
98
148
|
)
|
@@ -147,31 +197,140 @@ module Fzeet
|
|
147
197
|
} if handlers[Windows.HIWORD(wParam)]
|
148
198
|
end
|
149
199
|
|
200
|
+
if uMsg == Windows::WM_NOTIFY && (handlers = @notifies[(nmh = Windows::NMHDR.new(FFI::Pointer.new(lParam)))[:idFrom]])
|
201
|
+
args ||= self.class.crackMessage(hwnd, uMsg, wParam, lParam)
|
202
|
+
|
203
|
+
handlers[:all].each { |handler|
|
204
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
205
|
+
|
206
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
207
|
+
} if handlers[:all]
|
208
|
+
|
209
|
+
handlers[nmh[:code]].each { |handler|
|
210
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
211
|
+
|
212
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
213
|
+
} if handlers[nmh[:code]]
|
214
|
+
end
|
215
|
+
|
150
216
|
result
|
151
217
|
end
|
152
218
|
|
153
219
|
def onMessage(msg, &block)
|
154
|
-
((@messages ||= {})[
|
220
|
+
((@messages ||= {})[Fzeet.constant(msg, :wm_)] ||= []) << block
|
155
221
|
|
156
222
|
self
|
157
223
|
end
|
158
224
|
|
159
225
|
def onCommand(cmd, notification = :all, &block)
|
226
|
+
notification = Fzeet.constant(notification, *self[cmd].class::Prefix[:notification]) unless
|
227
|
+
notification.kind_of?(Integer) || notification == :all
|
228
|
+
|
160
229
|
(((@commands ||= {})[Command[cmd]] ||= {})[notification] ||= []) << block
|
161
230
|
|
162
231
|
self
|
163
232
|
end
|
164
233
|
|
234
|
+
def onNotify(cmd, notification = :all, &block)
|
235
|
+
notification = Fzeet.constant(notification, *self[cmd].class::Prefix[:notification]) unless
|
236
|
+
notification.kind_of?(Integer) || notification == :all
|
237
|
+
|
238
|
+
(((@notifies ||= {})[Command[cmd]] ||= {})[notification] ||= []) << block
|
239
|
+
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
165
243
|
def on(*args, &block)
|
166
244
|
case args.length
|
167
245
|
when 1; onMessage(*args, &block)
|
168
246
|
when 2, 3
|
169
|
-
case
|
247
|
+
case Fzeet.constant(args.shift, :wm_)
|
170
248
|
when Windows::WM_COMMAND; onCommand(*args, &block)
|
249
|
+
when Windows::WM_NOTIFY; onNotify(*args, &block)
|
171
250
|
else raise ArgumentError
|
172
251
|
end
|
173
252
|
else raise ArgumentError
|
174
253
|
end
|
175
254
|
end
|
176
255
|
end
|
256
|
+
|
257
|
+
Window = BasicWindow['Fzeet.Window']
|
258
|
+
|
259
|
+
class Window
|
260
|
+
def initialize(opts = {})
|
261
|
+
(opts[:style] ||= []) << :overlappedwindow << :clipchildren
|
262
|
+
|
263
|
+
super
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
Dialog = BasicWindow['Fzeet.Dialog',
|
268
|
+
wndProc: BasicWindow::DialogProc
|
269
|
+
]
|
270
|
+
|
271
|
+
class Dialog
|
272
|
+
def initialize(parent, opts = {}, &block)
|
273
|
+
@parent = parent
|
274
|
+
|
275
|
+
_opts = {
|
276
|
+
xstyle: [:noparentnotify],
|
277
|
+
caption: Application.name,
|
278
|
+
style: [:sysmenu, :visible],
|
279
|
+
x: 0,
|
280
|
+
y: 0,
|
281
|
+
width: 200,
|
282
|
+
height: 150,
|
283
|
+
modal: false
|
284
|
+
}
|
285
|
+
badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
|
286
|
+
_opts.merge!(opts)
|
287
|
+
|
288
|
+
_opts[:xstyle] = Fzeet.flags(_opts[:xstyle], *self.class::Prefix[:xstyle])
|
289
|
+
_opts[:caption] = _opts[:caption].to_s
|
290
|
+
_opts[:style] = Fzeet.flags(_opts[:style], *self.class::Prefix[:style])
|
291
|
+
|
292
|
+
@messages ||= {}
|
293
|
+
@commands ||= {}
|
294
|
+
@notifies ||= {}
|
295
|
+
|
296
|
+
@processed = [0, 0]
|
297
|
+
|
298
|
+
dt = Windows::DLGTEMPLATE.new
|
299
|
+
|
300
|
+
dt[:style] = _opts[:style]
|
301
|
+
dt[:dwExtendedStyle] = _opts[:xstyle]
|
302
|
+
dt[:x] = _opts[:x]
|
303
|
+
dt[:y] = _opts[:y]
|
304
|
+
dt[:cx] = _opts[:width]
|
305
|
+
dt[:cy] = _opts[:height]
|
306
|
+
|
307
|
+
on(:initdialog) { self.text = _opts[:caption] }
|
308
|
+
|
309
|
+
on(:initdialog, &block) if block
|
310
|
+
|
311
|
+
if _opts[:modal]
|
312
|
+
@result = Windows.DetonateLastError([-1, 0], :DialogBoxIndirectParam,
|
313
|
+
Windows.GetModuleHandle(nil),
|
314
|
+
dt,
|
315
|
+
@parent && @parent.handle,
|
316
|
+
BasicWindow::DialogProc,
|
317
|
+
object_id
|
318
|
+
)
|
319
|
+
else
|
320
|
+
@handle = Windows.DetonateLastError(FFI::Pointer::NULL, :CreateDialogIndirectParam,
|
321
|
+
Windows.GetModuleHandle(nil),
|
322
|
+
dt,
|
323
|
+
@parent && @parent.handle,
|
324
|
+
BasicWindow::DialogProc,
|
325
|
+
object_id
|
326
|
+
)
|
327
|
+
|
328
|
+
self.dialog = true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def end(result) Windows.DetonateLastError(0, :EndDialog, @handle, Command[result]); self end
|
333
|
+
|
334
|
+
def result; DialogResult.new(@result) end
|
335
|
+
end
|
177
336
|
end
|
data/lib/fzeet/WindowMethods.rb
CHANGED
@@ -4,12 +4,31 @@ module Fzeet
|
|
4
4
|
module WindowMethods
|
5
5
|
include Toggle
|
6
6
|
|
7
|
-
def show(cmdShow = :shownormal) Windows.ShowWindow(@handle,
|
7
|
+
def show(cmdShow = :shownormal) Windows.ShowWindow(@handle, Fzeet.constant(cmdShow, :sw_)); self end
|
8
8
|
def update; Windows.DetonateLastError(0, :UpdateWindow, @handle); self end
|
9
9
|
|
10
|
+
def textlen; Windows.GetWindowTextLength(@handle) end
|
11
|
+
|
12
|
+
def text
|
13
|
+
return '' if (len = textlen + 1) == 1
|
14
|
+
|
15
|
+
''.tap { |text|
|
16
|
+
FFI::MemoryPointer.new(:char, len) { |buf|
|
17
|
+
Windows.DetonateLastError(0, :GetWindowText, @handle, buf, len)
|
18
|
+
|
19
|
+
text << buf.read_string
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def text=(text) Windows.DetonateLastError(0, :SetWindowText, @handle, text) end
|
25
|
+
|
10
26
|
def enabled?; Windows.IsWindowEnabled(@handle) != 0 end
|
11
27
|
def enabled=(enabled) Windows.EnableWindow(@handle, (enabled) ? 1 : 0) end
|
12
28
|
|
29
|
+
def capture?; Windows.GetCapture().to_i == @handle.to_i end
|
30
|
+
def capture=(capture) (capture) ? Windows.SetCapture(@handle) : Windows.DetonateLastError(0, :ReleaseCapture) end
|
31
|
+
|
13
32
|
def [](id) Handle.instance(Windows.DetonateLastError(FFI::Pointer::NULL, :GetDlgItem, @handle, Command[id])) end
|
14
33
|
|
15
34
|
EnumChildProc = FFI::Function.new(:int, [:pointer, :long], convention: :stdcall) { |hwnd, lParam|
|
@@ -18,10 +37,18 @@ module Fzeet
|
|
18
37
|
|
19
38
|
def eachChild(&block) Windows.EnumChildWindows(@handle, EnumChildProc, block.object_id); self end
|
20
39
|
|
40
|
+
def menu; (Handle.instance?(handle = Windows.GetMenu(@handle))) ? Handle.instance(handle) : nil end
|
41
|
+
|
42
|
+
def menu=(menu)
|
43
|
+
self.menu.dispose if self.menu
|
44
|
+
|
45
|
+
Windows.DetonateLastError(0, :SetMenu, @handle, menu.handle) if menu
|
46
|
+
end
|
47
|
+
|
21
48
|
def sendmsg(msg, wParam = 0, lParam = 0)
|
22
49
|
Windows.SendMessage(
|
23
50
|
@handle,
|
24
|
-
|
51
|
+
Fzeet.constant(msg, *self.class::Prefix[:message]),
|
25
52
|
wParam.to_i,
|
26
53
|
((lparam = lParam.to_i) > 0x7fff_ffff) ? lparam - 0x1_0000_0000 : lparam
|
27
54
|
)
|
@@ -30,7 +57,7 @@ module Fzeet
|
|
30
57
|
def postmsg(msg, wParam = 0, lParam = 0)
|
31
58
|
Windows.DetonateLastError(0, :PostMessage,
|
32
59
|
@handle,
|
33
|
-
|
60
|
+
Fzeet.constant(msg, *self.class::Prefix[:message]),
|
34
61
|
wParam.to_i,
|
35
62
|
((lparam = lParam.to_i) > 0x7fff_ffff) ? lparam - 0x1_0000_0000 : lparam
|
36
63
|
)
|
@@ -38,12 +65,19 @@ module Fzeet
|
|
38
65
|
self
|
39
66
|
end
|
40
67
|
|
41
|
-
def
|
68
|
+
def dlgmsg?(msg) Windows.IsDialogMessage(@handle, msg) != 0 end
|
42
69
|
|
43
|
-
def
|
44
|
-
self.menu.dispose if self.menu
|
70
|
+
def dialog?; Application.dialogs.include?(self) end
|
45
71
|
|
46
|
-
|
72
|
+
def dialog=(dialog)
|
73
|
+
if dialog
|
74
|
+
Application.dialogs << self unless dialog?
|
75
|
+
else
|
76
|
+
Application.dialogs.delete(self)
|
77
|
+
end
|
47
78
|
end
|
79
|
+
|
80
|
+
def message(message, opts = {window: self}) Fzeet.message(message, opts) end
|
81
|
+
def question(message, opts = {window: self}) Fzeet.question(message, opts) end
|
48
82
|
end
|
49
83
|
end
|
data/lib/fzeet/common.rb
CHANGED
@@ -1,15 +1,59 @@
|
|
1
1
|
require_relative 'windows'
|
2
2
|
|
3
3
|
module Fzeet
|
4
|
+
Windows.DetonateLastError(0, :InitCommonControlsEx,
|
5
|
+
Windows::INITCOMMONCONTROLSEX.new.tap { |icc|
|
6
|
+
icc[:dwSize] = icc.size
|
7
|
+
icc[:dwICC] = \
|
8
|
+
Windows::ICC_WIN95_CLASSES |
|
9
|
+
Windows::ICC_DATE_CLASSES |
|
10
|
+
Windows::ICC_USEREX_CLASSES |
|
11
|
+
Windows::ICC_COOL_CLASSES |
|
12
|
+
Windows::ICC_INTERNET_CLASSES |
|
13
|
+
Windows::ICC_PAGESCROLLER_CLASS
|
14
|
+
}
|
15
|
+
)
|
16
|
+
|
17
|
+
Windows.EnableVisualStyles
|
18
|
+
|
19
|
+
def constant(c, *prefixes)
|
20
|
+
return c if c.kind_of?(Integer) || c.kind_of?(FFI::Pointer)
|
21
|
+
|
22
|
+
c = c.upcase
|
23
|
+
|
24
|
+
prefixes.map! { |prefix|
|
25
|
+
prefix = prefix.upcase
|
26
|
+
|
27
|
+
"#{prefix}#{c}".tap { |name|
|
28
|
+
return Windows.const_get(name) if Windows.const_defined?(name)
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
raise "Constant not found: #{c} (tried: #{prefixes.join(', ')})."
|
33
|
+
end
|
34
|
+
|
35
|
+
def flags(flags, *prefixes)
|
36
|
+
return flags if flags.kind_of?(Integer)
|
37
|
+
|
38
|
+
[*flags].inject(0) { |flags, flag| flags |= constant(flag, *prefixes) }
|
39
|
+
end
|
40
|
+
|
41
|
+
module_function :constant, :flags
|
42
|
+
|
4
43
|
module Toggle
|
5
44
|
def toggle(what) send("#{what}=", !send("#{what}?")); self end
|
6
45
|
end
|
7
46
|
|
8
47
|
class Command
|
9
|
-
@ids = {
|
48
|
+
@ids = {
|
49
|
+
OK: Windows::IDOK,
|
50
|
+
CANCEL: Windows::IDCANCEL,
|
51
|
+
}
|
10
52
|
@nextId = Windows::WM_APP + 1
|
11
53
|
|
12
54
|
def self.[](id)
|
55
|
+
return id if id.kind_of?(Integer)
|
56
|
+
|
13
57
|
id = id.upcase
|
14
58
|
|
15
59
|
@ids[id], @nextId = @nextId, @nextId + 1 unless @ids.include?(id)
|
@@ -43,15 +87,13 @@ module Fzeet
|
|
43
87
|
_opts.merge!(opts)
|
44
88
|
|
45
89
|
defbutton = nil
|
46
|
-
defbutton = if _opts[:buttons].find.with_index { |button, i| defbutton = i + 1; button =~ /^[A-Z]/ }
|
47
|
-
|
90
|
+
defbutton = if [*_opts[:buttons]].find.with_index { |button, i| defbutton = i + 1; button =~ /^[A-Z]/ }
|
91
|
+
constant(defbutton.to_s, :mb_defbutton)
|
48
92
|
else
|
49
93
|
0
|
50
94
|
end
|
51
95
|
|
52
|
-
flags =
|
53
|
-
defbutton |
|
54
|
-
Windows.const_get("MB_ICON#{_opts[:icon].upcase}")
|
96
|
+
flags = constant([*_opts[:buttons]].join(''), :mb_) | defbutton | constant(_opts[:icon], :mb_icon)
|
55
97
|
|
56
98
|
DialogResult.new(
|
57
99
|
Windows.DetonateLastError(0, :MessageBox, _opts[:window] && _opts[:window].handle, message.to_s, _opts[:caption].to_s, flags)
|
@@ -67,6 +109,82 @@ module Fzeet
|
|
67
109
|
|
68
110
|
module_function :message, :question
|
69
111
|
|
112
|
+
class WindowClass < Windows::WNDCLASSEX
|
113
|
+
def initialize(prototype, name, opts = {})
|
114
|
+
@prototype = prototype
|
115
|
+
@name = name
|
116
|
+
className = FFI::MemoryPointer.from_string(@name)
|
117
|
+
|
118
|
+
_opts = {
|
119
|
+
style: [],
|
120
|
+
wndProc: BasicWindow::WindowProc,
|
121
|
+
clsExtra: 0,
|
122
|
+
wndExtra: 0,
|
123
|
+
icon: SystemIcon.new(:application),
|
124
|
+
cursor: SystemCursor.new(:arrow),
|
125
|
+
background: SystemBrush.new(:window),
|
126
|
+
iconSm: nil
|
127
|
+
}
|
128
|
+
badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
|
129
|
+
|
130
|
+
self[:cbSize] = Windows::WNDCLASSEX.size
|
131
|
+
self[:hInstance] = Windows.GetModuleHandle(nil)
|
132
|
+
self[:lpszClassName] = className
|
133
|
+
|
134
|
+
if @prototype
|
135
|
+
self[:style] = Fzeet.flags(opts[:style] || [], :cs_) | @prototype[:style]
|
136
|
+
self[:lpfnWndProc] = opts[:wndProc] || @prototype[:lpfnWndProc]
|
137
|
+
self[:cbClsExtra] = (opts[:clsExtra] || 0) + @prototype[:cbClsExtra]
|
138
|
+
self[:cbWndExtra] = (opts[:wndExtra] || 0) + @prototype[:cbWndExtra]
|
139
|
+
self[:hIcon] = (opts[:icon] && opts[:icon].handle) || @prototype[:hIcon]
|
140
|
+
self[:hCursor] = (opts[:cursor] && opts[:cursor].handle) || @prototype[:hCursor]
|
141
|
+
self[:hbrBackground] = (opts[:background] && opts[:background].handle) || @prototype[:hbrBackground]
|
142
|
+
self[:hIconSm] = (opts[:iconSm] && _opts[:iconSm].handle) || @prototype[:hIconSm]
|
143
|
+
else
|
144
|
+
_opts.merge(opts)
|
145
|
+
|
146
|
+
self[:style] = Fzeet.flags(_opts[:style], :cs_)
|
147
|
+
self[:lpfnWndProc] = _opts[:wndProc]
|
148
|
+
self[:cbClsExtra] = _opts[:clsExtra]
|
149
|
+
self[:cbWndExtra] = _opts[:wndExtra]
|
150
|
+
self[:hIcon] = _opts[:icon].handle
|
151
|
+
self[:hCursor] = _opts[:cursor].handle
|
152
|
+
self[:hbrBackground] = _opts[:background].handle
|
153
|
+
self[:hIconSm] = _opts[:iconSm] && _opts[:iconSm].handle
|
154
|
+
end
|
155
|
+
|
156
|
+
Windows.DetonateLastError(0, :RegisterClassEx, self) { className.free }
|
157
|
+
end
|
158
|
+
|
159
|
+
attr_reader :prototype, :name
|
160
|
+
end
|
161
|
+
|
162
|
+
class Point < Windows::POINT
|
163
|
+
def initialize(x = 0, y = 0) self[:x], self[:y] = x, y end
|
164
|
+
|
165
|
+
def to_a; [self[:x], self[:y]] end
|
166
|
+
|
167
|
+
def client!(window) Windows.DetonateLastError(0, :ScreenToClient, window.handle, self); self end
|
168
|
+
def screen!(window) Windows.DetonateLastError(0, :ClientToScreen, window.handle, self); self end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Size < Windows::SIZE
|
172
|
+
def initialize(cx = 0, cy = 0) self[:cx], self[:cy] = cx, cy end
|
173
|
+
|
174
|
+
def to_a; [self[:cx], self[:cy]] end
|
175
|
+
end
|
176
|
+
|
177
|
+
class Rect < Windows::RECT
|
178
|
+
def initialize(l = 0, t = 0, r = 0, b = 0) self[:left], self[:top], self[:right], self[:bottom] = l, t, r, b end
|
179
|
+
|
180
|
+
def to_a; [self[:left], self[:top], self[:right], self[:bottom]] end
|
181
|
+
|
182
|
+
def lt; Point.new(self[:left], self[:top]) end
|
183
|
+
def lb; Point.new(self[:left], self[:bottom]) end
|
184
|
+
def rt; Point.new(self[:right], self[:top]) end
|
185
|
+
def rb; Point.new(self[:right], self[:bottom]) end
|
186
|
+
end
|
187
|
+
|
70
188
|
class Message < Windows::MSG
|
71
189
|
def get!(window = nil, msgFilterMin = 0, msgFilterMax = 0)
|
72
190
|
Windows.DetonateLastError(-1, :GetMessage, self, window && window.handle, msgFilterMin, msgFilterMax) != 0
|
@@ -104,6 +222,54 @@ module Fzeet
|
|
104
222
|
|
105
223
|
module_function :using
|
106
224
|
|
225
|
+
module IconMethods
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
class SystemIcon < Handle
|
230
|
+
include IconMethods
|
231
|
+
|
232
|
+
def initialize(id)
|
233
|
+
@handle = Windows.DetonateLastError(FFI::Pointer::NULL, :LoadIcon, nil, @id = Fzeet.constant(id, :idi_)); attach
|
234
|
+
end
|
235
|
+
|
236
|
+
attr_reader :id
|
237
|
+
|
238
|
+
def dispose; detach end
|
239
|
+
end
|
240
|
+
|
241
|
+
module CursorMethods
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
class SystemCursor < Handle
|
246
|
+
include CursorMethods
|
247
|
+
|
248
|
+
def initialize(id)
|
249
|
+
@handle = Windows.DetonateLastError(FFI::Pointer::NULL, :LoadCursor, nil, @id = Fzeet.constant(id, :idc_)); attach
|
250
|
+
end
|
251
|
+
|
252
|
+
attr_reader :id
|
253
|
+
|
254
|
+
def dispose; detach end
|
255
|
+
end
|
256
|
+
|
257
|
+
module BrushMethods
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
class SystemBrush < Handle
|
262
|
+
include BrushMethods
|
263
|
+
|
264
|
+
def initialize(id)
|
265
|
+
@handle = FFI::Pointer.new((@id = Fzeet.constant(id, :color_, :ctlcolor_)) + 1); attach
|
266
|
+
end
|
267
|
+
|
268
|
+
attr_reader :id
|
269
|
+
|
270
|
+
def dispose; detach end
|
271
|
+
end
|
272
|
+
|
107
273
|
module FontMethods
|
108
274
|
|
109
275
|
end
|
@@ -119,4 +285,46 @@ module Fzeet
|
|
119
285
|
|
120
286
|
def dispose; Windows.DeleteObject(@handle); detach end
|
121
287
|
end
|
288
|
+
|
289
|
+
module BitmapMethods
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
class PARGB32 < Handle
|
294
|
+
include BitmapMethods
|
295
|
+
|
296
|
+
def initialize(path, width = 0, height = 0)
|
297
|
+
@handle = Windows.DetonateLastError(FFI::Pointer::NULL, :LoadImage,
|
298
|
+
nil,
|
299
|
+
@path = path,
|
300
|
+
Windows::IMAGE_BITMAP,
|
301
|
+
width,
|
302
|
+
height,
|
303
|
+
Windows::LR_LOADFROMFILE | Windows::LR_CREATEDIBSECTION
|
304
|
+
)
|
305
|
+
|
306
|
+
attach
|
307
|
+
end
|
308
|
+
|
309
|
+
attr_reader :path
|
310
|
+
|
311
|
+
def dispose; Windows.DeleteObject(@handle); detach end
|
312
|
+
end
|
313
|
+
|
314
|
+
module DCMethods
|
315
|
+
def move(x, y) Windows.DetonateLastError(0, :MoveToEx, @handle, x, y, nil); self end
|
316
|
+
def line(x, y) Windows.DetonateLastError(0, :LineTo, @handle, x, y); self end
|
317
|
+
end
|
318
|
+
|
319
|
+
class ClientDC < Handle
|
320
|
+
include DCMethods
|
321
|
+
|
322
|
+
def initialize(window)
|
323
|
+
@handle = Windows.DetonateLastError(FFI::Pointer::NULL, :GetDC, (@window = window).handle); attach
|
324
|
+
end
|
325
|
+
|
326
|
+
attr_reader :window
|
327
|
+
|
328
|
+
def dispose; Windows.ReleaseDC(@window.handle, @handle); detach end
|
329
|
+
end
|
122
330
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Fzeet
|
4
|
+
module Windows
|
5
|
+
ffi_lib 'comctl32'
|
6
|
+
ffi_convention :stdcall
|
7
|
+
|
8
|
+
ICC_LISTVIEW_CLASSES = 0x00000001
|
9
|
+
ICC_TREEVIEW_CLASSES = 0x00000002
|
10
|
+
ICC_BAR_CLASSES = 0x00000004
|
11
|
+
ICC_TAB_CLASSES = 0x00000008
|
12
|
+
ICC_UPDOWN_CLASS = 0x00000010
|
13
|
+
ICC_PROGRESS_CLASS = 0x00000020
|
14
|
+
ICC_HOTKEY_CLASS = 0x00000040
|
15
|
+
ICC_ANIMATE_CLASS = 0x00000080
|
16
|
+
ICC_WIN95_CLASSES = 0x000000FF
|
17
|
+
ICC_DATE_CLASSES = 0x00000100
|
18
|
+
ICC_USEREX_CLASSES = 0x00000200
|
19
|
+
ICC_COOL_CLASSES = 0x00000400
|
20
|
+
ICC_INTERNET_CLASSES = 0x00000800
|
21
|
+
ICC_PAGESCROLLER_CLASS = 0x00001000
|
22
|
+
ICC_NATIVEFNTCTL_CLASS = 0x00002000
|
23
|
+
ICC_STANDARD_CLASSES = 0x00004000
|
24
|
+
ICC_LINK_CLASS = 0x00008000
|
25
|
+
|
26
|
+
class INITCOMMONCONTROLSEX < FFI::Struct
|
27
|
+
layout \
|
28
|
+
:dwSize, :ulong,
|
29
|
+
:dwICC, :ulong
|
30
|
+
end
|
31
|
+
|
32
|
+
attach_function :InitCommonControlsEx, [:pointer], :int
|
33
|
+
|
34
|
+
BS_SPLITBUTTON = 0x0000000C
|
35
|
+
BS_DEFSPLITBUTTON = 0x0000000D
|
36
|
+
BS_COMMANDLINK = 0x0000000E
|
37
|
+
BS_DEFCOMMANDLINK = 0x0000000F
|
38
|
+
|
39
|
+
BCM_FIRST = 0x1600
|
40
|
+
BCM_GETIDEALSIZE = BCM_FIRST + 0x0001
|
41
|
+
BCM_SETIMAGELIST = BCM_FIRST + 0x0002
|
42
|
+
BCM_GETIMAGELIST = BCM_FIRST + 0x0003
|
43
|
+
BCM_SETTEXTMARGIN = BCM_FIRST + 0x0004
|
44
|
+
BCM_GETTEXTMARGIN = BCM_FIRST + 0x0005
|
45
|
+
BCM_SETDROPDOWNSTATE = BCM_FIRST + 0x0006
|
46
|
+
BCM_SETSPLITINFO = BCM_FIRST + 0x0007
|
47
|
+
BCM_GETSPLITINFO = BCM_FIRST + 0x0008
|
48
|
+
BCM_SETNOTE = BCM_FIRST + 0x0009
|
49
|
+
BCM_GETNOTE = BCM_FIRST + 0x000A
|
50
|
+
BCM_GETNOTELENGTH = BCM_FIRST + 0x000B
|
51
|
+
BCM_SETSHIELD = BCM_FIRST + 0x000C
|
52
|
+
|
53
|
+
BCN_FIRST = 0x1_0000_0000 - 1250
|
54
|
+
BCN_LAST = 0x1_0000_0000 - 1350
|
55
|
+
BCN_HOTITEMCHANGE = BCN_FIRST + 0x0001
|
56
|
+
BCN_DROPDOWN = BCN_FIRST + 0x0002
|
57
|
+
NM_GETCUSTOMSPLITRECT = BCN_FIRST + 0x0003
|
58
|
+
end
|
59
|
+
end
|