reactive_view_wx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/History.txt +3 -0
  2. data/MIT-LICENSE +21 -0
  3. data/Manifest.txt +37 -0
  4. data/README.txt +19 -0
  5. data/Rakefile +16 -0
  6. data/lib/action_view_init.rb +13 -0
  7. data/lib/base.rb +59 -0
  8. data/lib/binder.rb +20 -0
  9. data/lib/helpers/model_edit_ctrl_class.rb +124 -0
  10. data/lib/helpers/model_list_ctrl_class.rb +82 -0
  11. data/lib/helpers/model_main_frame_class.rb +132 -0
  12. data/lib/helpers/model_show_ctrl_class.rb +35 -0
  13. data/lib/reactive_view_wx.rb +22 -0
  14. data/lib/request.rb +14 -0
  15. data/lib/template.rb +114 -0
  16. data/lib/version.rb +13 -0
  17. data/lib/xrc_pepper.rb +253 -0
  18. data/reactive_generators/application_view/USAGE +7 -0
  19. data/reactive_generators/application_view/application_view_generator.rb +49 -0
  20. data/reactive_generators/application_view/templates/application_helper.rb +40 -0
  21. data/reactive_generators/application_view/templates/application_layout.rb +2 -0
  22. data/reactive_generators/application_view/templates/application_view.rb +9 -0
  23. data/reactive_generators/application_view/templates/assets/delete16.png +0 -0
  24. data/reactive_generators/application_view/templates/assets/edit16.png +0 -0
  25. data/reactive_generators/application_view/templates/assets/show16.png +0 -0
  26. data/reactive_generators/application_view/templates/record_toolbar.rb +46 -0
  27. data/reactive_generators/view/USAGE +16 -0
  28. data/reactive_generators/view/templates/create.rb +1 -0
  29. data/reactive_generators/view/templates/delete.rb +3 -0
  30. data/reactive_generators/view/templates/destroy.rb +0 -0
  31. data/reactive_generators/view/templates/edit.rb +8 -0
  32. data/reactive_generators/view/templates/index.rb +4 -0
  33. data/reactive_generators/view/templates/layout.rb +2 -0
  34. data/reactive_generators/view/templates/new.rb +8 -0
  35. data/reactive_generators/view/templates/show.rb +4 -0
  36. data/reactive_generators/view/templates/update.rb +1 -0
  37. data/reactive_generators/view/view_generator.rb +69 -0
  38. metadata +146 -0
@@ -0,0 +1,35 @@
1
+ require 'wx_sugar'
2
+
3
+ module Reactive::View::Wx
4
+ module Helpers
5
+
6
+ class ModelShowCtrl < Wx::Panel
7
+ attr_reader :model
8
+
9
+ def selected_records
10
+ [@record]
11
+ end
12
+
13
+ def initialize(parent, record, options = {})
14
+ super(parent, options)
15
+ @model, @record = record.class, record
16
+
17
+ fill_controls(record)
18
+ end
19
+
20
+ protected
21
+
22
+ def fill_controls(record)
23
+ arrange_grid(:cols => 2, :padding => 5, :proportion => 1) {|sizer| sizer.add_growable_col(1, 1)}
24
+ read_only = Wx::SystemSettings.get_colour(Wx::SYS_COLOUR_BTNFACE)
25
+ @model.content_columns.each do |column|
26
+ nest(Wx::StaticText.new(self, :label => "#{column.human_name} :"), :pad => 'ALIGN_CENTER_VERTICAL,ALIGN_RIGHT')
27
+ nest(ctrl = Wx::TextCtrl.new(self, :value => record.send(column.name).to_s, :style => Wx::TE_READONLY), :proportion => 1, :align => 'CENTER_VERTICAL')
28
+ ctrl.set_background_colour(read_only)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ require 'activesupport'
2
+ require 'reactive'
3
+ require 'wx'
4
+
5
+ #require 'wx_pepper'
6
+ require 'xrc_pepper'
7
+
8
+ require 'request'
9
+ require 'binder'
10
+ require 'template'
11
+ require 'base'
12
+
13
+ Wx::EvtHandler.send(:include, Reactive::View::Wx::Binder)
14
+
15
+ # Let the controller know our temlate class
16
+ #Reactive::Controller::Base.template_class = Reactive::View::Wx::Template
17
+
18
+ # We want the ::Wx namespace to be directly available for our templates. Since ActionView compiles the template
19
+ # and hosts the compiled code into the ActionView::Base::CompiledTemplates, we have to inject ::Wx there.
20
+ ActionView::Base::CompiledTemplates.send(:include, ::Wx)
21
+
22
+ Reactive::View::Wx::Template.load_helpers
data/lib/request.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'reactive'
2
+
3
+ module Reactive
4
+ module View
5
+ module Wx
6
+
7
+ class Request < Reactive::Request
8
+ attr_accessor :local_assigns
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+
data/lib/template.rb ADDED
@@ -0,0 +1,114 @@
1
+ require 'action_view_init'
2
+
3
+ module Reactive
4
+ module View
5
+ module Wx
6
+
7
+ class RubyCodeTemplateHandler < ActionView::TemplateHandler
8
+ def compile(template)
9
+ template
10
+ end
11
+ end
12
+
13
+ class Template < ActionView::Base
14
+ register_default_template_handler(:rb, RubyCodeTemplateHandler)
15
+
16
+ def self.load_helpers #:nodoc:
17
+ # First load classes (all hosted under the Helper module)
18
+ Dir.entries("#{File.dirname(__FILE__)}/helpers").sort.each do |file|
19
+ next unless file =~ /^([a-z][a-z_]*_class|_classes).rb$/
20
+ require "helpers/#{$1}"
21
+ end
22
+ include Helpers
23
+ ActionView::Base::CompiledTemplates.send(:include, Helpers)
24
+ # Then helper modules
25
+ Dir.entries("#{File.dirname(__FILE__)}/helpers").sort.each do |file|
26
+ next unless file =~ /^([a-z][a-z_]*_helper).rb$/
27
+ require "helpers/#{$1}"
28
+ helper_module_name = $1.camelize
29
+ if Helpers.const_defined?(helper_module_name)
30
+ include Helpers.const_get(helper_module_name)
31
+ ActionView::Base::CompiledTemplates.send(:include, Helpers.const_get(helper_module_name))
32
+ end
33
+ end
34
+ end
35
+
36
+ # ActionView returns :html by default, we don't have any
37
+ def template_format
38
+ @template_format ||= :rb
39
+ end
40
+
41
+ def do_request(hash)
42
+ Base.do_request(hash)
43
+ end
44
+
45
+ # We accept the rendering of a plain file (not a partial) that lies in the current view directory
46
+ # For this, simply pass the name of the view like: render "create.xrc"
47
+ def render(options = {}, old_local_assigns = {}, &block)
48
+ if options.is_a?(String) && !options.include?('/')
49
+ super(File.join(controller.class.controller_path, options), old_local_assigns, &block)
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # handles format specification in the template_path.
56
+ # So it is possible to say: render "show.xrc", meaning XRC is the format and any available handler will
57
+ # be used. So the file may either be "show.xrc.erb" or "show.src.builder" or any custum registered handler.
58
+ def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc:
59
+ if controller && controller.request.respond_to?(:local_assigns) && controller.request.local_assigns
60
+ local_assigns.merge!(controller.request.local_assigns)
61
+ end
62
+
63
+ template_path_without_format, template_format = path_and_extension(template_path)
64
+ if template_format && !File.basename(template_path_without_format).include?('.')
65
+ @template_format = template_format.to_sym
66
+ super(template_path_without_format, use_full_path, local_assigns)
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ # The result of the rendering is used to create a Wx object (except when the format is :rb which means it is code)
73
+ def render_template(template_extension, template, file_path = nil, local_assigns = {}) #:nodoc:
74
+ output = super
75
+ if template_format == :xrc
76
+ template_key = file_path || template
77
+ parent = local_assigns[:parent_window]
78
+ load_xrc(template_key, output, parent)
79
+ else
80
+ output
81
+ end
82
+ end
83
+
84
+ # The controller may extend this object, especially with helpers
85
+ # Because we have compiled templates, when rendering such templates they
86
+ # are hosted in the ActionView::Base::CompiledTemplates module.
87
+ # This becomes a problem when a compiled template wants to access helper constants,
88
+ # they are hosted under XXXHelper and also in the controller master_helper_module.
89
+ # This method will also include the passed module into CompiledTemplates, so that everything
90
+ # is accessable. Note that this may break the class unloading feature for development mode.
91
+ def extend(*modules)
92
+ ActionView::Base::CompiledTemplates.send(:include, *modules)
93
+ super
94
+ end
95
+
96
+ private
97
+
98
+ def load_xrc(template_key, data, parent)
99
+ return nil # Not implemented.... not on my priority list
100
+ Xrc.instance.unload_object(template_key)
101
+ Xrc.instance.load_data(template_key, data)
102
+ each_toplevel_object(data) do |klass, name|
103
+ Xrc.instance.load_object(parent, name, klass)
104
+ end
105
+ end
106
+
107
+ def each_toplevel_object(xml)
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+ end
114
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Reactive
2
+ module View
3
+ module Wx
4
+ module VERSION #:nodoc:
5
+ MAJOR = 0
6
+ MINOR = 1
7
+ TINY = 0
8
+
9
+ STRING = [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/xrc_pepper.rb ADDED
@@ -0,0 +1,253 @@
1
+ #
2
+ # wxRuby extensions for XRC
3
+ #
4
+ # Author: Pascal Hurni
5
+ # License: MIT
6
+ #
7
+
8
+ require 'wx'
9
+ require 'wx_sugar/event_connector' # for listen()
10
+ require 'singleton'
11
+
12
+ #
13
+ # Access to Window children
14
+ # Injected into Wx::Window
15
+ #
16
+
17
+ module WindowEnumerable
18
+ include Enumerable
19
+
20
+ # ruby way to iterate over children
21
+ def each
22
+ get_children.each {|item| yield item}
23
+ end
24
+
25
+ # iterate over children and all descendant
26
+ def each_descendant(&block)
27
+ each {|item| block.call(item); item.each_descendant(block) }
28
+ end
29
+
30
+ # access children window by name directly as attribute reader
31
+ def method_missing(name, *args)
32
+ if [:ok, :cancel, :yes, :no].include?(name)
33
+ win = Wx::Window.find_window_by_name("wxID_#{name.to_s.upcase}", self)
34
+ return win if win
35
+ end
36
+ Wx::Window.find_window_by_name(name.to_s, self) || super
37
+ end
38
+ end
39
+
40
+ module MenuBarEnumerable
41
+ include Enumerable
42
+
43
+ def each
44
+ index = 0
45
+ while index < get_menu_count
46
+ yield get_menu(index)
47
+ index += 1
48
+ end
49
+ end
50
+
51
+ # find a menu_item in any of the bar's menu
52
+ def method_missing(name, *args)
53
+ each do |menu|
54
+ item = menu.find_item_by_name(name)
55
+ return item if item
56
+ end
57
+ super
58
+ end
59
+ end
60
+
61
+ module MenuEnumerable
62
+ include Enumerable
63
+
64
+ def each
65
+ index = 0
66
+ while index < get_menu_item_count
67
+ yield find_item_by_position(index)
68
+ index += 1
69
+ end
70
+ end
71
+
72
+ def find_item_by_name(name)
73
+ item = find_item(Wx::xrcid(name.to_s))
74
+ item == Wx::NOT_FOUND ? nil : item
75
+ end
76
+
77
+ def method_missing(name, *args)
78
+ find_item_by_name(name.to_s) || super
79
+ end
80
+ end
81
+
82
+ module AuiManagerEnumerable
83
+ include Enumerable
84
+
85
+ # alias_method :each, :each_pane
86
+
87
+ def method_missing(name, *args)
88
+ pi = get_pane(name.to_s)
89
+ pi.ok? ? pi : super
90
+ end
91
+ end
92
+
93
+
94
+ #
95
+ # Easily bind method named after this pattern: on_#{control_name}_#{event_name}
96
+ # to the corresponding event.
97
+ # Injected into Wx::EvtHandler
98
+ #
99
+ module EventBinder
100
+
101
+ def load_and_bind(options = {})
102
+ options[:name] ||= self.class.to_s
103
+ options[:class] ||= "wx" + self.class.ancestors.find {|ancestor| ancestor.to_s =~ /Wxruby2::/}.to_s.split("Wxruby2::")[1]
104
+ Xrc.instance.bind_object_subclass(self, options[:parent], options[:name], options[:class])
105
+
106
+ # needed anymore? Xrc.instance.bind_menu_bar(self, options[:menu]) if options[:menu]
107
+ end
108
+
109
+ #
110
+ # A little more rubyesque version of wxSugar#listen method
111
+ # Pass the window (control) to be bound and then a hash with event name as keys and method name as value
112
+ # (You may also pass a Proc as value)
113
+ #
114
+ def _bind(window, events)
115
+ events.each do |event, method|
116
+ case method
117
+ when Symbol
118
+ listen(event, window, method)
119
+ when Proc
120
+ listen(event, window, nil, &method)
121
+ end
122
+ end
123
+ end
124
+
125
+ def bind_standard_buttons
126
+ # bind the close button (something like a cross on top-right corner)
127
+ if respond_to? :on_close
128
+ arity = method(:on_close).arity
129
+ evt_close {|event| arity == 0 ? on_close : on_close(event)}
130
+ end
131
+
132
+ # bind the buttons with standard wxID_*
133
+ select{|item| item.kind_of?(Wx::Button) && item.name[0..4] == "wxID_"}.each do |button|
134
+ method_name = "on_#{button.name[5..-1].downcase}"
135
+ arity = method(method_name).arity
136
+ evt_button(button.get_id) {|event| arity == 0 ? send(method_name) : send(method_name, event)} if respond_to? method_name
137
+ end
138
+ end
139
+
140
+ def bind_events
141
+ # get all methods that are named 'on_*'
142
+ methods = self.methods.select{|method| method[0..2] == "on_" }
143
+
144
+ # for each control in this window
145
+ each do |control|
146
+ # iterate over the methods that target the current control (named 'on_#{control.name}_*')
147
+ methods.grep(Regexp.new("^on_#{control.name}_(.*)")) {|method| [$1,method]}.each do |event,method|
148
+ # bind the found method with the event
149
+ listen(event, control, method)
150
+ end
151
+ end
152
+ end
153
+
154
+ def bind_menus
155
+ # get all methods that are named 'on_*_menu'
156
+ self.methods.grep(/^on_(.*)_menu$/) {|method| [$1,method]}.each do |name,method|
157
+ # bind the found method with the event
158
+ evt_menu(Wx::xrcid(name), method)
159
+ end
160
+ end
161
+
162
+ end
163
+
164
+ =begin obsolete
165
+ #
166
+ # XrcDialog
167
+ # subclass your dialog from this class instead of Wx::Dialog
168
+ # so that button events are automatically bound to methods +on_XXX+ where XXX is the button name
169
+ #
170
+ class XrcFrame < Wx::Frame
171
+ def initialize(*args)
172
+ super
173
+ Xrc.instance.bind_frame_subclass(self)
174
+ end
175
+ end
176
+
177
+ class XrcDialog < Wx::Dialog
178
+ def initialize(*args)
179
+ super
180
+ Xrc.instance.bind_dialog_subclass(self)
181
+ end
182
+ end
183
+ =end
184
+
185
+ #
186
+ # Class to handle XRC
187
+ # This is a Singleton, so access it from everywhere with Xrc.instance
188
+ #
189
+ class Xrc < Wx::XmlResource
190
+ include ::Singleton
191
+
192
+ def initialize
193
+ super
194
+ init_all_handlers
195
+ end
196
+
197
+ def bind_object_subclass(win, parent = nil, name = win.class.to_s, klass = "wx#{win.class.to_s}")
198
+ raise StandardError unless load_object(win, parent, name, klass)
199
+ bind(win)
200
+ true
201
+ end
202
+
203
+ def bind_dialog_subclass(win, parent = nil, name = win.class.to_s)
204
+ raise StandardError unless load_dialog_subclass(win, parent, name)
205
+ bind(win)
206
+ true
207
+ end
208
+
209
+ def bind_frame_subclass(win, parent = nil, name = win.class.to_s)
210
+ raise StandardError unless load_frame_subclass(win, parent, name)
211
+ bind(win)
212
+ true
213
+ end
214
+
215
+ def bind_menu_bar(win, name)
216
+ load_menu_bar(win, name)
217
+ win.send(:bind_menus)
218
+ end
219
+
220
+ protected
221
+ def bind(win)
222
+ win.send(:bind_events)
223
+ win.send(:bind_standard_buttons)
224
+ win.send(:bind_menus)
225
+ end
226
+
227
+ end
228
+
229
+ module Accessors
230
+ module ControlWithItems
231
+ def value
232
+ val = get_item_data(get_selection)
233
+ val.kind_of?(ActiveRecord::Base) ? val.id : val
234
+ end
235
+ end
236
+ end
237
+
238
+
239
+
240
+ # WARNING: module WxRubyStyleAccessors is a mixin which also redefines method_missing. So it catches first.
241
+ # Thus, we first extend Dialog so that our sugar is before in the chain!
242
+ Wx::Dialog.send(:include, WindowEnumerable)
243
+ Wx::Panel.send(:include, WindowEnumerable)
244
+ Wx::Frame.send(:include, WindowEnumerable)
245
+ Wx::Window.send(:include, WindowEnumerable)
246
+
247
+ Wx::MenuBar.send(:include, MenuBarEnumerable)
248
+ Wx::Menu.send(:include, MenuEnumerable)
249
+
250
+ # Our EventBinder is useful for any class that has events
251
+ Wx::EvtHandler.send(:include, EventBinder)
252
+
253
+ Wx::ControlWithItems.send(:include, Accessors::ControlWithItems)