fidgit 0.2.4 → 0.2.5
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/.gitignore +7 -7
- data/.rspec +2 -2
- data/CHANGELOG.md +30 -30
- data/Gemfile +3 -3
- data/LICENSE.txt +19 -19
- data/README.textile +139 -139
- data/Rakefile +37 -37
- data/config/default_schema.yml +216 -216
- data/examples/_all_examples.rb +9 -9
- data/examples/align_example.rb +55 -55
- data/examples/button_and_toggle_button_example.rb +37 -37
- data/examples/color_picker_example.rb +16 -16
- data/examples/color_well_example.rb +24 -24
- data/examples/combo_box_example.rb +23 -23
- data/examples/file_dialog_example.rb +41 -41
- data/examples/grid_packer_example.rb +28 -28
- data/examples/helpers/example_window.rb +16 -16
- data/examples/label_example.rb +22 -22
- data/examples/list_example.rb +22 -22
- data/examples/menu_pane_example.rb +26 -26
- data/examples/message_dialog_example.rb +64 -64
- data/examples/radio_button_example.rb +36 -36
- data/examples/readme_example.rb +31 -31
- data/examples/scroll_window_example.rb +48 -48
- data/examples/slider_example.rb +33 -33
- data/examples/splash_example.rb +41 -41
- data/examples/text_area_example.rb +32 -32
- data/fidgit.gemspec +35 -35
- data/lib/fidgit.rb +50 -50
- data/lib/fidgit/chingu_ext/window.rb +5 -5
- data/lib/fidgit/cursor.rb +37 -37
- data/lib/fidgit/elements/button.rb +112 -112
- data/lib/fidgit/elements/color_picker.rb +62 -62
- data/lib/fidgit/elements/color_well.rb +38 -38
- data/lib/fidgit/elements/combo_box.rb +113 -113
- data/lib/fidgit/elements/composite.rb +16 -16
- data/lib/fidgit/elements/container.rb +208 -208
- data/lib/fidgit/elements/element.rb +297 -297
- data/lib/fidgit/elements/file_browser.rb +151 -151
- data/lib/fidgit/elements/grid.rb +226 -226
- data/lib/fidgit/elements/group.rb +64 -64
- data/lib/fidgit/elements/horizontal.rb +11 -11
- data/lib/fidgit/elements/image_frame.rb +64 -64
- data/lib/fidgit/elements/label.rb +84 -84
- data/lib/fidgit/elements/list.rb +46 -46
- data/lib/fidgit/elements/main_packer.rb +24 -24
- data/lib/fidgit/elements/menu_pane.rb +160 -160
- data/lib/fidgit/elements/packer.rb +41 -41
- data/lib/fidgit/elements/radio_button.rb +85 -85
- data/lib/fidgit/elements/scroll_area.rb +67 -67
- data/lib/fidgit/elements/scroll_bar.rb +127 -127
- data/lib/fidgit/elements/scroll_window.rb +82 -82
- data/lib/fidgit/elements/slider.rb +124 -124
- data/lib/fidgit/elements/text_area.rb +493 -493
- data/lib/fidgit/elements/text_line.rb +91 -91
- data/lib/fidgit/elements/toggle_button.rb +66 -66
- data/lib/fidgit/elements/tool_tip.rb +34 -34
- data/lib/fidgit/elements/vertical.rb +11 -11
- data/lib/fidgit/event.rb +158 -158
- data/lib/fidgit/gosu_ext/color.rb +135 -135
- data/lib/fidgit/gosu_ext/gosu_module.rb +24 -24
- data/lib/fidgit/history.rb +90 -90
- data/lib/fidgit/redirector.rb +82 -82
- data/lib/fidgit/schema.rb +123 -123
- data/lib/fidgit/selection.rb +105 -105
- data/lib/fidgit/standard_ext/hash.rb +20 -20
- data/lib/fidgit/states/dialog_state.rb +51 -51
- data/lib/fidgit/states/file_dialog.rb +24 -24
- data/lib/fidgit/states/gui_state.rb +329 -329
- data/lib/fidgit/states/message_dialog.rb +60 -60
- data/lib/fidgit/version.rb +4 -4
- data/lib/fidgit/window.rb +19 -19
- data/spec/fidgit/elements/helpers/helper.rb +2 -2
- data/spec/fidgit/elements/helpers/tex_play_helper.rb +8 -8
- data/spec/fidgit/elements/image_frame_spec.rb +68 -68
- data/spec/fidgit/elements/label_spec.rb +36 -36
- data/spec/fidgit/event_spec.rb +209 -209
- data/spec/fidgit/gosu_ext/color_spec.rb +129 -129
- data/spec/fidgit/gosu_ext/helpers/helper.rb +2 -2
- data/spec/fidgit/helpers/helper.rb +3 -3
- data/spec/fidgit/history_spec.rb +153 -153
- data/spec/fidgit/redirector_spec.rb +77 -77
- data/spec/fidgit/schema_spec.rb +66 -66
- data/spec/fidgit/schema_test.yml +32 -32
- metadata +67 -22
@@ -1,92 +1,92 @@
|
|
1
|
-
module Fidgit
|
2
|
-
# Used internally by the label.
|
3
|
-
class TextLine < Element
|
4
|
-
VALID_JUSTIFICATION = [:left, :right, :center]
|
5
|
-
|
6
|
-
attr_reader :color, :justify
|
7
|
-
|
8
|
-
def color=(color)
|
9
|
-
raise ArgumentError.new("Text must be a Gosu::Color") unless color.is_a? Gosu::Color
|
10
|
-
|
11
|
-
@color = color.dup
|
12
|
-
|
13
|
-
color
|
14
|
-
end
|
15
|
-
|
16
|
-
def text; @text.dup; end
|
17
|
-
|
18
|
-
def text=(text)
|
19
|
-
raise ArgumentError.new("Text must be a String") unless text.respond_to? :to_s
|
20
|
-
|
21
|
-
@text = text.to_s.dup
|
22
|
-
|
23
|
-
recalc
|
24
|
-
text
|
25
|
-
end
|
26
|
-
|
27
|
-
def justify=(justify)
|
28
|
-
raise ArgumentError.new("Justify must be one of #{VALID_JUSTIFICATION.inspect}") unless VALID_JUSTIFICATION.include? justify
|
29
|
-
@justify = justify
|
30
|
-
end
|
31
|
-
|
32
|
-
# @param (see Element#initialize)
|
33
|
-
# @param [String] text The string to display in the line of text.
|
34
|
-
#
|
35
|
-
# @option (see Element#initialize)
|
36
|
-
# @option options [:left, :right, :center] :justify (:left) Text justification.
|
37
|
-
def initialize(text, options = {})
|
38
|
-
options = {
|
39
|
-
color: default(:color),
|
40
|
-
justify: default(:justify),
|
41
|
-
}.merge! options
|
42
|
-
|
43
|
-
super(options)
|
44
|
-
|
45
|
-
self.justify = options[:justify]
|
46
|
-
self.color = options[:color]
|
47
|
-
self.text = text
|
48
|
-
end
|
49
|
-
|
50
|
-
def draw_foreground
|
51
|
-
case @justify
|
52
|
-
when :left
|
53
|
-
rel_x = 0.0
|
54
|
-
draw_x = x + padding_left
|
55
|
-
|
56
|
-
when :right
|
57
|
-
rel_x = 1.0
|
58
|
-
draw_x = x + rect.width - padding_right
|
59
|
-
|
60
|
-
when :center
|
61
|
-
rel_x = 0.5
|
62
|
-
draw_x = (x + padding_left) + (rect.width - padding_right - padding_left) / 2.0
|
63
|
-
end
|
64
|
-
|
65
|
-
font.draw_rel(@text, draw_x, y + padding_top, z, rel_x, 0, 1, 1, color)
|
66
|
-
end
|
67
|
-
|
68
|
-
def min_width
|
69
|
-
if @text.empty?
|
70
|
-
[padding_left + padding_right, super].max
|
71
|
-
else
|
72
|
-
[padding_left + font.text_width(@text) + padding_right, super].max
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
protected
|
77
|
-
def layout
|
78
|
-
rect.width = [min_width, max_width].min
|
79
|
-
|
80
|
-
if @text.empty?
|
81
|
-
rect.height = [[padding_top + padding_bottom, min_height].max, max_height].min
|
82
|
-
else
|
83
|
-
rect.height = [[padding_top + font.height + padding_bottom, min_height].max, max_height].min
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
public
|
88
|
-
def to_s
|
89
|
-
"#{super} '#{@text}'"
|
90
|
-
end
|
91
|
-
end
|
1
|
+
module Fidgit
|
2
|
+
# Used internally by the label.
|
3
|
+
class TextLine < Element
|
4
|
+
VALID_JUSTIFICATION = [:left, :right, :center]
|
5
|
+
|
6
|
+
attr_reader :color, :justify
|
7
|
+
|
8
|
+
def color=(color)
|
9
|
+
raise ArgumentError.new("Text must be a Gosu::Color") unless color.is_a? Gosu::Color
|
10
|
+
|
11
|
+
@color = color.dup
|
12
|
+
|
13
|
+
color
|
14
|
+
end
|
15
|
+
|
16
|
+
def text; @text.dup; end
|
17
|
+
|
18
|
+
def text=(text)
|
19
|
+
raise ArgumentError.new("Text must be a String") unless text.respond_to? :to_s
|
20
|
+
|
21
|
+
@text = text.to_s.dup
|
22
|
+
|
23
|
+
recalc
|
24
|
+
text
|
25
|
+
end
|
26
|
+
|
27
|
+
def justify=(justify)
|
28
|
+
raise ArgumentError.new("Justify must be one of #{VALID_JUSTIFICATION.inspect}") unless VALID_JUSTIFICATION.include? justify
|
29
|
+
@justify = justify
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param (see Element#initialize)
|
33
|
+
# @param [String] text The string to display in the line of text.
|
34
|
+
#
|
35
|
+
# @option (see Element#initialize)
|
36
|
+
# @option options [:left, :right, :center] :justify (:left) Text justification.
|
37
|
+
def initialize(text, options = {})
|
38
|
+
options = {
|
39
|
+
color: default(:color),
|
40
|
+
justify: default(:justify),
|
41
|
+
}.merge! options
|
42
|
+
|
43
|
+
super(options)
|
44
|
+
|
45
|
+
self.justify = options[:justify]
|
46
|
+
self.color = options[:color]
|
47
|
+
self.text = text
|
48
|
+
end
|
49
|
+
|
50
|
+
def draw_foreground
|
51
|
+
case @justify
|
52
|
+
when :left
|
53
|
+
rel_x = 0.0
|
54
|
+
draw_x = x + padding_left
|
55
|
+
|
56
|
+
when :right
|
57
|
+
rel_x = 1.0
|
58
|
+
draw_x = x + rect.width - padding_right
|
59
|
+
|
60
|
+
when :center
|
61
|
+
rel_x = 0.5
|
62
|
+
draw_x = (x + padding_left) + (rect.width - padding_right - padding_left) / 2.0
|
63
|
+
end
|
64
|
+
|
65
|
+
font.draw_rel(@text, draw_x, y + padding_top, z, rel_x, 0, 1, 1, color)
|
66
|
+
end
|
67
|
+
|
68
|
+
def min_width
|
69
|
+
if @text.empty?
|
70
|
+
[padding_left + padding_right, super].max
|
71
|
+
else
|
72
|
+
[padding_left + font.text_width(@text) + padding_right, super].max
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def layout
|
78
|
+
rect.width = [min_width, max_width].min
|
79
|
+
|
80
|
+
if @text.empty?
|
81
|
+
rect.height = [[padding_top + padding_bottom, min_height].max, max_height].min
|
82
|
+
else
|
83
|
+
rect.height = [[padding_top + font.height + padding_bottom, min_height].max, max_height].min
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
public
|
88
|
+
def to_s
|
89
|
+
"#{super} '#{@text}'"
|
90
|
+
end
|
91
|
+
end
|
92
92
|
end
|
@@ -1,67 +1,67 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Fidgit
|
4
|
-
# A button that toggles its value from false<->true when clicked.
|
5
|
-
class ToggleButton < Button
|
6
|
-
event :changed
|
7
|
-
|
8
|
-
attr_reader :value
|
9
|
-
def value=(value); @value = value; update_status; end
|
10
|
-
|
11
|
-
# @param (see Button#initialize)
|
12
|
-
#
|
13
|
-
# @option (see Button#initialize)
|
14
|
-
def initialize(text, options = {}, &block)
|
15
|
-
options = {
|
16
|
-
value: false
|
17
|
-
}.merge! options
|
18
|
-
|
19
|
-
@value = options[:value]
|
20
|
-
|
21
|
-
super(text, options)
|
22
|
-
|
23
|
-
@text_on = (options[:text_on] || text).dup
|
24
|
-
@icon_on = options[:icon_on] || icon
|
25
|
-
@tip_on = (options[:tip_on] || tip).dup
|
26
|
-
@border_color_on = (options[:border_color_on] || options[:border_color] || default(:toggled, :border_color)).dup
|
27
|
-
|
28
|
-
@text_off = (options[:text_off] || text).dup
|
29
|
-
@icon_off = options[:icon_off] || icon
|
30
|
-
@tip_off = (options[:tip_off] || tip).dup
|
31
|
-
@border_color_off = (options[:border_color_off] || options[:border_color] || default(:border_color)).dup
|
32
|
-
|
33
|
-
update_status
|
34
|
-
|
35
|
-
subscribe :clicked_left_mouse_button do |sender, x, y|
|
36
|
-
@value = (not @value)
|
37
|
-
update_status
|
38
|
-
publish :changed, @value
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
protected
|
43
|
-
# The block for a toggle-button is connected to :changed event.
|
44
|
-
def post_init_block(&block)
|
45
|
-
subscribe :changed, &block
|
46
|
-
end
|
47
|
-
|
48
|
-
protected
|
49
|
-
def update_status
|
50
|
-
if @value
|
51
|
-
self.text = @text_on.dup
|
52
|
-
@icon = @icon_on ? @icon_on.dup : nil
|
53
|
-
@tip = @tip_on.dup
|
54
|
-
@border_color = @border_color_on.dup
|
55
|
-
else
|
56
|
-
self.text = @text_off.dup
|
57
|
-
@icon = @icon_off ? @icon_off.dup : nil
|
58
|
-
@tip = @tip_off.dup
|
59
|
-
@border_color = @border_color_off.dup
|
60
|
-
end
|
61
|
-
|
62
|
-
recalc
|
63
|
-
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
# A button that toggles its value from false<->true when clicked.
|
5
|
+
class ToggleButton < Button
|
6
|
+
event :changed
|
7
|
+
|
8
|
+
attr_reader :value
|
9
|
+
def value=(value); @value = value; update_status; end
|
10
|
+
|
11
|
+
# @param (see Button#initialize)
|
12
|
+
#
|
13
|
+
# @option (see Button#initialize)
|
14
|
+
def initialize(text, options = {}, &block)
|
15
|
+
options = {
|
16
|
+
value: false
|
17
|
+
}.merge! options
|
18
|
+
|
19
|
+
@value = options[:value]
|
20
|
+
|
21
|
+
super(text, options)
|
22
|
+
|
23
|
+
@text_on = (options[:text_on] || text).dup
|
24
|
+
@icon_on = options[:icon_on] || icon
|
25
|
+
@tip_on = (options[:tip_on] || tip).dup
|
26
|
+
@border_color_on = (options[:border_color_on] || options[:border_color] || default(:toggled, :border_color)).dup
|
27
|
+
|
28
|
+
@text_off = (options[:text_off] || text).dup
|
29
|
+
@icon_off = options[:icon_off] || icon
|
30
|
+
@tip_off = (options[:tip_off] || tip).dup
|
31
|
+
@border_color_off = (options[:border_color_off] || options[:border_color] || default(:border_color)).dup
|
32
|
+
|
33
|
+
update_status
|
34
|
+
|
35
|
+
subscribe :clicked_left_mouse_button do |sender, x, y|
|
36
|
+
@value = (not @value)
|
37
|
+
update_status
|
38
|
+
publish :changed, @value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
# The block for a toggle-button is connected to :changed event.
|
44
|
+
def post_init_block(&block)
|
45
|
+
subscribe :changed, &block
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
def update_status
|
50
|
+
if @value
|
51
|
+
self.text = @text_on.dup
|
52
|
+
@icon = @icon_on ? @icon_on.dup : nil
|
53
|
+
@tip = @tip_on.dup
|
54
|
+
@border_color = @border_color_on.dup
|
55
|
+
else
|
56
|
+
self.text = @text_off.dup
|
57
|
+
@icon = @icon_off ? @icon_off.dup : nil
|
58
|
+
@tip = @tip_off.dup
|
59
|
+
@border_color = @border_color_off.dup
|
60
|
+
end
|
61
|
+
|
62
|
+
recalc
|
63
|
+
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
67
|
end
|
@@ -1,35 +1,35 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Fidgit
|
4
|
-
class ToolTip < TextLine
|
5
|
-
def x=(value); super(value); recalc; value; end
|
6
|
-
def y=(value); super(value); recalc; value; end
|
7
|
-
def hit?(x, y); false; end
|
8
|
-
|
9
|
-
|
10
|
-
# @param (see Label#initialize)
|
11
|
-
#
|
12
|
-
# @option (see Label#initialize)
|
13
|
-
def initialize(options = {}, &block)
|
14
|
-
options = {
|
15
|
-
z: Float::INFINITY,
|
16
|
-
background_color: default(:background_color),
|
17
|
-
border_color: default(:border_color),
|
18
|
-
text: '',
|
19
|
-
}.merge! options
|
20
|
-
|
21
|
-
super(options[:text], options)
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
def layout
|
26
|
-
super
|
27
|
-
|
28
|
-
# Ensure the tip can't go over the edge of the screen. If it can't be avoided, align with left edge of screen.
|
29
|
-
rect.x = [[x, $window.width - width - padding_right].min, 0].max
|
30
|
-
rect.y = [[y, $window.height - height - padding_bottom].min, 0].max
|
31
|
-
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
class ToolTip < TextLine
|
5
|
+
def x=(value); super(value); recalc; value; end
|
6
|
+
def y=(value); super(value); recalc; value; end
|
7
|
+
def hit?(x, y); false; end
|
8
|
+
|
9
|
+
|
10
|
+
# @param (see Label#initialize)
|
11
|
+
#
|
12
|
+
# @option (see Label#initialize)
|
13
|
+
def initialize(options = {}, &block)
|
14
|
+
options = {
|
15
|
+
z: Float::INFINITY,
|
16
|
+
background_color: default(:background_color),
|
17
|
+
border_color: default(:border_color),
|
18
|
+
text: '',
|
19
|
+
}.merge! options
|
20
|
+
|
21
|
+
super(options[:text], options)
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def layout
|
26
|
+
super
|
27
|
+
|
28
|
+
# Ensure the tip can't go over the edge of the screen. If it can't be avoided, align with left edge of screen.
|
29
|
+
rect.x = [[x, $window.width - width - padding_right].min, 0].max
|
30
|
+
rect.y = [[y, $window.height - height - padding_bottom].min, 0].max
|
31
|
+
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
35
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Fidgit
|
4
|
-
# A vertically aligned element packing container.
|
5
|
-
class Vertical < Grid
|
6
|
-
def initialize(options = {})
|
7
|
-
options[:num_columns] = 1
|
8
|
-
|
9
|
-
super options
|
10
|
-
end
|
11
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
# A vertically aligned element packing container.
|
5
|
+
class Vertical < Grid
|
6
|
+
def initialize(options = {})
|
7
|
+
options[:num_columns] = 1
|
8
|
+
|
9
|
+
super options
|
10
|
+
end
|
11
|
+
end
|
12
12
|
end
|
data/lib/fidgit/event.rb
CHANGED
@@ -1,159 +1,159 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Fidgit
|
4
|
-
# Adds simple event handling methods to an object (subscribe/publish pattern).
|
5
|
-
#
|
6
|
-
# @example
|
7
|
-
# class JumpingBean
|
8
|
-
# include Event
|
9
|
-
# event :jump
|
10
|
-
# end
|
11
|
-
#
|
12
|
-
# bean = JumpingBean.new
|
13
|
-
# bean.subscribe :jump do
|
14
|
-
# puts "Whee!"
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# bean.subscribe :jump do |object, direction, distance|
|
18
|
-
# puts "#{object.class.name} jumped #{distance} metres #{direction}"
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# bean.publish :jump, :up, 4
|
22
|
-
# # Whee!
|
23
|
-
# # JumpingBean jumped 4 metres up
|
24
|
-
#
|
25
|
-
module Event
|
26
|
-
# Created and returned by {Event#subscribe} and can be used to unsubscribe from the event.
|
27
|
-
class Subscription
|
28
|
-
attr_reader :publisher, :event, :handler
|
29
|
-
|
30
|
-
def initialize(publisher, event, handler)
|
31
|
-
raise TypeError unless publisher.is_a? Event
|
32
|
-
raise TypeError unless event.is_a? Symbol
|
33
|
-
raise TypeError unless handler.is_a? Proc or handler.is_a? Method
|
34
|
-
|
35
|
-
@publisher, @event, @handler = publisher, event, handler
|
36
|
-
end
|
37
|
-
|
38
|
-
def unsubscribe
|
39
|
-
@publisher.unsubscribe self
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class << self
|
44
|
-
def new_event_handlers
|
45
|
-
# Don't use Set, since it is not guaranteed to be ordered.
|
46
|
-
Hash.new {|h, k| h[k] = [] }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# @return [Subscription] Definition of this the handler created by this subscription, to be used with {#unsubscribe}
|
51
|
-
def subscribe(event, method = nil, &block)
|
52
|
-
raise ArgumentError, "Expected method or block for event handler" unless !block.nil? ^ !method.nil?
|
53
|
-
raise ArgumentError, "#{self.class} does not handle #{event.inspect}" unless events.include? event
|
54
|
-
|
55
|
-
@_event_handlers ||= Event.new_event_handlers
|
56
|
-
handler = method || block
|
57
|
-
@_event_handlers[event] << handler
|
58
|
-
|
59
|
-
Subscription.new self, event, handler
|
60
|
-
end
|
61
|
-
|
62
|
-
# @overload unsubscribe(subscription)
|
63
|
-
# Unsubscribe from a #{Subscription}, as returned from {#subscribe}
|
64
|
-
# @param subscription [Subscription]
|
65
|
-
# @return [Boolean] true if the handler was able to be deleted.
|
66
|
-
#
|
67
|
-
# @overload unsubscribe(handler)
|
68
|
-
# Unsubscribe from first event this handler has been used to subscribe to..
|
69
|
-
# @param handler [Block, Method] Event handler used.
|
70
|
-
# @return [Boolean] true if the handler was able to be deleted.
|
71
|
-
#
|
72
|
-
# @overload unsubscribe(event, handler)
|
73
|
-
# Unsubscribe from specific handler on particular event.
|
74
|
-
# @param event [Symbol] Name of event originally subscribed to.
|
75
|
-
# @param handler [Block, Method] Event handler used.
|
76
|
-
# @return [Boolean] true if the handler was able to be deleted.
|
77
|
-
#
|
78
|
-
def unsubscribe(*args)
|
79
|
-
@_event_handlers ||= Event.new_event_handlers
|
80
|
-
|
81
|
-
case args.size
|
82
|
-
when 1
|
83
|
-
case args.first
|
84
|
-
when Subscription
|
85
|
-
# Delete specific event handler.
|
86
|
-
subscription = args.first
|
87
|
-
raise ArgumentError, "Incorrect publisher for #{Subscription}: #{subscription.publisher}" unless subscription.publisher == self
|
88
|
-
unsubscribe subscription.event, subscription.handler
|
89
|
-
when Proc, Method
|
90
|
-
# Delete first events that use the handler.
|
91
|
-
handler = args.first
|
92
|
-
!!@_event_handlers.find {|_, handlers| handlers.delete handler }
|
93
|
-
else
|
94
|
-
raise TypeError, "handler must be a #{Subscription}, Block or Method: #{args.first}"
|
95
|
-
end
|
96
|
-
when 2
|
97
|
-
event, handler = args
|
98
|
-
raise TypeError, "event name must be a Symbol: #{event}" unless event.is_a? Symbol
|
99
|
-
raise TypeError, "handler name must be a Proc/Method: #{handler}" unless handler.is_a? Proc or handler.is_a? Method
|
100
|
-
!!@_event_handlers[event].delete(handler)
|
101
|
-
else
|
102
|
-
raise ArgumentError, "Requires 1..2 arguments, but received #{args.size} arguments"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
# Publish an event to all previously added handlers in the order they were added.
|
108
|
-
# It will automatically call the publishing object with the method named after the event if it is defined
|
109
|
-
# (this will be done before the manually added handlers are called).
|
110
|
-
#
|
111
|
-
# If any handler returns :handled, then no further handlers will be called.
|
112
|
-
#
|
113
|
-
# @param [Symbol] event Name of the event to publish.
|
114
|
-
# @param [Array] args Arguments to pass to the event handlers.
|
115
|
-
# @return [Symbol, nil] :handled if any handler handled the event or nil if none did.
|
116
|
-
def publish(event, *args)
|
117
|
-
raise ArgumentError, "#{self.class} does not handle #{event.inspect}" unless events.include? event
|
118
|
-
|
119
|
-
# Do nothing if the object is disabled.
|
120
|
-
return if respond_to?(:enabled?) and not enabled?
|
121
|
-
|
122
|
-
if respond_to? event
|
123
|
-
return :handled if send(event, self, *args) == :handled
|
124
|
-
end
|
125
|
-
|
126
|
-
if defined? @_event_handlers
|
127
|
-
@_event_handlers[event].reverse_each do |handler|
|
128
|
-
return :handled if handler.call(self, *args) == :handled
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
nil
|
133
|
-
end
|
134
|
-
|
135
|
-
# The list of events that this object can publish/subscribe.
|
136
|
-
def events
|
137
|
-
self.class.events
|
138
|
-
end
|
139
|
-
|
140
|
-
# Add singleton methods to the class that includes Event.
|
141
|
-
def self.included(base)
|
142
|
-
class << base
|
143
|
-
def events
|
144
|
-
# Copy the events already set up for your parent.
|
145
|
-
@events ||= if superclass.respond_to? :events
|
146
|
-
superclass.events.dup
|
147
|
-
else
|
148
|
-
[]
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def event(event)
|
153
|
-
events.push event.to_sym unless events.include? event
|
154
|
-
event
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
# Adds simple event handling methods to an object (subscribe/publish pattern).
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class JumpingBean
|
8
|
+
# include Event
|
9
|
+
# event :jump
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# bean = JumpingBean.new
|
13
|
+
# bean.subscribe :jump do
|
14
|
+
# puts "Whee!"
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# bean.subscribe :jump do |object, direction, distance|
|
18
|
+
# puts "#{object.class.name} jumped #{distance} metres #{direction}"
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# bean.publish :jump, :up, 4
|
22
|
+
# # Whee!
|
23
|
+
# # JumpingBean jumped 4 metres up
|
24
|
+
#
|
25
|
+
module Event
|
26
|
+
# Created and returned by {Event#subscribe} and can be used to unsubscribe from the event.
|
27
|
+
class Subscription
|
28
|
+
attr_reader :publisher, :event, :handler
|
29
|
+
|
30
|
+
def initialize(publisher, event, handler)
|
31
|
+
raise TypeError unless publisher.is_a? Event
|
32
|
+
raise TypeError unless event.is_a? Symbol
|
33
|
+
raise TypeError unless handler.is_a? Proc or handler.is_a? Method
|
34
|
+
|
35
|
+
@publisher, @event, @handler = publisher, event, handler
|
36
|
+
end
|
37
|
+
|
38
|
+
def unsubscribe
|
39
|
+
@publisher.unsubscribe self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def new_event_handlers
|
45
|
+
# Don't use Set, since it is not guaranteed to be ordered.
|
46
|
+
Hash.new {|h, k| h[k] = [] }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Subscription] Definition of this the handler created by this subscription, to be used with {#unsubscribe}
|
51
|
+
def subscribe(event, method = nil, &block)
|
52
|
+
raise ArgumentError, "Expected method or block for event handler" unless !block.nil? ^ !method.nil?
|
53
|
+
raise ArgumentError, "#{self.class} does not handle #{event.inspect}" unless events.include? event
|
54
|
+
|
55
|
+
@_event_handlers ||= Event.new_event_handlers
|
56
|
+
handler = method || block
|
57
|
+
@_event_handlers[event] << handler
|
58
|
+
|
59
|
+
Subscription.new self, event, handler
|
60
|
+
end
|
61
|
+
|
62
|
+
# @overload unsubscribe(subscription)
|
63
|
+
# Unsubscribe from a #{Subscription}, as returned from {#subscribe}
|
64
|
+
# @param subscription [Subscription]
|
65
|
+
# @return [Boolean] true if the handler was able to be deleted.
|
66
|
+
#
|
67
|
+
# @overload unsubscribe(handler)
|
68
|
+
# Unsubscribe from first event this handler has been used to subscribe to..
|
69
|
+
# @param handler [Block, Method] Event handler used.
|
70
|
+
# @return [Boolean] true if the handler was able to be deleted.
|
71
|
+
#
|
72
|
+
# @overload unsubscribe(event, handler)
|
73
|
+
# Unsubscribe from specific handler on particular event.
|
74
|
+
# @param event [Symbol] Name of event originally subscribed to.
|
75
|
+
# @param handler [Block, Method] Event handler used.
|
76
|
+
# @return [Boolean] true if the handler was able to be deleted.
|
77
|
+
#
|
78
|
+
def unsubscribe(*args)
|
79
|
+
@_event_handlers ||= Event.new_event_handlers
|
80
|
+
|
81
|
+
case args.size
|
82
|
+
when 1
|
83
|
+
case args.first
|
84
|
+
when Subscription
|
85
|
+
# Delete specific event handler.
|
86
|
+
subscription = args.first
|
87
|
+
raise ArgumentError, "Incorrect publisher for #{Subscription}: #{subscription.publisher}" unless subscription.publisher == self
|
88
|
+
unsubscribe subscription.event, subscription.handler
|
89
|
+
when Proc, Method
|
90
|
+
# Delete first events that use the handler.
|
91
|
+
handler = args.first
|
92
|
+
!!@_event_handlers.find {|_, handlers| handlers.delete handler }
|
93
|
+
else
|
94
|
+
raise TypeError, "handler must be a #{Subscription}, Block or Method: #{args.first}"
|
95
|
+
end
|
96
|
+
when 2
|
97
|
+
event, handler = args
|
98
|
+
raise TypeError, "event name must be a Symbol: #{event}" unless event.is_a? Symbol
|
99
|
+
raise TypeError, "handler name must be a Proc/Method: #{handler}" unless handler.is_a? Proc or handler.is_a? Method
|
100
|
+
!!@_event_handlers[event].delete(handler)
|
101
|
+
else
|
102
|
+
raise ArgumentError, "Requires 1..2 arguments, but received #{args.size} arguments"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Publish an event to all previously added handlers in the order they were added.
|
108
|
+
# It will automatically call the publishing object with the method named after the event if it is defined
|
109
|
+
# (this will be done before the manually added handlers are called).
|
110
|
+
#
|
111
|
+
# If any handler returns :handled, then no further handlers will be called.
|
112
|
+
#
|
113
|
+
# @param [Symbol] event Name of the event to publish.
|
114
|
+
# @param [Array] args Arguments to pass to the event handlers.
|
115
|
+
# @return [Symbol, nil] :handled if any handler handled the event or nil if none did.
|
116
|
+
def publish(event, *args)
|
117
|
+
raise ArgumentError, "#{self.class} does not handle #{event.inspect}" unless events.include? event
|
118
|
+
|
119
|
+
# Do nothing if the object is disabled.
|
120
|
+
return if respond_to?(:enabled?) and not enabled?
|
121
|
+
|
122
|
+
if respond_to? event
|
123
|
+
return :handled if send(event, self, *args) == :handled
|
124
|
+
end
|
125
|
+
|
126
|
+
if defined? @_event_handlers
|
127
|
+
@_event_handlers[event].reverse_each do |handler|
|
128
|
+
return :handled if handler.call(self, *args) == :handled
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# The list of events that this object can publish/subscribe.
|
136
|
+
def events
|
137
|
+
self.class.events
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add singleton methods to the class that includes Event.
|
141
|
+
def self.included(base)
|
142
|
+
class << base
|
143
|
+
def events
|
144
|
+
# Copy the events already set up for your parent.
|
145
|
+
@events ||= if superclass.respond_to? :events
|
146
|
+
superclass.events.dup
|
147
|
+
else
|
148
|
+
[]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def event(event)
|
153
|
+
events.push event.to_sym unless events.include? event
|
154
|
+
event
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
159
|
end
|