reactive_view_wx 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +37 -0
- data/README.txt +19 -0
- data/Rakefile +16 -0
- data/lib/action_view_init.rb +13 -0
- data/lib/base.rb +59 -0
- data/lib/binder.rb +20 -0
- data/lib/helpers/model_edit_ctrl_class.rb +124 -0
- data/lib/helpers/model_list_ctrl_class.rb +82 -0
- data/lib/helpers/model_main_frame_class.rb +132 -0
- data/lib/helpers/model_show_ctrl_class.rb +35 -0
- data/lib/reactive_view_wx.rb +22 -0
- data/lib/request.rb +14 -0
- data/lib/template.rb +114 -0
- data/lib/version.rb +13 -0
- data/lib/xrc_pepper.rb +253 -0
- data/reactive_generators/application_view/USAGE +7 -0
- data/reactive_generators/application_view/application_view_generator.rb +49 -0
- data/reactive_generators/application_view/templates/application_helper.rb +40 -0
- data/reactive_generators/application_view/templates/application_layout.rb +2 -0
- data/reactive_generators/application_view/templates/application_view.rb +9 -0
- data/reactive_generators/application_view/templates/assets/delete16.png +0 -0
- data/reactive_generators/application_view/templates/assets/edit16.png +0 -0
- data/reactive_generators/application_view/templates/assets/show16.png +0 -0
- data/reactive_generators/application_view/templates/record_toolbar.rb +46 -0
- data/reactive_generators/view/USAGE +16 -0
- data/reactive_generators/view/templates/create.rb +1 -0
- data/reactive_generators/view/templates/delete.rb +3 -0
- data/reactive_generators/view/templates/destroy.rb +0 -0
- data/reactive_generators/view/templates/edit.rb +8 -0
- data/reactive_generators/view/templates/index.rb +4 -0
- data/reactive_generators/view/templates/layout.rb +2 -0
- data/reactive_generators/view/templates/new.rb +8 -0
- data/reactive_generators/view/templates/show.rb +4 -0
- data/reactive_generators/view/templates/update.rb +1 -0
- data/reactive_generators/view/view_generator.rb +69 -0
- 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
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
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)
|