colstrom-fidgit 0.2.7

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