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