Class: TuiTui::Canvas
- Inherits:
-
Object
- Object
- TuiTui::Canvas
- Defined in:
- lib/tui_tui/canvas.rb
Overview
Pure drawing surface. Coordinates are 1-origin to match terminal cursor addressing, and text layout is terminal-column aware.
Constant Summary collapse
- CONTROL_GLYPH =
Control bytes are rendered visibly instead of being emitted to the terminal.
"?"- FRAME =
Style.new(fg: :bright_black)
Instance Attribute Summary collapse
-
#chrome ⇒ Object
readonly
Returns the value of attribute chrome.
-
#cols ⇒ Object
readonly
Returns the value of attribute cols.
-
#cursor ⇒ Object
readonly
Returns the value of attribute cursor.
-
#rows ⇒ Object
readonly
Returns the value of attribute rows.
Class Method Summary collapse
Instance Method Summary collapse
- #cell(row, col) ⇒ Object
-
#changed_span(other, r) ⇒ Object
The changed column span of row ‘r` versus `other`, as [from, to] (1-origin, inclusive), or nil if the row is identical.
- #cursor_at(row, col) ⇒ Object
- #fill(rect, style, char = " ") ⇒ Object
- #frame(rect, style: FRAME, chrome: @chrome) ⇒ Object
- #grid_row(r) ⇒ Object
- #hline(row, col, len, char = "-", style = nil) ⇒ Object
-
#initialize(rows, cols, chrome: BoxChrome::ASCII) ⇒ Canvas
constructor
A new instance of Canvas.
- #line(row, col, spans) ⇒ Object
-
#render_row(r, from: 1, to: @cols, depth: :ansi256, enabled: true) ⇒ Object
Render row ‘r`, or just the column span [from, to], coalescing same-styled runs and skipping wide-char continuation cells.
- #same_row?(other, r) ⇒ Boolean
- #same_size?(other) ⇒ Boolean
- #text(row, col, string, style = nil) ⇒ Object
Constructor Details
#initialize(rows, cols, chrome: BoxChrome::ASCII) ⇒ Canvas
Returns a new instance of Canvas.
26 27 28 29 30 31 32 |
# File 'lib/tui_tui/canvas.rb', line 26 def initialize(rows, cols, chrome: BoxChrome::ASCII) @rows = rows @cols = cols @grid = Array.new(rows) { Array.new(cols, Cell::BLANK) } @cursor = nil @chrome = chrome end |
Instance Attribute Details
#chrome ⇒ Object (readonly)
Returns the value of attribute chrome.
24 25 26 |
# File 'lib/tui_tui/canvas.rb', line 24 def chrome @chrome end |
#cols ⇒ Object (readonly)
Returns the value of attribute cols.
22 23 24 |
# File 'lib/tui_tui/canvas.rb', line 22 def cols @cols end |
#cursor ⇒ Object (readonly)
Returns the value of attribute cursor.
23 24 25 |
# File 'lib/tui_tui/canvas.rb', line 23 def cursor @cursor end |
#rows ⇒ Object (readonly)
Returns the value of attribute rows.
22 23 24 |
# File 'lib/tui_tui/canvas.rb', line 22 def rows @rows end |
Class Method Details
Instance Method Details
#cell(row, col) ⇒ Object
39 40 41 42 43 |
# File 'lib/tui_tui/canvas.rb', line 39 def cell(row, col) return nil unless row.between?(1, @rows) && col.between?(1, @cols) @grid[row - 1][col - 1] end |
#changed_span(other, r) ⇒ Object
The changed column span of row ‘r` versus `other`, as [from, to] (1-origin, inclusive), or nil if the row is identical. The start is backed up off any wide-char continuation cell so a partial repaint never begins mid-glyph. Used by the compositor to repaint only the part of a row that moved.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/tui_tui/canvas.rb', line 123 def changed_span(other, r) mine = grid_row(r) theirs = other.grid_row(r) first = last = nil mine.each_index do |i| next if mine[i] == theirs[i] first ||= i last = i end return nil if first.nil? first -= 1 while first.positive? && mine[first].continuation? [first + 1, last + 1] end |
#cursor_at(row, col) ⇒ Object
34 35 36 37 |
# File 'lib/tui_tui/canvas.rb', line 34 def cursor_at(row, col) @cursor = [row, col] if row.between?(1, @rows) && col.between?(1, @cols) self end |
#fill(rect, style, char = " ") ⇒ Object
84 85 86 87 88 89 90 91 92 |
# File 'lib/tui_tui/canvas.rb', line 84 def fill(rect, style, char = " ") cell = Cell.new(char: fill_char(char), style: style) rect.rows.times do |dr| row = rect.row + dr rect.cols.times { |dc| place(row, rect.col + dc, cell) } end self end |
#frame(rect, style: FRAME, chrome: @chrome) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/tui_tui/canvas.rb', line 98 def frame(rect, style: FRAME, chrome: @chrome) fill(rect, nil) mid = chrome.h * (rect.cols - 2) text(rect.row, rect.col, chrome.tl + mid + chrome.tr, style) text(rect.row + rect.rows - 1, rect.col, chrome.bl + mid + chrome.br, style) (1...(rect.rows - 1)).each do |dy| text(rect.row + dy, rect.col, chrome.v, style) text(rect.row + dy, rect.col + rect.cols - 1, chrome.v, style) end self end |
#grid_row(r) ⇒ Object
162 |
# File 'lib/tui_tui/canvas.rb', line 162 def grid_row(r) = @grid[r - 1] |
#hline(row, col, len, char = "-", style = nil) ⇒ Object
94 95 96 |
# File 'lib/tui_tui/canvas.rb', line 94 def hline(row, col, len, char = "-", style = nil) text(row, col, char * len, style) end |
#line(row, col, spans) ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/tui_tui/canvas.rb', line 74 def line(row, col, spans) column = col spans.each do |span| text(row, column, span.text, span.style) column += DisplayText.new(span.text).width end self end |
#render_row(r, from: 1, to: @cols, depth: :ansi256, enabled: true) ⇒ Object
Render row ‘r`, or just the column span [from, to], coalescing same-styled runs and skipping wide-char continuation cells.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/tui_tui/canvas.rb', line 142 def render_row(r, from: 1, to: @cols, depth: :ansi256, enabled: true) out = +"" run = +"" run_style = :none grid_row(r)[(from - 1)..(to - 1)].each do |c| next if c.continuation? if run_style != :none && run_style != c.style out << paint(run, run_style, depth, enabled) run = +"" end run_style = c.style run << c.char end out << paint(run, run_style, depth, enabled) unless run.empty? out end |
#same_row?(other, r) ⇒ Boolean
111 112 113 |
# File 'lib/tui_tui/canvas.rb', line 111 def same_row?(other, r) grid_row(r) == other.grid_row(r) end |
#same_size?(other) ⇒ Boolean
115 116 117 |
# File 'lib/tui_tui/canvas.rb', line 115 def same_size?(other) @rows == other.rows && @cols == other.cols end |
#text(row, col, string, style = nil) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/tui_tui/canvas.rb', line 45 def text(row, col, string, style = nil) return self unless row.between?(1, @rows) column = col TextSanitizer.sanitize(string.to_s).each_grapheme_cluster do |grapheme| if Width.control?(grapheme.ord) break if column > @cols place(row, column, Cell.new(char: CONTROL_GLYPH, style: style)) column += 1 next end width = Width.cluster(grapheme) # Leading combining marks have no base cell to attach to. next if width.zero? break if column > @cols # Do not split a wide glyph across the right edge. break if width == 2 && column == @cols place(row, column, Cell.new(char: grapheme, style: style)) place(row, column + 1, Cell.new(char: nil, style: style)) if width == 2 column += width end self end |