class MiniGL::TextField

This class represents a text field (input).

Attributes

focused[R]

Whether the text field is focused (accepting input)

locale[R]

The current ‘locale’ used for detecting the keys. THIS FEATURE IS INCOMPLETE!

Public Class Methods

new(x, y = nil, font = nil, img = nil, cursor_img = nil, disabled_img = nil, margin_x = 0, margin_y = 0, max_length = 100, focused = false, text = '', allowed_chars = nil, text_color = 0, disabled_text_color = 0, selection_color = 0, locale = 'en-us', params = nil, retro = nil, scale_x = 1, scale_y = 1, anchor = nil, &on_text_changed) click to toggle source

Creates a new text field.

Parameters:

x

The x-coordinate where the text field will be drawn in the screen.

y

The y-coordinate where the text field will be drawn in the screen.

font

The Gosu::Font object that will be used to draw the text inside the field.

img

The image of the text field. For a good result, you would likely want something like a rectangle, horizontally wide, vertically short, and with a color that contrasts with the text_color.

cursor_img

An image for the blinking cursor that stands in the point where text will be inserted. If nil, a simple black line will be drawn instead.

disabled_img

Image for the text field when it’s disabled. If nil, a darkened version of img will be used.

text_color

Color of the button text, in hexadecimal RRGGBB format.

margin_x

The x offset, from the field x-coordinate, to draw the text.

margin_y

The y offset, from the field y-coordinate, to draw the text.

max_length

The maximum length of the text inside the field.

focused

Whether the text field must be focused by default. If false, focus can be granted by clicking inside the text field or by calling the focus method.

text

The starting text. Must not be nil.

allowed_chars

A string containing all characters that can be typed inside the text field. The complete set of supported characters is given by the string "abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ'-=/[]\\,.;\"_+?{}|<>:!@#$%¨&*()".

text_color

The color with which the text will be drawn, in hexadecimal RRGGBB format.

disabled_text_color

The color with which the text will be drawn, when the text field is disabled, in hexadecimal RRGGBB format.

selection_color

The color of the rectangle highlighting selected text, in hexadecimal RRGGBB format. The rectangle will always be drawn with 50% of opacity.

locale

The locale to be used when detecting keys. By now, only ‘en-US’ and ‘pt-BR’ are partially supported. Default is ‘en-US’. If any different value is supplied, all typed characters will be mapped to ‘#’.

params

An object containing any parameters you want passed to the on_text_changed block. When the text of the text field is changed, the following is called:

@on_text_changed.call @text, @params

Thus, params will be the second parameter. Note that this doesn’t force you to declare a block that takes parameters.

retro

Whether the images should be loaded with the ‘retro’ option set (see Gosu::Image for details). If the value is omitted, the Res.retro_images value will be used.

scale_x

Horizontal scale to draw the component with.

scale_y

Vertical scale to draw the component with.

anchor

See parameter with the same name in Panel#initialize for details.

on_text_changed

The block of code executed when the text in the text field is changed, either by user input or by calling text=. The new text is passed as a first parameter to this block, followed by params. Can be nil.

Obs.: This method accepts named parameters, but x, y, font and img are mandatory.

Calls superclass method
# File lib/minigl/forms.rb, line 630
def initialize(x, y = nil, font = nil, img = nil, cursor_img = nil, disabled_img = nil, margin_x = 0, margin_y = 0,
               max_length = 100, focused = false, text = '', allowed_chars = nil,
               text_color = 0, disabled_text_color = 0, selection_color = 0, locale = 'en-us',
               params = nil, retro = nil, scale_x = 1, scale_y = 1, anchor = nil, &on_text_changed)
  if x.is_a? Hash
    y = x[:y]
    font = x[:font]
    img = x[:img]
    cursor_img = x.fetch(:cursor_img, nil)
    disabled_img = x.fetch(:disabled_img, nil)
    margin_x = x.fetch(:margin_x, 0)
    margin_y = x.fetch(:margin_y, 0)
    max_length = x.fetch(:max_length, 100)
    focused = x.fetch(:focused, false)
    text = x.fetch(:text, '')
    allowed_chars = x.fetch(:allowed_chars, nil)
    text_color = x.fetch(:text_color, 0)
    disabled_text_color = x.fetch(:disabled_text_color, 0)
    selection_color = x.fetch(:selection_color, 0)
    locale = x.fetch(:locale, 'en-us')
    params = x.fetch(:params, nil)
    retro = x.fetch(:retro, nil)
    scale_x = x.fetch(:scale_x, 1)
    scale_y = x.fetch(:scale_y, 1)
    anchor = x.fetch(:anchor, nil)
    x = x[:x]
  end

  retro = Res.retro_images if retro.nil?
  @scale_x = scale_x
  @scale_y = scale_y
  @img = Res.img img, false, false, '.png', retro
  @w = @img.width * @scale_x
  @h = @img.height * @scale_y

  @anchor_offset_x = x; @anchor_offset_y = y
  @anchor, x, y = FormUtils.check_anchor(anchor, x, y, @w, @h)

  super x, y, font, text, text_color, disabled_text_color
  @cursor_img = Res.img(cursor_img, false, false, '.png', retro) if cursor_img
  @disabled_img = Res.img(disabled_img, false, false, '.png', retro) if disabled_img
  @max_length = max_length
  @focused = focused
  @text_x = x + margin_x * @scale_x
  @text_y = y + margin_y * @scale_y
  @selection_color = selection_color

  @nodes = [@text_x]
  send(:text=, text, false) if text

  @cur_node = 0
  @cursor_visible = false
  @cursor_timer = 0

  @k = [
    Gosu::KbA, Gosu::KbB, Gosu::KbC, Gosu::KbD, Gosu::KbE, Gosu::KbF,
    Gosu::KbG, Gosu::KbH, Gosu::KbI, Gosu::KbJ, Gosu::KbK, Gosu::KbL,
    Gosu::KbM, Gosu::KbN, Gosu::KbO, Gosu::KbP, Gosu::KbQ, Gosu::KbR,
    Gosu::KbS, Gosu::KbT, Gosu::KbU, Gosu::KbV, Gosu::KbW, Gosu::KbX,
    Gosu::KbY, Gosu::KbZ, Gosu::Kb1, Gosu::Kb2, Gosu::Kb3, Gosu::Kb4,
    Gosu::Kb5, Gosu::Kb6, Gosu::Kb7, Gosu::Kb8, Gosu::Kb9, Gosu::Kb0,
    Gosu::KbNumpad1, Gosu::KbNumpad2, Gosu::KbNumpad3, Gosu::KbNumpad4,
    Gosu::KbNumpad5, Gosu::KbNumpad6, Gosu::KbNumpad7, Gosu::KbNumpad8,
    Gosu::KbNumpad9, Gosu::KbNumpad0, Gosu::KbSpace, Gosu::KbBackspace,
    Gosu::KbDelete, Gosu::KbLeft, Gosu::KbRight, Gosu::KbHome,
    Gosu::KbEnd, Gosu::KbLeftShift, Gosu::KbRightShift,
    Gosu::KbBacktick, Gosu::KbMinus, Gosu::KbEqual, Gosu::KbBracketLeft,
    Gosu::KbBracketRight, Gosu::KbBackslash, Gosu::KbSemicolon,
    Gosu::KbApostrophe, Gosu::KbComma, Gosu::KbPeriod, Gosu::KbSlash,
    Gosu::KbNumpadAdd, Gosu::KbNumpadSubtract,
    Gosu::KbNumpadMultiply, Gosu::KbNumpadDivide
  ]
  @user_allowed_chars = allowed_chars
  self.locale = locale

  @on_text_changed = on_text_changed
  @params = params
end

Public Instance Methods

draw(alpha = 0xff, z_index = 0, color = 0xffffff, disabled_color = 0x808080) click to toggle source

Draws the text field in the screen.

Parameters:

alpha

The opacity with which the text field will be drawn. Allowed values vary between 0 (fully transparent) and 255 (fully opaque).

z_index

The z-order to draw the object. Objects with larger z-orders will be drawn on top of the ones with smaller z-orders.

color

Color to apply a filter to the image.

disabled_color

Color to apply a filter to the image when the field is disabled.

# File lib/minigl/forms.rb, line 965
def draw(alpha = 0xff, z_index = 0, color = 0xffffff, disabled_color = 0x808080)
  @z_index = z_index
  return unless @visible

  color = (alpha << 24) | ((@enabled or @disabled_img) ? color : disabled_color)
  text_color = (alpha << 24) | (@enabled ? @text_color : @disabled_text_color)
  img = ((@enabled or @disabled_img.nil?) ? @img : @disabled_img)
  img.draw @x, @y, z_index, @scale_x, @scale_y, color
  @font.draw_text @text, @text_x, @text_y, z_index, @scale_x, @scale_y, text_color

  if @anchor1 and @anchor2
    selection_color = ((alpha / 2) << 24) | @selection_color
    G.window.draw_quad @nodes[@anchor1], @text_y, selection_color,
                       @nodes[@anchor2] + 1, @text_y, selection_color,
                       @nodes[@anchor2] + 1, @text_y + @font.height * @scale_y, selection_color,
                       @nodes[@anchor1], @text_y + @font.height * @scale_y, selection_color, z_index
  end

  if @cursor_visible
    if @cursor_img
      @cursor_img.draw @nodes[@cur_node] - (@cursor_img.width * @scale_x) / 2, @text_y, z_index, @scale_x, @scale_y
    else
      cursor_color = alpha << 24
      G.window.draw_quad @nodes[@cur_node], @text_y, cursor_color,
                         @nodes[@cur_node] + 1, @text_y, cursor_color,
                         @nodes[@cur_node] + 1, @text_y + @font.height * @scale_y, cursor_color,
                         @nodes[@cur_node], @text_y + @font.height * @scale_y, cursor_color, z_index
    end
  end
end
focus() click to toggle source

Grants focus to the text field, so that it allows keyboard input.

# File lib/minigl/forms.rb, line 922
def focus
  @focused = true
  set_node_by_mouse
  @anchor2 = nil
  @double_clicked = false
  set_cursor_visible
end
locale=(value) click to toggle source

Sets the locale used by the text field to detect keys. Only ‘en-us’ and ‘pt-br’ are partially supported. If any different value is supplied, all typed characters will be mapped to ‘#’.

# File lib/minigl/forms.rb, line 897
def locale=(value)
  @locale = value.downcase
  @chars =
    case @locale
      when 'en-us' then "abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ`-=[]\\;',./~_+{}|:\"<>?!@#$%^&*()+-*/"
      when 'pt-br' then "abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ'-=/[]ç~,.;\"_+?{}Ç^<>:!@#$%¨&*()+-*/"
      else              '###################################################################################################'
    end
  @allowed_chars =
    if @user_allowed_chars
      @user_allowed_chars
    else
      @chars
    end
end
selected_text() click to toggle source

Returns the currently selected text.

# File lib/minigl/forms.rb, line 914
def selected_text
  return '' if @anchor2.nil?
  min = @anchor1 < @anchor2 ? @anchor1 : @anchor2
  max = min == @anchor1 ? @anchor2 : @anchor1
  @text[min..max]
end
set_position(x, y) click to toggle source

Sets the position of the text field in the screen.

Parameters:

x

The new x-coordinate for the text field.

y

The new y-coordinate for the text field.

# File lib/minigl/forms.rb, line 944
def set_position(x, y)
  d_x = x - @x
  d_y = y - @y
  @x = x; @y = y
  @text_x += d_x
  @text_y += d_y
  @nodes.map! do |n|
    n + d_x
  end
end
text=(value, trigger_changed = true) click to toggle source

Sets the text of the text field to the specified value.

Parameters:

value

The new text to be set. If it’s longer than the max_length parameter used in the constructor, it will be truncated to max_length characters.

# File lib/minigl/forms.rb, line 879
def text=(value, trigger_changed = true)
  @text = value[0...@max_length]
  @nodes.clear; @nodes << @text_x
  x = @nodes[0]
  @text.chars.each { |char|
    x += @font.text_width(char) * @scale_x
    @nodes << x
  }
  @cur_node = @nodes.size - 1
  @anchor1 = nil
  @anchor2 = nil
  set_cursor_visible
  @on_text_changed.call @text, @params if trigger_changed && @on_text_changed
end
unfocus() click to toggle source

Removes focus from the text field, so that no keyboard input will be accepted.

# File lib/minigl/forms.rb, line 932
def unfocus
  @anchor1 = @anchor2 = nil
  @cursor_visible = false
  @cursor_timer = 0
  @focused = false
end
update() click to toggle source

Updates the text field, checking for mouse events and keyboard input.

# File lib/minigl/forms.rb, line 710
def update
  return unless @enabled and @visible

  ################################ Mouse ################################
  if Mouse.over? @x, @y, @w, @h
    if not @focused and Mouse.button_pressed? :left
      Mouse.add_click(@z_index || 0, method(:focus))
    end
  elsif Mouse.button_pressed? :left
    unfocus
  end

  return unless @focused

  if Mouse.double_click? :left
    if @nodes.size > 1
      @anchor1 = 0
      @anchor2 = @nodes.size - 1
      @cur_node = @anchor2
      @double_clicked = true
    end
    set_cursor_visible
  elsif Mouse.button_pressed? :left
    Mouse.add_click(@z_index || 0, method(:focus_and_set_anchor))
  elsif Mouse.button_down? :left
    if @anchor1 and not @double_clicked
      set_node_by_mouse
      if @cur_node != @anchor1; @anchor2 = @cur_node
      else; @anchor2 = nil; end
      set_cursor_visible
    end
  elsif Mouse.button_released? :left
    if @anchor1 and not @double_clicked
      if @cur_node != @anchor1; @anchor2 = @cur_node
      else; @anchor1 = nil; end
    end
  end

  @cursor_timer += 1
  if @cursor_timer >= 30
    @cursor_visible = (not @cursor_visible)
    @cursor_timer = 0
  end

  ############################### Keyboard ##############################
  shift = (KB.key_down?(@k[53]) or KB.key_down?(@k[54]))
  if KB.key_pressed?(@k[53]) or KB.key_pressed?(@k[54]) # shift
    @anchor1 = @cur_node if @anchor1.nil?
  elsif KB.key_released?(@k[53]) or KB.key_released?(@k[54])
    @anchor1 = nil if @anchor2.nil?
  end
  inserted = false
  for i in 0..46 # alnum
    if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
      remove_interval true if @anchor1 and @anchor2
      if i < 26
        if shift
          insert_char @chars[i + 37]
        else
          insert_char @chars[i]
        end
      elsif i < 36
        if shift; insert_char @chars[i + 59]
        else; insert_char @chars[i]; end
      elsif shift
        insert_char(@chars[i + 49])
      else
        insert_char(@chars[i - 10])
      end
      inserted = true
      break
    end
  end

  return if inserted
  for i in 55..65 # special
    if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
      remove_interval true if @anchor1 and @anchor2
      if shift; insert_char @chars[i + 19]
      else; insert_char @chars[i + 8]; end
      inserted = true
      break
    end
  end

  return if inserted
  for i in 66..69 # numpad operators
    if KB.key_pressed?(@k[i]) or KB.key_held?(@k[i])
      remove_interval true if @anchor1 and @anchor2
      insert_char @chars[i + 19]
      inserted = true
      break
    end
  end

  return if inserted
  if KB.key_pressed?(@k[47]) or KB.key_held?(@k[47]) # back
    if @anchor1 and @anchor2
      remove_interval
    elsif @cur_node > 0
      remove_char true
    end
  elsif KB.key_pressed?(@k[48]) or KB.key_held?(@k[48]) # del
    if @anchor1 and @anchor2
      remove_interval
    elsif @cur_node < @nodes.size - 1
      remove_char false
    end
  elsif KB.key_pressed?(@k[49]) or KB.key_held?(@k[49]) # left
    if @anchor1
      if shift
        if @cur_node > 0
          @cur_node -= 1
          @anchor2 = @cur_node
          set_cursor_visible
        end
      elsif @anchor2
        @cur_node = @anchor1 < @anchor2 ? @anchor1 : @anchor2
        @anchor1 = nil
        @anchor2 = nil
        set_cursor_visible
      end
    elsif @cur_node > 0
      @cur_node -= 1
      set_cursor_visible
    end
  elsif KB.key_pressed?(@k[50]) or KB.key_held?(@k[50]) # right
    if @anchor1
      if shift
        if @cur_node < @nodes.size - 1
          @cur_node += 1
          @anchor2 = @cur_node
          set_cursor_visible
        end
      elsif @anchor2
        @cur_node = @anchor1 > @anchor2 ? @anchor1 : @anchor2
        @anchor1 = nil
        @anchor2 = nil
        set_cursor_visible
      end
    elsif @cur_node < @nodes.size - 1
      @cur_node += 1
      set_cursor_visible
    end
  elsif KB.key_pressed?(@k[51]) # home
    @cur_node = 0
    if shift; @anchor2 = @cur_node
    else
      @anchor1 = nil
      @anchor2 = nil
    end
    set_cursor_visible
  elsif KB.key_pressed?(@k[52]) # end
    @cur_node = @nodes.size - 1
    if shift; @anchor2 = @cur_node
    else
      @anchor1 = nil
      @anchor2 = nil
    end
    set_cursor_visible
  end
end

Private Instance Methods

focus_and_set_anchor() click to toggle source
# File lib/minigl/forms.rb, line 1008
def focus_and_set_anchor
  focus
  @anchor1 = @cur_node
end
insert_char(char) click to toggle source
# File lib/minigl/forms.rb, line 1033
def insert_char(char)
  return unless @allowed_chars.index char and @text.length < @max_length
  @text.insert @cur_node, char
  @nodes.insert @cur_node + 1, @nodes[@cur_node] + @font.text_width(char) * @scale_x
  for i in (@cur_node + 2)..(@nodes.size - 1)
    @nodes[i] += @font.text_width(char) * @scale_x
  end
  @cur_node += 1
  set_cursor_visible
  @on_text_changed.call @text, @params if @on_text_changed
end
remove_char(back) click to toggle source
# File lib/minigl/forms.rb, line 1064
def remove_char(back)
  @cur_node -= 1 if back
  char_width = @font.text_width(@text[@cur_node]) * @scale_x
  @text[@cur_node] = ''
  @nodes.delete_at @cur_node + 1
  for i in (@cur_node + 1)..(@nodes.size - 1)
    @nodes[i] -= char_width
  end
  set_cursor_visible
  @on_text_changed.call @text, @params if @on_text_changed
end
remove_interval(will_insert = false) click to toggle source
# File lib/minigl/forms.rb, line 1045
def remove_interval(will_insert = false)
  min = @anchor1 < @anchor2 ? @anchor1 : @anchor2
  max = min == @anchor1 ? @anchor2 : @anchor1
  interval_width = 0
  for i in min...max
    interval_width += @font.text_width(@text[i]) * @scale_x
    @nodes.delete_at min + 1
  end
  @text[min...max] = ''
  for i in (min + 1)..(@nodes.size - 1)
    @nodes[i] -= interval_width
  end
  @cur_node = min
  @anchor1 = nil
  @anchor2 = nil
  set_cursor_visible
  @on_text_changed.call @text, @params if @on_text_changed and not will_insert
end
set_cursor_visible() click to toggle source
# File lib/minigl/forms.rb, line 1013
def set_cursor_visible
  @cursor_visible = true
  @cursor_timer = 0
end
set_node_by_mouse() click to toggle source
# File lib/minigl/forms.rb, line 1018
def set_node_by_mouse
  index = @nodes.size - 1
  @nodes.each_with_index do |n, i|
    if n >= Mouse.x
      index = i
      break
    end
  end
  if index > 0
    d1 = @nodes[index] - Mouse.x; d2 = Mouse.x - @nodes[index - 1]
    index -= 1 if d1 > d2
  end
  @cur_node = index
end