minigl 1.3.7 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 334e4fb063765088eb6bc85cb3d67b68d28b1867
4
- data.tar.gz: ce24cfb378fe102dd475e32ff636b542b9b9f841
3
+ metadata.gz: f5cc361e823fff93b7cbe75f559300c5addd4335
4
+ data.tar.gz: 050e44d9b9b8471c59257b4986e838064c95ba9f
5
5
  SHA512:
6
- metadata.gz: d9d84abe68d022276574fc48baeea9c41a1188b1bc9b359dd88716384f0b9760c8ee28f0f1e7f9a911cfc25983c2d07d234bbfee63b9079987b7e442ce2ef8b4
7
- data.tar.gz: 30392926054fc4e1567647dcc72f14c2479ad48bbbd66b2285b5424258b0eed0b73983f4b956eb99a62b4ea61064cc41f1f2dbb15c5230878982ee9e6400b29e
6
+ metadata.gz: 8d5cbce4c28f817a897bcf4dd20c631bbf7e198ccbe3a6631a586a57c29c4f741771f23eca457337e10887ce5161e6677e7f367b31d557e2f66e93a9692722ba
7
+ data.tar.gz: aa955ada44f1963555e46038cdeaf14e433eda9e606c0644d5f027a0de3710f2a1e33d1f8ffd52a1ad4126844b76cf3fbbc31174ddecc39ae2b88ae8010002f8
data/README.md CHANGED
@@ -32,8 +32,13 @@ this [working game example](https://github.com/victords/aventura-do-saber).
32
32
  * An auxiliary, tutorial-like documentation is under construction
33
33
  [here](https://github.com/victords/minigl/wiki).
34
34
 
35
- **Version 1.3.7**
35
+ **Version 1.3.8**
36
36
 
37
- * Minor adjustment and exposure of `prefix` attribute of `Res`.
38
- * Removed debug message that was being output when starting the game.
39
- * Improved the `Vector` class with operations, comparisons, etc.
37
+ * Fix in `Map` for non-integer camera coordinates in ortogonal maps.
38
+ * Fixed documentation for `Map` class.
39
+ * Fixed bug in `text=` of `TextField`.
40
+ * Fixed indentation of all files.
41
+
42
+ **P.S.** I'm sorry for the few last releases which brought so few
43
+ improvements to the library... Still, I think this is better than leaving those
44
+ small errors uncorrected.
@@ -1,702 +1,698 @@
1
1
  require_relative 'global'
2
2
 
3
3
  module AGL
4
- # This class is an abstract ancestor for all form components (Button,
5
- # ToggleButton and TextField).
6
- class Component
7
- # Determines whether the control is enabled, i.e., will process user input.
8
- attr_accessor :enabled
9
-
10
- # Determines whether the control is visible, i.e., will be drawn in the
11
- # screen and process user input, if enabled.
12
- attr_accessor :visible
13
-
14
- # A container for any parameters to be passed to the code blocks called
15
- # in response to events of the control (click of a button, change of the
16
- # text in a text field, etc.). More detail can be found in the constructor
17
- # for each specific component class.
18
- attr_accessor :params
19
-
20
- # This constructor is for internal use of the subclasses only. Do not
21
- # instantiate objects of this class.
22
- def initialize x, y, font, text, text_color, disabled_text_color
23
- @x = x
24
- @y = y
25
- @font = font
26
- @text = text
27
- @text_color = text_color
28
- @disabled_text_color = disabled_text_color
29
- @enabled = @visible = true
30
- end
31
- end
32
-
33
- # This class represents a button.
34
- class Button < Component
35
- # Creates a button.
36
- #
37
- # Parameters:
38
- # [x] The x-coordinate where the button will be drawn in the screen.
39
- # [y] The y-coordinate where the button will be drawn in the screen.
40
- # [font] The <code>Gosu::Font</code> object that will be used to draw the
41
- # button text.
42
- # [text] The button text. Can be +nil+ or empty.
43
- # [img] A spritesheet containing four images in a column, representing,
44
- # from top to bottom, the default state, the hover state (when the
45
- # mouse is over the button), the pressed state (when the mouse
46
- # button is down and the cursor is over the button) and the disabled
47
- # state. If +nil+, the +width+ and +height+ parameters must be
48
- # provided.
49
- # [text_color] Color of the button text, in hexadecimal RRGGBB format.
50
- # [disabled_text_color] Color of the button text, when it's disabled, in
51
- # hexadecimal RRGGBB format.
52
- # [center] Whether the button text should be centered in its area (the
53
- # area is defined by the image size, when an image is given, or
54
- # by the +width+ and +height+ parameters, otherwise).
55
- # [margin_x] The x offset, from the button x-coordinate, to draw the text.
56
- # This parameter is used only if +center+ is false.
57
- # [margin_y] The y offset, from the button y-coordinate, to draw the text.
58
- # This parameter is used only if +center+ is false.
59
- # [width] Width of the button clickable area. This parameter is used only
60
- # if +img+ is +nil+.
61
- # [height] Height of the button clickable area. This parameter is used
62
- # only if +img+ is +nil+.
63
- # [params] An object containing any parameters you want passed to the
64
- # +action+ block. When the button is clicked, the following is
65
- # called:
66
- # @action.call @params
67
- # Note that this doesn't force you to declare a block that takes
68
- # parameters.
69
- # [action] The block of code executed when the button is clicked (or by
70
- # calling the +click+ method).
71
- def initialize x, y, font, text, img, text_color = 0, disabled_text_color = 0, center = true, margin_x = 0, margin_y = 0,
72
- width = nil, height = nil, params = nil, &action
73
- super x, y, font, text, text_color, disabled_text_color
74
- @img =
75
- if img; Res.imgs img, 1, 4, true
76
- else; nil; end
77
- @w =
78
- if img; @img[0].width
79
- else; width; end
80
- @h =
81
- if img; @img[0].height
82
- else; height; end
83
- if center
84
- @text_x = x + @w / 2 if @w
85
- @text_y = y + @h / 2 if @h
86
- else
87
- @text_x = x + margin_x
88
- @text_y = y + margin_y
89
- end
90
- @center = center
91
- @action = action
92
- @params = params
93
-
94
- @state = :up
95
- @img_index = @enabled ? 0 : 3
96
- end
97
-
98
- # Updates the button, checking the mouse movement and buttons to define
99
- # the button state.
100
- def update
101
- return unless @enabled and @visible
102
-
103
- mouse_over = Mouse.over? @x, @y, @w, @h
104
- mouse_press = Mouse.button_pressed? :left
105
- mouse_rel = Mouse.button_released? :left
106
-
107
- if @state == :up
108
- if mouse_over
109
- @img_index = 1
110
- @state = :over
111
- else
112
- @img_index = 0
113
- end
114
- elsif @state == :over
115
- if not mouse_over
116
- @img_index = 0
117
- @state = :up
118
- elsif mouse_press
119
- @img_index = 2
120
- @state = :down
121
- else
122
- @img_index = 1
123
- end
124
- elsif @state == :down
125
- if not mouse_over
126
- @img_index = 0
127
- @state = :down_out
128
- elsif mouse_rel
129
- @img_index = 1
130
- @state = :over
131
- click
132
- else
133
- @img_index = 2
134
- end
135
- else # :down_out
136
- if mouse_over
137
- @img_index = 2
138
- @state = :down
139
- elsif mouse_rel
140
- @img_index = 0
141
- @state = :up
142
- else
143
- @img_index = 0
144
- end
145
- end
146
- end
147
-
148
- # Executes the button click action.
149
- def click
150
- @action.call @params
151
- end
152
-
153
- # Sets the position of the button in the screen.
154
- #
155
- # Parameters:
156
- # [x] The new x-coordinate for the button.
157
- # [y] The new y-coordinate for the button.
158
- def set_position x, y
159
- d_x = x - @x
160
- d_y = y - @y
161
- @x = x; @y = y
162
- if @center
163
- @text_x = x + @w / 2
164
- @text_y = y + @h / 2
165
- else
166
- @text_x += d_x
167
- @text_y += d_y
168
- end
169
- end
170
-
171
- # Draws the button in the screen.
172
- #
173
- # Parameters:
174
- # [alpha] The opacity with which the button will be drawn. Allowed values
175
- # vary between 0 (fully transparent) and 255 (fully opaque).
176
- # [z_index] The z-order to draw the object. Objects with larger z-orders
177
- # will be drawn on top of the ones with smaller z-orders.
178
- def draw alpha = 0xff, z_index = 0
179
- return unless @visible
180
-
181
- color = (alpha << 24) | 0xffffff
182
- text_color = (alpha << 24) | (@enabled ? @text_color : @disabled_text_color)
183
- @img[@img_index].draw @x, @y, z_index, 1, 1, color if @img
184
- if @text
185
- if @center
186
- @font.draw_rel @text, @text_x, @text_y, z_index, 0.5, 0.5, 1, 1, text_color
187
- else
188
- @font.draw @text, @text_x, @text_y, z_index, 1, 1, text_color
189
- end
190
- end
191
- end
192
-
193
- def enabled= value # :nodoc:
194
- @enabled = value
195
- @state = :up
196
- @img_index = 3
197
- end
198
- end
199
-
200
- # This class represents a toggle button, which can be also interpreted as a
201
- # check box. It is always in one of two states, given as +true+ or +false+
202
- # by its property +checked+.
203
- class ToggleButton < Button
204
- # Defines the state of the button (returns +true+ or +false+).
205
- attr_reader :checked
206
-
207
- # Creates a ToggleButton. All parameters work the same as in Button,
208
- # except for the image, +img+, which now has to be composed of two columns
209
- # and four rows, the first column with images for the unchecked state,
210
- # and the second with images for the checked state, and for +checked+,
211
- # which defines the initial state of the ToggleButton.
212
- #
213
- # The +action+ block now will always receive a first boolean parameter
214
- # corresponding to the value of +checked+. So, if you want to pass
215
- # parameters to the block, you should declare it like this:
216
- # b = ToggleButton.new ... { |checked, params|
217
- # puts "button was checked" if checked
218
- # # do something with params
219
- # }
220
- def initialize x, y, font, text, img, checked = false, text_color = 0, disabled_text_color = 0, center = true, margin_x = 0, margin_y = 0,
221
- width = nil, height = nil, params = nil, &action
222
- super x, y, font, text, nil, text_color, disabled_text_color, center, margin_x, margin_y, width, height, params, &action
223
- @img =
224
- if img; Res.imgs img, 2, 4, true
225
- else; nil; end
226
- @w =
227
- if img; @img[0].width
228
- else; width; end
229
- @h =
230
- if img; @img[0].height
231
- else; height; end
232
- if center
233
- @text_x = x + @w / 2
234
- @text_y = y + @h / 2
235
- end
236
- @checked = checked
237
- end
238
-
239
- # Updates the button, checking the mouse movement and buttons to define
240
- # the button state.
241
- def update
242
- return unless @enabled and @visible
243
-
244
- super
245
- @img_index *= 2
246
- @img_index += 1 if @checked
247
- end
248
-
249
- # Executes the button click action, and toggles its state. The +action+
250
- # block always receives as a first parameter +true+, if the button has
251
- # been changed to checked, or +false+, otherwise.
252
- def click
253
- @checked = !@checked
254
- @action.call @checked, @params
255
- end
256
-
257
- # Sets the state of the button to the value given.
258
- #
259
- # Parameters:
260
- # [value] The state to be set (+true+ for checked, +false+ for unchecked).
261
- def checked= value
262
- click if value != @checked
263
- @checked = value
264
- end
265
-
266
- def enabled= value # :nodoc:
267
- @enabled = value
268
- @state = :up
269
- @img_index = @checked ? 7 : 6
270
- end
271
- end
272
-
273
- # This class represents a text field (input).
274
- class TextField < Component
275
- # The current text inside the text field.
276
- attr_reader :text
277
-
278
- # Creates a new text field.
279
- #
280
- # Parameters:
281
- # [x] The x-coordinate where the text field will be drawn in the screen.
282
- # [y] The y-coordinate where the text field will be drawn in the screen.
283
- # [font] The <code>Gosu::Font</code> object that will be used to draw the
284
- # text inside the field.
285
- # [img] The image of the text field. For a good result, you would likely
286
- # want something like a rectangle, horizontally wide, vertically
287
- # short, and with a color that contrasts with the +text_color+.
288
- # [cursor_img] An image for the blinking cursor that stands in the point
289
- # where text will be inserted. If +nil+, a simple black line
290
- # will be drawn instead.
291
- # [disabled_img] Image for the text field when it's disabled. If +nil+,
292
- # a darkened version of +img+ will be used.
293
- # [text_color] Color of the button text, in hexadecimal RRGGBB format.
294
- # [margin_x] The x offset, from the field x-coordinate, to draw the text.
295
- # [margin_y] The y offset, from the field y-coordinate, to draw the text.
296
- # [max_length] The maximum length of the text inside the field.
297
- # [active] Whether the text field must be focused by default. If +false+,
298
- # focus can be granted by clicking inside the text field or by
299
- # calling the +focus+ method.
300
- # [text] The starting text. Must not be +nil+.
301
- # [allowed_chars] A string containing all characters that can be typed
302
- # inside the text field. The complete set of supported
303
- # characters is given by the string
304
- # <code>"abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ'-=/[]\\\\,.;\"_+?{}|<>:!@#$%¨&*()"</code>.
305
- # [text_color] The color with which the text will be drawn, in hexadecimal
306
- # RRGGBB format.
307
- # [disabled_text_color] The color with which the text will be drawn, when
308
- # the text field is disabled, in hexadecimal RRGGBB
309
- # format.
310
- # [selection_color] The color of the rectangle highlighting selected text,
311
- # in hexadecimal RRGGBB format. The rectangle will
312
- # always be drawn with 50% of opacity.
313
- # [params] An object containing any parameters you want passed to the
314
- # +on_text_changed+ block. When the text of the text field is
315
- # changed, the following is called:
316
- # @on_text_changed.call @text, @params
317
- # Thus, +params+ will be the second parameter. Note that this
318
- # doesn't force you to declare a block that takes parameters.
319
- # [on_text_changed] The block of code executed when the text in the text
320
- # field is changed, either by user input or by calling
321
- # +text=+. The new text is passed as a first parameter
322
- # to this block, followed by +params+. Can be +nil+.
323
- def initialize x, y, font, img, cursor_img = nil, disabled_img = nil, margin_x = 0, margin_y = 0, max_length = 100, active = false, text = "",
324
- allowed_chars = nil, text_color = 0, disabled_text_color = 0, selection_color = 0, params = nil, &on_text_changed
325
- super x, y, font, text, text_color, disabled_text_color
326
- @img = Res.img img
327
- @w = @img.width
328
- @h = @img.height
329
- @cursor_img = Res.img(cursor_img) if cursor_img
330
- @disabled_img = Res.img(disabled_img) if disabled_img
331
- @max_length = max_length
332
- @active = active
333
- @text_x = x + margin_x
334
- @text_y = y + margin_y
335
- @selection_color = selection_color
336
-
337
- @nodes = [x + margin_x]
338
- @cur_node = 0
339
- @cursor_visible = false
340
- @cursor_timer = 0
341
-
342
- @k = [
343
- Gosu::KbA, Gosu::KbB, Gosu::KbC, Gosu::KbD, Gosu::KbE, Gosu::KbF,
344
- Gosu::KbG, Gosu::KbH, Gosu::KbI, Gosu::KbJ, Gosu::KbK, Gosu::KbL,
345
- Gosu::KbM, Gosu::KbN, Gosu::KbO, Gosu::KbP, Gosu::KbQ, Gosu::KbR,
346
- Gosu::KbS, Gosu::KbT, Gosu::KbU, Gosu::KbV, Gosu::KbW, Gosu::KbX,
347
- Gosu::KbY, Gosu::KbZ, Gosu::Kb1, Gosu::Kb2, Gosu::Kb3, Gosu::Kb4,
348
- Gosu::Kb5, Gosu::Kb6, Gosu::Kb7, Gosu::Kb8, Gosu::Kb9, Gosu::Kb0,
349
- Gosu::KbNumpad1, Gosu::KbNumpad2, Gosu::KbNumpad3, Gosu::KbNumpad4,
350
- Gosu::KbNumpad5, Gosu::KbNumpad6, Gosu::KbNumpad7, Gosu::KbNumpad8,
351
- Gosu::KbNumpad9, Gosu::KbNumpad0, Gosu::KbSpace, Gosu::KbBackspace,
352
- Gosu::KbDelete, Gosu::KbLeft, Gosu::KbRight, Gosu::KbHome,
353
- Gosu::KbEnd, Gosu::KbLeftShift, Gosu::KbRightShift,
354
- Gosu::KbBacktick, Gosu::KbMinus, Gosu::KbEqual, Gosu::KbBracketLeft,
355
- Gosu::KbBracketRight, Gosu::KbBackslash, Gosu::KbApostrophe,
356
- Gosu::KbComma, Gosu::KbPeriod, Gosu::KbSlash
357
- ]
358
- @chars = "abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ'-=/[]\\,.;\"_+?{}|<>:!@#$%¨&*()"
359
- @allowed_chars =
360
- if allowed_chars
361
- allowed_chars
362
- else
363
- @chars
364
- end
365
-
366
- @on_text_changed = on_text_changed
367
- @params = params
368
- end
369
-
370
- # Updates the text field, checking for mouse events and keyboard input.
371
- def update
372
- return unless @enabled and @visible
373
-
374
- ################################ Mouse ################################
375
- if Mouse.over? @x, @y, @w, @h
376
- if not @active and Mouse.button_pressed? :left
377
- focus
378
- end
379
- elsif Mouse.button_pressed? :left
380
- unfocus
381
- end
382
-
383
- return unless @active
384
-
385
- if Mouse.double_click? :left
386
- if @nodes.size > 1
387
- @anchor1 = 0
388
- @anchor2 = @nodes.size - 1
389
- @cur_node = @anchor2
390
- @double_clicked = true
391
- end
392
- set_cursor_visible
393
- elsif Mouse.button_pressed? :left
394
- set_node_by_mouse
395
- @anchor1 = @cur_node
396
- @anchor2 = nil
397
- @double_clicked = false
398
- set_cursor_visible
399
- elsif Mouse.button_down? :left
400
- if @anchor1 and not @double_clicked
401
- set_node_by_mouse
402
- if @cur_node != @anchor1; @anchor2 = @cur_node
403
- else; @anchor2 = nil; end
404
- set_cursor_visible
405
- end
406
- elsif Mouse.button_released? :left
407
- if @anchor1 and not @double_clicked
408
- if @cur_node != @anchor1; @anchor2 = @cur_node
409
- else; @anchor1 = nil; end
410
- end
411
- end
412
-
413
- @cursor_timer += 1
414
- if @cursor_timer >= 30
415
- @cursor_visible = (not @cursor_visible)
416
- @cursor_timer = 0
417
- end
418
-
419
- ############################### Keyboard ##############################
420
- shift = ((KB.key_down? @k[53]) or (KB.key_down? @k[54]))
421
- if ((KB.key_pressed? @k[53]) or (KB.key_pressed? @k[54])) # shift
422
- @anchor1 = @cur_node if @anchor1.nil?
423
- elsif ((KB.key_released? @k[53]) or (KB.key_released? @k[54]))
424
- @anchor1 = nil if @anchor2.nil?
425
- end
426
- inserted = false
427
- for i in 0..46 # alnum
428
- if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
429
- remove_interval true if @anchor1 and @anchor2
430
- if i < 26
431
- # bool capsLock = Console.CapsLock;
432
- if shift
433
- # if (capsLock) insert_char(@chars[i]);
434
- # else
435
- insert_char @chars[i + 37]
436
- else
437
- # if (capsLock) insert_char(@chars[i + 37]);
438
- # else
439
- insert_char @chars[i]
440
- end
441
- elsif i < 36
442
- if shift
443
- insert_char @chars[i + 57]
444
- else; insert_char @chars[i]; end
445
- elsif shift
446
- insert_char(@chars[i + 47]);
447
- else; insert_char(@chars[i - 10]); end
448
- inserted = true
449
- break
450
- end
451
- end
452
-
453
- return if inserted
454
- for i in 55..64 # special
455
- if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
456
- if shift; insert_char @chars[i + 18]
457
- else; insert_char @chars[i + 8]; end
458
- inserted = true
459
- break
460
- end
461
- end
462
-
463
- return if inserted
464
- if KB.key_pressed?(@k[47]) or KB.key_held?(@k[47]) # back
465
- if @anchor1 and @anchor2
466
- remove_interval
467
- elsif @cur_node > 0
468
- remove_char true
469
- end
470
- elsif KB.key_pressed?(@k[48]) or KB.key_held?(@k[48]) # del
471
- if @anchor1 and @anchor2
472
- remove_interval
473
- elsif @cur_node < @nodes.size - 1
474
- remove_char false
475
- end
476
- elsif KB.key_pressed?(@k[49]) or KB.key_held?(@k[49]) # left
477
- if @anchor1
478
- if shift
479
- if @cur_node > 0
480
- @cur_node -= 1
481
- @anchor2 = @cur_node
482
- set_cursor_visible
483
- end
484
- elsif @anchor2
485
- @cur_node = @anchor1 < @anchor2 ? @anchor1 : @anchor2
486
- @anchor1 = nil
487
- @anchor2 = nil
488
- set_cursor_visible
489
- end
490
- elsif @cur_node > 0
491
- @cur_node -= 1
492
- set_cursor_visible
493
- end
494
- elsif KB.key_pressed?(@k[50]) or KB.key_held?(@k[50]) # right
495
- if @anchor1
496
- if shift
497
- if @cur_node < @nodes.size - 1
498
- @cur_node += 1
499
- @anchor2 = @cur_node
500
- set_cursor_visible
501
- end
502
- elsif @anchor2
503
- @cur_node = @anchor1 > @anchor2 ? @anchor1 : @anchor2
504
- @anchor1 = nil
505
- @anchor2 = nil
506
- set_cursor_visible
507
- end
508
- elsif @cur_node < @nodes.size - 1
509
- @cur_node += 1
510
- set_cursor_visible
511
- end
512
- elsif KB.key_pressed?(@k[51]) # home
513
- @cur_node = 0
514
- if shift; @anchor2 = @cur_node
515
- else
516
- @anchor1 = nil
517
- @anchor2 = nil
518
- end
519
- set_cursor_visible
520
- elsif KB.key_pressed?(@k[52]) # end
521
- @cur_node = @nodes.size - 1
522
- if shift; @anchor2 = @cur_node
523
- else
524
- @anchor1 = nil
525
- @anchor2 = nil
526
- end
527
- set_cursor_visible
528
- end
529
- end
530
-
531
- # Sets the text of the text field to the specified value.
532
- #
533
- # Parameters:
534
- # [value] The new text to be set. If it's longer than the +max_length+
535
- # parameter used in the constructor, it will be truncated to
536
- # +max_length+ characters.
537
- def text= value
538
- @text = value[0...max_length]
539
- @nodes.clear; @nodes << (@x + @margin_x)
540
- x = @nodes[0]
541
- for char in @text
542
- x += @font.text_width char
543
- @nodes << x
544
- end
545
- @cur_node = @nodes.size - 1
546
- @anchor1 = nil
547
- @anchor2 = nil
548
- set_cursor_visible
549
- @on_text_changed.call @text, @params if @on_text_changed
550
- end
551
-
552
- # Returns the currently selected text.
553
- def selected_text
554
- return "" if @anchor2.nil?
555
- min = @anchor1 < @anchor2 ? @anchor1 : @anchor2
556
- max = min == @anchor1 ? @anchor2 : @anchor1
557
- @text[min..max]
558
- end
559
-
560
- # Grants focus to the text field, so that it allows keyboard input.
561
- def focus
562
- @active = true
563
- end
564
-
565
- # Removes focus from the text field, so that no keyboard input will be
566
- # accepted.
567
- def unfocus
568
- @anchor1 = @anchor2 = nil
569
- @cursor_visible = false
570
- @cursor_timer = 0
571
- @active = false
572
- end
573
-
574
- # Sets the position of the text field in the screen.
575
- #
576
- # Parameters:
577
- # [x] The new x-coordinate for the text field.
578
- # [y] The new y-coordinate for the text field.
579
- def set_position x, y
580
- d_x = x - @x
581
- d_y = y - @y
582
- @x = x; @y = y
583
- @text_x += d_x
584
- @text_y += d_y
585
- @nodes.map! do |n|
586
- n + d_x
587
- end
588
- end
589
-
590
- # Draws the text field in the screen.
591
- #
592
- # Parameters:
593
- # [alpha] The opacity with which the text field will be drawn. Allowed
594
- # values vary between 0 (fully transparent) and 255 (fully opaque).
595
- # [z_index] The z-order to draw the object. Objects with larger z-orders
596
- # will be drawn on top of the ones with smaller z-orders.
597
- def draw alpha = 0xff, z_index = 0
598
- return unless @visible
599
-
600
- color = (alpha << 24) | ((@enabled or @disabled_img) ? 0xffffff : 0x808080)
601
- text_color = (alpha << 24) | (@enabled ? @text_color : @disabled_text_color)
602
- img = ((@enabled or @disabled_img.nil?) ? @img : @disabled_img)
603
- img.draw @x, @y, z_index, 1, 1, color
604
- @font.draw @text, @text_x, @text_y, z_index, 1, 1, text_color
605
-
606
- if @anchor1 and @anchor2
607
- selection_color = ((alpha / 2) << 24) | @selection_color
608
- Game.window.draw_quad @nodes[@anchor1], @text_y, selection_color,
609
- @nodes[@anchor2] + 1, @text_y, selection_color,
610
- @nodes[@anchor2] + 1, @text_y + @font.height, selection_color,
611
- @nodes[@anchor1], @text_y + @font.height, selection_color, z_index
612
- end
613
-
614
- if @cursor_visible
615
- if @cursor_img
616
- @cursor_img.draw @nodes[@cur_node] - @cursor_img.width / 2, @text_y, z_index
617
- else
618
- cursor_color = alpha << 24
619
- Game.window.draw_quad @nodes[@cur_node], @text_y, cursor_color,
620
- @nodes[@cur_node] + 1, @text_y, cursor_color,
621
- @nodes[@cur_node] + 1, @text_y + @font.height, cursor_color,
622
- @nodes[@cur_node], @text_y + @font.height, cursor_color, z_index
623
- end
624
- end
625
- end
626
-
627
- def enabled= value # :nodoc:
628
- @enabled = value
629
- unfocus unless @enabled
630
- end
631
-
632
- def visible= value # :nodoc:
633
- @visible = value
634
- unfocus unless @visible
635
- end
636
-
637
- private
638
-
639
- def set_cursor_visible
640
- @cursor_visible = true
641
- @cursor_timer = 0
642
- end
643
-
644
- def set_node_by_mouse
645
- index = @nodes.size - 1
646
- @nodes.each_with_index do |n, i|
647
- if n >= Mouse.x
648
- index = i
649
- break
650
- end
651
- end
652
- if index > 0
653
- d1 = @nodes[index] - Mouse.x; d2 = Mouse.x - @nodes[index - 1]
654
- index -= 1 if d1 > d2
655
- end
656
- @cur_node = index
657
- end
658
-
659
- def insert_char char
660
- return unless @allowed_chars.index char and @text.length < @max_length
661
- @text.insert @cur_node, char
662
- @nodes.insert @cur_node + 1, @nodes[@cur_node] + @font.text_width(char)
663
- for i in (@cur_node + 2)..(@nodes.size - 1)
664
- @nodes[i] += @font.text_width(char)
665
- end
666
- @cur_node += 1
667
- set_cursor_visible
668
- @on_text_changed.call @text, @params if @on_text_changed
669
- end
670
-
671
- def remove_interval will_insert = false
672
- min = @anchor1 < @anchor2 ? @anchor1 : @anchor2
673
- max = min == @anchor1 ? @anchor2 : @anchor1
674
- interval_width = 0
675
- for i in min...max
676
- interval_width += @font.text_width(@text[i])
677
- @nodes.delete_at min + 1
678
- end
679
- @text[min...max] = ""
680
- for i in (min + 1)..(@nodes.size - 1)
681
- @nodes[i] -= interval_width
682
- end
683
- @cur_node = min
684
- @anchor1 = nil
685
- @anchor2 = nil
686
- set_cursor_visible
687
- @on_text_changed.call @text, @params if @on_text_changed and not will_insert
688
- end
689
-
690
- def remove_char back
691
- @cur_node -= 1 if back
692
- char_width = @font.text_width(@text[@cur_node])
693
- @text[@cur_node] = ""
694
- @nodes.delete_at @cur_node + 1
695
- for i in (@cur_node + 1)..(@nodes.size - 1)
696
- @nodes[i] -= char_width
697
- end
698
- set_cursor_visible
699
- @on_text_changed.call @text, @params if @on_text_changed
700
- end
701
- end
4
+ # This class is an abstract ancestor for all form components (Button,
5
+ # ToggleButton and TextField).
6
+ class Component
7
+ # Determines whether the control is enabled, i.e., will process user input.
8
+ attr_accessor :enabled
9
+
10
+ # Determines whether the control is visible, i.e., will be drawn in the
11
+ # screen and process user input, if enabled.
12
+ attr_accessor :visible
13
+
14
+ # A container for any parameters to be passed to the code blocks called
15
+ # in response to events of the control (click of a button, change of the
16
+ # text in a text field, etc.). More detail can be found in the constructor
17
+ # for each specific component class.
18
+ attr_accessor :params
19
+
20
+ # This constructor is for internal use of the subclasses only. Do not
21
+ # instantiate objects of this class.
22
+ def initialize x, y, font, text, text_color, disabled_text_color
23
+ @x = x
24
+ @y = y
25
+ @font = font
26
+ @text = text
27
+ @text_color = text_color
28
+ @disabled_text_color = disabled_text_color
29
+ @enabled = @visible = true
30
+ end
31
+ end
32
+
33
+ # This class represents a button.
34
+ class Button < Component
35
+ # Creates a button.
36
+ #
37
+ # Parameters:
38
+ # [x] The x-coordinate where the button will be drawn in the screen.
39
+ # [y] The y-coordinate where the button will be drawn in the screen.
40
+ # [font] The <code>Gosu::Font</code> object that will be used to draw the
41
+ # button text.
42
+ # [text] The button text. Can be +nil+ or empty.
43
+ # [img] A spritesheet containing four images in a column, representing,
44
+ # from top to bottom, the default state, the hover state (when the
45
+ # mouse is over the button), the pressed state (when the mouse
46
+ # button is down and the cursor is over the button) and the disabled
47
+ # state. If +nil+, the +width+ and +height+ parameters must be
48
+ # provided.
49
+ # [text_color] Color of the button text, in hexadecimal RRGGBB format.
50
+ # [disabled_text_color] Color of the button text, when it's disabled, in
51
+ # hexadecimal RRGGBB format.
52
+ # [center] Whether the button text should be centered in its area (the
53
+ # area is defined by the image size, when an image is given, or
54
+ # by the +width+ and +height+ parameters, otherwise).
55
+ # [margin_x] The x offset, from the button x-coordinate, to draw the text.
56
+ # This parameter is used only if +center+ is false.
57
+ # [margin_y] The y offset, from the button y-coordinate, to draw the text.
58
+ # This parameter is used only if +center+ is false.
59
+ # [width] Width of the button clickable area. This parameter is used only
60
+ # if +img+ is +nil+.
61
+ # [height] Height of the button clickable area. This parameter is used
62
+ # only if +img+ is +nil+.
63
+ # [params] An object containing any parameters you want passed to the
64
+ # +action+ block. When the button is clicked, the following is
65
+ # called:
66
+ # @action.call @params
67
+ # Note that this doesn't force you to declare a block that takes
68
+ # parameters.
69
+ # [action] The block of code executed when the button is clicked (or by
70
+ # calling the +click+ method).
71
+ def initialize x, y, font, text, img, text_color = 0, disabled_text_color = 0, center = true, margin_x = 0, margin_y = 0,
72
+ width = nil, height = nil, params = nil, &action
73
+ super x, y, font, text, text_color, disabled_text_color
74
+ @img =
75
+ if img; Res.imgs img, 1, 4, true
76
+ else; nil; end
77
+ @w =
78
+ if img; @img[0].width
79
+ else; width; end
80
+ @h =
81
+ if img; @img[0].height
82
+ else; height; end
83
+ if center
84
+ @text_x = x + @w / 2 if @w
85
+ @text_y = y + @h / 2 if @h
86
+ else
87
+ @text_x = x + margin_x
88
+ @text_y = y + margin_y
89
+ end
90
+ @center = center
91
+ @action = action
92
+ @params = params
93
+
94
+ @state = :up
95
+ @img_index = @enabled ? 0 : 3
96
+ end
97
+
98
+ # Updates the button, checking the mouse movement and buttons to define
99
+ # the button state.
100
+ def update
101
+ return unless @enabled and @visible
102
+
103
+ mouse_over = Mouse.over? @x, @y, @w, @h
104
+ mouse_press = Mouse.button_pressed? :left
105
+ mouse_rel = Mouse.button_released? :left
106
+
107
+ if @state == :up
108
+ if mouse_over
109
+ @img_index = 1
110
+ @state = :over
111
+ else
112
+ @img_index = 0
113
+ end
114
+ elsif @state == :over
115
+ if not mouse_over
116
+ @img_index = 0
117
+ @state = :up
118
+ elsif mouse_press
119
+ @img_index = 2
120
+ @state = :down
121
+ else
122
+ @img_index = 1
123
+ end
124
+ elsif @state == :down
125
+ if not mouse_over
126
+ @img_index = 0
127
+ @state = :down_out
128
+ elsif mouse_rel
129
+ @img_index = 1
130
+ @state = :over
131
+ click
132
+ else
133
+ @img_index = 2
134
+ end
135
+ else # :down_out
136
+ if mouse_over
137
+ @img_index = 2
138
+ @state = :down
139
+ elsif mouse_rel
140
+ @img_index = 0
141
+ @state = :up
142
+ else
143
+ @img_index = 0
144
+ end
145
+ end
146
+ end
147
+
148
+ # Executes the button click action.
149
+ def click
150
+ @action.call @params
151
+ end
152
+
153
+ # Sets the position of the button in the screen.
154
+ #
155
+ # Parameters:
156
+ # [x] The new x-coordinate for the button.
157
+ # [y] The new y-coordinate for the button.
158
+ def set_position x, y
159
+ d_x = x - @x
160
+ d_y = y - @y
161
+ @x = x; @y = y
162
+ if @center
163
+ @text_x = x + @w / 2
164
+ @text_y = y + @h / 2
165
+ else
166
+ @text_x += d_x
167
+ @text_y += d_y
168
+ end
169
+ end
170
+
171
+ # Draws the button in the screen.
172
+ #
173
+ # Parameters:
174
+ # [alpha] The opacity with which the button will be drawn. Allowed values
175
+ # vary between 0 (fully transparent) and 255 (fully opaque).
176
+ # [z_index] The z-order to draw the object. Objects with larger z-orders
177
+ # will be drawn on top of the ones with smaller z-orders.
178
+ def draw alpha = 0xff, z_index = 0
179
+ return unless @visible
180
+
181
+ color = (alpha << 24) | 0xffffff
182
+ text_color = (alpha << 24) | (@enabled ? @text_color : @disabled_text_color)
183
+ @img[@img_index].draw @x, @y, z_index, 1, 1, color if @img
184
+ if @text
185
+ if @center
186
+ @font.draw_rel @text, @text_x, @text_y, z_index, 0.5, 0.5, 1, 1, text_color
187
+ else
188
+ @font.draw @text, @text_x, @text_y, z_index, 1, 1, text_color
189
+ end
190
+ end
191
+ end
192
+
193
+ def enabled= value # :nodoc:
194
+ @enabled = value
195
+ @state = :up
196
+ @img_index = 3
197
+ end
198
+ end
199
+
200
+ # This class represents a toggle button, which can be also interpreted as a
201
+ # check box. It is always in one of two states, given as +true+ or +false+
202
+ # by its property +checked+.
203
+ class ToggleButton < Button
204
+ # Defines the state of the button (returns +true+ or +false+).
205
+ attr_reader :checked
206
+
207
+ # Creates a ToggleButton. All parameters work the same as in Button,
208
+ # except for the image, +img+, which now has to be composed of two columns
209
+ # and four rows, the first column with images for the unchecked state,
210
+ # and the second with images for the checked state, and for +checked+,
211
+ # which defines the initial state of the ToggleButton.
212
+ #
213
+ # The +action+ block now will always receive a first boolean parameter
214
+ # corresponding to the value of +checked+. So, if you want to pass
215
+ # parameters to the block, you should declare it like this:
216
+ # b = ToggleButton.new ... { |checked, params|
217
+ # puts "button was checked" if checked
218
+ # # do something with params
219
+ # }
220
+ def initialize x, y, font, text, img, checked = false, text_color = 0, disabled_text_color = 0, center = true, margin_x = 0, margin_y = 0,
221
+ width = nil, height = nil, params = nil, &action
222
+ super x, y, font, text, nil, text_color, disabled_text_color, center, margin_x, margin_y, width, height, params, &action
223
+ @img =
224
+ if img; Res.imgs img, 2, 4, true
225
+ else; nil; end
226
+ @w =
227
+ if img; @img[0].width
228
+ else; width; end
229
+ @h =
230
+ if img; @img[0].height
231
+ else; height; end
232
+ if center
233
+ @text_x = x + @w / 2
234
+ @text_y = y + @h / 2
235
+ end
236
+ @checked = checked
237
+ end
238
+
239
+ # Updates the button, checking the mouse movement and buttons to define
240
+ # the button state.
241
+ def update
242
+ return unless @enabled and @visible
243
+
244
+ super
245
+ @img_index *= 2
246
+ @img_index += 1 if @checked
247
+ end
248
+
249
+ # Executes the button click action, and toggles its state. The +action+
250
+ # block always receives as a first parameter +true+, if the button has
251
+ # been changed to checked, or +false+, otherwise.
252
+ def click
253
+ @checked = !@checked
254
+ @action.call @checked, @params
255
+ end
256
+
257
+ # Sets the state of the button to the value given.
258
+ #
259
+ # Parameters:
260
+ # [value] The state to be set (+true+ for checked, +false+ for unchecked).
261
+ def checked= value
262
+ click if value != @checked
263
+ @checked = value
264
+ end
265
+
266
+ def enabled= value # :nodoc:
267
+ @enabled = value
268
+ @state = :up
269
+ @img_index = @checked ? 7 : 6
270
+ end
271
+ end
272
+
273
+ # This class represents a text field (input).
274
+ class TextField < Component
275
+ # The current text inside the text field.
276
+ attr_reader :text
277
+
278
+ # Creates a new text field.
279
+ #
280
+ # Parameters:
281
+ # [x] The x-coordinate where the text field will be drawn in the screen.
282
+ # [y] The y-coordinate where the text field will be drawn in the screen.
283
+ # [font] The <code>Gosu::Font</code> object that will be used to draw the
284
+ # text inside the field.
285
+ # [img] The image of the text field. For a good result, you would likely
286
+ # want something like a rectangle, horizontally wide, vertically
287
+ # short, and with a color that contrasts with the +text_color+.
288
+ # [cursor_img] An image for the blinking cursor that stands in the point
289
+ # where text will be inserted. If +nil+, a simple black line
290
+ # will be drawn instead.
291
+ # [disabled_img] Image for the text field when it's disabled. If +nil+,
292
+ # a darkened version of +img+ will be used.
293
+ # [text_color] Color of the button text, in hexadecimal RRGGBB format.
294
+ # [margin_x] The x offset, from the field x-coordinate, to draw the text.
295
+ # [margin_y] The y offset, from the field y-coordinate, to draw the text.
296
+ # [max_length] The maximum length of the text inside the field.
297
+ # [active] Whether the text field must be focused by default. If +false+,
298
+ # focus can be granted by clicking inside the text field or by
299
+ # calling the +focus+ method.
300
+ # [text] The starting text. Must not be +nil+.
301
+ # [allowed_chars] A string containing all characters that can be typed
302
+ # inside the text field. The complete set of supported
303
+ # characters is given by the string
304
+ # <code>"abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ'-=/[]\\\\,.;\"_+?{}|<>:!@#$%¨&*()"</code>.
305
+ # [text_color] The color with which the text will be drawn, in hexadecimal
306
+ # RRGGBB format.
307
+ # [disabled_text_color] The color with which the text will be drawn, when
308
+ # the text field is disabled, in hexadecimal RRGGBB
309
+ # format.
310
+ # [selection_color] The color of the rectangle highlighting selected text,
311
+ # in hexadecimal RRGGBB format. The rectangle will
312
+ # always be drawn with 50% of opacity.
313
+ # [params] An object containing any parameters you want passed to the
314
+ # +on_text_changed+ block. When the text of the text field is
315
+ # changed, the following is called:
316
+ # @on_text_changed.call @text, @params
317
+ # Thus, +params+ will be the second parameter. Note that this
318
+ # doesn't force you to declare a block that takes parameters.
319
+ # [on_text_changed] The block of code executed when the text in the text
320
+ # field is changed, either by user input or by calling
321
+ # +text=+. The new text is passed as a first parameter
322
+ # to this block, followed by +params+. Can be +nil+.
323
+ def initialize x, y, font, img, cursor_img = nil, disabled_img = nil, margin_x = 0, margin_y = 0, max_length = 100, active = false, text = "",
324
+ allowed_chars = nil, text_color = 0, disabled_text_color = 0, selection_color = 0, params = nil, &on_text_changed
325
+ super x, y, font, text, text_color, disabled_text_color
326
+ @img = Res.img img
327
+ @w = @img.width
328
+ @h = @img.height
329
+ @cursor_img = Res.img(cursor_img) if cursor_img
330
+ @disabled_img = Res.img(disabled_img) if disabled_img
331
+ @max_length = max_length
332
+ @active = active
333
+ @text_x = x + margin_x
334
+ @text_y = y + margin_y
335
+ @selection_color = selection_color
336
+
337
+ @nodes = [x + margin_x]
338
+ @cur_node = 0
339
+ @cursor_visible = false
340
+ @cursor_timer = 0
341
+
342
+ @k = [
343
+ Gosu::KbA, Gosu::KbB, Gosu::KbC, Gosu::KbD, Gosu::KbE, Gosu::KbF,
344
+ Gosu::KbG, Gosu::KbH, Gosu::KbI, Gosu::KbJ, Gosu::KbK, Gosu::KbL,
345
+ Gosu::KbM, Gosu::KbN, Gosu::KbO, Gosu::KbP, Gosu::KbQ, Gosu::KbR,
346
+ Gosu::KbS, Gosu::KbT, Gosu::KbU, Gosu::KbV, Gosu::KbW, Gosu::KbX,
347
+ Gosu::KbY, Gosu::KbZ, Gosu::Kb1, Gosu::Kb2, Gosu::Kb3, Gosu::Kb4,
348
+ Gosu::Kb5, Gosu::Kb6, Gosu::Kb7, Gosu::Kb8, Gosu::Kb9, Gosu::Kb0,
349
+ Gosu::KbNumpad1, Gosu::KbNumpad2, Gosu::KbNumpad3, Gosu::KbNumpad4,
350
+ Gosu::KbNumpad5, Gosu::KbNumpad6, Gosu::KbNumpad7, Gosu::KbNumpad8,
351
+ Gosu::KbNumpad9, Gosu::KbNumpad0, Gosu::KbSpace, Gosu::KbBackspace,
352
+ Gosu::KbDelete, Gosu::KbLeft, Gosu::KbRight, Gosu::KbHome,
353
+ Gosu::KbEnd, Gosu::KbLeftShift, Gosu::KbRightShift,
354
+ Gosu::KbBacktick, Gosu::KbMinus, Gosu::KbEqual, Gosu::KbBracketLeft,
355
+ Gosu::KbBracketRight, Gosu::KbBackslash, Gosu::KbApostrophe,
356
+ Gosu::KbComma, Gosu::KbPeriod, Gosu::KbSlash
357
+ ]
358
+ @chars = "abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ'-=/[]\\,.;\"_+?{}|<>:!@#$%¨&*()"
359
+ @allowed_chars =
360
+ if allowed_chars
361
+ allowed_chars
362
+ else
363
+ @chars
364
+ end
365
+
366
+ @on_text_changed = on_text_changed
367
+ @params = params
368
+ end
369
+
370
+ # Updates the text field, checking for mouse events and keyboard input.
371
+ def update
372
+ return unless @enabled and @visible
373
+
374
+ ################################ Mouse ################################
375
+ if Mouse.over? @x, @y, @w, @h
376
+ if not @active and Mouse.button_pressed? :left
377
+ focus
378
+ end
379
+ elsif Mouse.button_pressed? :left
380
+ unfocus
381
+ end
382
+
383
+ return unless @active
384
+
385
+ if Mouse.double_click? :left
386
+ if @nodes.size > 1
387
+ @anchor1 = 0
388
+ @anchor2 = @nodes.size - 1
389
+ @cur_node = @anchor2
390
+ @double_clicked = true
391
+ end
392
+ set_cursor_visible
393
+ elsif Mouse.button_pressed? :left
394
+ set_node_by_mouse
395
+ @anchor1 = @cur_node
396
+ @anchor2 = nil
397
+ @double_clicked = false
398
+ set_cursor_visible
399
+ elsif Mouse.button_down? :left
400
+ if @anchor1 and not @double_clicked
401
+ set_node_by_mouse
402
+ if @cur_node != @anchor1; @anchor2 = @cur_node
403
+ else; @anchor2 = nil; end
404
+ set_cursor_visible
405
+ end
406
+ elsif Mouse.button_released? :left
407
+ if @anchor1 and not @double_clicked
408
+ if @cur_node != @anchor1; @anchor2 = @cur_node
409
+ else; @anchor1 = nil; end
410
+ end
411
+ end
412
+
413
+ @cursor_timer += 1
414
+ if @cursor_timer >= 30
415
+ @cursor_visible = (not @cursor_visible)
416
+ @cursor_timer = 0
417
+ end
418
+
419
+ ############################### Keyboard ##############################
420
+ shift = ((KB.key_down? @k[53]) or (KB.key_down? @k[54]))
421
+ if ((KB.key_pressed? @k[53]) or (KB.key_pressed? @k[54])) # shift
422
+ @anchor1 = @cur_node if @anchor1.nil?
423
+ elsif ((KB.key_released? @k[53]) or (KB.key_released? @k[54]))
424
+ @anchor1 = nil if @anchor2.nil?
425
+ end
426
+ inserted = false
427
+ for i in 0..46 # alnum
428
+ if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
429
+ remove_interval true if @anchor1 and @anchor2
430
+ if i < 26
431
+ if shift
432
+ insert_char @chars[i + 37]
433
+ else
434
+ insert_char @chars[i]
435
+ end
436
+ elsif i < 36
437
+ if shift; insert_char @chars[i + 57]
438
+ else; insert_char @chars[i]; end
439
+ elsif shift
440
+ insert_char(@chars[i + 47])
441
+ else
442
+ insert_char(@chars[i - 10])
443
+ end
444
+ inserted = true
445
+ break
446
+ end
447
+ end
448
+
449
+ return if inserted
450
+ for i in 55..64 # special
451
+ if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
452
+ if shift; insert_char @chars[i + 18]
453
+ else; insert_char @chars[i + 8]; end
454
+ inserted = true
455
+ break
456
+ end
457
+ end
458
+
459
+ return if inserted
460
+ if KB.key_pressed?(@k[47]) or KB.key_held?(@k[47]) # back
461
+ if @anchor1 and @anchor2
462
+ remove_interval
463
+ elsif @cur_node > 0
464
+ remove_char true
465
+ end
466
+ elsif KB.key_pressed?(@k[48]) or KB.key_held?(@k[48]) # del
467
+ if @anchor1 and @anchor2
468
+ remove_interval
469
+ elsif @cur_node < @nodes.size - 1
470
+ remove_char false
471
+ end
472
+ elsif KB.key_pressed?(@k[49]) or KB.key_held?(@k[49]) # left
473
+ if @anchor1
474
+ if shift
475
+ if @cur_node > 0
476
+ @cur_node -= 1
477
+ @anchor2 = @cur_node
478
+ set_cursor_visible
479
+ end
480
+ elsif @anchor2
481
+ @cur_node = @anchor1 < @anchor2 ? @anchor1 : @anchor2
482
+ @anchor1 = nil
483
+ @anchor2 = nil
484
+ set_cursor_visible
485
+ end
486
+ elsif @cur_node > 0
487
+ @cur_node -= 1
488
+ set_cursor_visible
489
+ end
490
+ elsif KB.key_pressed?(@k[50]) or KB.key_held?(@k[50]) # right
491
+ if @anchor1
492
+ if shift
493
+ if @cur_node < @nodes.size - 1
494
+ @cur_node += 1
495
+ @anchor2 = @cur_node
496
+ set_cursor_visible
497
+ end
498
+ elsif @anchor2
499
+ @cur_node = @anchor1 > @anchor2 ? @anchor1 : @anchor2
500
+ @anchor1 = nil
501
+ @anchor2 = nil
502
+ set_cursor_visible
503
+ end
504
+ elsif @cur_node < @nodes.size - 1
505
+ @cur_node += 1
506
+ set_cursor_visible
507
+ end
508
+ elsif KB.key_pressed?(@k[51]) # home
509
+ @cur_node = 0
510
+ if shift; @anchor2 = @cur_node
511
+ else
512
+ @anchor1 = nil
513
+ @anchor2 = nil
514
+ end
515
+ set_cursor_visible
516
+ elsif KB.key_pressed?(@k[52]) # end
517
+ @cur_node = @nodes.size - 1
518
+ if shift; @anchor2 = @cur_node
519
+ else
520
+ @anchor1 = nil
521
+ @anchor2 = nil
522
+ end
523
+ set_cursor_visible
524
+ end
525
+ end
526
+
527
+ # Sets the text of the text field to the specified value.
528
+ #
529
+ # Parameters:
530
+ # [value] The new text to be set. If it's longer than the +max_length+
531
+ # parameter used in the constructor, it will be truncated to
532
+ # +max_length+ characters.
533
+ def text= value
534
+ @text = value[0...@max_length]
535
+ @nodes.clear; @nodes << @text_x
536
+ x = @nodes[0]
537
+ @text.chars.each { |char|
538
+ x += @font.text_width char
539
+ @nodes << x
540
+ }
541
+ @cur_node = @nodes.size - 1
542
+ @anchor1 = nil
543
+ @anchor2 = nil
544
+ set_cursor_visible
545
+ @on_text_changed.call @text, @params if @on_text_changed
546
+ end
547
+
548
+ # Returns the currently selected text.
549
+ def selected_text
550
+ return "" if @anchor2.nil?
551
+ min = @anchor1 < @anchor2 ? @anchor1 : @anchor2
552
+ max = min == @anchor1 ? @anchor2 : @anchor1
553
+ @text[min..max]
554
+ end
555
+
556
+ # Grants focus to the text field, so that it allows keyboard input.
557
+ def focus
558
+ @active = true
559
+ end
560
+
561
+ # Removes focus from the text field, so that no keyboard input will be
562
+ # accepted.
563
+ def unfocus
564
+ @anchor1 = @anchor2 = nil
565
+ @cursor_visible = false
566
+ @cursor_timer = 0
567
+ @active = false
568
+ end
569
+
570
+ # Sets the position of the text field in the screen.
571
+ #
572
+ # Parameters:
573
+ # [x] The new x-coordinate for the text field.
574
+ # [y] The new y-coordinate for the text field.
575
+ def set_position x, y
576
+ d_x = x - @x
577
+ d_y = y - @y
578
+ @x = x; @y = y
579
+ @text_x += d_x
580
+ @text_y += d_y
581
+ @nodes.map! do |n|
582
+ n + d_x
583
+ end
584
+ end
585
+
586
+ # Draws the text field in the screen.
587
+ #
588
+ # Parameters:
589
+ # [alpha] The opacity with which the text field will be drawn. Allowed
590
+ # values vary between 0 (fully transparent) and 255 (fully opaque).
591
+ # [z_index] The z-order to draw the object. Objects with larger z-orders
592
+ # will be drawn on top of the ones with smaller z-orders.
593
+ def draw alpha = 0xff, z_index = 0
594
+ return unless @visible
595
+
596
+ color = (alpha << 24) | ((@enabled or @disabled_img) ? 0xffffff : 0x808080)
597
+ text_color = (alpha << 24) | (@enabled ? @text_color : @disabled_text_color)
598
+ img = ((@enabled or @disabled_img.nil?) ? @img : @disabled_img)
599
+ img.draw @x, @y, z_index, 1, 1, color
600
+ @font.draw @text, @text_x, @text_y, z_index, 1, 1, text_color
601
+
602
+ if @anchor1 and @anchor2
603
+ selection_color = ((alpha / 2) << 24) | @selection_color
604
+ Game.window.draw_quad @nodes[@anchor1], @text_y, selection_color,
605
+ @nodes[@anchor2] + 1, @text_y, selection_color,
606
+ @nodes[@anchor2] + 1, @text_y + @font.height, selection_color,
607
+ @nodes[@anchor1], @text_y + @font.height, selection_color, z_index
608
+ end
609
+
610
+ if @cursor_visible
611
+ if @cursor_img
612
+ @cursor_img.draw @nodes[@cur_node] - @cursor_img.width / 2, @text_y, z_index
613
+ else
614
+ cursor_color = alpha << 24
615
+ Game.window.draw_quad @nodes[@cur_node], @text_y, cursor_color,
616
+ @nodes[@cur_node] + 1, @text_y, cursor_color,
617
+ @nodes[@cur_node] + 1, @text_y + @font.height, cursor_color,
618
+ @nodes[@cur_node], @text_y + @font.height, cursor_color, z_index
619
+ end
620
+ end
621
+ end
622
+
623
+ def enabled= value # :nodoc:
624
+ @enabled = value
625
+ unfocus unless @enabled
626
+ end
627
+
628
+ def visible= value # :nodoc:
629
+ @visible = value
630
+ unfocus unless @visible
631
+ end
632
+
633
+ private
634
+
635
+ def set_cursor_visible
636
+ @cursor_visible = true
637
+ @cursor_timer = 0
638
+ end
639
+
640
+ def set_node_by_mouse
641
+ index = @nodes.size - 1
642
+ @nodes.each_with_index do |n, i|
643
+ if n >= Mouse.x
644
+ index = i
645
+ break
646
+ end
647
+ end
648
+ if index > 0
649
+ d1 = @nodes[index] - Mouse.x; d2 = Mouse.x - @nodes[index - 1]
650
+ index -= 1 if d1 > d2
651
+ end
652
+ @cur_node = index
653
+ end
654
+
655
+ def insert_char char
656
+ return unless @allowed_chars.index char and @text.length < @max_length
657
+ @text.insert @cur_node, char
658
+ @nodes.insert @cur_node + 1, @nodes[@cur_node] + @font.text_width(char)
659
+ for i in (@cur_node + 2)..(@nodes.size - 1)
660
+ @nodes[i] += @font.text_width(char)
661
+ end
662
+ @cur_node += 1
663
+ set_cursor_visible
664
+ @on_text_changed.call @text, @params if @on_text_changed
665
+ end
666
+
667
+ def remove_interval will_insert = false
668
+ min = @anchor1 < @anchor2 ? @anchor1 : @anchor2
669
+ max = min == @anchor1 ? @anchor2 : @anchor1
670
+ interval_width = 0
671
+ for i in min...max
672
+ interval_width += @font.text_width(@text[i])
673
+ @nodes.delete_at min + 1
674
+ end
675
+ @text[min...max] = ""
676
+ for i in (min + 1)..(@nodes.size - 1)
677
+ @nodes[i] -= interval_width
678
+ end
679
+ @cur_node = min
680
+ @anchor1 = nil
681
+ @anchor2 = nil
682
+ set_cursor_visible
683
+ @on_text_changed.call @text, @params if @on_text_changed and not will_insert
684
+ end
685
+
686
+ def remove_char back
687
+ @cur_node -= 1 if back
688
+ char_width = @font.text_width(@text[@cur_node])
689
+ @text[@cur_node] = ""
690
+ @nodes.delete_at @cur_node + 1
691
+ for i in (@cur_node + 1)..(@nodes.size - 1)
692
+ @nodes[i] -= char_width
693
+ end
694
+ set_cursor_visible
695
+ @on_text_changed.call @text, @params if @on_text_changed
696
+ end
697
+ end
702
698
  end