fidgit 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/.gitignore +7 -7
  2. data/.rspec +2 -2
  3. data/CHANGELOG.md +30 -30
  4. data/Gemfile +3 -3
  5. data/LICENSE.txt +19 -19
  6. data/README.textile +139 -139
  7. data/Rakefile +37 -37
  8. data/config/default_schema.yml +216 -216
  9. data/examples/_all_examples.rb +9 -9
  10. data/examples/align_example.rb +55 -55
  11. data/examples/button_and_toggle_button_example.rb +37 -37
  12. data/examples/color_picker_example.rb +16 -16
  13. data/examples/color_well_example.rb +24 -24
  14. data/examples/combo_box_example.rb +23 -23
  15. data/examples/file_dialog_example.rb +41 -41
  16. data/examples/grid_packer_example.rb +28 -28
  17. data/examples/helpers/example_window.rb +16 -16
  18. data/examples/label_example.rb +22 -22
  19. data/examples/list_example.rb +22 -22
  20. data/examples/menu_pane_example.rb +26 -26
  21. data/examples/message_dialog_example.rb +64 -64
  22. data/examples/radio_button_example.rb +36 -36
  23. data/examples/readme_example.rb +31 -31
  24. data/examples/scroll_window_example.rb +48 -48
  25. data/examples/slider_example.rb +33 -33
  26. data/examples/splash_example.rb +41 -41
  27. data/examples/text_area_example.rb +32 -32
  28. data/fidgit.gemspec +35 -35
  29. data/lib/fidgit.rb +50 -50
  30. data/lib/fidgit/chingu_ext/window.rb +5 -5
  31. data/lib/fidgit/cursor.rb +37 -37
  32. data/lib/fidgit/elements/button.rb +112 -112
  33. data/lib/fidgit/elements/color_picker.rb +62 -62
  34. data/lib/fidgit/elements/color_well.rb +38 -38
  35. data/lib/fidgit/elements/combo_box.rb +113 -113
  36. data/lib/fidgit/elements/composite.rb +16 -16
  37. data/lib/fidgit/elements/container.rb +208 -208
  38. data/lib/fidgit/elements/element.rb +297 -297
  39. data/lib/fidgit/elements/file_browser.rb +151 -151
  40. data/lib/fidgit/elements/grid.rb +226 -226
  41. data/lib/fidgit/elements/group.rb +64 -64
  42. data/lib/fidgit/elements/horizontal.rb +11 -11
  43. data/lib/fidgit/elements/image_frame.rb +64 -64
  44. data/lib/fidgit/elements/label.rb +84 -84
  45. data/lib/fidgit/elements/list.rb +46 -46
  46. data/lib/fidgit/elements/main_packer.rb +24 -24
  47. data/lib/fidgit/elements/menu_pane.rb +160 -160
  48. data/lib/fidgit/elements/packer.rb +41 -41
  49. data/lib/fidgit/elements/radio_button.rb +85 -85
  50. data/lib/fidgit/elements/scroll_area.rb +67 -67
  51. data/lib/fidgit/elements/scroll_bar.rb +127 -127
  52. data/lib/fidgit/elements/scroll_window.rb +82 -82
  53. data/lib/fidgit/elements/slider.rb +124 -124
  54. data/lib/fidgit/elements/text_area.rb +493 -493
  55. data/lib/fidgit/elements/text_line.rb +91 -91
  56. data/lib/fidgit/elements/toggle_button.rb +66 -66
  57. data/lib/fidgit/elements/tool_tip.rb +34 -34
  58. data/lib/fidgit/elements/vertical.rb +11 -11
  59. data/lib/fidgit/event.rb +158 -158
  60. data/lib/fidgit/gosu_ext/color.rb +135 -135
  61. data/lib/fidgit/gosu_ext/gosu_module.rb +24 -24
  62. data/lib/fidgit/history.rb +90 -90
  63. data/lib/fidgit/redirector.rb +82 -82
  64. data/lib/fidgit/schema.rb +123 -123
  65. data/lib/fidgit/selection.rb +105 -105
  66. data/lib/fidgit/standard_ext/hash.rb +20 -20
  67. data/lib/fidgit/states/dialog_state.rb +51 -51
  68. data/lib/fidgit/states/file_dialog.rb +24 -24
  69. data/lib/fidgit/states/gui_state.rb +329 -329
  70. data/lib/fidgit/states/message_dialog.rb +60 -60
  71. data/lib/fidgit/version.rb +4 -4
  72. data/lib/fidgit/window.rb +19 -19
  73. data/spec/fidgit/elements/helpers/helper.rb +2 -2
  74. data/spec/fidgit/elements/helpers/tex_play_helper.rb +8 -8
  75. data/spec/fidgit/elements/image_frame_spec.rb +68 -68
  76. data/spec/fidgit/elements/label_spec.rb +36 -36
  77. data/spec/fidgit/event_spec.rb +209 -209
  78. data/spec/fidgit/gosu_ext/color_spec.rb +129 -129
  79. data/spec/fidgit/gosu_ext/helpers/helper.rb +2 -2
  80. data/spec/fidgit/helpers/helper.rb +3 -3
  81. data/spec/fidgit/history_spec.rb +153 -153
  82. data/spec/fidgit/redirector_spec.rb +77 -77
  83. data/spec/fidgit/schema_spec.rb +66 -66
  84. data/spec/fidgit/schema_test.yml +32 -32
  85. metadata +67 -22
@@ -1,152 +1,152 @@
1
- # encoding: utf-8
2
-
3
- module Fidgit
4
- class FileBrowser < Composite
5
- VALID_TYPES = [:open, :save]
6
-
7
- event :selected
8
-
9
- attr_reader :pattern, :base_directory
10
-
11
- def show_extension?; @show_extension; end
12
- def directory
13
- dir = File.join(*@directories)
14
- dir = File.join(@base_directory, dir) unless @base_directory.empty?
15
- dir
16
- end
17
- def file_name; @file_name_text.text; end
18
- def file_path; File.join(directory, file_name); end
19
-
20
- # @param [Symbol] type One of :open, :save
21
- # @option options [String] :base_directory ('') Outermost directory that the browser will see.
22
- # @option options [String] :directory (current working directory).
23
- # @option options [String] :file_name ('') Initially selected file in the directory.
24
- # @option options [String] :pattern ('*.*')
25
- # @option options [Boolean] :show_extension (true)
26
- def initialize(type, options = {})
27
- options = {
28
- base_directory: '',
29
- directory: Dir.pwd,
30
- file_name: '',
31
- pattern: default(:pattern),
32
- show_extension: default(:show_extension),
33
- width: 400,
34
- save_text: "Save",
35
- open_text: "Open",
36
- cancel_text: "Cancel",
37
- }.merge! options
38
-
39
- @type = type
40
- raise ArgumentError, "type must be one of #{VALID_TYPES}, not #{@type}" unless VALID_TYPES.include? @type
41
-
42
- @pattern = options[:pattern]
43
- @show_extension = options[:show_extension]
44
- @base_directory = options[:base_directory].chomp File::SEPARATOR
45
-
46
- @directories = options[:directory].sub(/^#{@base_directory}/, '').split(File::SEPARATOR)
47
- if @directories.first == ''
48
- @directories[0] = File::SEPARATOR
49
- end
50
-
51
- super options
52
-
53
- vertical do
54
- @nav_buttons = horizontal padding: 0, spacing: 2
55
-
56
- @scroll_window = scroll_window(height: 250, width: options[:width]) do
57
- @files_list = list(width: options[:width]) do
58
- subscribe :changed do |sender, file_path|
59
- if file_path
60
- file_name = File.basename file_path
61
- if File.directory? file_path
62
- @directories.push file_name
63
- create_nav_buttons
64
- update_files_list
65
- else
66
- @file_name_text.text = file_name
67
- end
68
- end
69
- end
70
- end
71
- end
72
-
73
- @file_name_text = text_area(text: options[:file_name], max_height: font.height * 1.5, width: options[:width], border_thickness: 1)
74
-
75
- create_nav_buttons
76
-
77
- horizontal align: :center, padding: 0 do
78
- @action_button = button(options[:"#{type}_text"]) do
79
- publish :selected, @type, file_path
80
- end
81
-
82
- button(options[:cancel_text]) do
83
- publish :selected, :cancel, file_path
84
- end
85
- end
86
-
87
- # Ensure that the open/save button is enabled only when the path is sensible.
88
- @file_name_text.subscribe :changed do |sender, text|
89
- @action_button.enabled = case @type
90
- when :open
91
- File.exists? file_path and not File.directory? file_path
92
- when :save
93
- not text.empty?
94
- end
95
- end
96
-
97
- update_files_list
98
- end
99
- end
100
-
101
- protected
102
- def create_nav_buttons(size = @directories.size)
103
- @nav_buttons.clear
104
-
105
- @directories = @directories[0..size]
106
-
107
- @directories.each_with_index do |dir, i|
108
- if i < @directories.size - 1
109
- @nav_buttons.button(dir) do
110
- create_nav_buttons(i)
111
- end
112
- else
113
- @nav_buttons.label dir, border_color: @@schema.default(Button, :border_color), border_thickness: @@schema.default(Button, :border_thickness)
114
- end
115
- end
116
-
117
- update_files_list
118
- end
119
-
120
- protected
121
- def update_files_list
122
- @files_list.clear
123
- @file_name_text.text = ''
124
- @scroll_window.offset_x = @scroll_window.offset_y = 0
125
-
126
- # Add folders.
127
- Dir.glob(File.join(directory, "*")).each do |file_path|
128
- if File.directory? file_path
129
- @files_list.item File.basename(file_path), file_path, icon: Gosu::Image["file_directory.png"]
130
- end
131
- end
132
-
133
- # Add files that match the pattern.
134
- Dir.glob(File.join(directory, pattern)).each do |file_path|
135
- unless File.directory? file_path
136
- file_name = if @show_extension
137
- File.basename(file_path)
138
- else
139
- File.basename(file_path, File.extname(file_path))
140
- end
141
-
142
- @files_list.item file_name, file_path, icon: Gosu::Image["file_file.png"]
143
- end
144
- end
145
- end
146
-
147
- protected
148
- def post_init_block(&block)
149
- subscribe :selected, &block
150
- end
151
- end
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class FileBrowser < Composite
5
+ VALID_TYPES = [:open, :save]
6
+
7
+ event :selected
8
+
9
+ attr_reader :pattern, :base_directory
10
+
11
+ def show_extension?; @show_extension; end
12
+ def directory
13
+ dir = File.join(*@directories)
14
+ dir = File.join(@base_directory, dir) unless @base_directory.empty?
15
+ dir
16
+ end
17
+ def file_name; @file_name_text.text; end
18
+ def file_path; File.join(directory, file_name); end
19
+
20
+ # @param [Symbol] type One of :open, :save
21
+ # @option options [String] :base_directory ('') Outermost directory that the browser will see.
22
+ # @option options [String] :directory (current working directory).
23
+ # @option options [String] :file_name ('') Initially selected file in the directory.
24
+ # @option options [String] :pattern ('*.*')
25
+ # @option options [Boolean] :show_extension (true)
26
+ def initialize(type, options = {})
27
+ options = {
28
+ base_directory: '',
29
+ directory: Dir.pwd,
30
+ file_name: '',
31
+ pattern: default(:pattern),
32
+ show_extension: default(:show_extension),
33
+ width: 400,
34
+ save_text: "Save",
35
+ open_text: "Open",
36
+ cancel_text: "Cancel",
37
+ }.merge! options
38
+
39
+ @type = type
40
+ raise ArgumentError, "type must be one of #{VALID_TYPES}, not #{@type}" unless VALID_TYPES.include? @type
41
+
42
+ @pattern = options[:pattern]
43
+ @show_extension = options[:show_extension]
44
+ @base_directory = options[:base_directory].chomp File::SEPARATOR
45
+
46
+ @directories = options[:directory].sub(/^#{@base_directory}/, '').split(File::SEPARATOR)
47
+ if @directories.first == ''
48
+ @directories[0] = File::SEPARATOR
49
+ end
50
+
51
+ super options
52
+
53
+ vertical do
54
+ @nav_buttons = horizontal padding: 0, spacing: 2
55
+
56
+ @scroll_window = scroll_window(height: 250, width: options[:width]) do
57
+ @files_list = list(width: options[:width]) do
58
+ subscribe :changed do |sender, file_path|
59
+ if file_path
60
+ file_name = File.basename file_path
61
+ if File.directory? file_path
62
+ @directories.push file_name
63
+ create_nav_buttons
64
+ update_files_list
65
+ else
66
+ @file_name_text.text = file_name
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ @file_name_text = text_area(text: options[:file_name], max_height: font.height * 1.5, width: options[:width], border_thickness: 1)
74
+
75
+ create_nav_buttons
76
+
77
+ horizontal align: :center, padding: 0 do
78
+ @action_button = button(options[:"#{type}_text"]) do
79
+ publish :selected, @type, file_path
80
+ end
81
+
82
+ button(options[:cancel_text]) do
83
+ publish :selected, :cancel, file_path
84
+ end
85
+ end
86
+
87
+ # Ensure that the open/save button is enabled only when the path is sensible.
88
+ @file_name_text.subscribe :changed do |sender, text|
89
+ @action_button.enabled = case @type
90
+ when :open
91
+ File.exists? file_path and not File.directory? file_path
92
+ when :save
93
+ not text.empty?
94
+ end
95
+ end
96
+
97
+ update_files_list
98
+ end
99
+ end
100
+
101
+ protected
102
+ def create_nav_buttons(size = @directories.size)
103
+ @nav_buttons.clear
104
+
105
+ @directories = @directories[0..size]
106
+
107
+ @directories.each_with_index do |dir, i|
108
+ if i < @directories.size - 1
109
+ @nav_buttons.button(dir) do
110
+ create_nav_buttons(i)
111
+ end
112
+ else
113
+ @nav_buttons.label dir, border_color: @@schema.default(Button, :border_color), border_thickness: @@schema.default(Button, :border_thickness)
114
+ end
115
+ end
116
+
117
+ update_files_list
118
+ end
119
+
120
+ protected
121
+ def update_files_list
122
+ @files_list.clear
123
+ @file_name_text.text = ''
124
+ @scroll_window.offset_x = @scroll_window.offset_y = 0
125
+
126
+ # Add folders.
127
+ Dir.glob(File.join(directory, "*")).each do |file_path|
128
+ if File.directory? file_path
129
+ @files_list.item File.basename(file_path), file_path, icon: Gosu::Image["file_directory.png"]
130
+ end
131
+ end
132
+
133
+ # Add files that match the pattern.
134
+ Dir.glob(File.join(directory, pattern)).each do |file_path|
135
+ unless File.directory? file_path
136
+ file_name = if @show_extension
137
+ File.basename(file_path)
138
+ else
139
+ File.basename(file_path, File.extname(file_path))
140
+ end
141
+
142
+ @files_list.item file_name, file_path, icon: Gosu::Image["file_file.png"]
143
+ end
144
+ end
145
+ end
146
+
147
+ protected
148
+ def post_init_block(&block)
149
+ subscribe :selected, &block
150
+ end
151
+ end
152
152
  end
@@ -1,227 +1,227 @@
1
- # encoding: utf-8
2
-
3
- module Fidgit
4
- # A vertically aligned element packing container.
5
-
6
- class Grid < Packer
7
- # @return [Symbol]
8
- attr_reader :type
9
-
10
- # @return [Integer]
11
- attr_reader :num_rows
12
-
13
- # @return [Integer]
14
- attr_reader :num_columns
15
-
16
- # @note Currently only supports +num_columns+ mode (not +num_rows+).
17
- #
18
- # @param (see Packer#initialize)
19
- #
20
- # @option (see Packer#initialize)
21
- # @option options [Integer] :num_columns Maximum number of columns to use (incompatible with :num_rows)
22
- # @option options [Integer] :num_rows Maximum number of rows to use (incompatible with :num_columns)
23
- def initialize(options = {})
24
- options = {
25
- cell_border_color: default(:cell_border_color),
26
- cell_background_color: default(:cell_background_color),
27
- cell_border_thickness: default(:cell_border_thickness),
28
- }.merge! options
29
-
30
- @num_columns = options[:num_columns]
31
- @num_rows = options[:num_rows]
32
- raise ArgumentError, "options :num_rows and :num_columns are not compatible" if @num_rows and @num_columns
33
-
34
- @cell_border_color = options[:cell_border_color].dup
35
- @cell_border_thickness = options[:cell_border_thickness]
36
- @cell_background_color = options[:cell_background_color].dup
37
-
38
- @type = @num_rows ? :fixed_rows : :fixed_columns
39
-
40
- super options
41
- end
42
-
43
- protected
44
- def layout
45
- rearrange
46
- repack
47
- end
48
-
49
- protected
50
- # Rearrange the cells based on changes to the number of rows/columns or adding/removing elements.
51
- def rearrange
52
- # Calculate the number of the dynamic dimension.
53
- case @type
54
- when :fixed_rows
55
- @num_columns = (size / @num_rows.to_f).ceil
56
- when :fixed_columns
57
- @num_rows = (size / @num_columns.to_f).ceil
58
- end
59
-
60
- # Create an array containing all the rows.
61
- @rows = case @type
62
- when :fixed_rows
63
- # Rearrange the list, arranged by columns, into rows.
64
- rows = Array.new(@num_rows) { [] }
65
- @children.each_with_index do |child, i|
66
- rows[i % @num_rows].push child
67
- end
68
- rows
69
- when :fixed_columns
70
- @children.each_slice(@num_columns).to_a
71
- end
72
-
73
- nil
74
- end
75
-
76
- protected
77
- # Repack all the elements into their positions.
78
- def repack
79
- @widths = Array.new(@rows.empty? ? 0 : @rows[0].size, 0)
80
- @heights = Array.new(@rows.size, 0)
81
-
82
- filled_columns = []
83
- filled_rows = []
84
-
85
- # Calculate the maximum widths of each column and the maximum height of each row.
86
- @rows.each_with_index do |row, row_num|
87
- row.each_with_index do |element, column_num|
88
- fills = (element.align_h == :fill)
89
- @widths[column_num] = [fills ? element.min_width : element.outer_width, @widths[column_num]].max
90
- filled_columns.push fills
91
-
92
- fills = (element.align_v == :fill)
93
- @heights[row_num] = [fills ? element.min_width : element.outer_height, @heights[row_num]].max
94
- filled_rows.push fills
95
- end
96
- end
97
-
98
- # Expand the size of each filled column to make the minimum size required.
99
- unless @widths.empty?
100
- num_filled_columns = filled_columns.select {|value| value }.count
101
- total_width = @widths.inject(0, :+) + (padding_left + padding_right) + ((@num_columns - 1) * spacing_h)
102
- extra_width = min_width - total_width
103
- if extra_width > 0
104
- if num_filled_columns > 0
105
- @widths[filled_columns.index true] += extra_width
106
- else
107
- @widths[-1] += extra_width
108
- end
109
- end
110
- end
111
-
112
- # Expand the size of each filled row to make the minimum size required.
113
- unless @heights.empty?
114
- num_filled_rows = filled_rows.select {|value| value }.count
115
- total_height = @heights.inject(0, :+) + (padding_left + padding_right) + ((@num_rows - 1) * spacing_v)
116
- extra_height = min_height - total_height
117
- if extra_height > 0
118
- if num_filled_rows > 0
119
- @heights[filled_rows.index true] += extra_height
120
- else
121
- @heights[-1] += extra_height
122
- end
123
- end
124
- end
125
-
126
- # Actually place all the elements into the grid positions, modified by valign and align.
127
- current_y = y + padding_top
128
- @rows.each_with_index do |row, row_num|
129
- current_x = x + padding_left
130
-
131
- row.each_with_index do |element, column_num|
132
- element.x = current_x + element.border_thickness
133
-
134
- case element.align_h # Take horizontal alignment into consideration.
135
- when :fill
136
- if element.width < @widths[column_num]
137
- element.width = @widths[column_num]
138
- element.send :repack if element.is_a? Grid
139
- end
140
- when :center
141
- element.x += (@widths[column_num] - element.width) / 2
142
- when :right
143
- element.x += @widths[column_num] - element.width
144
- end
145
-
146
- current_x += @widths[column_num]
147
- current_x += spacing_h unless column_num == @num_columns - 1
148
-
149
- element.y = current_y + element.border_thickness
150
-
151
- case element.align_v # Take horizontal alignment into consideration.
152
- when :fill
153
- if element.height < @heights[row_num]
154
- element.height = @heights[row_num]
155
- element.send :repack if element.is_a? Grid
156
- end
157
- when :center
158
- element.y += (@heights[row_num] - element.height) / 2
159
- when :bottom
160
- element.y += @heights[row_num] - element.height
161
- else
162
- end
163
- end
164
-
165
- self.width = current_x - x + padding_left if row_num == 0
166
-
167
- current_y += @heights[row_num] unless row.empty?
168
- current_y += spacing_h unless row_num == num_rows - 1
169
- end
170
-
171
- self.height = current_y - y + padding_top
172
-
173
- nil
174
- end
175
-
176
- protected
177
- # @yield The rectangle of each cell within the grid.
178
- # @yieldparam [Number] x
179
- # @yieldparam [Number] y
180
- # @yieldparam [Number] width
181
- # @yieldparam [Number] height
182
- def each_cell_rect
183
- x = self.x + padding_left
184
-
185
- @widths.each_with_index do |width, column_num|
186
- y = self.y + padding_top
187
-
188
- @heights.each_with_index do |height, row_num|
189
- yield x, y, width, height if @rows[row_num][column_num]
190
- y += height + spacing_v
191
- end
192
-
193
- x += width + spacing_h
194
- end
195
-
196
- nil
197
- end
198
-
199
- protected
200
- def draw_background
201
- super
202
-
203
- # Draw the cell backgrounds.
204
- unless @cell_background_color.transparent?
205
- each_cell_rect do |x, y, width, height|
206
- draw_rect x, y, width, height, z, @cell_background_color
207
- end
208
- end
209
-
210
- nil
211
- end
212
-
213
- protected
214
- def draw_border
215
- super
216
-
217
- # Draw the cell borders.
218
- if @cell_border_thickness > 0 and not @cell_border_color.transparent?
219
- each_cell_rect do |x, y, width, height|
220
- draw_frame x, y, width, height, @cell_border_thickness, z, @cell_border_color
221
- end
222
- end
223
-
224
- nil
225
- end
226
- end
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ # A vertically aligned element packing container.
5
+
6
+ class Grid < Packer
7
+ # @return [Symbol]
8
+ attr_reader :type
9
+
10
+ # @return [Integer]
11
+ attr_reader :num_rows
12
+
13
+ # @return [Integer]
14
+ attr_reader :num_columns
15
+
16
+ # @note Currently only supports +num_columns+ mode (not +num_rows+).
17
+ #
18
+ # @param (see Packer#initialize)
19
+ #
20
+ # @option (see Packer#initialize)
21
+ # @option options [Integer] :num_columns Maximum number of columns to use (incompatible with :num_rows)
22
+ # @option options [Integer] :num_rows Maximum number of rows to use (incompatible with :num_columns)
23
+ def initialize(options = {})
24
+ options = {
25
+ cell_border_color: default(:cell_border_color),
26
+ cell_background_color: default(:cell_background_color),
27
+ cell_border_thickness: default(:cell_border_thickness),
28
+ }.merge! options
29
+
30
+ @num_columns = options[:num_columns]
31
+ @num_rows = options[:num_rows]
32
+ raise ArgumentError, "options :num_rows and :num_columns are not compatible" if @num_rows and @num_columns
33
+
34
+ @cell_border_color = options[:cell_border_color].dup
35
+ @cell_border_thickness = options[:cell_border_thickness]
36
+ @cell_background_color = options[:cell_background_color].dup
37
+
38
+ @type = @num_rows ? :fixed_rows : :fixed_columns
39
+
40
+ super options
41
+ end
42
+
43
+ protected
44
+ def layout
45
+ rearrange
46
+ repack
47
+ end
48
+
49
+ protected
50
+ # Rearrange the cells based on changes to the number of rows/columns or adding/removing elements.
51
+ def rearrange
52
+ # Calculate the number of the dynamic dimension.
53
+ case @type
54
+ when :fixed_rows
55
+ @num_columns = (size / @num_rows.to_f).ceil
56
+ when :fixed_columns
57
+ @num_rows = (size / @num_columns.to_f).ceil
58
+ end
59
+
60
+ # Create an array containing all the rows.
61
+ @rows = case @type
62
+ when :fixed_rows
63
+ # Rearrange the list, arranged by columns, into rows.
64
+ rows = Array.new(@num_rows) { [] }
65
+ @children.each_with_index do |child, i|
66
+ rows[i % @num_rows].push child
67
+ end
68
+ rows
69
+ when :fixed_columns
70
+ @children.each_slice(@num_columns).to_a
71
+ end
72
+
73
+ nil
74
+ end
75
+
76
+ protected
77
+ # Repack all the elements into their positions.
78
+ def repack
79
+ @widths = Array.new(@rows.empty? ? 0 : @rows[0].size, 0)
80
+ @heights = Array.new(@rows.size, 0)
81
+
82
+ filled_columns = []
83
+ filled_rows = []
84
+
85
+ # Calculate the maximum widths of each column and the maximum height of each row.
86
+ @rows.each_with_index do |row, row_num|
87
+ row.each_with_index do |element, column_num|
88
+ fills = (element.align_h == :fill)
89
+ @widths[column_num] = [fills ? element.min_width : element.outer_width, @widths[column_num]].max
90
+ filled_columns.push fills
91
+
92
+ fills = (element.align_v == :fill)
93
+ @heights[row_num] = [fills ? element.min_width : element.outer_height, @heights[row_num]].max
94
+ filled_rows.push fills
95
+ end
96
+ end
97
+
98
+ # Expand the size of each filled column to make the minimum size required.
99
+ unless @widths.empty?
100
+ num_filled_columns = filled_columns.select {|value| value }.count
101
+ total_width = @widths.inject(0, :+) + (padding_left + padding_right) + ((@num_columns - 1) * spacing_h)
102
+ extra_width = min_width - total_width
103
+ if extra_width > 0
104
+ if num_filled_columns > 0
105
+ @widths[filled_columns.index true] += extra_width
106
+ else
107
+ @widths[-1] += extra_width
108
+ end
109
+ end
110
+ end
111
+
112
+ # Expand the size of each filled row to make the minimum size required.
113
+ unless @heights.empty?
114
+ num_filled_rows = filled_rows.select {|value| value }.count
115
+ total_height = @heights.inject(0, :+) + (padding_left + padding_right) + ((@num_rows - 1) * spacing_v)
116
+ extra_height = min_height - total_height
117
+ if extra_height > 0
118
+ if num_filled_rows > 0
119
+ @heights[filled_rows.index true] += extra_height
120
+ else
121
+ @heights[-1] += extra_height
122
+ end
123
+ end
124
+ end
125
+
126
+ # Actually place all the elements into the grid positions, modified by valign and align.
127
+ current_y = y + padding_top
128
+ @rows.each_with_index do |row, row_num|
129
+ current_x = x + padding_left
130
+
131
+ row.each_with_index do |element, column_num|
132
+ element.x = current_x + element.border_thickness
133
+
134
+ case element.align_h # Take horizontal alignment into consideration.
135
+ when :fill
136
+ if element.width < @widths[column_num]
137
+ element.width = @widths[column_num]
138
+ element.send :repack if element.is_a? Grid
139
+ end
140
+ when :center
141
+ element.x += (@widths[column_num] - element.width) / 2
142
+ when :right
143
+ element.x += @widths[column_num] - element.width
144
+ end
145
+
146
+ current_x += @widths[column_num]
147
+ current_x += spacing_h unless column_num == @num_columns - 1
148
+
149
+ element.y = current_y + element.border_thickness
150
+
151
+ case element.align_v # Take horizontal alignment into consideration.
152
+ when :fill
153
+ if element.height < @heights[row_num]
154
+ element.height = @heights[row_num]
155
+ element.send :repack if element.is_a? Grid
156
+ end
157
+ when :center
158
+ element.y += (@heights[row_num] - element.height) / 2
159
+ when :bottom
160
+ element.y += @heights[row_num] - element.height
161
+ else
162
+ end
163
+ end
164
+
165
+ self.width = current_x - x + padding_left if row_num == 0
166
+
167
+ current_y += @heights[row_num] unless row.empty?
168
+ current_y += spacing_h unless row_num == num_rows - 1
169
+ end
170
+
171
+ self.height = current_y - y + padding_top
172
+
173
+ nil
174
+ end
175
+
176
+ protected
177
+ # @yield The rectangle of each cell within the grid.
178
+ # @yieldparam [Number] x
179
+ # @yieldparam [Number] y
180
+ # @yieldparam [Number] width
181
+ # @yieldparam [Number] height
182
+ def each_cell_rect
183
+ x = self.x + padding_left
184
+
185
+ @widths.each_with_index do |width, column_num|
186
+ y = self.y + padding_top
187
+
188
+ @heights.each_with_index do |height, row_num|
189
+ yield x, y, width, height if @rows[row_num][column_num]
190
+ y += height + spacing_v
191
+ end
192
+
193
+ x += width + spacing_h
194
+ end
195
+
196
+ nil
197
+ end
198
+
199
+ protected
200
+ def draw_background
201
+ super
202
+
203
+ # Draw the cell backgrounds.
204
+ unless @cell_background_color.transparent?
205
+ each_cell_rect do |x, y, width, height|
206
+ draw_rect x, y, width, height, z, @cell_background_color
207
+ end
208
+ end
209
+
210
+ nil
211
+ end
212
+
213
+ protected
214
+ def draw_border
215
+ super
216
+
217
+ # Draw the cell borders.
218
+ if @cell_border_thickness > 0 and not @cell_border_color.transparent?
219
+ each_cell_rect do |x, y, width, height|
220
+ draw_frame x, y, width, height, @cell_border_thickness, z, @cell_border_color
221
+ end
222
+ end
223
+
224
+ nil
225
+ end
226
+ end
227
227
  end