fzeet 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: