wx_sugar 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/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
|