fidgit 0.0.2alpha

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 (87) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +2 -0
  3. data/COPYING.txt +674 -0
  4. data/Gemfile +4 -0
  5. data/README.textile +138 -0
  6. data/Rakefile +38 -0
  7. data/config/default_schema.yml +180 -0
  8. data/examples/_all_examples.rb +9 -0
  9. data/examples/align_example.rb +56 -0
  10. data/examples/button_and_toggle_button_example.rb +27 -0
  11. data/examples/color_picker_example.rb +17 -0
  12. data/examples/color_well_example.rb +25 -0
  13. data/examples/combo_box_example.rb +24 -0
  14. data/examples/file_dialog_example.rb +42 -0
  15. data/examples/grid_packer_example.rb +29 -0
  16. data/examples/helpers/example_window.rb +17 -0
  17. data/examples/label_example.rb +17 -0
  18. data/examples/list_example.rb +23 -0
  19. data/examples/media/images/head_icon.png +0 -0
  20. data/examples/menu_pane_example.rb +27 -0
  21. data/examples/message_dialog_example.rb +65 -0
  22. data/examples/radio_button_example.rb +37 -0
  23. data/examples/readme_example.rb +32 -0
  24. data/examples/scroll_window_example.rb +49 -0
  25. data/examples/slider_example.rb +30 -0
  26. data/examples/splash_example.rb +42 -0
  27. data/examples/text_area_example.rb +28 -0
  28. data/fidgit.gemspec +28 -0
  29. data/lib/fidgit.rb +4 -0
  30. data/lib/fidgit/chingu_ext/window.rb +6 -0
  31. data/lib/fidgit/clipboard.rb +23 -0
  32. data/lib/fidgit/cursor.rb +38 -0
  33. data/lib/fidgit/elements/button.rb +68 -0
  34. data/lib/fidgit/elements/color_picker.rb +63 -0
  35. data/lib/fidgit/elements/color_well.rb +39 -0
  36. data/lib/fidgit/elements/combo_box.rb +85 -0
  37. data/lib/fidgit/elements/composite.rb +17 -0
  38. data/lib/fidgit/elements/container.rb +187 -0
  39. data/lib/fidgit/elements/element.rb +252 -0
  40. data/lib/fidgit/elements/file_browser.rb +152 -0
  41. data/lib/fidgit/elements/grid_packer.rb +219 -0
  42. data/lib/fidgit/elements/group.rb +66 -0
  43. data/lib/fidgit/elements/horizontal_packer.rb +12 -0
  44. data/lib/fidgit/elements/label.rb +77 -0
  45. data/lib/fidgit/elements/list.rb +47 -0
  46. data/lib/fidgit/elements/menu_pane.rb +149 -0
  47. data/lib/fidgit/elements/packer.rb +42 -0
  48. data/lib/fidgit/elements/radio_button.rb +86 -0
  49. data/lib/fidgit/elements/scroll_area.rb +75 -0
  50. data/lib/fidgit/elements/scroll_bar.rb +114 -0
  51. data/lib/fidgit/elements/scroll_window.rb +92 -0
  52. data/lib/fidgit/elements/slider.rb +119 -0
  53. data/lib/fidgit/elements/text_area.rb +351 -0
  54. data/lib/fidgit/elements/toggle_button.rb +67 -0
  55. data/lib/fidgit/elements/tool_tip.rb +35 -0
  56. data/lib/fidgit/elements/vertical_packer.rb +12 -0
  57. data/lib/fidgit/event.rb +99 -0
  58. data/lib/fidgit/gosu_ext/color.rb +123 -0
  59. data/lib/fidgit/history.rb +85 -0
  60. data/lib/fidgit/redirector.rb +83 -0
  61. data/lib/fidgit/schema.rb +123 -0
  62. data/lib/fidgit/selection.rb +106 -0
  63. data/lib/fidgit/standard_ext/hash.rb +21 -0
  64. data/lib/fidgit/states/dialog_state.rb +42 -0
  65. data/lib/fidgit/states/file_dialog.rb +24 -0
  66. data/lib/fidgit/states/gui_state.rb +301 -0
  67. data/lib/fidgit/states/message_dialog.rb +61 -0
  68. data/lib/fidgit/thumbnail.rb +29 -0
  69. data/lib/fidgit/version.rb +5 -0
  70. data/lib/fidgit/window.rb +19 -0
  71. data/media/images/arrow.png +0 -0
  72. data/media/images/file_directory.png +0 -0
  73. data/media/images/file_file.png +0 -0
  74. data/media/images/pixel.png +0 -0
  75. data/spec/fidgit/elements/helpers/helper.rb +3 -0
  76. data/spec/fidgit/elements/label_spec.rb +49 -0
  77. data/spec/fidgit/event_spec.rb +149 -0
  78. data/spec/fidgit/gosu_ext/color_spec.rb +130 -0
  79. data/spec/fidgit/gosu_ext/helpers/helper.rb +3 -0
  80. data/spec/fidgit/helpers/helper.rb +4 -0
  81. data/spec/fidgit/helpers/tex_play_helper.rb +9 -0
  82. data/spec/fidgit/history_spec.rb +144 -0
  83. data/spec/fidgit/redirector_spec.rb +78 -0
  84. data/spec/fidgit/schema_spec.rb +67 -0
  85. data/spec/fidgit/schema_test.yml +32 -0
  86. data/spec/fidgit/thumbnail_spec.rb +50 -0
  87. metadata +177 -0
@@ -0,0 +1,219 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ # A vertically aligned element packing container.
5
+
6
+ class GridPacker < Packer
7
+ # @return [Integer]
8
+ attr_reader :num_rows
9
+ # @return [Integer]
10
+ attr_reader :num_columns
11
+
12
+ # @note Currently only supports +num_columns+ mode (not +num_rows+).
13
+ #
14
+ # @param (see Packer#initialize)
15
+ #
16
+ # @option (see Packer#initialize)
17
+ # @option options [Integer] :num_columns Maximum number of columns to use (incompatible with :num_rows)
18
+ # @option options [Integer] :num_rows Maximum number of rows to use (incompatible with :num_columns)
19
+ def initialize(options = {})
20
+ options = {
21
+ cell_border_color: default(:cell_border_color),
22
+ cell_background_color: default(:cell_background_color),
23
+ cell_border_thickness: default(:cell_border_thickness),
24
+ }.merge! options
25
+
26
+ @num_columns = options[:num_columns]
27
+ @num_rows = options[:num_rows]
28
+ raise ArgumentError, "options :num_rows and :num_columns are not compatible" if @num_rows and @num_columns
29
+
30
+ @cell_border_color = options[:cell_border_color].dup
31
+ @cell_border_thickness = options[:cell_border_thickness]
32
+ @cell_background_color = options[:cell_background_color].dup
33
+
34
+ @type = @num_rows ? :fixed_rows : :fixed_columns
35
+
36
+ super options
37
+ end
38
+
39
+ protected
40
+ def layout
41
+ rearrange
42
+ repack
43
+ end
44
+
45
+ protected
46
+ # Rearrange the cells based on changes to the number of rows/columns or adding/removing elements.
47
+ def rearrange
48
+ # Calculate the number of the dynamic dimension.
49
+ case @type
50
+ when :fixed_rows
51
+ @num_columns = (size / @num_rows.to_f).ceil
52
+ when :fixed_columns
53
+ @num_rows = (size / @num_columns.to_f).ceil
54
+ end
55
+
56
+ # Create an array containing all the rows.
57
+ @rows = case @type
58
+ when :fixed_rows
59
+ # Rearrange the list, arranged by columns, into rows.
60
+ rows = Array.new(@num_rows) { [] }
61
+ @children.each_with_index do |child, i|
62
+ rows[i % @num_rows].push child
63
+ end
64
+ rows
65
+ when :fixed_columns
66
+ @children.each_slice(@num_columns).to_a
67
+ end
68
+
69
+ nil
70
+ end
71
+
72
+ protected
73
+ # Repack all the elements into their positions.
74
+ def repack
75
+ @widths = Array.new(@num_columns, 0)
76
+ @heights = Array.new(@num_rows, 0)
77
+
78
+ filled_columns = []
79
+ filled_rows = []
80
+
81
+ # Calculate the maximum widths of each column and the maximum height of each row.
82
+ @rows.each_with_index do |row, row_num|
83
+ row.each_with_index do |element, column_num|
84
+ fills = (element.align_h == :fill)
85
+ @widths[column_num] = [fills ? 0 : element.outer_width, @widths[column_num] || 0].max
86
+ filled_columns.push fills
87
+
88
+ fills = (element.align_v == :fill)
89
+ @heights[row_num] = [fills ? 0 : element.outer_height, @heights[row_num] || 0].max
90
+ filled_rows.push fills
91
+ end
92
+ end
93
+
94
+ # Expand the size of each filled column to make the minimum size required.
95
+ num_filled_columns = filled_columns.select {|value| value }.count
96
+ if num_filled_columns > 0
97
+ total_width = @widths.inject(0, :+) + (padding_left + padding_right) + ((@num_columns - 1) * spacing_h)
98
+ if total_width < min_width
99
+ extra_width = total_width / num_filled_columns
100
+ filled_columns.each_with_index do |filled, i|
101
+ @widths[i] += extra_width if filled
102
+ end
103
+ end
104
+ end
105
+
106
+ # Expand the size of each filled row to make the minimum size required.
107
+ num_filled_rows = filled_rows.select {|value| value }.count
108
+ if num_filled_rows > 0
109
+ total_height = @heights.inject(0, :+) + (padding_left + padding_right) + ((@num_rows - 1) * spacing_v)
110
+ if total_height < min_height
111
+ extra_height = total_height / num_filled_rows
112
+ filled_rows.each_with_index do |filled, i|
113
+ @heights[i] += extra_height if filled
114
+ end
115
+ end
116
+ end
117
+
118
+ # Actually place all the elements into the grid positions, modified by valign and align.
119
+ current_y = y + padding_top
120
+ @rows.each_with_index do |row, row_num|
121
+ current_x = x + padding_left
122
+
123
+ row.each_with_index do |element, column_num|
124
+ element.x = current_x + element.border_thickness
125
+
126
+ case element.align_h # Take horizontal alignment into consideration.
127
+ when :fill
128
+ if element.width < @widths[column_num]
129
+ element.width = @widths[column_num]
130
+ element.send :repack if element.is_a? GridPacker
131
+ end
132
+ when :center
133
+ element.x += (@widths[column_num] - element.width) / 2
134
+ when :right
135
+ element.x += @widths[column_num] - element.width
136
+ end
137
+
138
+ current_x += @widths[column_num]
139
+ current_x += spacing_h unless column_num == @num_columns - 1
140
+
141
+ element.y = current_y + element.border_thickness
142
+
143
+ case element.align_v # Take horizontal alignment into consideration.
144
+ when :fill
145
+ if element.height < @heights[row_num]
146
+ element.height = @heights[row_num]
147
+ element.send :repack if element.is_a? GridPacker
148
+ end
149
+ when :center
150
+ element.y += (@heights[row_num] - element.height) / 2
151
+ when :bottom
152
+ element.y += @heights[row_num] - element.height
153
+ else
154
+ end
155
+ end
156
+
157
+ self.width = current_x - x + padding_left if row_num == 0
158
+
159
+ current_y += @heights[row_num] unless row.empty?
160
+ current_y += spacing_h unless row_num == num_rows - 1
161
+ end
162
+
163
+ self.height = current_y - y + padding_top
164
+
165
+ nil
166
+ end
167
+
168
+ protected
169
+ # @yield The rectangle of each cell within the grid.
170
+ # @yieldparam [Number] x
171
+ # @yieldparam [Number] y
172
+ # @yieldparam [Number] width
173
+ # @yieldparam [Number] height
174
+ def each_cell_rect
175
+ x = self.x + padding_left
176
+
177
+ @widths.each_with_index do |width, column_num|
178
+ y = self.y + padding_top
179
+
180
+ @heights.each_with_index do |height, row_num|
181
+ yield x, y, width, height if @rows[row_num][column_num]
182
+ y += height + spacing_v
183
+ end
184
+
185
+ x += width + spacing_h
186
+ end
187
+
188
+ nil
189
+ end
190
+
191
+ protected
192
+ def draw_background
193
+ super
194
+
195
+ # Draw the cell backgrounds.
196
+ unless @cell_background_color.transparent?
197
+ each_cell_rect do |x, y, width, height|
198
+ draw_rect x, y, width, height, z, @cell_background_color
199
+ end
200
+ end
201
+
202
+ nil
203
+ end
204
+
205
+ protected
206
+ def draw_border
207
+ super
208
+
209
+ # Draw the cell borders.
210
+ if @cell_border_thickness > 0 and not @cell_border_color.transparent?
211
+ each_cell_rect do |x, y, width, height|
212
+ draw_frame x, y, width, height, @cell_border_thickness, z, @cell_border_color
213
+ end
214
+ end
215
+
216
+ nil
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class Group < Packer
5
+ attr_reader :selected
6
+
7
+ event :changed
8
+
9
+ def value; @selected ? @selected.value : nil; end
10
+
11
+ # @example
12
+ # group do
13
+ # pack :horizontal do
14
+ # radio_button 1, text: '1', checked: true
15
+ # radio_button 2, text: '2'
16
+ # subscribe :changed do |sender, value|
17
+ # puts value
18
+ # end
19
+ # end
20
+ # end
21
+ #
22
+ # @param (see Packer#initialize)
23
+ #
24
+ # @option (see Packer#initialize)
25
+ def initialize(options = {}, &block)
26
+ super(options)
27
+
28
+ @selected = nil
29
+ @buttons = []
30
+ end
31
+
32
+ def add_button(button)
33
+ @buttons.push button
34
+ self.value = button.value if button.checked?
35
+ nil
36
+ end
37
+
38
+ def remove_button(button)
39
+ self.value = nil if button == @selected
40
+ @buttons.delete button
41
+ nil
42
+ end
43
+
44
+ # @example
45
+ # @my_group = group do
46
+ # pack :horizontal do
47
+ # radio_button(1, text: '1', checked: true)
48
+ # radio_button(2, text: '2')
49
+ # end
50
+ # end
51
+ #
52
+ # # later
53
+ # @my_group.value = 2
54
+ def value=(value)
55
+ if value != self.value
56
+ button = @buttons.find { |b| b.value == value }
57
+ @selected.uncheck if @selected and @selected.checked?
58
+ @selected = button
59
+ @selected.check if @selected and not @selected.checked?
60
+ publish :changed, self.value
61
+ end
62
+
63
+ value
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ # A vertically aligned element packing container.
5
+ class HorizontalPacker < GridPacker
6
+ def initialize(options = {})
7
+ options[:num_rows] = 1
8
+
9
+ super options
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class Label < Element
5
+ attr_accessor :color, :background_color, :border_color
6
+ attr_reader :text, :icon
7
+
8
+ def text=(value)
9
+ @text = value
10
+ recalc
11
+ nil
12
+ end
13
+
14
+ def icon=(value)
15
+ @icon = value
16
+ recalc
17
+ nil
18
+ end
19
+
20
+ # @param (see Element#initialize)
21
+ # @param [String] text The string to display in the label.
22
+ #
23
+ # @option (see Element#initialize)
24
+ # @option options [Fidgit::Thumbnail, Gosu::Image, nil] :icon (nil)
25
+ def initialize(text, options = {})
26
+ options = {
27
+ color: default(:color),
28
+ background_color: default(:background_color),
29
+ border_color: default(:border_color),
30
+ }.merge! options
31
+
32
+ @text = text.dup
33
+ @icon = options[:icon]
34
+ @color = options[:color].dup
35
+
36
+ super(options)
37
+ end
38
+
39
+ def draw_foreground
40
+ current_x = x + padding_left
41
+ if @icon
42
+ @icon.draw(current_x, y + padding_top, z)
43
+ current_x += @icon.width + padding_left
44
+ end
45
+
46
+ unless @text.empty?
47
+ font.draw(@text, current_x, y + padding_top, z, 1, 1, @color)
48
+ end
49
+
50
+ nil
51
+ end
52
+
53
+ protected
54
+ def layout
55
+ if @icon
56
+ if @text.empty?
57
+ rect.width = [padding_left + @icon.width + padding_right, width].max
58
+ rect.height = [padding_top + @icon.height + padding_bottom, height].max
59
+ else
60
+ # Todo: Use padding_h inside here? Probably by making this a Composite.
61
+ rect.width = [padding_left + @icon.width + [padding_left + padding_right].max + font.text_width(@text) + padding_right, width].max
62
+ rect.height = [padding_top + [@icon.height, font_size].max + padding_bottom, height].max
63
+ end
64
+ else
65
+ if @text.empty?
66
+ rect.width = [padding_left + padding_right, width].max
67
+ rect.height = [padding_top + padding_bottom, height].max
68
+ else
69
+ rect.width = [padding_left + font.text_width(@text) + padding_right, width].max
70
+ rect.height = [padding_top + font_size + padding_bottom, height].max
71
+ end
72
+ end
73
+
74
+ nil
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class List < Composite
5
+ class Item < RadioButton
6
+ end
7
+
8
+ event :changed
9
+
10
+ def size; @items.size; end
11
+ def clear; @items.clear; end
12
+
13
+ def initialize(options = {})
14
+ options = {
15
+ background_color: default(:background_color),
16
+ border_color: default(:border_color),
17
+ }.merge! options
18
+
19
+ super options
20
+
21
+ group do
22
+ subscribe :changed do |sender, value|
23
+ publish :changed, value
24
+ end
25
+
26
+ @items = pack :vertical, spacing: 0
27
+ end
28
+ end
29
+
30
+ # @param [String] text
31
+ # @option options [Gosu::Image] :icon
32
+ def item(text, value, options = {}, &block)
33
+ Item.new(text, value, { parent: @items }.merge!(options), &block)
34
+ end
35
+
36
+ protected
37
+ def layout
38
+ super
39
+ if @items
40
+ max_width = @items.each.to_a.map {|c| c.width }.max || 0
41
+ @items.each {|c| c.rect.width = max_width }
42
+ end
43
+
44
+ nil
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,149 @@
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class MenuPane < Composite
5
+ # An item within the menu.
6
+ class Item < Button
7
+ attr_reader :value, :shortcut
8
+
9
+ # @param (see Button#initialize)
10
+ #
11
+ # @option (see Button#initialize)
12
+ # @param [any] value Value if the user picks this item
13
+ # @option options [Boolean] :enabled (true)
14
+ # @option options [String] :shortcut ('')
15
+ def initialize(text, value, options = {})
16
+ options = {
17
+ enabled: true,
18
+ border_color: default(:border_color),
19
+ }.merge! options
20
+
21
+ @value = value
22
+ @enabled = [true, false].include?(options[:enabled]) ? options[:enabled] : true
23
+ @shortcut = options[:shortcut] || ''
24
+
25
+ super(text, options)
26
+ end
27
+
28
+ def draw_foreground
29
+ super
30
+ unless @shortcut.empty?
31
+ font.draw_rel("#{@shortcut}", rect.right - padding_right, y + ((height - font_size) / 2).floor, z, 1, 0, 1, 1, color)
32
+ end
33
+
34
+ nil
35
+ end
36
+
37
+ protected
38
+ def layout
39
+ super
40
+ rect.width += font.text_width(" #{@shortcut}") unless @shortcut.empty?
41
+ nil
42
+ end
43
+ end
44
+
45
+ class Separator < Label
46
+ # @param (see Item#initialize)
47
+ #
48
+ # @option (see Item#initialize)
49
+ def initialize(options = {})
50
+ options = {
51
+ height: default(:line_height),
52
+ background_color: default(:background_color),
53
+ padding: 0,
54
+ }.merge! options
55
+
56
+ super '', options
57
+ end
58
+ end
59
+
60
+ event :selected
61
+
62
+ def index(value); @items.index find(value); end
63
+ def size; @items.size; end
64
+ def [](index); @items[index]; end
65
+
66
+ # @option (see Composite#initialize)
67
+ # @option options [Float] :x (cursor x, if in a GuiState)
68
+ # @option options [Float] :y (cursor y, if in a GuiState)
69
+ # @option options [Boolean] :show (true) Whether to show immediately (show later with #show).
70
+ def initialize(options = {}, &block)
71
+ options = {
72
+ background_color: default(:color),
73
+ z: Float::INFINITY,
74
+ show: true,
75
+ }.merge! options
76
+
77
+ state = $window.current_game_state
78
+ if state.is_a? GuiState
79
+ cursor = $window.current_game_state.cursor
80
+ options = {
81
+ x: cursor.x,
82
+ y: cursor.y,
83
+ }.merge! options
84
+ end
85
+
86
+ super(options)
87
+
88
+ @items = pack :vertical, spacing: 0, padding: 0
89
+
90
+ if options[:show] and state.is_a? GuiState
91
+ show
92
+ end
93
+ end
94
+
95
+ def find(value)
96
+ @items.find {|c| c.value == value }
97
+ end
98
+
99
+ def separator(options = {})
100
+ options[:z] = z
101
+
102
+ Separator.new({ parent: @items }.merge!(options))
103
+ end
104
+
105
+ def item(text, value, options = {}, &block)
106
+ options[:z] = z
107
+ item = Item.new(text, value, { parent: @items }.merge!(options), &block)
108
+
109
+ item.subscribe :left_mouse_button, method(:item_selected)
110
+ item.subscribe :right_mouse_button, method(:item_selected)
111
+
112
+ item
113
+ end
114
+
115
+ def item_selected(sender, x, y)
116
+ publish(:selected, sender.value)
117
+
118
+ $window.game_state_manager.current_game_state.hide_menu
119
+
120
+ nil
121
+ end
122
+
123
+ def show
124
+ $window.game_state_manager.current_game_state.show_menu self
125
+ nil
126
+ end
127
+
128
+ protected
129
+ def layout
130
+ super
131
+
132
+ if @items
133
+ # Ensure the menu can't go over the edge of the screen. If it can't be avoided, align with left edge of screen.
134
+ rect.x = [[x, $window.width - width - padding_right].min, 0].max
135
+ rect.y = [[y, $window.height - height - padding_bottom].min, 0].max
136
+
137
+ # Move the actual list if the menu has moved to keep on the screen.
138
+ @items.x = x + padding_left
139
+ @items.y = y + padding_top
140
+
141
+ # Ensure that all items are of the same width.
142
+ max_width = @items.each.to_a.map {|c| c.width }.max || 0
143
+ @items.each {|c| c.rect.width = max_width }
144
+ end
145
+
146
+ nil
147
+ end
148
+ end
149
+ end