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
-
#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) ⇒ Object
- #grid_row(r) ⇒ Object
- #hline(row, col, len, char = "-", style = nil) ⇒ Object
-
#initialize(rows, cols) ⇒ 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
Instance Attribute Details
#cols ⇒ Object (readonly)
Returns the value of attribute cols.
21 22 23 |
# File 'lib/tui_tui/canvas.rb', line 21 def cols @cols end |
#cursor ⇒ Object (readonly)
Returns the value of attribute cursor.
22 23 24 |
# File 'lib/tui_tui/canvas.rb', line 22 def cursor @cursor end |
#rows ⇒ Object (readonly)
Returns the value of attribute rows.
21 22 23 |
# File 'lib/tui_tui/canvas.rb', line 21 def rows @rows end |
Class Method Details
.blank(size) ⇒ Object
17 18 19 |
# File 'lib/tui_tui/canvas.rb', line 17 def self.blank(size) new(size.rows, size.cols) end |
Instance Method Details
#cell(row, col) ⇒ Object
36 37 38 39 40 |
# File 'lib/tui_tui/canvas.rb', line 36 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.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/tui_tui/canvas.rb', line 120 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
31 32 33 34 |
# File 'lib/tui_tui/canvas.rb', line 31 def cursor_at(row, col) @cursor = [row, col] if row.between?(1, @rows) && col.between?(1, @cols) self end |
#fill(rect, style, char = " ") ⇒ Object
81 82 83 84 85 86 87 88 89 |
# File 'lib/tui_tui/canvas.rb', line 81 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) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/tui_tui/canvas.rb', line 95 def frame(rect, style: FRAME) fill(rect, nil) = "+#{"-" * (rect.cols - 2)}+" text(rect.row, rect.col, , style) text(rect.row + rect.rows - 1, rect.col, , style) (1...(rect.rows - 1)).each do |dy| text(rect.row + dy, rect.col, "|", style) text(rect.row + dy, rect.col + rect.cols - 1, "|", style) end self end |
#grid_row(r) ⇒ Object
159 |
# File 'lib/tui_tui/canvas.rb', line 159 def grid_row(r) = @grid[r - 1] |
#hline(row, col, len, char = "-", style = nil) ⇒ Object
91 92 93 |
# File 'lib/tui_tui/canvas.rb', line 91 def hline(row, col, len, char = "-", style = nil) text(row, col, char * len, style) end |
#line(row, col, spans) ⇒ Object
71 72 73 74 75 76 77 78 79 |
# File 'lib/tui_tui/canvas.rb', line 71 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.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/tui_tui/canvas.rb', line 139 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
108 109 110 |
# File 'lib/tui_tui/canvas.rb', line 108 def same_row?(other, r) grid_row(r) == other.grid_row(r) end |
#same_size?(other) ⇒ Boolean
112 113 114 |
# File 'lib/tui_tui/canvas.rb', line 112 def same_size?(other) @rows == other.rows && @cols == other.cols end |
#text(row, col, string, style = nil) ⇒ Object
42 43 44 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 |
# File 'lib/tui_tui/canvas.rb', line 42 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 |