Module: Clogs::Editable

Included in:
EditBox, EditLine
Defined in:
lib/clogs/drawables/controls.rb

Overview

Shared single/multi-line text editing.

This is a deliberately small editor: caret, selection, the usual navigation keys, and clipboard via Clipboard. libui offers no way to embed its own entry widget in an area, so there is nothing to delegate to.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#anchorObject (readonly)

Returns the value of attribute anchor.



183
184
185
# File 'lib/clogs/drawables/controls.rb', line 183

def anchor
  @anchor
end

#caretObject (readonly)

Returns the value of attribute caret.



183
184
185
# File 'lib/clogs/drawables/controls.rb', line 183

def caret
  @caret
end

Instance Method Details

#caret_line_offset(direction) ⇒ Object



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/clogs/drawables/controls.rb', line 333

def caret_line_offset(direction)
  return @caret unless multiline?

  column = @caret - line_start(@caret)
  if direction.negative?
    prev_end = line_start(@caret) - 1
    return @caret if prev_end.negative?

    prev_start = line_start(prev_end)
    [prev_start + column, prev_end].min
  else
    next_start = line_end(@caret) + 1
    return @caret if next_start > text_value.length

    [next_start + column, line_end(next_start)].min
  end
end

#delete_selectionObject



220
221
222
223
224
225
226
227
228
229
# File 'lib/clogs/drawables/controls.rb', line 220

def delete_selection
  range = selection_range
  return false unless range

  from, to = range
  self.text_value = text_value.dup.tap { |t| t[from...to] = "" }
  @caret = from
  @anchor = nil
  true
end

#editable_initObject



185
186
187
188
189
190
# File 'lib/clogs/drawables/controls.rb', line 185

def editable_init
  @caret = text_value.length
  @anchor = nil
  @scroll_x = 0
  @scroll_y = 0
end

#focus_gainedObject



205
206
207
# File 'lib/clogs/drawables/controls.rb', line 205

def focus_gained
  redraw!
end

#focus_lostObject



209
210
211
212
# File 'lib/clogs/drawables/controls.rb', line 209

def focus_lost
  @anchor = nil
  redraw!
end

#focusable?Boolean

Returns:

  • (Boolean)


201
202
203
# File 'lib/clogs/drawables/controls.rb', line 201

def focusable?
  true
end

#handle_char(event) ⇒ Object



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/clogs/drawables/controls.rb', line 266

def handle_char(event)
  char = event.char
  return false unless char

  case char
  when "\b", "\x7F"
    unless delete_selection
      if @caret.positive?
        t = text_value.dup
        t[@caret - 1] = ""
        self.text_value = t
        @caret -= 1
      end
    end
  when "\r", "\n"
    return false unless multiline?

    insert("\n")
  when "\t"
    return false
  else
    return false if char.ord < 32

    insert(char)
  end
  redraw!
  true
end

#handle_ctrl(event) ⇒ Object



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/clogs/drawables/controls.rb', line 295

def handle_ctrl(event)
  case event.char
  when "a", "\x01"
    @anchor = 0
    @caret = text_value.length
  when "c", "\x03"
    range = selection_range
    Clipboard.write(text_value[range[0]...range[1]]) if range
  when "x", "\x18"
    range = selection_range
    if range
      Clipboard.write(text_value[range[0]...range[1]])
      delete_selection
    end
  when "v", "\x16"
    text = Clipboard.read
    insert(text) if text && !text.empty?
  else
    return false
  end
  redraw!
  true
end

#insert(str) ⇒ Object



231
232
233
234
235
236
237
# File 'lib/clogs/drawables/controls.rb', line 231

def insert(str)
  delete_selection
  t = text_value.dup
  t.insert(@caret, str)
  self.text_value = t
  @caret += str.length
end

#line_end(pos) ⇒ Object



329
330
331
# File 'lib/clogs/drawables/controls.rb', line 329

def line_end(pos)
  text_value.index("\n", pos) || text_value.length
end

#line_start(pos) ⇒ Object



325
326
327
# File 'lib/clogs/drawables/controls.rb', line 325

def line_start(pos)
  (text_value.rindex("\n", [pos - 1, 0].max) || -1) + 1
end

#move_caret(pos, extend_selection) ⇒ Object



319
320
321
322
323
# File 'lib/clogs/drawables/controls.rb', line 319

def move_caret(pos, extend_selection)
  @anchor = @caret if extend_selection && @anchor.nil?
  @anchor = nil unless extend_selection
  @caret = pos.clamp(0, text_value.length)
end

#multiline?Boolean

Returns:

  • (Boolean)


351
352
353
# File 'lib/clogs/drawables/controls.rb', line 351

def multiline?
  false
end

#on_key(event) ⇒ Object



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
264
# File 'lib/clogs/drawables/controls.rb', line 239

def on_key(event)
  return false if event.up

  if event.ctrl?
    return handle_ctrl(event)
  end

  case event.ext
  when :left then move_caret(@caret - 1, event.shift?)
  when :right then move_caret(@caret + 1, event.shift?)
  when :home then move_caret(line_start(@caret), event.shift?)
  when :end then move_caret(line_end(@caret), event.shift?)
  when :up then move_caret(caret_line_offset(-1), event.shift?)
  when :down then move_caret(caret_line_offset(1), event.shift?)
  when :delete
    unless delete_selection
      t = text_value.dup
      t[@caret] = "" if @caret < t.length
      self.text_value = t
    end
  else
    return handle_char(event)
  end
  redraw!
  true
end

#selection_rangeObject



214
215
216
217
218
# File 'lib/clogs/drawables/controls.rb', line 214

def selection_range
  return nil if @anchor.nil? || @anchor == @caret

  [[@anchor, @caret].min, [@anchor, @caret].max]
end

#text_valueObject



192
193
194
# File 'lib/clogs/drawables/controls.rb', line 192

def text_value
  style(:text).to_s
end

#text_value=(new_text) ⇒ Object



196
197
198
199
# File 'lib/clogs/drawables/controls.rb', line 196

def text_value=(new_text)
  @styles["text"] = new_text
  notify("change", new_text)
end