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/lib/fzeet/Window.rb CHANGED
@@ -1,9 +1,16 @@
1
1
  require_relative 'WindowMethods'
2
2
 
3
3
  module Fzeet
4
- class Window < Handle
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
- WindowClass = Windows::WNDCLASSEX.new.tap { |wc|
30
- wc[:cbSize] = wc.size
31
- wc[:lpfnWndProc] = WindowProc
32
- wc[:hInstance] = Windows.GetModuleHandle(nil)
33
- wc[:hIcon] = Windows.LoadIcon(nil, Windows::IDI_APPLICATION)
34
- wc[:hCursor] = Windows.LoadCursor(nil, Windows::IDC_ARROW)
35
- wc[:hbrBackground] = FFI::Pointer.new(Windows::COLOR_WINDOW + 1)
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
- Windows.DetonateLastError(0, :RegisterClassEx, wc) { className.free }
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: [:overlappedwindow, :clipchildren],
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].inject(0) { |flags, xstyle| flags |= Windows.const_get("WS_EX_#{xstyle.upcase}") }
134
+ _opts[:xstyle] = Fzeet.flags(_opts[:xstyle], *self.class::Prefix[:xstyle])
86
135
  _opts[:caption] = _opts[:caption].to_s
87
- _opts[:style] = _opts[:style].inject(0) { |flags, style| flags |= Windows.const_get("WS_#{style.upcase}") }
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], 'Fzeet.Window', _opts[:caption], _opts[:style],
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 ||= {})[Windows.const_get("WM_#{msg.upcase}")] ||= []) << block
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 Windows.const_get("WM_#{args.shift.upcase}")
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
@@ -4,12 +4,31 @@ module Fzeet
4
4
  module WindowMethods
5
5
  include Toggle
6
6
 
7
- def show(cmdShow = :shownormal) Windows.ShowWindow(@handle, Windows.const_get("SW_#{cmdShow.upcase}")); self end
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
- Windows.const_get("WM_#{msg.upcase}"),
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
- Windows.const_get("WM_#{msg.upcase}"),
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 menu; (Handle.instance?(handle = Windows.GetMenu(@handle))) ? Handle.instance(handle) : nil end
68
+ def dlgmsg?(msg) Windows.IsDialogMessage(@handle, msg) != 0 end
42
69
 
43
- def menu=(menu)
44
- self.menu.dispose if self.menu
70
+ def dialog?; Application.dialogs.include?(self) end
45
71
 
46
- Windows.DetonateLastError(0, :SetMenu, @handle, menu.handle) if menu
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
- Windows.const_get("MB_DEFBUTTON#{defbutton}")
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 = Windows.const_get("MB_#{_opts[:buttons].join('').upcase}") |
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