Class: Fidgit::TextArea

Inherits:
Element show all
Defined in:
lib/fidgit/elements/text_area.rb

Constant Summary collapse

ENTITY_PLACEHOLDER =
"*"
ENTITIES_AND_TAGS_PATTERN =
%r%<[a-z](?:=[a-f0-9]+)?>|</[a-z]>|&\w+;%i

Constants inherited from Element

Element::DEFAULT_SCHEMA_FILE, Element::VALID_ALIGN_H, Element::VALID_ALIGN_V

Instance Attribute Summary collapse

Attributes inherited from Element

#align_h, #align_v, #background_color, #border_thickness, #font, #padding_bottom, #padding_left, #padding_right, #padding_top, #parent, #tip, #z

Instance Method Summary collapse

Methods inherited from Element

#default, #draw, #draw_frame, #draw_rect, #enabled=, #enabled?, #height, #height=, #hit?, #max_width, #min_width, new, original_new, #outer_height, #outer_width, #recalc, schema, #to_s, #update, #width, #width=, #with, #x, #x=, #y, #y=

Methods included from Event

#events, included, new_event_handlers, #publish, #subscribe, #unsubscribe

Constructor Details

#initialize(options = {}, &block) ⇒ TextArea

Returns a new instance of TextArea.

Options Hash (options):

  • :text (String) — default: ""
  • :height (Integer)

    Sets both min and max height at once.

  • :min_height (Integer)
  • :max_height (Integer) — default: Infinite
  • :line_spacing (Number) — default: 0
  • :editable (Boolean) — default: true

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/fidgit/elements/text_area.rb', line 125

def initialize(options = {}, &block)
  options = {
    text: '',
    max_height: Float::INFINITY,
    line_spacing: default(:line_spacing),
    background_color: default(:background_color),
    border_color: default(:border_color),
    caret_color: default(:caret_color),
    caret_period: default(:caret_period),
    focused_border_color: default(:focused, :border_color),
    selection_color: default(:selection_color),
    editable: true,
  }.merge! options

  @line_spacing = options[:line_spacing]
  @caret_color = options[:caret_color].dup
  @caret_period = options[:caret_period]
  @focused_border_color = options[:focused_border_color].dup
  @selection_color = options[:selection_color].dup
  @editable = options[:editable]

  @lines = [''] # List of lines of wrapped text.
  @caret_positions = [[0, 0]] # [x, y] of each position the caret can be in.
  @char_widths = [] # Width of each character in the text.
  @text_input = Gosu::TextInput.new
  @old_text = ''
  @old_caret_position = 0
  @old_selection_start = 0
  @tags = Hash.new("") # Hash of tags embedded in the text.

  @text_input.text = options[:text].dup
  @stripped_text = '' # Text stripped of xml tags.

  super(options)

  min_height = padding_left + padding_right + font.height
  if options[:height]
    @max_height = @min_height = [options[:height], min_height].max
  else
    @max_height = [options[:max_height], min_height].max
    @min_height = options[:min_height] ? [options[:min_height], min_height].max : min_height
  end
  rect.height = [padding_left + padding_right + font.height, @min_height].max

  subscribe :left_mouse_button, method(:click_in_text)
  subscribe :right_mouse_button, method(:click_in_text)

  # Handle dragging.
  subscribe :begin_drag do |sender, x, y|
    # Store position of the handle when it starts to drag.
    @drag_start_pos = [x - self.x, y - self.y]
  end

  subscribe :update_drag do |sender, x, y|
    index = text_index_at_position(x, y)
    self.caret_position = [index, @stripped_text.length].min if index
  end

  subscribe :end_drag do
    @drag_start_pos = nil
  end
end

Instance Attribute Details

#editable=(value) ⇒ Boolean (writeonly)


18
19
20
# File 'lib/fidgit/elements/text_area.rb', line 18

def editable=(value)
  @editable = value
end

#line_spacingNumber (readonly)


14
15
16
# File 'lib/fidgit/elements/text_area.rb', line 14

def line_spacing
  @line_spacing
end

#max_heightNumber (readonly)


11
12
13
# File 'lib/fidgit/elements/text_area.rb', line 11

def max_height
  @max_height
end

#min_heightNumber (readonly)


9
10
11
# File 'lib/fidgit/elements/text_area.rb', line 9

def min_height
  @min_height
end

#stripped_textString (readonly)


21
22
23
# File 'lib/fidgit/elements/text_area.rb', line 21

def stripped_text
  @stripped_text
end

Instance Method Details

#blur(sender) ⇒ nil


212
213
214
215
216
217
218
219
220
221
# File 'lib/fidgit/elements/text_area.rb', line 212

def blur(sender)
  if focused?
    $window.current_game_state.focus = nil
    $window.text_input = nil
  end

  @focused = false

  nil
end

#caret_positionInteger

Position of the caret.


89
90
91
# File 'lib/fidgit/elements/text_area.rb', line 89

def caret_position
  @text_input.caret_pos
end

#caret_position=(position) ⇒ Integer

Position of the caret.

Raises:

  • (ArgumentError)

97
98
99
100
101
102
# File 'lib/fidgit/elements/text_area.rb', line 97

def caret_position=(position)
  raise ArgumentError, "Caret position must be in the range 0 to the length of the text (inclusive)" unless position.between?(0, stripped_text.length)
  @text_input.caret_pos = position

  position
end

#click_in_text(sender, x, y) ⇒ nil


189
190
191
192
193
194
195
196
197
# File 'lib/fidgit/elements/text_area.rb', line 189

def click_in_text(sender, x, y)
  publish :focus unless focused?

  # Move caret to position the user clicks on.
  index = text_index_at_position x, y
  self.caret_position = @text_input.selection_start = [index, @stripped_text.length].min if index

  nil
end

#copyObject

Copy the selection to the clipboard.


454
455
456
457
# File 'lib/fidgit/elements/text_area.rb', line 454

def copy
  str = selection_text
  Clipboard.copy str unless str.empty?
end

#cutObject

Cut the selection and copy it to the clipboard.


444
445
446
447
448
449
450
# File 'lib/fidgit/elements/text_area.rb', line 444

def cut
  str = selection_text
  unless str.empty?
    Clipboard.copy str
    self.selection_text = '' if editable?
  end
end

#drag?(button) ⇒ Boolean


31
# File 'lib/fidgit/elements/text_area.rb', line 31

def drag?(button); button == :left; end

#draw_foregroundnil

Draw the text area.


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/fidgit/elements/text_area.rb', line 226

def draw_foreground
  # Always roll back changes made by the user unless the text is editable.
  if editable? or text == @old_text
    recalc if focused? # Workaround for Windows draw/update bug.
    @old_caret_position = caret_position
    @old_selection_start = @text_input.selection_start
  else
    roll_back
  end

  if caret_position > stripped_text.length
    self.caret_position = stripped_text.length
  end

  if @text_input.selection_start >= stripped_text.length
    @text_input.selection_start = stripped_text.length
  end

  # Draw the selection.
  selection_range.each do |pos|
    char_x, char_y = @caret_positions[pos]
    char_width = @char_widths[pos]
    left, top = x + padding_left + char_x, y + padding_top + char_y
    draw_rect left, top, char_width, font.height, z, @selection_color
  end

  # Draw text.
  @lines.each_with_index do |line, index|
    font.draw(line, x + padding_left, y + padding_top + y_at_line(index), z)
  end

  # Draw the caret.
  if focused? and ((Gosu::milliseconds / @caret_period) % 2 == 0)
    caret_x, caret_y = @caret_positions[caret_position]
    left, top = x + padding_left + caret_x, y + padding_top + caret_y
    draw_rect left, top, 1, font.height, z, @caret_color
  end
end

#editable?Boolean

Is the area editable? This will always be false if the Element is disabled.


34
35
36
# File 'lib/fidgit/elements/text_area.rb', line 34

def editable?
  enabled? and @editable
end

#focus(sender) ⇒ nil


203
204
205
206
207
208
209
# File 'lib/fidgit/elements/text_area.rb', line 203

def focus(sender)
  @focused = true
  $window.current_game_state.focus = self
  $window.text_input = @text_input

  nil
end

#focused?Boolean

Does the element have the focus?


200
# File 'lib/fidgit/elements/text_area.rb', line 200

def focused?; @focused; end

#pasteObject

Paste the contents of the clipboard into the TextArea.


461
462
463
# File 'lib/fidgit/elements/text_area.rb', line 461

def paste
  self.selection_text = Clipboard.paste
end

#selection_rangeRange

Returns the range of the selection.


47
48
49
50
51
52
# File 'lib/fidgit/elements/text_area.rb', line 47

def selection_range
  from = [@text_input.selection_start, caret_position].min
  to = [@text_input.selection_start, caret_position].max

  (from...to)
end

#selection_textString

Returns the text within the selection.


57
58
59
# File 'lib/fidgit/elements/text_area.rb', line 57

def selection_text
  stripped_text[selection_range]
end

#selection_text=(str) ⇒ String

Sets the text within the selection. The caret will be placed at the end of the inserted text.


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/fidgit/elements/text_area.rb', line 65

def selection_text=(str)
  from = [@text_input.selection_start, @text_input.caret_pos].min
  to = [@text_input.selection_start, @text_input.caret_pos].max
  new_length = str.length

  full_text = text
  tags_length_before = (0...from).inject(0) {|m, i| m + @tags[i].length }
  tags_length_inside = (from...to).inject(0) {|m, i| m + @tags[i].length }
  range = (selection_range.first + tags_length_before)...(selection_range.last + tags_length_before + tags_length_inside)
  full_text[range] = str.encode('UTF-8', undef: :replace)
  @text_input.text = full_text

  @text_input.selection_start = @text_input.caret_pos = from + new_length

  recalc # This may roll back the text if it is too long!

  publish :changed, self.text

  str
end

#textString

Text within the element.


40
41
42
# File 'lib/fidgit/elements/text_area.rb', line 40

def text
  @text_input.text.force_encoding 'UTF-8'
end

#text=(text) ⇒ String

Sets caret to the end of the text.


108
109
110
111
112
113
# File 'lib/fidgit/elements/text_area.rb', line 108

def text=(text)
  @text_input.text = text
  recalc # This may roll back the text if it is too long.
  publish :changed, self.text
  self.text
end