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.
Files changed (85) hide show
  1. data/.gitignore +7 -7
  2. data/.rspec +2 -2
  3. data/CHANGELOG.md +30 -30
  4. data/Gemfile +3 -3
  5. data/LICENSE.txt +19 -19
  6. data/README.textile +139 -139
  7. data/Rakefile +37 -37
  8. data/config/default_schema.yml +216 -216
  9. data/examples/_all_examples.rb +9 -9
  10. data/examples/align_example.rb +55 -55
  11. data/examples/button_and_toggle_button_example.rb +37 -37
  12. data/examples/color_picker_example.rb +16 -16
  13. data/examples/color_well_example.rb +24 -24
  14. data/examples/combo_box_example.rb +23 -23
  15. data/examples/file_dialog_example.rb +41 -41
  16. data/examples/grid_packer_example.rb +28 -28
  17. data/examples/helpers/example_window.rb +16 -16
  18. data/examples/label_example.rb +22 -22
  19. data/examples/list_example.rb +22 -22
  20. data/examples/menu_pane_example.rb +26 -26
  21. data/examples/message_dialog_example.rb +64 -64
  22. data/examples/radio_button_example.rb +36 -36
  23. data/examples/readme_example.rb +31 -31
  24. data/examples/scroll_window_example.rb +48 -48
  25. data/examples/slider_example.rb +33 -33
  26. data/examples/splash_example.rb +41 -41
  27. data/examples/text_area_example.rb +32 -32
  28. data/fidgit.gemspec +35 -35
  29. data/lib/fidgit.rb +50 -50
  30. data/lib/fidgit/chingu_ext/window.rb +5 -5
  31. data/lib/fidgit/cursor.rb +37 -37
  32. data/lib/fidgit/elements/button.rb +112 -112
  33. data/lib/fidgit/elements/color_picker.rb +62 -62
  34. data/lib/fidgit/elements/color_well.rb +38 -38
  35. data/lib/fidgit/elements/combo_box.rb +113 -113
  36. data/lib/fidgit/elements/composite.rb +16 -16
  37. data/lib/fidgit/elements/container.rb +208 -208
  38. data/lib/fidgit/elements/element.rb +297 -297
  39. data/lib/fidgit/elements/file_browser.rb +151 -151
  40. data/lib/fidgit/elements/grid.rb +226 -226
  41. data/lib/fidgit/elements/group.rb +64 -64
  42. data/lib/fidgit/elements/horizontal.rb +11 -11
  43. data/lib/fidgit/elements/image_frame.rb +64 -64
  44. data/lib/fidgit/elements/label.rb +84 -84
  45. data/lib/fidgit/elements/list.rb +46 -46
  46. data/lib/fidgit/elements/main_packer.rb +24 -24
  47. data/lib/fidgit/elements/menu_pane.rb +160 -160
  48. data/lib/fidgit/elements/packer.rb +41 -41
  49. data/lib/fidgit/elements/radio_button.rb +85 -85
  50. data/lib/fidgit/elements/scroll_area.rb +67 -67
  51. data/lib/fidgit/elements/scroll_bar.rb +127 -127
  52. data/lib/fidgit/elements/scroll_window.rb +82 -82
  53. data/lib/fidgit/elements/slider.rb +124 -124
  54. data/lib/fidgit/elements/text_area.rb +493 -493
  55. data/lib/fidgit/elements/text_line.rb +91 -91
  56. data/lib/fidgit/elements/toggle_button.rb +66 -66
  57. data/lib/fidgit/elements/tool_tip.rb +34 -34
  58. data/lib/fidgit/elements/vertical.rb +11 -11
  59. data/lib/fidgit/event.rb +158 -158
  60. data/lib/fidgit/gosu_ext/color.rb +135 -135
  61. data/lib/fidgit/gosu_ext/gosu_module.rb +24 -24
  62. data/lib/fidgit/history.rb +90 -90
  63. data/lib/fidgit/redirector.rb +82 -82
  64. data/lib/fidgit/schema.rb +123 -123
  65. data/lib/fidgit/selection.rb +105 -105
  66. data/lib/fidgit/standard_ext/hash.rb +20 -20
  67. data/lib/fidgit/states/dialog_state.rb +51 -51
  68. data/lib/fidgit/states/file_dialog.rb +24 -24
  69. data/lib/fidgit/states/gui_state.rb +329 -329
  70. data/lib/fidgit/states/message_dialog.rb +60 -60
  71. data/lib/fidgit/version.rb +4 -4
  72. data/lib/fidgit/window.rb +19 -19
  73. data/spec/fidgit/elements/helpers/helper.rb +2 -2
  74. data/spec/fidgit/elements/helpers/tex_play_helper.rb +8 -8
  75. data/spec/fidgit/elements/image_frame_spec.rb +68 -68
  76. data/spec/fidgit/elements/label_spec.rb +36 -36
  77. data/spec/fidgit/event_spec.rb +209 -209
  78. data/spec/fidgit/gosu_ext/color_spec.rb +129 -129
  79. data/spec/fidgit/gosu_ext/helpers/helper.rb +2 -2
  80. data/spec/fidgit/helpers/helper.rb +3 -3
  81. data/spec/fidgit/history_spec.rb +153 -153
  82. data/spec/fidgit/redirector_spec.rb +77 -77
  83. data/spec/fidgit/schema_spec.rb +66 -66
  84. data/spec/fidgit/schema_test.yml +32 -32
  85. metadata +67 -22
@@ -1,83 +1,83 @@
1
- module Fidgit
2
- # Redirects methods to an object, but does not mask methods and ivars from the calling context.
3
- module RedirectorMethods
4
- # Evaluate a block accessing methods and ivars from the calling context, but calling public methods
5
- # (not ivars or non-public methods) on this object in preference.
6
- def instance_methods_eval(&block)
7
- raise ArgumentError, "block required" unless block_given?
8
-
9
- context = eval('self', block.binding)
10
-
11
- context.send :push_redirection_target, self
12
-
13
- begin
14
- yield context
15
- ensure
16
- context.send :pop_redirection_target
17
- end
18
-
19
- self
20
- end
21
-
22
- protected
23
- def push_redirection_target(target)
24
- meta_class = class << self; self; end
25
- base_methods = Object.public_instance_methods
26
-
27
- # Redirect just the public methods of the target, less those that are on Object.
28
- methods_to_redirect = target.public_methods - base_methods
29
-
30
- # Only hide those public/private/protected methods that are being redirected.
31
- methods_overridden = []
32
- [:public, :protected, :private].each do |access|
33
- methods_to_hide = meta_class.send("#{access}_instance_methods", false) & methods_to_redirect
34
- methods_to_hide.each do |meth|
35
- # Take a reference to the method we are about to override.
36
- methods_overridden.push [meth, method(meth), access]
37
- meta_class.send :remove_method, meth
38
- end
39
- end
40
-
41
- # Add a method, to redirect calls to the target.
42
- methods_to_redirect.each do |meth|
43
- meta_class.send :define_method, meth do |*args, &block|
44
- target.send meth, *args, &block
45
- end
46
- end
47
-
48
- redirection_stack.push [target, methods_overridden, methods_to_redirect]
49
-
50
- target
51
- end
52
-
53
- protected
54
- def pop_redirection_target
55
- meta_class = class << self; self; end
56
-
57
- target, methods_to_recreate, methods_to_remove = redirection_stack.pop
58
-
59
- # Remove the redirection methods
60
- methods_to_remove.reverse_each do |meth|
61
- meta_class.send :remove_method, meth
62
- end
63
-
64
- # Replace with the previous versions of the methods.
65
- methods_to_recreate.reverse_each do |meth, reference, access|
66
- meta_class.send :define_method, meth, reference
67
- meta_class.send access, meth unless access == :public
68
- end
69
-
70
- target
71
- end
72
-
73
- # Direct access to the redirection stack.
74
- private
75
- def redirection_stack
76
- @_redirection_stack ||= []
77
- end
78
- end
79
- end
80
-
81
- class Object
82
- include Fidgit::RedirectorMethods
1
+ module Fidgit
2
+ # Redirects methods to an object, but does not mask methods and ivars from the calling context.
3
+ module RedirectorMethods
4
+ # Evaluate a block accessing methods and ivars from the calling context, but calling public methods
5
+ # (not ivars or non-public methods) on this object in preference.
6
+ def instance_methods_eval(&block)
7
+ raise ArgumentError, "block required" unless block_given?
8
+
9
+ context = eval('self', block.binding)
10
+
11
+ context.send :push_redirection_target, self
12
+
13
+ begin
14
+ yield context
15
+ ensure
16
+ context.send :pop_redirection_target
17
+ end
18
+
19
+ self
20
+ end
21
+
22
+ protected
23
+ def push_redirection_target(target)
24
+ meta_class = class << self; self; end
25
+ base_methods = Object.public_instance_methods
26
+
27
+ # Redirect just the public methods of the target, less those that are on Object.
28
+ methods_to_redirect = target.public_methods - base_methods
29
+
30
+ # Only hide those public/private/protected methods that are being redirected.
31
+ methods_overridden = []
32
+ [:public, :protected, :private].each do |access|
33
+ methods_to_hide = meta_class.send("#{access}_instance_methods", false) & methods_to_redirect
34
+ methods_to_hide.each do |meth|
35
+ # Take a reference to the method we are about to override.
36
+ methods_overridden.push [meth, method(meth), access]
37
+ meta_class.send :remove_method, meth
38
+ end
39
+ end
40
+
41
+ # Add a method, to redirect calls to the target.
42
+ methods_to_redirect.each do |meth|
43
+ meta_class.send :define_method, meth do |*args, &block|
44
+ target.send meth, *args, &block
45
+ end
46
+ end
47
+
48
+ redirection_stack.push [target, methods_overridden, methods_to_redirect]
49
+
50
+ target
51
+ end
52
+
53
+ protected
54
+ def pop_redirection_target
55
+ meta_class = class << self; self; end
56
+
57
+ target, methods_to_recreate, methods_to_remove = redirection_stack.pop
58
+
59
+ # Remove the redirection methods
60
+ methods_to_remove.reverse_each do |meth|
61
+ meta_class.send :remove_method, meth
62
+ end
63
+
64
+ # Replace with the previous versions of the methods.
65
+ methods_to_recreate.reverse_each do |meth, reference, access|
66
+ meta_class.send :define_method, meth, reference
67
+ meta_class.send access, meth unless access == :public
68
+ end
69
+
70
+ target
71
+ end
72
+
73
+ # Direct access to the redirection stack.
74
+ private
75
+ def redirection_stack
76
+ @_redirection_stack ||= []
77
+ end
78
+ end
79
+ end
80
+
81
+ class Object
82
+ include Fidgit::RedirectorMethods
83
83
  end
@@ -1,123 +1,123 @@
1
- # encoding: utf-8
2
-
3
- module Fidgit
4
- # An object that manages Schema values. Usually loaded from a YAML file.
5
- #
6
- # @example
7
- # schema = Schema.new(YAML.load(file.read('default_schema.yml')))
8
- # default_color = schema.default(Element, :disabled, :color)
9
- # schema.merge_schema!(YAML.load(file.read('override_schema.yml'))
10
- # overridden_color = schema.default(Element, :disabled, :color)
11
- class Schema
12
- CONSTANT_PREFIX = '?'
13
-
14
- # @param [Hash<Symbol => Hash>] schema data containing
15
- def initialize(schema)
16
- @constants = {}
17
- @elements = {}
18
-
19
- merge_schema! schema
20
- end
21
-
22
- # Merge in a hash containing constant values.
23
- #
24
- # @param [Hash<Symbol => Hash>] constants_hash Containing :colors, :constants and :elements hashes.
25
- def merge_schema!(schema)
26
- merge_constants!(schema[:constants]) if schema[:constants]
27
- merge_elements!(schema[:elements]) if schema[:elements]
28
-
29
- self
30
- end
31
-
32
- # Merge in a hash containing constant values. Arrays will be resolved as colors in RGBA or RGB format.
33
- #
34
- # @param [Hash<Symbol => Object>] constants_hash
35
- def merge_constants!(constants_hash)
36
- constants_hash.each_pair do |name, value|
37
- @constants[name] = case value
38
- when Array
39
- case value.size
40
- when 3 then Gosu::Color.rgb(*value)
41
- when 4 then Gosu::Color.rgba(*value)
42
- else
43
- raise "Colors must be in 0..255, RGB or RGBA array format"
44
- end
45
- else
46
- value
47
- end
48
- end
49
-
50
- self
51
- end
52
-
53
- # Merge in a hash containing default values for each element.
54
- #
55
- # @param [Hash<Symbol => Hash>] elements_hash
56
- def merge_elements!(elements_hash)
57
- elements_hash.each_pair do |klass_names, data|
58
- klass = Fidgit
59
- klass_names.to_s.split('::').each do |klass_name|
60
- klass = klass.const_get klass_name
61
- end
62
-
63
- raise "elements must be names of classes derived from #{Element}" unless klass.ancestors.include? Fidgit::Element
64
- @elements[klass] ||= {}
65
- @elements[klass].deep_merge! data
66
- end
67
-
68
- self
69
- end
70
-
71
- # Get the constant value associated with +name+.
72
- #
73
- # @param [Symbol] name
74
- # @return [Object]
75
- def constant(name)
76
- @constants[name]
77
- end
78
-
79
- # @param [Class] klass Class to look for defaults for.
80
- # @param [Symbol, Array<Symbol>] names Hash names to search for in that class's schema.
81
- def default(klass, names)
82
- raise ArgumentError, "#{klass} is not a descendent of the #{Element} class" unless klass.ancestors.include? Element
83
- value = default_internal(klass, Array(names), true)
84
- raise("Failed to find named value #{names.inspect} for class #{klass}") unless value
85
- value
86
- end
87
-
88
- protected
89
- # @param [Class] klass Class to look for defaults for.
90
- # @param [Array<Symbol>] names Hash names to search for in that class's schema.
91
- # @param [Boolean] default_to_outer Whether to default to an outer value (used internally)
92
- def default_internal(klass, names, default_to_outer)
93
- # Find the value by moving through the nested hash via the names.
94
- value = @elements[klass]
95
-
96
- names.each do |name|
97
- break unless value.is_a? Hash
98
- value = value.has_key?(name) ? value[name] : nil
99
- end
100
-
101
- # Convert the value to a color/constant if they are symbols.
102
- value = if value.is_a? String and value[0] == CONSTANT_PREFIX
103
- str = value[1..-1]
104
- constant(str.to_sym) || value # If the value isn't a constant, return the string.
105
- else
106
- value
107
- end
108
-
109
- # If we didn't find the value for this class, default to parent class value.
110
- if value.nil? and klass != Element and klass.ancestors.include? Element
111
- # Check if any ancestors define the fully named value.
112
- value = default_internal(klass.superclass, names, false)
113
- end
114
-
115
- if value.nil? and default_to_outer and names.size > 1
116
- # Check the outer values (e.g. if [:hover, :color] is not defined, try [:color]).
117
- value = default_internal(klass, names[1..-1], true)
118
- end
119
-
120
- value
121
- end
122
- end
123
- end
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ # An object that manages Schema values. Usually loaded from a YAML file.
5
+ #
6
+ # @example
7
+ # schema = Schema.new(YAML.load(file.read('default_schema.yml')))
8
+ # default_color = schema.default(Element, :disabled, :color)
9
+ # schema.merge_schema!(YAML.load(file.read('override_schema.yml'))
10
+ # overridden_color = schema.default(Element, :disabled, :color)
11
+ class Schema
12
+ CONSTANT_PREFIX = '?'
13
+
14
+ # @param [Hash<Symbol => Hash>] schema data containing
15
+ def initialize(schema)
16
+ @constants = {}
17
+ @elements = {}
18
+
19
+ merge_schema! schema
20
+ end
21
+
22
+ # Merge in a hash containing constant values.
23
+ #
24
+ # @param [Hash<Symbol => Hash>] constants_hash Containing :colors, :constants and :elements hashes.
25
+ def merge_schema!(schema)
26
+ merge_constants!(schema[:constants]) if schema[:constants]
27
+ merge_elements!(schema[:elements]) if schema[:elements]
28
+
29
+ self
30
+ end
31
+
32
+ # Merge in a hash containing constant values. Arrays will be resolved as colors in RGBA or RGB format.
33
+ #
34
+ # @param [Hash<Symbol => Object>] constants_hash
35
+ def merge_constants!(constants_hash)
36
+ constants_hash.each_pair do |name, value|
37
+ @constants[name] = case value
38
+ when Array
39
+ case value.size
40
+ when 3 then Gosu::Color.rgb(*value)
41
+ when 4 then Gosu::Color.rgba(*value)
42
+ else
43
+ raise "Colors must be in 0..255, RGB or RGBA array format"
44
+ end
45
+ else
46
+ value
47
+ end
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ # Merge in a hash containing default values for each element.
54
+ #
55
+ # @param [Hash<Symbol => Hash>] elements_hash
56
+ def merge_elements!(elements_hash)
57
+ elements_hash.each_pair do |klass_names, data|
58
+ klass = Fidgit
59
+ klass_names.to_s.split('::').each do |klass_name|
60
+ klass = klass.const_get klass_name
61
+ end
62
+
63
+ raise "elements must be names of classes derived from #{Element}" unless klass.ancestors.include? Fidgit::Element
64
+ @elements[klass] ||= {}
65
+ @elements[klass].deep_merge! data
66
+ end
67
+
68
+ self
69
+ end
70
+
71
+ # Get the constant value associated with +name+.
72
+ #
73
+ # @param [Symbol] name
74
+ # @return [Object]
75
+ def constant(name)
76
+ @constants[name]
77
+ end
78
+
79
+ # @param [Class] klass Class to look for defaults for.
80
+ # @param [Symbol, Array<Symbol>] names Hash names to search for in that class's schema.
81
+ def default(klass, names)
82
+ raise ArgumentError, "#{klass} is not a descendent of the #{Element} class" unless klass.ancestors.include? Element
83
+ value = default_internal(klass, Array(names), true)
84
+ raise("Failed to find named value #{names.inspect} for class #{klass}") unless value
85
+ value
86
+ end
87
+
88
+ protected
89
+ # @param [Class] klass Class to look for defaults for.
90
+ # @param [Array<Symbol>] names Hash names to search for in that class's schema.
91
+ # @param [Boolean] default_to_outer Whether to default to an outer value (used internally)
92
+ def default_internal(klass, names, default_to_outer)
93
+ # Find the value by moving through the nested hash via the names.
94
+ value = @elements[klass]
95
+
96
+ names.each do |name|
97
+ break unless value.is_a? Hash
98
+ value = value.has_key?(name) ? value[name] : nil
99
+ end
100
+
101
+ # Convert the value to a color/constant if they are symbols.
102
+ value = if value.is_a? String and value[0] == CONSTANT_PREFIX
103
+ str = value[1..-1]
104
+ constant(str.to_sym) || value # If the value isn't a constant, return the string.
105
+ else
106
+ value
107
+ end
108
+
109
+ # If we didn't find the value for this class, default to parent class value.
110
+ if value.nil? and klass != Element and klass.ancestors.include? Element
111
+ # Check if any ancestors define the fully named value.
112
+ value = default_internal(klass.superclass, names, false)
113
+ end
114
+
115
+ if value.nil? and default_to_outer and names.size > 1
116
+ # Check the outer values (e.g. if [:hover, :color] is not defined, try [:color]).
117
+ value = default_internal(klass, names[1..-1], true)
118
+ end
119
+
120
+ value
121
+ end
122
+ end
123
+ end
@@ -1,106 +1,106 @@
1
- # encoding: utf-8
2
-
3
- module Fidgit
4
- class Selection
5
- MIN_DRAG_DISTANCE = 2
6
-
7
- def size; @items.size; end
8
- def empty?; @items.empty?; end
9
- def [](index); @items[index]; end
10
- def each(&block); @items.each(&block); end
11
- def to_a; @items.dup; end
12
- def include?(object); @items.include? object; end
13
-
14
- # Current being dragged?
15
- def dragging?; @dragging; end
16
-
17
- # Actually moved during a dragging operation?
18
- def moved?; @moved; end
19
-
20
- def initialize
21
- @items = []
22
- @moved = false
23
- @dragging = false
24
- end
25
-
26
- def add(object)
27
- object.selected = true
28
- @items.push(object)
29
-
30
- self
31
- end
32
-
33
- def remove(object)
34
- @items.delete(object)
35
- object.selected = false
36
- object.dragging = false
37
-
38
- self
39
- end
40
-
41
- def clear
42
- end_drag if dragging?
43
- @items.each { |o| o.selected = false; o.dragging = false }
44
- @items.clear
45
-
46
- self
47
- end
48
-
49
- def begin_drag(x, y)
50
- @initial_x, @initial_y = x, y
51
- @last_x, @last_y = x, y
52
- @dragging = true
53
- @moved = false
54
-
55
- self
56
- end
57
-
58
- def end_drag
59
- @items.each do |object|
60
- object.x, object.y = object.x.round, object.y.round
61
- object.dragging = false
62
- end
63
- @dragging = false
64
- @moved = false
65
-
66
- self
67
- end
68
-
69
- # Move all dragged object back to original positions.
70
- def reset_drag
71
- if moved?
72
- @items.each do |o|
73
- o.x += @initial_x - @last_x
74
- o.y += @initial_y - @last_y
75
- end
76
- end
77
-
78
- self.end_drag
79
-
80
- self
81
- end
82
-
83
- def update_drag(x, y)
84
- x, y = x.round, y.round
85
-
86
- # If the mouse has been dragged far enough from the initial click position, then 'pick up' the objects and drag.
87
- unless moved?
88
- if distance(@initial_x, @initial_y, x, y) > MIN_DRAG_DISTANCE
89
- @items.each { |o| o.dragging = true }
90
- @moved = true
91
- end
92
- end
93
-
94
- if moved?
95
- @items.each do |o|
96
- o.x += x - @last_x
97
- o.y += y - @last_y
98
- end
99
-
100
- @last_x, @last_y = x, y
101
- end
102
-
103
- self
104
- end
105
- end
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class Selection
5
+ MIN_DRAG_DISTANCE = 2
6
+
7
+ def size; @items.size; end
8
+ def empty?; @items.empty?; end
9
+ def [](index); @items[index]; end
10
+ def each(&block); @items.each(&block); end
11
+ def to_a; @items.dup; end
12
+ def include?(object); @items.include? object; end
13
+
14
+ # Current being dragged?
15
+ def dragging?; @dragging; end
16
+
17
+ # Actually moved during a dragging operation?
18
+ def moved?; @moved; end
19
+
20
+ def initialize
21
+ @items = []
22
+ @moved = false
23
+ @dragging = false
24
+ end
25
+
26
+ def add(object)
27
+ object.selected = true
28
+ @items.push(object)
29
+
30
+ self
31
+ end
32
+
33
+ def remove(object)
34
+ @items.delete(object)
35
+ object.selected = false
36
+ object.dragging = false
37
+
38
+ self
39
+ end
40
+
41
+ def clear
42
+ end_drag if dragging?
43
+ @items.each { |o| o.selected = false; o.dragging = false }
44
+ @items.clear
45
+
46
+ self
47
+ end
48
+
49
+ def begin_drag(x, y)
50
+ @initial_x, @initial_y = x, y
51
+ @last_x, @last_y = x, y
52
+ @dragging = true
53
+ @moved = false
54
+
55
+ self
56
+ end
57
+
58
+ def end_drag
59
+ @items.each do |object|
60
+ object.x, object.y = object.x.round, object.y.round
61
+ object.dragging = false
62
+ end
63
+ @dragging = false
64
+ @moved = false
65
+
66
+ self
67
+ end
68
+
69
+ # Move all dragged object back to original positions.
70
+ def reset_drag
71
+ if moved?
72
+ @items.each do |o|
73
+ o.x += @initial_x - @last_x
74
+ o.y += @initial_y - @last_y
75
+ end
76
+ end
77
+
78
+ self.end_drag
79
+
80
+ self
81
+ end
82
+
83
+ def update_drag(x, y)
84
+ x, y = x.round, y.round
85
+
86
+ # If the mouse has been dragged far enough from the initial click position, then 'pick up' the objects and drag.
87
+ unless moved?
88
+ if distance(@initial_x, @initial_y, x, y) > MIN_DRAG_DISTANCE
89
+ @items.each { |o| o.dragging = true }
90
+ @moved = true
91
+ end
92
+ end
93
+
94
+ if moved?
95
+ @items.each do |o|
96
+ o.x += x - @last_x
97
+ o.y += y - @last_y
98
+ end
99
+
100
+ @last_x, @last_y = x, y
101
+ end
102
+
103
+ self
104
+ end
105
+ end
106
106
  end