Class: Tui::Screen

Inherits:
Object
  • Object
show all
Includes:
Helpers
Defined in:
lib/tui.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Helpers

#accent, #bold, #dim, #emoji, #fill, #highlight

Constructor Details

#initialize(io: $stderr, width: nil, height: nil) ⇒ Screen

Returns a new instance of Screen.



345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/tui.rb', line 345

def initialize(io: $stderr, width: nil, height: nil)
  @io = io
  @fixed_width = width
  @fixed_height = height
  @width = @height = nil
  refresh_size
  @header = Section.new(self)
  @body   = Section.new(self)
  @footer = Section.new(self)
  @sections = [@header, @body, @footer]
  @input_field = nil
  @cursor_row = nil
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



343
344
345
# File 'lib/tui.rb', line 343

def body
  @body
end

Returns the value of attribute footer.



343
344
345
# File 'lib/tui.rb', line 343

def footer
  @footer
end

#headerObject (readonly)

Returns the value of attribute header.



343
344
345
# File 'lib/tui.rb', line 343

def header
  @header
end

#heightObject (readonly)

Returns the value of attribute height.



343
344
345
# File 'lib/tui.rb', line 343

def height
  @height
end

#input_fieldObject (readonly)

Returns the value of attribute input_field.



343
344
345
# File 'lib/tui.rb', line 343

def input_field
  @input_field
end

#widthObject (readonly)

Returns the value of attribute width.



343
344
345
# File 'lib/tui.rb', line 343

def width
  @width
end

Instance Method Details

#clearObject



371
372
373
374
# File 'lib/tui.rb', line 371

def clear
  @sections.each(&:clear)
  self
end

#flushObject



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/tui.rb', line 376

def flush
  refresh_size
  begin
    @io.write(ANSI::HOME)
  rescue IOError
  end

  cursor_row = nil
  cursor_col = nil
  current_row = 0

  # Render header at top
  @header.lines.each do |line|
    if @input_field && line.has_input?
      cursor_row = current_row + 1
      cursor_col = line.cursor_column(@input_field, @width)
    end
    line.render(@io, @width)
    current_row += 1
  end

  # Calculate available body space (total height minus header and footer)
  footer_lines = @footer.lines.length
  body_space = @height - current_row - footer_lines

  # Render body lines (limited to available space)
  body_rendered = 0
  @body.lines.each do |line|
    break if body_rendered >= body_space
    if @input_field && line.has_input?
      cursor_row = current_row + 1
      cursor_col = line.cursor_column(@input_field, @width)
    end
    line.render(@io, @width)
    current_row += 1
    body_rendered += 1
  end

  # Fill gap between body and footer with blank lines
  # Use \r to position at column 0, clear line, fill with spaces for reliability
  gap = body_space - body_rendered
  blank_line = "\r#{ANSI::CLEAR_EOL}#{' ' * (@width - 1)}\n"
  blank_line_no_newline = "\r#{ANSI::CLEAR_EOL}#{' ' * (@width - 1)}"
  gap.times do |i|
    # Last gap line without newline if no footer follows
    if i == gap - 1 && @footer.lines.empty?
      @io.write(blank_line_no_newline)
    else
      @io.write(blank_line)
    end
    current_row += 1
  end

  # Render footer at the bottom (sticky)
  @footer.lines.each_with_index do |line, idx|
    if @input_field && line.has_input?
      cursor_row = current_row + 1
      cursor_col = line.cursor_column(@input_field, @width)
    end
    # Last line: don't write \n to avoid scrolling
    if idx == footer_lines - 1
      line.render_no_newline(@io, @width)
    else
      line.render(@io, @width)
    end
    current_row += 1
  end

  # Position cursor at input field if present, otherwise hide cursor
  if cursor_row && cursor_col && @input_field
    @io.write("\e[#{cursor_row};#{cursor_col}H")
    @io.write(ANSI::SHOW)
  else
    @io.write(ANSI::HIDE)
  end

  @io.write(ANSI::RESET)
  @io.flush
ensure
  clear
end

#input(placeholder = "", value: "", cursor: nil) ⇒ Object

Raises:

  • (ArgumentError)


366
367
368
369
# File 'lib/tui.rb', line 366

def input(placeholder = "", value: "", cursor: nil)
  raise ArgumentError, "screen already has an input" if @input_field
  @input_field = InputField.new(placeholder: placeholder, text: value, cursor: cursor)
end

#refresh_sizeObject



359
360
361
362
363
364
# File 'lib/tui.rb', line 359

def refresh_size
  rows, cols = Terminal.size(@io)
  @height = @fixed_height || rows
  @width = @fixed_width || cols
  self
end