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.
@@ -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
@@ -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