fzeet 0.4.1 → 0.4.2
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.
- 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
|