fzeet 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ require 'fzeet'
2
+
3
+ include Fzeet
4
+
5
+ message 'TODO...'
@@ -0,0 +1,5 @@
1
+ require 'fzeet'
2
+
3
+ include Fzeet
4
+
5
+ message 'Hello, world!'
@@ -0,0 +1,18 @@
1
+ require 'fzeet'
2
+
3
+ include Fzeet
4
+
5
+ Application.run { |window|
6
+ window.
7
+ on(:create) { |args|
8
+ args[:result] = -1 if question('Create?').no?
9
+ }.
10
+
11
+ on(:close) {
12
+ window.dispose if question('Close?', buttons: [:yes, :No]).yes?
13
+ }.
14
+
15
+ on(:destroy) {
16
+ message 'on(:destroy)'
17
+ }
18
+ }
@@ -0,0 +1,27 @@
1
+ require 'fzeet'
2
+
3
+ include Fzeet
4
+
5
+ class LifeCycle1Window < Window
6
+ def initialize
7
+ on :create, &method(:onCreate)
8
+ on :close, &method(:onClose)
9
+ on :destroy, &method(:onDestroy)
10
+
11
+ super
12
+ end
13
+
14
+ def onCreate(args)
15
+ args[:result] = -1 if question('Create?').no?
16
+ end
17
+
18
+ def onClose(args)
19
+ dispose if question('Close?', buttons: [:yes, :No]).yes?
20
+ end
21
+
22
+ def onDestroy(args)
23
+ message __method__
24
+ end
25
+ end
26
+
27
+ Application.run(LifeCycle1Window.new)
@@ -0,0 +1,15 @@
1
+ require 'fzeet'
2
+
3
+ Fzeet::Application.run do |window|
4
+ window.on :create do |args|
5
+ args[:result] = -1 if Fzeet.question('Create?').no?
6
+ end
7
+
8
+ window.on :close do
9
+ window.dispose if Fzeet.question('Close?', buttons: [:yes, :No]).yes?
10
+ end
11
+
12
+ window.on :destroy do
13
+ Fzeet.message 'on(:destroy)'
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ require 'fzeet'
2
+
3
+ include Fzeet
4
+
5
+ message 'Foo'
6
+ message 'Foo', caption: 'Bar', buttons: [:yes, :No], icon: :error
7
+ message message('Foo').ok?
8
+
9
+ question 'Foo'
10
+ question 'Foo', caption: 'Bar', buttons: [:yes, :No], icon: :exclamation
11
+ message question('Foo').no?
@@ -0,0 +1,5 @@
1
+ require 'fzeet'
2
+
3
+ include Fzeet
4
+
5
+ Application.run
@@ -0,0 +1,7 @@
1
+ require_relative 'common'
2
+
3
+ module Fzeet
4
+ class AcceleratorTable < Handle
5
+
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'common'
2
+
3
+ module Fzeet
4
+ class Application
5
+ @name = File.basename($0, '*.rbw')
6
+ @version = '0.1.0'
7
+ @authors = []
8
+
9
+ @window = nil
10
+
11
+ class << self
12
+ attr_accessor :name, :version, :authors
13
+ attr_reader :window
14
+ end
15
+
16
+ def self.run(window = nil, &block)
17
+ return 0 if (@window = window || Window.new(&block)).handle.null?
18
+
19
+ @window.on(:destroy) { Windows.PostQuitMessage(0) }
20
+
21
+ Windows.ShowWindow(@window.handle, Windows::SW_SHOWNORMAL)
22
+ Windows.UpdateWindow(@window.handle)
23
+
24
+ msg = Windows::MSG.new
25
+
26
+ while (get = Windows.GetMessage(msg, nil, 0, 0)) != 0
27
+ raise 'GetMessage failed.' if get == -1
28
+
29
+ Windows.TranslateMessage(msg)
30
+ Windows.DispatchMessage(msg)
31
+ end
32
+
33
+ msg[:wParam]
34
+ rescue
35
+ Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, icon: :error
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'common'
2
+
3
+ module Fzeet
4
+ class Control < Handle
5
+
6
+ end
7
+ end
data/lib/fzeet/Menu.rb ADDED
@@ -0,0 +1,15 @@
1
+ require_relative 'common'
2
+
3
+ module Fzeet
4
+ class MenuItem
5
+
6
+ end
7
+
8
+ class Menu < Handle
9
+
10
+ end
11
+
12
+ class PopupMenu < Handle
13
+
14
+ end
15
+ end
@@ -0,0 +1,138 @@
1
+ require_relative 'common'
2
+
3
+ module Fzeet
4
+ class Window < Handle
5
+ WindowProc = FFI::Function.new(:long, [:pointer, :uint, :uint, :long], convention: :stdcall) { |hwnd, uMsg, wParam, lParam|
6
+ begin
7
+ if uMsg == Windows::WM_NCCREATE
8
+ @@instances[hwnd.to_i] = ObjectSpace._id2ref(
9
+ Windows::CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
10
+ )
11
+
12
+ @@instances[hwnd.to_i].instance_variable_set(:@handle, hwnd)
13
+ end
14
+
15
+ result = @@instances[hwnd.to_i].windowProc(hwnd, uMsg, wParam, lParam) if @@instances[hwnd.to_i]
16
+ rescue
17
+ Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, icon: :error
18
+ ensure
19
+ if uMsg == Windows::WM_NCDESTROY
20
+ @@instances.delete(hwnd.to_i)
21
+ end
22
+ end
23
+
24
+ result || Windows.DefWindowProc(hwnd, uMsg, wParam, lParam)
25
+ }
26
+
27
+ WindowClass = Windows::WNDCLASSEX.new.tap { |wc|
28
+ wc[:cbSize] = wc.size
29
+ wc[:lpfnWndProc] = WindowProc
30
+ wc[:hInstance] = Windows.GetModuleHandle(nil)
31
+ wc[:hIcon] = Windows.LoadIcon(nil, Windows::IDI_APPLICATION)
32
+ wc[:hCursor] = Windows.LoadCursor(nil, Windows::IDC_ARROW)
33
+ wc[:hbrBackground] = FFI::Pointer.new(Windows::COLOR_WINDOW + 1)
34
+ wc[:lpszClassName] = className = FFI::MemoryPointer.from_string('Fzeet.Window')
35
+
36
+ Windows.DetonateLastError(0, :RegisterClassEx, wc) { className.free }
37
+ }
38
+
39
+ def self.crackMessage(hwnd, uMsg, wParam, lParam)
40
+ window = @@instances[hwnd.to_i]
41
+
42
+ args = {
43
+ hwnd: hwnd,
44
+ uMsg: uMsg,
45
+ wParam: wParam,
46
+ lParam: lParam,
47
+ result: 0,
48
+ window: window,
49
+ sender: window
50
+ }
51
+
52
+ case uMsg
53
+ when Windows::WM_CREATE
54
+ args[:cs] = Windows::CREATESTRUCT.new(FFI::Pointer.new(lParam))
55
+ when Windows::WM_NCCREATE
56
+ args[:result] = 1
57
+ args[:cs] = Windows::CREATESTRUCT.new(FFI::Pointer.new(lParam))
58
+ end
59
+
60
+ args
61
+ end
62
+
63
+ def initialize(opts = {})
64
+ _opts = {
65
+ xstyle: [],
66
+ caption: Application.name,
67
+ style: [:overlappedwindow, :clipchildren],
68
+ x: Windows::CW_USEDEFAULT,
69
+ y: Windows::CW_USEDEFAULT,
70
+ width: Windows::CW_USEDEFAULT,
71
+ height: Windows::CW_USEDEFAULT,
72
+ parent: nil,
73
+ menu: nil
74
+ }
75
+ badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
76
+ _opts.merge!(opts)
77
+
78
+ yield self, _opts if block_given?
79
+
80
+ _opts[:xstyle] = _opts[:xstyle].inject(0) { |flags, xstyle| flags |= Windows.const_get("WS_EX_#{xstyle.upcase}") }
81
+ _opts[:caption] = _opts[:caption].to_s
82
+ _opts[:style] = _opts[:style].inject(0) { |flags, style| flags |= Windows.const_get("WS_#{style.upcase}") }
83
+
84
+ @messages ||= {}
85
+
86
+ @processed = [0, 0]
87
+
88
+ @handle = Windows.CreateWindowEx(
89
+ _opts[:xstyle], 'Fzeet.Window', _opts[:caption], _opts[:style],
90
+ _opts[:x], _opts[:y], _opts[:width], _opts[:height],
91
+ _opts[:parent] && _opts[:parent].handle, nil, Windows.GetModuleHandle(nil), FFI::Pointer.new(object_id)
92
+ )
93
+
94
+ if @handle.null?
95
+ raise 'CreateWindowEx failed.' unless [
96
+ [Windows::WM_NCCREATE, 0], [Windows::WM_CREATE, -1],
97
+ [Windows::WM_DESTROY, 0], [Windows::WM_NCDESTROY, 0]
98
+ ].include?(@processed)
99
+ else
100
+ @parent = _opts[:parent]
101
+ self.menu = _opts[:menu] if _opts[:menu]
102
+ end
103
+ end
104
+
105
+ attr_reader :parent
106
+
107
+ def dispose; Windows.DestroyWindow(@handle) end
108
+
109
+ def windowProc(hwnd, uMsg, wParam, lParam)
110
+ args, result = nil, nil
111
+
112
+ if (handlers = @messages[uMsg])
113
+ args ||= self.class.crackMessage(hwnd, uMsg, wParam, lParam)
114
+
115
+ handlers.each { |handler|
116
+ handler.call(args)
117
+
118
+ result = args[:result]; @processed[0], @processed[1] = uMsg, result
119
+ }
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ def onMessage(msg, &block)
126
+ ((@messages ||= {})[Windows.const_get("WM_#{msg.upcase}")] ||= []) << block
127
+
128
+ self
129
+ end
130
+
131
+ def on(*args, &block)
132
+ case args.length
133
+ when 1; onMessage(*args, &block)
134
+ else raise ArgumentError
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'windows'
2
+
3
+ module Fzeet
4
+ class DialogResult
5
+ def initialize(id)
6
+ @id = id
7
+ end
8
+
9
+ attr_reader :id
10
+
11
+ Windows.constants.grep(/^ID[A-Z]{2,}$/).each { |c|
12
+ define_method("#{c[2..-1].downcase}?") {
13
+ @id == Windows.const_get(c)
14
+ }
15
+ }
16
+ end
17
+
18
+ def message(message, opts = {})
19
+ _opts = {
20
+ window: Application.window,
21
+ caption: Application.name,
22
+ buttons: [:ok],
23
+ icon: :information
24
+ }
25
+ badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
26
+ _opts.merge!(opts)
27
+
28
+ defbutton = nil
29
+ defbutton = if _opts[:buttons].find.with_index { |button, i| defbutton = i + 1; button =~ /^[A-Z]/ }
30
+ Windows.const_get("MB_DEFBUTTON#{defbutton}")
31
+ else
32
+ 0
33
+ end
34
+
35
+ flags = Windows.const_get("MB_#{_opts[:buttons].join('').upcase}") |
36
+ defbutton |
37
+ Windows.const_get("MB_ICON#{_opts[:icon].upcase}")
38
+
39
+ DialogResult.new(
40
+ Windows.MessageBox(_opts[:window] && _opts[:window].handle, message.to_s, _opts[:caption].to_s, flags)
41
+ )
42
+ end
43
+
44
+ def question(message, opts = {})
45
+ opts[:buttons] ||= [:yes, :no]
46
+ opts[:icon] ||= :question
47
+
48
+ message(message, opts)
49
+ end
50
+
51
+ module_function :message, :question
52
+
53
+ class Handle
54
+ @@instances = {}
55
+
56
+ def self.instance?(handle) @@instances.include?(handle.to_i) end
57
+ def self.instance(handle) raise "#{self}.instance failed." unless (instance = @@instances[handle.to_i]); instance end
58
+
59
+ def self.wrap(handle, iface)
60
+ Object.new.tap { |o|
61
+ o.instance_variable_set(:@handle, handle)
62
+ o.class.send(:attr_reader, :handle)
63
+ o.class.send(:include, iface)
64
+ }
65
+ end
66
+
67
+ attr_reader :handle
68
+
69
+ def attach; @@instances[@handle.to_i] = self end
70
+ def detach; @@instances.delete(@handle.to_i) end
71
+ end
72
+ end
@@ -5,6 +5,18 @@ module Fzeet
5
5
  ffi_lib 'kernel32'
6
6
  ffi_convention :stdcall
7
7
 
8
+ attach_function :GetLastError, [], :ulong
9
+
10
+ def DetonateLastError(on, name, *args)
11
+ raise "#{name} failed (last error #{GetLastError()})." if (failed = [*on].include?(result = send(name, *args)))
12
+
13
+ result
14
+ ensure
15
+ yield failed if block_given?
16
+ end
17
+
18
+ module_function :DetonateLastError
19
+
8
20
  class ACTCTX < FFI::Struct
9
21
  layout \
10
22
  :cbSize, :ulong,
@@ -5,6 +5,7 @@ module Fzeet
5
5
  ffi_lib 'user32'
6
6
  ffi_convention :stdcall
7
7
 
8
+ MB_OK = 0x00000000
8
9
  MB_OKCANCEL = 0x00000001
9
10
  MB_YESNOCANCEL = 0x00000003
10
11
  MB_YESNO = 0x00000004
@@ -59,6 +60,7 @@ module Fzeet
59
60
  WS_OVERLAPPED = 0x00000000
60
61
  WS_CHILD = 0x40000000
61
62
  WS_VISIBLE = 0x10000000
63
+ WS_CLIPCHILDREN = 0x02000000
62
64
  WS_CAPTION = 0x00C00000
63
65
  WS_SYSMENU = 0x00080000
64
66
  WS_THICKFRAME = 0x00040000
@@ -130,6 +132,8 @@ module Fzeet
130
132
  WM_DESTROY = 0x0002
131
133
  WM_CLOSE = 0x0010
132
134
  WM_SETFONT = 0x0030
135
+ WM_NCCREATE = 0x0081
136
+ WM_NCDESTROY = 0x0082
133
137
  WM_COMMAND = 0x0111
134
138
  WM_APP = 0x8000
135
139
 
data/lib/fzeet/windows.rb CHANGED
@@ -37,13 +37,13 @@ module Fzeet
37
37
  ac[:cbSize] = ac.size
38
38
  ac[:lpSource] = source = FFI::MemoryPointer.from_string(File.expand_path(manifest))
39
39
 
40
- raise 'CreateActCtx failed.' if (COMMON_CONTROLS_ACTCTX[:handle] = CreateActCtx(ac).tap { source.free }) == INVALID_HANDLE_VALUE
40
+ COMMON_CONTROLS_ACTCTX[:handle] = DetonateLastError(INVALID_HANDLE_VALUE, :CreateActCtx, ac) { source.free }
41
41
 
42
- if ActivateActCtx(COMMON_CONTROLS_ACTCTX[:handle], COMMON_CONTROLS_ACTCTX[:cookie]) == 0
43
- ReleaseActCtx(COMMON_CONTROLS_ACTCTX[:handle]); COMMON_CONTROLS_ACTCTX[:handle] = INVALID_HANDLE_VALUE
42
+ DetonateLastError(0, :ActivateActCtx, COMMON_CONTROLS_ACTCTX[:handle], COMMON_CONTROLS_ACTCTX[:cookie]) { |failed|
43
+ next unless failed
44
44
 
45
- raise 'ActivateActCtx failed.'
46
- end
45
+ ReleaseActCtx(COMMON_CONTROLS_ACTCTX[:handle]); COMMON_CONTROLS_ACTCTX[:handle] = INVALID_HANDLE_VALUE
46
+ }
47
47
 
48
48
  COMMON_CONTROLS_ACTCTX[:activated] = true
49
49
  end
data/lib/fzeet.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative 'fzeet/Window'
2
+ require_relative 'fzeet/Application'
3
+ require_relative 'fzeet/Menu'
4
+ require_relative 'fzeet/Accelerator'
5
+ require_relative 'fzeet/Control'
6
+
7
+ Fzeet::Windows.EnableVisualStyles
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fzeet
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Radoslav Peev
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-07 00:00:00 +03:00
18
+ date: 2010-08-09 00:00:00 +03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -48,11 +48,25 @@ files:
48
48
  - lib/fzeet/windows/kernel.rb
49
49
  - lib/fzeet/windows/libc.rb
50
50
  - lib/fzeet/windows/user.rb
51
+ - lib/fzeet/Accelerator.rb
52
+ - lib/fzeet/Application.rb
53
+ - lib/fzeet/common.rb
54
+ - lib/fzeet/Control.rb
55
+ - lib/fzeet/Menu.rb
56
+ - lib/fzeet/Window.rb
51
57
  - lib/fzeet/windows.rb
58
+ - lib/fzeet.rb
52
59
  - examples/Raw/Command.rbw
53
60
  - examples/Raw/Hello.rbw
54
61
  - examples/Raw/LifeCycle.rbw
55
62
  - examples/Raw/Minimal.rbw
63
+ - examples/Command.rbw
64
+ - examples/Hello.rbw
65
+ - examples/LifeCycle.rbw
66
+ - examples/LifeCycle1.rbw
67
+ - examples/LifeCycle2.rbw
68
+ - examples/MessageBox.rbw
69
+ - examples/Minimal.rbw
56
70
  - LICENSE
57
71
  has_rdoc: true
58
72
  homepage: