reactive_view_wx 0.1.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.
- 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)
|