Class: Potty::Surfaces::InlineSurface

Inherits:
Potty::Surface show all
Defined in:
lib/potty/surfaces/inline_surface.rb

Overview

Renders an N-line region in place under the cursor — like docker compose / npm / cargo progress — instead of taking over the screen. No init_screen, no alt-screen; the terminal stays in cooked mode, so Ctrl-C behaves normally and input is left alone (passive widgets only).

Model: a small cell grid (rows x cols). Widgets draw into it via the same setpos/addstr/attron calls they use on a curses surface; present repaints the region with ANSI (carriage-return + clear-line per row, then cursor back to the top). finalize freezes the last frame and drops the cursor to the line below so the next prompt lands cleanly.

Constant Summary collapse

SGR_FG =
{
  default: 39, black: 30, red: 31, green: 32, yellow: 33,
  blue: 34, magenta: 35, cyan: 36, white: 37, bright_black: 90
}.freeze
SGR_BG =
{
  default: 49, black: 40, red: 41, green: 42, yellow: 43,
  blue: 44, magenta: 45, cyan: 46, white: 47, bright_black: 100
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(theme:, lines: nil, tick_interval: 40, out: $stdout) ⇒ InlineSurface

Returns a new instance of InlineSurface.



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/potty/surfaces/inline_surface.rb', line 27

def initialize(theme:, lines: nil, tick_interval: 40, out: $stdout)
  super()
  @theme = theme
  @rows = [lines || 1, 1].max
  @tick_interval = tick_interval
  @out = out
  @cols = detect_cols
  @cursor = [0, 0]
  @cur_style = nil
  @primed = false
  erase
end

Instance Method Details

#addstr(str) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/potty/surfaces/inline_surface.rb', line 64

def addstr(str)
  row, col = @cursor
  return unless row.between?(0, @rows - 1)

  str.to_s.each_char do |ch|
    break if col >= @cols

    @cells[row][col] = [ch, @cur_style] if col >= 0
    col += 1
  end
  @cursor = [row, col]
end

#attron(style) ⇒ Object



77
78
79
80
81
82
83
# File 'lib/potty/surfaces/inline_surface.rb', line 77

def attron(style)
  prev = @cur_style
  @cur_style = style.is_a?(Potty::Style) ? style : nil
  yield if block_given?
ensure
  @cur_style = prev
end

#eraseObject



56
57
58
# File 'lib/potty/surfaces/inline_surface.rb', line 56

def erase
  @cells = Array.new(@rows) { Array.new(@cols) { [' ', nil] } }
end

#finalizeObject



49
50
51
52
53
54
# File 'lib/potty/surfaces/inline_surface.rb', line 49

def finalize
  present                 # freeze the final frame
  @out.write("\n")        # drop below the region
  @out.write("\e[?25h")   # restore cursor
  @out.flush
end

#presentObject



85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/potty/surfaces/inline_surface.rb', line 85

def present
  if @primed
    @out.write("\e[#{@rows - 1}A") if @rows > 1 # back to the top row
  else
    @primed = true
  end

  @rows.times do |i|
    @out.write("\r\e[2K") # carriage return + clear line
    @out.write(render_row(@cells[i]))
    @out.write("\n") unless i == @rows - 1
  end
  @out.flush
end

#read_keyObject

Inline mode ignores input; sleeping here gives the loop its tick cadence. Terminal stays cooked, so Ctrl-C raises Interrupt normally.



102
103
104
105
# File 'lib/potty/surfaces/inline_surface.rb', line 102

def read_key
  sleep(@tick_interval / 1000.0) if @tick_interval
  nil
end

#setpos(row, col) ⇒ Object



60
61
62
# File 'lib/potty/surfaces/inline_surface.rb', line 60

def setpos(row, col)
  @cursor = [row, col]
end

#sizeObject



40
41
42
# File 'lib/potty/surfaces/inline_surface.rb', line 40

def size
  [@rows, @cols]
end

#startObject



44
45
46
47
# File 'lib/potty/surfaces/inline_surface.rb', line 44

def start
  @out.write("\e[?25l") # hide cursor
  @out.flush
end