Class: MiniGL::TextField

Inherits:
Component show all
Defined in:
lib/minigl/forms.rb

Overview

This class represents a text field (input).

Instance Attribute Summary collapse

Attributes inherited from Component

#anchor, #anchor_offset_x, #anchor_offset_y, #enabled, #h, #panel, #params, #text, #visible, #w, #x, #y

Instance Method Summary collapse

Constructor Details

#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) ⇒ TextField

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.



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
# 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

Instance Attribute Details

#focusedObject (readonly)

Whether the text field is focused (accepting input)



570
571
572
# File 'lib/minigl/forms.rb', line 570

def focused
  @focused
end

#localeObject

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



567
568
569
# File 'lib/minigl/forms.rb', line 567

def locale
  @locale
end

Instance Method Details

#draw(alpha = 0xff, z_index = 0, color = 0xffffff, disabled_color = 0x808080) ⇒ Object

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.



965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
# 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

#enabled=(value) ⇒ Object

:nodoc:



996
997
998
999
# File 'lib/minigl/forms.rb', line 996

def enabled=(value) # :nodoc:
  @enabled = value
  unfocus unless @enabled
end

#focusObject

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



922
923
924
925
926
927
928
# File 'lib/minigl/forms.rb', line 922

def focus
  @focused = true
  set_node_by_mouse
  @anchor2 = nil
  @double_clicked = false
  set_cursor_visible
end

#selected_textObject

Returns the currently selected text.



914
915
916
917
918
919
# 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) ⇒ Object

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.



944
945
946
947
948
949
950
951
952
953
# 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) ⇒ Object

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.



879
880
881
882
883
884
885
886
887
888
889
890
891
892
# 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

#unfocusObject

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



932
933
934
935
936
937
# File 'lib/minigl/forms.rb', line 932

def unfocus
  @anchor1 = @anchor2 = nil
  @cursor_visible = false
  @cursor_timer = 0
  @focused = false
end

#updateObject

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



710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
# 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

#visible=(value) ⇒ Object

:nodoc:



1001
1002
1003
1004
# File 'lib/minigl/forms.rb', line 1001

def visible=(value) # :nodoc:
  @visible = value
  unfocus unless @visible
end