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/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