fidgit 0.0.2alpha

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