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.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/COPYING.txt +674 -0
- data/Gemfile +4 -0
- data/README.textile +138 -0
- data/Rakefile +38 -0
- data/config/default_schema.yml +180 -0
- data/examples/_all_examples.rb +9 -0
- data/examples/align_example.rb +56 -0
- data/examples/button_and_toggle_button_example.rb +27 -0
- data/examples/color_picker_example.rb +17 -0
- data/examples/color_well_example.rb +25 -0
- data/examples/combo_box_example.rb +24 -0
- data/examples/file_dialog_example.rb +42 -0
- data/examples/grid_packer_example.rb +29 -0
- data/examples/helpers/example_window.rb +17 -0
- data/examples/label_example.rb +17 -0
- data/examples/list_example.rb +23 -0
- data/examples/media/images/head_icon.png +0 -0
- data/examples/menu_pane_example.rb +27 -0
- data/examples/message_dialog_example.rb +65 -0
- data/examples/radio_button_example.rb +37 -0
- data/examples/readme_example.rb +32 -0
- data/examples/scroll_window_example.rb +49 -0
- data/examples/slider_example.rb +30 -0
- data/examples/splash_example.rb +42 -0
- data/examples/text_area_example.rb +28 -0
- data/fidgit.gemspec +28 -0
- data/lib/fidgit.rb +4 -0
- data/lib/fidgit/chingu_ext/window.rb +6 -0
- data/lib/fidgit/clipboard.rb +23 -0
- data/lib/fidgit/cursor.rb +38 -0
- data/lib/fidgit/elements/button.rb +68 -0
- data/lib/fidgit/elements/color_picker.rb +63 -0
- data/lib/fidgit/elements/color_well.rb +39 -0
- data/lib/fidgit/elements/combo_box.rb +85 -0
- data/lib/fidgit/elements/composite.rb +17 -0
- data/lib/fidgit/elements/container.rb +187 -0
- data/lib/fidgit/elements/element.rb +252 -0
- data/lib/fidgit/elements/file_browser.rb +152 -0
- data/lib/fidgit/elements/grid_packer.rb +219 -0
- data/lib/fidgit/elements/group.rb +66 -0
- data/lib/fidgit/elements/horizontal_packer.rb +12 -0
- data/lib/fidgit/elements/label.rb +77 -0
- data/lib/fidgit/elements/list.rb +47 -0
- data/lib/fidgit/elements/menu_pane.rb +149 -0
- data/lib/fidgit/elements/packer.rb +42 -0
- data/lib/fidgit/elements/radio_button.rb +86 -0
- data/lib/fidgit/elements/scroll_area.rb +75 -0
- data/lib/fidgit/elements/scroll_bar.rb +114 -0
- data/lib/fidgit/elements/scroll_window.rb +92 -0
- data/lib/fidgit/elements/slider.rb +119 -0
- data/lib/fidgit/elements/text_area.rb +351 -0
- data/lib/fidgit/elements/toggle_button.rb +67 -0
- data/lib/fidgit/elements/tool_tip.rb +35 -0
- data/lib/fidgit/elements/vertical_packer.rb +12 -0
- data/lib/fidgit/event.rb +99 -0
- data/lib/fidgit/gosu_ext/color.rb +123 -0
- data/lib/fidgit/history.rb +85 -0
- data/lib/fidgit/redirector.rb +83 -0
- data/lib/fidgit/schema.rb +123 -0
- data/lib/fidgit/selection.rb +106 -0
- data/lib/fidgit/standard_ext/hash.rb +21 -0
- data/lib/fidgit/states/dialog_state.rb +42 -0
- data/lib/fidgit/states/file_dialog.rb +24 -0
- data/lib/fidgit/states/gui_state.rb +301 -0
- data/lib/fidgit/states/message_dialog.rb +61 -0
- data/lib/fidgit/thumbnail.rb +29 -0
- data/lib/fidgit/version.rb +5 -0
- data/lib/fidgit/window.rb +19 -0
- data/media/images/arrow.png +0 -0
- data/media/images/file_directory.png +0 -0
- data/media/images/file_file.png +0 -0
- data/media/images/pixel.png +0 -0
- data/spec/fidgit/elements/helpers/helper.rb +3 -0
- data/spec/fidgit/elements/label_spec.rb +49 -0
- data/spec/fidgit/event_spec.rb +149 -0
- data/spec/fidgit/gosu_ext/color_spec.rb +130 -0
- data/spec/fidgit/gosu_ext/helpers/helper.rb +3 -0
- data/spec/fidgit/helpers/helper.rb +4 -0
- data/spec/fidgit/helpers/tex_play_helper.rb +9 -0
- data/spec/fidgit/history_spec.rb +144 -0
- data/spec/fidgit/redirector_spec.rb +78 -0
- data/spec/fidgit/schema_spec.rb +67 -0
- data/spec/fidgit/schema_test.yml +32 -0
- data/spec/fidgit/thumbnail_spec.rb +50 -0
- metadata +177 -0
@@ -0,0 +1,351 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
class TextArea < Element
|
5
|
+
# @return [Number]
|
6
|
+
attr_reader :min_height
|
7
|
+
# @return [Number]
|
8
|
+
attr_reader :max_height
|
9
|
+
|
10
|
+
# @return [Number]
|
11
|
+
attr_reader :line_spacing
|
12
|
+
|
13
|
+
# @param [Boolean] value
|
14
|
+
# @return [Boolean]
|
15
|
+
attr_writer :editable
|
16
|
+
|
17
|
+
event :changed
|
18
|
+
event :focus
|
19
|
+
event :blur
|
20
|
+
|
21
|
+
# Is the area editable?
|
22
|
+
def editable?
|
23
|
+
enabled?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Text within the element.
|
27
|
+
# @return [String]
|
28
|
+
def text
|
29
|
+
@text_input.text.force_encoding 'UTF-8'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the range of the selection.
|
33
|
+
#
|
34
|
+
# @return [Range]
|
35
|
+
def selection_range
|
36
|
+
from = [@text_input.selection_start, caret_position].min
|
37
|
+
to = [@text_input.selection_start, caret_position].max
|
38
|
+
|
39
|
+
(from...to)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the text within the selection.
|
43
|
+
#
|
44
|
+
# @return [String]
|
45
|
+
def selection_text
|
46
|
+
text[selection_range]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Position of the caret.
|
50
|
+
#
|
51
|
+
# @return [Integer] Number in range 0..text.length
|
52
|
+
def caret_position
|
53
|
+
@text_input.caret_pos
|
54
|
+
end
|
55
|
+
|
56
|
+
# Position of the caret.
|
57
|
+
#
|
58
|
+
# @param [Integer] pos Position of caret in the text.
|
59
|
+
# @return [Integer] New position of caret.
|
60
|
+
def caret_position=(position)
|
61
|
+
raise ArgumentError, "Caret position must be in the range 0 to the length of the text (inclusive)" unless position.between?(0, text.length)
|
62
|
+
@text_input.caret_pos = position
|
63
|
+
|
64
|
+
position
|
65
|
+
end
|
66
|
+
|
67
|
+
# Sets caret to the end of the text.
|
68
|
+
#
|
69
|
+
# @param [String] text
|
70
|
+
# @return [String] Current string (may be the old one if passed on was too long).
|
71
|
+
def text=(text)
|
72
|
+
@text_input.text = text
|
73
|
+
recalc # This may roll back the text if it is too long.
|
74
|
+
publish :changed, self.text
|
75
|
+
self.text
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# @param (see Element#initialize)
|
80
|
+
#
|
81
|
+
# @option (see Element#initialize)
|
82
|
+
# @option options [String] :text ("")
|
83
|
+
# @option options [Integer] :height Sets both min and max height at once.
|
84
|
+
# @option options [Integer] :min_height
|
85
|
+
# @option options [Integer] :max_height (Infinite)
|
86
|
+
# @option options [Number] :line_spacing (0)
|
87
|
+
def initialize(options = {}, &block)
|
88
|
+
options = {
|
89
|
+
text: '',
|
90
|
+
max_height: Float::INFINITY,
|
91
|
+
line_spacing: default(:line_spacing),
|
92
|
+
background_color: default(:background_color),
|
93
|
+
border_color: default(:border_color),
|
94
|
+
caret_color: default(:caret_color),
|
95
|
+
caret_period: default(:caret_period),
|
96
|
+
focused_border_color: default(:focused, :border_color),
|
97
|
+
selection_color: default(:selection_color),
|
98
|
+
}.merge! options
|
99
|
+
|
100
|
+
@line_spacing = options[:line_spacing]
|
101
|
+
@caret_color = options[:caret_color].dup
|
102
|
+
@caret_period = options[:caret_period]
|
103
|
+
@focused_border_color = options[:focused_border_color].dup
|
104
|
+
@selection_color = options[:selection_color].dup
|
105
|
+
|
106
|
+
@lines = [''] # List of lines of wrapped text.
|
107
|
+
@caret_positions = [[0, 0]] # [x, y] of each position the caret can be in.
|
108
|
+
@char_widths = [] # Width of each character in the text.
|
109
|
+
@text_input = Gosu::TextInput.new
|
110
|
+
@old_text = ''
|
111
|
+
@old_caret_position = 0
|
112
|
+
@old_selection_start = 0
|
113
|
+
|
114
|
+
@text_input.text = options[:text].dup
|
115
|
+
|
116
|
+
super(options)
|
117
|
+
|
118
|
+
min_height = padding_left + padding_right + font_size
|
119
|
+
if options[:height]
|
120
|
+
@max_height = @min_height = [options[:height], min_height].max
|
121
|
+
else
|
122
|
+
@max_height = [options[:max_height], min_height].max
|
123
|
+
@min_height = options[:min_height] ? [options[:min_height], min_height].max : min_height
|
124
|
+
end
|
125
|
+
rect.height = [padding_left + padding_right + font_size, @min_height].max
|
126
|
+
|
127
|
+
subscribe :left_mouse_button, method(:click_in_text)
|
128
|
+
subscribe :right_mouse_button, method(:click_in_text)
|
129
|
+
end
|
130
|
+
|
131
|
+
# @return [nil]
|
132
|
+
def click_in_text(sender, x, y)
|
133
|
+
publish :focus unless focused?
|
134
|
+
|
135
|
+
# Move caret to position the user clicks on.
|
136
|
+
mouse_x, mouse_y = x - (self.x + padding_left), y - (self.y + padding_top)
|
137
|
+
@char_widths.each_with_index do |width, i|
|
138
|
+
char_x, char_y = @caret_positions[i]
|
139
|
+
if mouse_x.between?(char_x, char_x + width) and mouse_y.between?(char_y, char_y + font_size)
|
140
|
+
self.caret_position = @text_input.selection_start = i
|
141
|
+
break
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
|
148
|
+
# Does the element have the focus?
|
149
|
+
def focused?; @focused; end
|
150
|
+
|
151
|
+
# @return [nil]
|
152
|
+
def focus(sender)
|
153
|
+
@focused = true
|
154
|
+
$window.current_game_state.focus = self
|
155
|
+
$window.text_input = @text_input
|
156
|
+
|
157
|
+
nil
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return [nil]
|
161
|
+
def blur(sender)
|
162
|
+
if focused?
|
163
|
+
$window.current_game_state.focus = nil
|
164
|
+
$window.text_input = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
@focused = false
|
168
|
+
|
169
|
+
nil
|
170
|
+
end
|
171
|
+
|
172
|
+
# Draw the text area.
|
173
|
+
#
|
174
|
+
# @return [nil]
|
175
|
+
def draw_foreground
|
176
|
+
# Always roll back changes made by the user unless the text is editable.
|
177
|
+
if not editable? and text != @old_text
|
178
|
+
@text_input.text = @old_text
|
179
|
+
@text_input.selection_start = @old_selection_start
|
180
|
+
self.caret_position = @old_caret_position
|
181
|
+
else
|
182
|
+
recalc if focused? # Workaround for Windows draw/update bug.
|
183
|
+
@old_caret_position = caret_position
|
184
|
+
@old_selection_start = @text_input.selection_start
|
185
|
+
end
|
186
|
+
|
187
|
+
# Draw the selection.
|
188
|
+
selection_range.each do |pos|
|
189
|
+
char_x, char_y = @caret_positions[pos]
|
190
|
+
char_width = @char_widths[pos]
|
191
|
+
left, top = x + padding_left + char_x, y + padding_top + char_y
|
192
|
+
draw_rect left, top, char_width, font_size, z, @selection_color
|
193
|
+
end
|
194
|
+
|
195
|
+
# Draw text.
|
196
|
+
@lines.each_with_index do |line, index|
|
197
|
+
font.draw(line, x + padding_left, y + padding_top + y_at_line(index), z)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Draw the caret.
|
201
|
+
if focused? and ((Gosu::milliseconds / @caret_period) % 2 == 0)
|
202
|
+
caret_x, caret_y = @caret_positions[caret_position]
|
203
|
+
left, top = x + padding_left + caret_x, y + padding_top + caret_y
|
204
|
+
draw_rect left, top, 1, font_size, z, @caret_color
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# y position of the
|
209
|
+
protected
|
210
|
+
def y_at_line(lines_number)
|
211
|
+
lines_number * (font_size + line_spacing)
|
212
|
+
end
|
213
|
+
|
214
|
+
protected
|
215
|
+
# Helper for #recalc
|
216
|
+
# @return [Integer]
|
217
|
+
def position_letters_in_word(word, line_width)
|
218
|
+
word.each_char do |c|
|
219
|
+
char_width = font.text_width(c)
|
220
|
+
line_width += char_width
|
221
|
+
@caret_positions.push [line_width, y_at_line(@lines.size)]
|
222
|
+
@char_widths.push char_width
|
223
|
+
end
|
224
|
+
|
225
|
+
line_width
|
226
|
+
end
|
227
|
+
|
228
|
+
protected
|
229
|
+
# @return [nil]
|
230
|
+
def layout
|
231
|
+
# Don't need to re-layout if the text hasn't changed.
|
232
|
+
return if @old_text == text
|
233
|
+
|
234
|
+
publish :changed, self.text
|
235
|
+
|
236
|
+
# Save these in case we are too long.
|
237
|
+
old_lines = @lines
|
238
|
+
old_caret_positions = @caret_positions
|
239
|
+
old_char_widths = @char_widths
|
240
|
+
|
241
|
+
@lines = []
|
242
|
+
@caret_positions = [[0, 0]] # Position 0 is before the first character.
|
243
|
+
@char_widths = []
|
244
|
+
|
245
|
+
space_width = font.text_width ' '
|
246
|
+
max_width = width - padding_left - padding_right - space_width
|
247
|
+
|
248
|
+
line = ''
|
249
|
+
line_width = 0
|
250
|
+
word = ''
|
251
|
+
word_width = 0
|
252
|
+
|
253
|
+
text.each_char do |char|
|
254
|
+
char_width = font.text_width(char)
|
255
|
+
|
256
|
+
overall_width = line_width + (line_width == 0 ? 0 : space_width) + word_width + char_width
|
257
|
+
if overall_width > max_width and not (char == ' ' and not word.empty?)
|
258
|
+
if line.empty?
|
259
|
+
# The current word is longer than the whole word, so split it.
|
260
|
+
# Go back and set all the character positions we have.
|
261
|
+
position_letters_in_word(word, line_width)
|
262
|
+
|
263
|
+
# Push as much of the current word as possible as a complete line.
|
264
|
+
@lines.push word + (char == ' ' ? '' : '-')
|
265
|
+
line_width = font.text_width(word)
|
266
|
+
|
267
|
+
word = ''
|
268
|
+
word_width = 0
|
269
|
+
else
|
270
|
+
|
271
|
+
# Adding the current word would be too wide, so add the current line and start a new one.
|
272
|
+
@lines.push line
|
273
|
+
line = ''
|
274
|
+
end
|
275
|
+
|
276
|
+
@char_widths[-1] += (width - line_width - padding_left - padding_right) unless @char_widths.empty?
|
277
|
+
line_width = 0
|
278
|
+
end
|
279
|
+
|
280
|
+
case char
|
281
|
+
when "\n"
|
282
|
+
# A new-line ends the word and puts it on the line.
|
283
|
+
line += word
|
284
|
+
line_width = position_letters_in_word(word, line_width)
|
285
|
+
@caret_positions.push [line_width, y_at_line(@lines.size)]
|
286
|
+
@char_widths[-1] += (width - line_width - (padding_left + padding_right)) unless @char_widths.empty?
|
287
|
+
@char_widths.push 0
|
288
|
+
@lines.push line
|
289
|
+
word = ''
|
290
|
+
word_width = 0
|
291
|
+
line = ''
|
292
|
+
line_width = 0
|
293
|
+
|
294
|
+
when ' '
|
295
|
+
# A space ends a word and puts it on the line.
|
296
|
+
line += word + char
|
297
|
+
line_width = position_letters_in_word(word, line_width)
|
298
|
+
line_width += space_width
|
299
|
+
@caret_positions.push [line_width, y_at_line(@lines.size)]
|
300
|
+
@char_widths.push space_width
|
301
|
+
|
302
|
+
word = ''
|
303
|
+
word_width = 0
|
304
|
+
|
305
|
+
else
|
306
|
+
# If there was a previous line and we start a new line, put the caret pos on the current line.
|
307
|
+
if line.empty?
|
308
|
+
@caret_positions[-1] = [0, y_at_line(@lines.size)]
|
309
|
+
end
|
310
|
+
|
311
|
+
# Start building up a new word.
|
312
|
+
word += char
|
313
|
+
word_width += char_width
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Add any remaining word on the last line.
|
318
|
+
unless word.empty?
|
319
|
+
position_letters_in_word(word, line_width)
|
320
|
+
line += word
|
321
|
+
end
|
322
|
+
|
323
|
+
@lines.push line if @lines.empty? or not line.empty?
|
324
|
+
|
325
|
+
# Roll back if the height is too long.
|
326
|
+
new_height = padding_left + padding_right + y_at_line(@lines.size)
|
327
|
+
if new_height <= max_height
|
328
|
+
@old_text = text
|
329
|
+
rect.height = [new_height, @min_height].max
|
330
|
+
@old_caret_position = caret_position
|
331
|
+
@old_selection_start = @text_input.selection_start
|
332
|
+
else
|
333
|
+
# Roll back!
|
334
|
+
@lines = old_lines
|
335
|
+
@caret_positions = old_caret_positions
|
336
|
+
@char_widths = old_char_widths
|
337
|
+
@text_input.text = @old_text
|
338
|
+
self.caret_position = @old_caret_position
|
339
|
+
@text_input.selection_start = @old_selection_start
|
340
|
+
end
|
341
|
+
|
342
|
+
nil
|
343
|
+
end
|
344
|
+
|
345
|
+
protected
|
346
|
+
# Use block as an event handler.
|
347
|
+
def post_init_block(&block)
|
348
|
+
subscribe :changed, &block
|
349
|
+
end
|
350
|
+
end
|
351
|
+
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
|
+
@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
|
+
@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 < Label
|
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
|