wx_sugar 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/wx_sugar/accessors.rb +44 -0
- data/lib/wx_sugar/all.rb +56 -0
- data/lib/wx_sugar/class_definitions.rb +114 -0
- data/lib/wx_sugar/delayed_constructors.rb +25 -0
- data/lib/wx_sugar/event_connector.rb +151 -0
- data/lib/wx_sugar/itemdata.rb +80 -0
- data/lib/wx_sugar/keyword_classes.rb +392 -0
- data/lib/wx_sugar/keyword_constructors.rb +268 -0
- data/lib/wx_sugar/layout.rb +231 -0
- data/lib/wx_sugar/menu.rb +186 -0
- metadata +46 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
# = WxSugar - Accessors
|
2
|
+
#
|
3
|
+
# The default WxRuby interface has lots and lots of methods like
|
4
|
+
#
|
5
|
+
# * get_position()
|
6
|
+
# * set_size(a_size)
|
7
|
+
# * is_checked()
|
8
|
+
#
|
9
|
+
# and so on. Methods that retrieve set, or query attributes of an object
|
10
|
+
# are more normally in Ruby called simply by the attribute name:
|
11
|
+
#
|
12
|
+
# * position()
|
13
|
+
# * size = a_size
|
14
|
+
# * checked?
|
15
|
+
#
|
16
|
+
# This extension creates an alias for every WxRuby instance method that
|
17
|
+
# begins with +get_+, +set_+ or +is_+. Note that if you are calling a
|
18
|
+
# 'setter' method on self, you must explicitly send the message to self:
|
19
|
+
#
|
20
|
+
# # set's self size to be 100px by 100px
|
21
|
+
# self.size = Wx::Size.new(100, 100)
|
22
|
+
# # only sets the value of a local variable 'size'
|
23
|
+
# size = Wx::Size.new
|
24
|
+
|
25
|
+
require 'wx_sugar/class_definitions.rb'
|
26
|
+
|
27
|
+
module NiceRubyMethodNames
|
28
|
+
def self.included(klass)
|
29
|
+
klass.class_eval do
|
30
|
+
instance_methods.grep(/^([gs]et|is)_(\w+)/) do | meth |
|
31
|
+
prefix, basename = $1, $2
|
32
|
+
case prefix
|
33
|
+
when 'get' : alias_method(basename, meth)
|
34
|
+
when 'set' : alias_method("#{basename}=", meth)
|
35
|
+
when 'is' : alias_method("#{basename}?", meth)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
WxSugar::ALL_CLASSES.each do | klass |
|
43
|
+
klass.class_eval { include NiceRubyMethodNames }
|
44
|
+
end
|
data/lib/wx_sugar/all.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# = WxSugar
|
2
|
+
#
|
3
|
+
# WxSugar is a set of additions to the WxRuby API, written in pure
|
4
|
+
# ruby. They're intended to:
|
5
|
+
#
|
6
|
+
# * Make some tricky WxWidgets things easier and more consistent
|
7
|
+
# * Reduce repetitiveness and redundancy for common tasks
|
8
|
+
# * Make code more expressive
|
9
|
+
# * Make code more idiomatically Rubyish
|
10
|
+
#
|
11
|
+
# Not everyone will like all the extensions to WxRuby here. So the
|
12
|
+
# Sugar modules:
|
13
|
+
#
|
14
|
+
# * Can generally be used separately, without depending on each other
|
15
|
+
# * Supplement rather than replace the underlying 'raw' API
|
16
|
+
#
|
17
|
+
# == Using WxSugar
|
18
|
+
#
|
19
|
+
# The extensions can currently be used with either WxRuby (0.6.0) or
|
20
|
+
# WxRuby2 (the SWIG version). To use all of the extensions, simple write
|
21
|
+
#
|
22
|
+
# # EITHER ... New development
|
23
|
+
# require 'wx'
|
24
|
+
# # OR ... old non-SWIG beta veresion
|
25
|
+
# require 'wxruby'
|
26
|
+
#
|
27
|
+
# require 'wx_sugar/all'
|
28
|
+
# ...
|
29
|
+
#
|
30
|
+
# If you only want to load specific behaviours, just +require+ the
|
31
|
+
# desired extensions only
|
32
|
+
#
|
33
|
+
# require 'wx_sugar/keyword_constructors'
|
34
|
+
# require 'wx_sugar/acccessors'
|
35
|
+
#
|
36
|
+
# == Overview of extensions
|
37
|
+
#
|
38
|
+
# [accessors.rb]
|
39
|
+
# Provide ruby-style getters, setters and question-mark methods
|
40
|
+
# [delayed_constructors.rb]
|
41
|
+
# For use with +layout+, limited independent interest
|
42
|
+
# [event_connector.rb]
|
43
|
+
# Neater syntax for connecting event listeners
|
44
|
+
# [itemdata.rb]
|
45
|
+
# Associate ruby objects with items in controls *NEEDS UPDATE*
|
46
|
+
# [keyword_constructors.rb]
|
47
|
+
# Use keyword-style hash arguments to construct widgets
|
48
|
+
# [layout.rb]
|
49
|
+
# Easy interface to using WxWidgets Sizers
|
50
|
+
# [menu.rb]
|
51
|
+
# Create and update menus without a mess of system ids
|
52
|
+
|
53
|
+
%w[ accessors delayed_constructors event_connector
|
54
|
+
itemdata keyword_constructors layout menu ].each do | ext_feature |
|
55
|
+
require 'wx_sugar/' + ext_feature
|
56
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# The WxExtensions defines a number of contants which are groups of available
|
2
|
+
# WxRuby classes.
|
3
|
+
#
|
4
|
+
# ALL_CLASSES : As it sounds
|
5
|
+
# FRAME_CLASSES : All available WxWidgets that are standalone frames
|
6
|
+
# DIALOG_CLASSES : All available dialog classes
|
7
|
+
# MISC_WINDOW_CLASSES : Things that are displayed, but aren't 'controls'
|
8
|
+
# CONTROL_CLASSES : Controls that accept user input
|
9
|
+
# SIZER_CLASSES : Sizers
|
10
|
+
|
11
|
+
module WxSugar
|
12
|
+
# helper function to check what widgets are available
|
13
|
+
ALL_CLASSES = Wx::constants.collect { | c | Wx::const_get(c) }.grep(Class)
|
14
|
+
|
15
|
+
# named constant groups
|
16
|
+
def self.define_class_group(const_name, *possibles)
|
17
|
+
klasses = []
|
18
|
+
possibles.each do | poss |
|
19
|
+
begin
|
20
|
+
klasses << Wx.const_get(poss)
|
21
|
+
rescue NameError
|
22
|
+
next
|
23
|
+
end
|
24
|
+
end
|
25
|
+
const_set(const_name, klasses)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
WxSugar.define_class_group('FRAME_CLASSES',
|
30
|
+
'TopLevelWindow',
|
31
|
+
'Dialog',
|
32
|
+
'Frame',
|
33
|
+
'MDIChildFrame',
|
34
|
+
'MDIParentFrame',
|
35
|
+
'MiniFrame',
|
36
|
+
'SplashScreen',
|
37
|
+
'PropertySheetDialog',
|
38
|
+
'TipWindow',
|
39
|
+
'Wizard' )
|
40
|
+
|
41
|
+
WxSugar.define_class_group('MISC_WINDOW_CLASSES',
|
42
|
+
'Panel',
|
43
|
+
'ScrolledWindow',
|
44
|
+
'Grid',
|
45
|
+
'SplitterWindow',
|
46
|
+
'StatusBar',
|
47
|
+
'ToolBar',
|
48
|
+
'Notebook',
|
49
|
+
'Listbook',
|
50
|
+
'Choicebook',
|
51
|
+
'SashWindow',
|
52
|
+
'SashLayoutWindow',
|
53
|
+
'VScrolledWindow',
|
54
|
+
'WizardPage',
|
55
|
+
'WizardPageSimple'
|
56
|
+
)
|
57
|
+
|
58
|
+
WxSugar.define_class_group('CONTROL_CLASSES',
|
59
|
+
'Control',
|
60
|
+
'Button',
|
61
|
+
'BitmapButton',
|
62
|
+
'ToggleButton',
|
63
|
+
'CalendarCtrl',
|
64
|
+
'CheckBox',
|
65
|
+
'CheckListBox',
|
66
|
+
'Choice',
|
67
|
+
'ComboBox',
|
68
|
+
'DatePickerCtrl',
|
69
|
+
'Gauge',
|
70
|
+
'GenericDirCtrl',
|
71
|
+
'HtmlListBox',
|
72
|
+
'StaticBox',
|
73
|
+
'ListBox',
|
74
|
+
'ListCtrl',
|
75
|
+
'ListView',
|
76
|
+
'TextCtrl',
|
77
|
+
'TreeCtrl',
|
78
|
+
'ScrollBar',
|
79
|
+
'SpinButton',
|
80
|
+
'SpinCtrl',
|
81
|
+
'StaticText',
|
82
|
+
'StaticBitmap',
|
83
|
+
'RadioBox',
|
84
|
+
'RadioButton',
|
85
|
+
'Slider',
|
86
|
+
'VListBox'
|
87
|
+
)
|
88
|
+
|
89
|
+
# TODO
|
90
|
+
WxSugar::define_class_group('DIALOG_CLASSES',
|
91
|
+
'Dialog', # Base class for common dialogs
|
92
|
+
'wxColourDialog', # Colour chooser dialog
|
93
|
+
'DirDialog', # Directory selector dialog
|
94
|
+
'FileDialog', # File selector dialog
|
95
|
+
'FindReplaceDialog', # Search/replace dialog
|
96
|
+
'MultiChoiceDialog', # Dialog to get one or more selections from a list
|
97
|
+
'SingleChoiceDialog',# Dialog to get a single selection from a list and return the string
|
98
|
+
'TextEntryDialog', # Dialog to get a single line of text from the user
|
99
|
+
'PasswordEntryDialog', # Get a password from the user
|
100
|
+
'FontDialog', # Font chooser dialog
|
101
|
+
'PageSetupDialog', # Standard page setup dialog
|
102
|
+
'PrintDialog', # Standard print dialog
|
103
|
+
'MessageDialog', # Simple message box dialog
|
104
|
+
'Wizard' # A wizard dialog.
|
105
|
+
)
|
106
|
+
|
107
|
+
WxSugar::define_class_group('SIZER_CLASSES',
|
108
|
+
'Sizer', # Abstract base class
|
109
|
+
'GridSizer', # A sizer for laying out windows in a grid with all fields having the same size
|
110
|
+
'FlexGridSizer', # A sizer for laying out windows in a flexible grid
|
111
|
+
'BoxSizer', # A sizer for laying out windows in a row or column
|
112
|
+
'StaticBoxSizer', # Same as wxBoxSizer, but with a surrounding static box
|
113
|
+
'NotebookSizer' # Sizer to use with the 'Notebook', # control
|
114
|
+
)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'wx_sugar/class_definitions'
|
2
|
+
|
3
|
+
win_classes = [ WxSugar::FRAME_CLASSES,
|
4
|
+
WxSugar::DIALOG_CLASSES,
|
5
|
+
WxSugar::MISC_WINDOW_CLASSES,
|
6
|
+
WxSugar::CONTROL_CLASSES ].flatten
|
7
|
+
|
8
|
+
# = Wx Extensions - Delayed Constructors
|
9
|
+
#
|
10
|
+
# Intended mainly for use with the +layout+ extension, adds additional
|
11
|
+
# class methods to all Window classes that return a proc that can be
|
12
|
+
# called later to attach a Window created with the specified args to a
|
13
|
+
# parent.
|
14
|
+
win_classes.flatten.each do | klass |
|
15
|
+
# Returns a Proc object that when called with a single argument,
|
16
|
+
# +parent+, will return a new widget constructed according to +args+
|
17
|
+
def klass.[](*args)
|
18
|
+
lambda { | parent | new(parent, *args) }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Alias for klass.[]
|
22
|
+
def klass.child(*args)
|
23
|
+
lambda { | parent | new(parent, *args) }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# = EventConnector
|
2
|
+
#
|
3
|
+
# This module is meant to make it easier to connect ruby methods to
|
4
|
+
# WxWidgets events generated by user interaction. It introduces a
|
5
|
+
# consistent syntax for linking events to handlers, using the +listen+
|
6
|
+
# method:
|
7
|
+
#
|
8
|
+
# listen(evt, source, handler) { block }
|
9
|
+
#
|
10
|
+
# The parameter +evt+ is the type of event that should be listened for,
|
11
|
+
# for example the click of a button. To ask to listen to 'click' events,
|
12
|
+
# pass the argument +:click+
|
13
|
+
#
|
14
|
+
# The parameter +source+ is the widget whose events should be listened
|
15
|
+
# for. This might be a particular button. If no +source+ is specified,
|
16
|
+
# and lots of widgets might generate this kind of event (for example, if
|
17
|
+
# you have multiple buttons), it assumes that *ANY* button's clicking
|
18
|
+
# should be handled.
|
19
|
+
#
|
20
|
+
# The final part of dealing with user events is specifying what should
|
21
|
+
# be done when the event occurs. One way is to pass the +handler+
|
22
|
+
# argument, which should be the name of a method (as a string or symbol)
|
23
|
+
# which will handle the event. The other way is to pass a block which
|
24
|
+
# should be called when the event is triggered.
|
25
|
+
#
|
26
|
+
# This handling method or block may, optionally, accept a single
|
27
|
+
# argument, which will be the Event object generated by the event.
|
28
|
+
#
|
29
|
+
# == Explanation
|
30
|
+
#
|
31
|
+
# This module is meant to take some of the pain out of hooking up event
|
32
|
+
# handlers to objects. There are subtle differences in WxWidgets in how
|
33
|
+
# different types of events are passed to widgets. Some events
|
34
|
+
# (typically those fired by controls, subclasses of +CommandEvent+) are
|
35
|
+
# passed upwards to any containing window that is interested. Others,
|
36
|
+
# WxWidgets assumes, are only of interest to the window that generates
|
37
|
+
# the event: most events related to frames (ActivateEvent, for example)
|
38
|
+
# and miscellaneous windows (Sashes, ScrollWindows) fall under this
|
39
|
+
# category.
|
40
|
+
module EventConnector
|
41
|
+
module ClassMethods
|
42
|
+
def event_hooks()
|
43
|
+
@__ehooks__ ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
# look out for on_xxx methods being added, so they can be hooked up
|
47
|
+
# automatically when instances of this widget are created
|
48
|
+
def method_added(name)
|
49
|
+
if name.to_s =~ /^on_(.*)/
|
50
|
+
event_hooks[$1.intern] = name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.included(klass)
|
56
|
+
klass.extend ClassMethods
|
57
|
+
# need a general way to safely interpose in Module methods
|
58
|
+
end
|
59
|
+
|
60
|
+
# Listen to events of type +evt+ (eg. 'click', 'mousedown'), on the
|
61
|
+
# widget +source+, and handle it by +handler+ or +block+
|
62
|
+
# The handling code can be specified in a number of different ways:
|
63
|
+
# If neither +handler+ or +block+ is passed, then the method will
|
64
|
+
# attempt to attach events to a listener method called "on_EVT", where
|
65
|
+
# EVT is 'click', 'mousedown' etc).
|
66
|
+
#
|
67
|
+
# listen(:click, my_button) # assumes on_click is defined
|
68
|
+
#
|
69
|
+
# If handler is specified as a symbol, the listener will call this
|
70
|
+
# named method when the event is triggered
|
71
|
+
#
|
72
|
+
# listen(:click, my_button, :on_click_my_button)
|
73
|
+
#
|
74
|
+
# If a block is passed, it will be run when the event is triggered.
|
75
|
+
#
|
76
|
+
# listen(:click, my_button) { p "click my button" }
|
77
|
+
#
|
78
|
+
# Methods or blocks that are defined as handlers may optionally
|
79
|
+
# receive one argument, the Wx event object (see the WxWidgets documentation
|
80
|
+
# for more details on this object):
|
81
|
+
#
|
82
|
+
# listen(:checkbox, my_checkbox) { | e | puts e.is_checked }
|
83
|
+
#
|
84
|
+
# Or in a handler method:
|
85
|
+
#
|
86
|
+
# def on_checkbox(e)
|
87
|
+
# if e.is_checked
|
88
|
+
# puts "The checkbox is now checked"
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
def listen(evt, source = nil, handler = nil, &block)
|
93
|
+
# get the WxWidget evt_xxx method that will be called for binding
|
94
|
+
event = "evt_#{evt}"
|
95
|
+
if self.respond_to?(event)
|
96
|
+
# try to bind to recipient's methods
|
97
|
+
evt_meth = method(event)
|
98
|
+
elsif source and source.respond_to?(event)
|
99
|
+
evt_meth = source.method(event)
|
100
|
+
else
|
101
|
+
Kernel.raise NameError,
|
102
|
+
"Widget #{self} doesn't generate events of type '#{evt}'"
|
103
|
+
end
|
104
|
+
|
105
|
+
# get the block or method that will handle the event
|
106
|
+
begin
|
107
|
+
if block
|
108
|
+
handler_meth = block
|
109
|
+
elsif handler
|
110
|
+
handler_meth = self.method(handler)
|
111
|
+
else
|
112
|
+
handler ||= "on_#{evt}"
|
113
|
+
handler_meth = self.method(handler)
|
114
|
+
end
|
115
|
+
rescue NameError
|
116
|
+
Kernel.raise NameError, "#{self} has no handler #{handler}"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Optionally allow block or handler methods to receive the event
|
120
|
+
# object as an argument
|
121
|
+
if handler_meth.arity == 0
|
122
|
+
proc = lambda { handler_meth.call() }
|
123
|
+
else
|
124
|
+
proc = lambda { | e | handler_meth.call(e) }
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
source_id = source ? source.get_id : Wx::ID_ANY
|
129
|
+
begin
|
130
|
+
# Some WxWidgest event connector methods expect the ID of the
|
131
|
+
# triggering widget, others don't. So we try both ways to hide
|
132
|
+
# this complexity
|
133
|
+
evt_meth.call( source_id, &proc )
|
134
|
+
rescue ArgumentError
|
135
|
+
# Try with no ID specified.
|
136
|
+
evt_meth.call( &proc )
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# TODO - not called - needs to be done by redefining new
|
141
|
+
def initialize(*args)
|
142
|
+
super(*args)
|
143
|
+
self.class.event_hooks.each do | sym, handler |
|
144
|
+
listen(self, sym, handler)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Wx::EvtHandler
|
150
|
+
include EventConnector
|
151
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module WxSugar
|
2
|
+
module ItemData
|
3
|
+
# for faked-up item data
|
4
|
+
def get_item_data(id)
|
5
|
+
data[id]
|
6
|
+
end
|
7
|
+
alias :get_client_data :get_item_data
|
8
|
+
|
9
|
+
# for faked-up item data
|
10
|
+
def set_item_data(id, data_obj)
|
11
|
+
data[id] = data_obj
|
12
|
+
end
|
13
|
+
alias :set_client_data :set_item_data
|
14
|
+
|
15
|
+
# returns the key associated with the value +value+
|
16
|
+
def value_to_ident(value)
|
17
|
+
return nil if value.nil?
|
18
|
+
if value.nil?
|
19
|
+
return nil
|
20
|
+
elsif value.kind_of?(Fixnum)
|
21
|
+
the_id = value
|
22
|
+
elsif value.respond_to?(:id)
|
23
|
+
the_id = value.id
|
24
|
+
else
|
25
|
+
Kernel.raise "Cannot search for invalid value #{value.inspect}"
|
26
|
+
end
|
27
|
+
return index(the_id)
|
28
|
+
end
|
29
|
+
alias :object_to_item :value_to_ident
|
30
|
+
end
|
31
|
+
|
32
|
+
module ListLikeItemData
|
33
|
+
include ItemData
|
34
|
+
def data()
|
35
|
+
@data_table ||= Array.new()
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_item_data(the_data)
|
39
|
+
if i = value_to_ident(the_data)
|
40
|
+
data.delete_at(i)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def unshift_item_data(the_data)
|
45
|
+
delete_item_data(the_data)
|
46
|
+
data.unshift(the_data)
|
47
|
+
end
|
48
|
+
|
49
|
+
def push_item_data(the_data)
|
50
|
+
delete_item_data(the_data)
|
51
|
+
data.push(the_data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def index(the_id)
|
55
|
+
data.each_with_index do | val, i |
|
56
|
+
return i if val.id == the_id
|
57
|
+
end
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module HashLikeItemData
|
63
|
+
include ItemData
|
64
|
+
def data()
|
65
|
+
@data_table ||= Hash.new()
|
66
|
+
end
|
67
|
+
|
68
|
+
def delete(the_id)
|
69
|
+
data.delete(the_id)
|
70
|
+
super(the_id)
|
71
|
+
end
|
72
|
+
|
73
|
+
def index(the_id)
|
74
|
+
data.each do | k, val |
|
75
|
+
return k if val.id == the_id
|
76
|
+
end
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|