wx_sugar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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