colstrom-fidgit 0.2.7

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 (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +31 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +154 -0
  8. data/Rakefile +38 -0
  9. data/config/default_schema.yml +216 -0
  10. data/examples/_all_examples.rb +9 -0
  11. data/examples/align_example.rb +56 -0
  12. data/examples/button_and_toggle_button_example.rb +38 -0
  13. data/examples/color_picker_example.rb +17 -0
  14. data/examples/color_well_example.rb +25 -0
  15. data/examples/combo_box_example.rb +24 -0
  16. data/examples/file_dialog_example.rb +42 -0
  17. data/examples/grid_packer_example.rb +29 -0
  18. data/examples/helpers/example_window.rb +17 -0
  19. data/examples/label_example.rb +23 -0
  20. data/examples/list_example.rb +23 -0
  21. data/examples/media/images/head_icon.png +0 -0
  22. data/examples/menu_pane_example.rb +27 -0
  23. data/examples/message_dialog_example.rb +65 -0
  24. data/examples/radio_button_example.rb +37 -0
  25. data/examples/readme_example.rb +32 -0
  26. data/examples/scroll_window_example.rb +49 -0
  27. data/examples/slider_example.rb +34 -0
  28. data/examples/splash_example.rb +42 -0
  29. data/examples/text_area_example.rb +33 -0
  30. data/fidgit.gemspec +35 -0
  31. data/lib/fidgit.rb +51 -0
  32. data/lib/fidgit/chingu_ext/window.rb +6 -0
  33. data/lib/fidgit/cursor.rb +38 -0
  34. data/lib/fidgit/elements/button.rb +113 -0
  35. data/lib/fidgit/elements/color_picker.rb +63 -0
  36. data/lib/fidgit/elements/color_well.rb +39 -0
  37. data/lib/fidgit/elements/combo_box.rb +115 -0
  38. data/lib/fidgit/elements/composite.rb +17 -0
  39. data/lib/fidgit/elements/container.rb +210 -0
  40. data/lib/fidgit/elements/element.rb +298 -0
  41. data/lib/fidgit/elements/file_browser.rb +152 -0
  42. data/lib/fidgit/elements/grid.rb +227 -0
  43. data/lib/fidgit/elements/group.rb +64 -0
  44. data/lib/fidgit/elements/horizontal.rb +12 -0
  45. data/lib/fidgit/elements/image_frame.rb +65 -0
  46. data/lib/fidgit/elements/label.rb +85 -0
  47. data/lib/fidgit/elements/list.rb +47 -0
  48. data/lib/fidgit/elements/main_packer.rb +25 -0
  49. data/lib/fidgit/elements/menu_pane.rb +163 -0
  50. data/lib/fidgit/elements/packer.rb +42 -0
  51. data/lib/fidgit/elements/radio_button.rb +86 -0
  52. data/lib/fidgit/elements/scroll_area.rb +68 -0
  53. data/lib/fidgit/elements/scroll_bar.rb +128 -0
  54. data/lib/fidgit/elements/scroll_window.rb +83 -0
  55. data/lib/fidgit/elements/slider.rb +125 -0
  56. data/lib/fidgit/elements/text_area.rb +494 -0
  57. data/lib/fidgit/elements/text_line.rb +92 -0
  58. data/lib/fidgit/elements/toggle_button.rb +67 -0
  59. data/lib/fidgit/elements/tool_tip.rb +35 -0
  60. data/lib/fidgit/elements/vertical.rb +12 -0
  61. data/lib/fidgit/event.rb +159 -0
  62. data/lib/fidgit/gosu_ext/color.rb +136 -0
  63. data/lib/fidgit/gosu_ext/gosu_module.rb +25 -0
  64. data/lib/fidgit/history.rb +91 -0
  65. data/lib/fidgit/redirector.rb +83 -0
  66. data/lib/fidgit/schema.rb +123 -0
  67. data/lib/fidgit/selection.rb +106 -0
  68. data/lib/fidgit/standard_ext/hash.rb +21 -0
  69. data/lib/fidgit/states/dialog_state.rb +52 -0
  70. data/lib/fidgit/states/file_dialog.rb +24 -0
  71. data/lib/fidgit/states/gui_state.rb +331 -0
  72. data/lib/fidgit/states/message_dialog.rb +61 -0
  73. data/lib/fidgit/version.rb +5 -0
  74. data/lib/fidgit/window.rb +19 -0
  75. data/media/images/arrow.png +0 -0
  76. data/media/images/combo_arrow.png +0 -0
  77. data/media/images/file_directory.png +0 -0
  78. data/media/images/file_file.png +0 -0
  79. data/media/images/pixel.png +0 -0
  80. data/spec/fidgit/elements/helpers/helper.rb +3 -0
  81. data/spec/fidgit/elements/helpers/tex_play_helper.rb +9 -0
  82. data/spec/fidgit/elements/image_frame_spec.rb +69 -0
  83. data/spec/fidgit/elements/label_spec.rb +37 -0
  84. data/spec/fidgit/event_spec.rb +210 -0
  85. data/spec/fidgit/gosu_ext/color_spec.rb +130 -0
  86. data/spec/fidgit/gosu_ext/helpers/helper.rb +3 -0
  87. data/spec/fidgit/helpers/helper.rb +4 -0
  88. data/spec/fidgit/history_spec.rb +153 -0
  89. data/spec/fidgit/redirector_spec.rb +78 -0
  90. data/spec/fidgit/schema_spec.rb +67 -0
  91. data/spec/fidgit/schema_test.yml +32 -0
  92. metadata +320 -0
@@ -0,0 +1,25 @@
1
+ module Gosu
2
+ class << self
3
+ alias_method :register_entity_fidgit, :register_entity
4
+
5
+ protected
6
+ def init_entities
7
+ @entities = {}
8
+ end
9
+
10
+ public
11
+ def register_entity(name, image)
12
+ name = name.to_sym
13
+ register_entity_fidgit(name, image)
14
+ @entities[name] = image
15
+ nil
16
+ end
17
+
18
+ public
19
+ def entity(name)
20
+ @entities[name.to_sym]
21
+ end
22
+ end
23
+
24
+ init_entities
25
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ # Manages a history of actions, along with doing, undoing and redoing those actions.
5
+ class History
6
+ # Maximum number of actions in the History before Actions are deleted.
7
+ DEFAULT_MAX_SIZE = 250
8
+
9
+ # An action in the History. Inherit actions from this in order to add them to a History.
10
+ class Action
11
+ # Perform the action.
12
+ def do; raise NotImplementedError, "#{self.class} does not have a do method defined"; end
13
+
14
+ # Reverse the action.
15
+ def undo; raise NotImplementedError, "#{self.class} does not have an undo method defined"; end
16
+ end
17
+
18
+ # Is there an action that can be undone?
19
+ def can_undo?; @last_done >= 0; end
20
+
21
+ # Is there an action that has been undone that can now be redone?
22
+ def can_redo?; @last_done < (@actions.size - 1); end
23
+
24
+ def initialize(max_size = DEFAULT_MAX_SIZE)
25
+ @max_size = max_size
26
+ @actions = []
27
+ @last_done = -1 # Last command that was performed.
28
+ end
29
+
30
+ # Perform a History::Action, adding it to the history.
31
+ # If there are currently any actions that have been undone, they will be permanently lost and cannot be redone.
32
+ #
33
+ # @param [History::Action] action Action to be performed
34
+ def do(action)
35
+ raise ArgumentError, "Parameter, 'action', expected to be a #{Action}, but received: #{action}" unless action.is_a? Action
36
+
37
+ # Remove all undone actions when a new one is performed.
38
+ if can_redo?
39
+ if @last_done == -1
40
+ @actions.clear
41
+ else
42
+ @actions = @actions[0..@last_done]
43
+ end
44
+ end
45
+
46
+ # If history is too big, remove the oldest action.
47
+ if @actions.size >= @max_size
48
+ @actions.shift
49
+ end
50
+
51
+ @last_done = @actions.size
52
+ @actions << action
53
+ action.do
54
+
55
+ nil
56
+ end
57
+
58
+ # Perform a History::Action, replacing the last action that was performed.
59
+ #
60
+ # @param [History::Action] action Action to be performed
61
+ def replace_last(action)
62
+ raise ArgumentError, "Parameter, 'action', expected to be a #{Action}, but received: #{action}" unless action.is_a? Action
63
+
64
+ @actions[@last_done].undo
65
+ @actions[@last_done] = action
66
+ action.do
67
+
68
+ nil
69
+ end
70
+
71
+ # Undo the last action that was performed.
72
+ def undo
73
+ raise "Can't undo unless there are commands in past" unless can_undo?
74
+
75
+ @actions[@last_done].undo
76
+ @last_done -= 1
77
+
78
+ nil
79
+ end
80
+
81
+ # Redo the last action that was undone.
82
+ def redo
83
+ raise "Can't redo if there are no commands in the future" unless can_redo?
84
+
85
+ @last_done += 1
86
+ @actions[@last_done].do
87
+
88
+ nil
89
+ end
90
+ end
91
+ end
@@ -0,0 +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
83
+ end
@@ -0,0 +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
@@ -0,0 +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
106
+ end
@@ -0,0 +1,21 @@
1
+ class Hash
2
+ # Merge not only the hashes, but all nested hashes as well.
3
+ # Written by Stefan Rusterholz (apeiros) from http://www.ruby-forum.com/topic/142809
4
+ def deep_merge!(other)
5
+ merger = lambda do |key, a, b|
6
+ (a.is_a?(Hash) && b.is_a?(Hash)) ? a.merge!(b, &merger) : b
7
+ end
8
+
9
+ merge!(other, &merger)
10
+ end
11
+
12
+ # Merge not only the hashes, but all nested hashes as well.
13
+ # Written by Stefan Rusterholz (apeiros) from http://www.ruby-forum.com/topic/142809
14
+ def deep_merge(other)
15
+ merger = lambda do |key, a, b|
16
+ (a.is_a?(Hash) && b.is_a?(Hash)) ? a.merge(b, &merger) : b
17
+ end
18
+
19
+ merge(other, &merger)
20
+ end
21
+ end