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,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
92
+ end
@@ -0,0 +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
67
+ end
@@ -0,0 +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
35
+ end
@@ -0,0 +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
12
+ end
@@ -0,0 +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
159
+ end
@@ -0,0 +1,136 @@
1
+ # encoding: utf-8
2
+
3
+ module Gosu
4
+ class Color
5
+ # Is the color completely transparent?
6
+ def transparent?; alpha == 0; end
7
+ # Is the color completely opaque?
8
+ def opaque?; alpha == 255; end
9
+
10
+ # RGB in 0..255 format (Alpha assumed 255)
11
+ #
12
+ # @param [Integer] red
13
+ # @param [Integer] green
14
+ # @param [Integer] blue
15
+ # @return [Color]
16
+ def self.rgb(red, green, blue)
17
+ new(255, red, green, blue)
18
+ end
19
+
20
+ # RGBA in 0..255 format
21
+ #
22
+ # @param [Integer] red
23
+ # @param [Integer] green
24
+ # @param [Integer] blue
25
+ # @param [Integer] alpha
26
+ # @return [Color]
27
+ def self.rgba(red, green, blue, alpha)
28
+ new(alpha, red, green, blue)
29
+ end
30
+
31
+ # ARGB in 0..255 format (equivalent to Color.new, but explicit)
32
+ #
33
+ # @param [Integer] alpha
34
+ # @param (see Color.rgb)
35
+ # @return [Color]
36
+ def self.argb(alpha, red, green, blue)
37
+ new(alpha, red, green, blue)
38
+ end
39
+
40
+ # HSV format (alpha assumed to be 255)
41
+ #
42
+ # @param [Float] hue 0.0..360.0
43
+ # @param [Float] saturation 0.0..1.0
44
+ # @param [Float] value 0.0..1.0
45
+ # @return [Color]
46
+ def self.hsv(hue, saturation, value)
47
+ from_hsv(hue, saturation, value)
48
+ end
49
+
50
+ # HSVA format
51
+ #
52
+ # @param [Float] hue 0.0..360.0
53
+ # @param [Float] saturation 0.0..1.0
54
+ # @param [Float] value 0.0..1.0
55
+ # @param [Integer] alpha 1..255
56
+ # @return [Color]
57
+ def self.hsva(hue, saturation, value, alpha)
58
+ from_ahsv(alpha, hue, saturation, value)
59
+ end
60
+
61
+ class << self
62
+ alias_method :ahsv, :from_ahsv
63
+ end
64
+
65
+ # Convert from an RGBA array, as used by TexPlay.
66
+ #
67
+ # @param [Array<Float>] color TexPlay color [r, g, b, a] in range 0.0..1.0
68
+ # @return [Color]
69
+ def self.from_tex_play(color)
70
+ rgba(*color.map {|c| (c * 255).to_i })
71
+ end
72
+
73
+ # Convert to an RGBA array, as used by TexPlay.
74
+ #
75
+ # @return [Array<Float>] TexPlay color array [r, g, b, a] in range 0.0..1.0
76
+ def to_tex_play
77
+ [red / 255.0, green / 255.0, blue / 255.0, alpha / 255.0]
78
+ end
79
+
80
+ # Convert to a 6-digit hexadecimal value, appropriate for passing to the Gosu +<c>+ tag. The alpha channel is ignored.
81
+ #
82
+ # @return [String] RGB hexadecimal string, such as "AABB00"
83
+ def to_hex
84
+ "%02x%02x%02x" % [red, green, blue]
85
+ end
86
+
87
+ # Colorize text for in-line rendering by Gosu.
88
+ # e.g. "frog" => "<c=00ff9a>frog</c>"
89
+ def colorize(text)
90
+ "<c=#{to_hex}>#{text}</c>"
91
+ end
92
+
93
+ # Show the Color as <RGBA [0, 0, 0, 0]> or, if opaque, <RGB [0, 0, 0]> (Gosu default is '(ARGB:0/0/0/0)')
94
+ def to_s
95
+ if opaque?
96
+ "<RGB [#{red}, #{green}, #{blue}]>"
97
+ else
98
+ "<RGBA [#{red}, #{green}, #{blue}, #{alpha}]>"
99
+ end
100
+ end
101
+
102
+ def +(other)
103
+ raise ArgumentError, "Can only add another #{self.class}" unless other.is_a? Color
104
+
105
+ copy = Color.new(0)
106
+
107
+ copy.red = [red + other.red, 255].min
108
+ copy.green = [green + other.green, 255].min
109
+ copy.blue = [blue + other.blue, 255].min
110
+ copy.alpha = [alpha + other.alpha, 255].min
111
+
112
+ copy
113
+ end
114
+
115
+ def -(other)
116
+ raise ArgumentError, "Can only take away another #{self.class}" unless other.is_a? Color
117
+
118
+ copy = Color.new(0)
119
+
120
+ copy.red = [red - other.red, 0].max
121
+ copy.green = [green - other.green, 0].max
122
+ copy.blue = [blue - other.blue, 0].max
123
+ copy.alpha = [alpha - other.alpha, 0].max
124
+
125
+ copy
126
+ end
127
+
128
+ def ==(other)
129
+ if other.is_a? Color
130
+ red == other.red and green == other.green and blue == other.blue and alpha == other.alpha
131
+ else
132
+ false
133
+ end
134
+ end
135
+ end
136
+ end