Class: Fatty::Renderer

Inherits:
Object
  • Object
show all
Defined in:
lib/fatty/renderer.rb,
lib/fatty/renderer/curses.rb,
lib/fatty/renderer/truecolor.rb

Overview

The Renderer class implements drawing the elements of a Fatty terminal (output, input, alert, status, and popups) on the screen. There are two ways of doing so: (1) using normal curses operations with curses-defined pairs and (2) using ANSI codes to write with a Truecolor full-color palette. This class is the base class for those two rendering methods and provides methods common to them.

Direct Known Subclasses

Curses, Truecolor

Defined Under Namespace

Classes: Curses, Truecolor

Constant Summary collapse

FRAME_STYLES =
{
  ascii: { h: "-", v: "|" },
  single: { h: "", v: "" },
  double: { h: "", v: "" },
}.freeze
CORNER_STYLES =
{
  square: {
    ascii: { tl: "+", tr: "+", bl: "+", br: "+" },
    single: { tl: "", tr: "", bl: "", br: "" },
    double: { tl: "", tr: "", bl: "", br: "" },
  },
  rounded: {
    single: { tl: "", tr: "", bl: "", br: "" },
  },
}.freeze
""
"  "

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(screen:, palette:, context:) ⇒ Renderer

Returns a new instance of Renderer.



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/fatty/renderer.rb', line 42

def initialize(screen:, palette:, context:)
  @screen = screen
  @palette = palette
  @context = context
  @last_status_state = nil
  @last_alert_state = nil
  @last_output_state = nil
  @last_popup_state = nil
  @last_prompt_popup_state = nil
  @last_pager_field_state = nil
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



40
41
42
# File 'lib/fatty/renderer.rb', line 40

def context
  @context
end

#paletteObject (readonly)

Returns the value of attribute palette.



40
41
42
# File 'lib/fatty/renderer.rb', line 40

def palette
  @palette
end

#screenObject

Returns the value of attribute screen.



40
41
42
# File 'lib/fatty/renderer.rb', line 40

def screen
  @screen
end

Instance Method Details

#apply_theme!(theme) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/fatty/renderer.rb', line 56

def apply_theme!(theme)
  Fatty::Themes::Manager.set(theme)
  theme_spec = Fatty::Themes::Manager.roles(Fatty::Themes::Manager.current) || {}
  @palette = Fatty::Colors::Palette.compile(theme_spec, available_colors: available_colors)

  after_apply_theme!
  invalidate!
  sync_backgrounds!
end

#flush_ansi_drawsObject



571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/fatty/renderer/truecolor.rb', line 571

def flush_ansi_draws
  Fatty.debug("flush_ansi_draws pending_count=#{@pending_ansi_draws.length}", tag: :render)

  # Hide the cursor
  @ansi_renderer.write_ansi("\e[?25l")

  @pending_ansi_draws.each do |draw|
    case draw[:type]
    when :cursor
      @ansi_renderer.write_ansi("\e[#{draw[:row] + 1};#{draw[:col] + 1}H")
    when :segments_line
      @ansi_renderer.render_segments_line(
        row: draw[:row],
        col: draw[:col],
        width: draw[:width],
        segments: draw[:segments],
        palette: palette,
        fill_role: draw[:fill_role] || :output,
      )
    else
      @ansi_renderer.render_line(
        row: draw[:row],
        col: draw[:col],
        width: draw[:width],
        text: draw[:text],
        role: draw[:role],
        palette: palette,
      )
    end
  end
  @pending_ansi_draws.clear
ensure
  # Unhide the cursor
  @ansi_renderer.write_ansi("\e[?25h")
end

#invalidate!Object



94
95
96
97
98
99
100
101
# File 'lib/fatty/renderer.rb', line 94

def invalidate!
  @last_output_state = nil
  @last_input_state = nil
  @last_alert_state = nil
  @last_status_state = nil
  @last_pager_field_state = nil
  @last_popup_state = nil
end

#queue_ansi_line(row:, col:, width:, text:, role: nil) ⇒ Object



544
545
546
547
548
549
550
551
552
553
554
# File 'lib/fatty/renderer/truecolor.rb', line 544

def queue_ansi_line(row:, col:, width:, text:, role: nil)
  spec = palette[role] || {}
  @pending_ansi_draws << {
    row: row,
    col: col,
    width: width,
    text: text,
    role: role,
    spec: spec,
  }
end

#queue_ansi_popup_line(win:, inner_row:, inner_col: 0, width:, text:, role:) ⇒ Object



519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/fatty/renderer/truecolor.rb', line 519

def queue_ansi_popup_line(win:, inner_row:, inner_col: 0, width:, text:, role:)
  row, col = win.origin
  return unless row && col

  queue_ansi_line(
    row: row + 1 + inner_row,
    col: col + 1 + inner_col,
    width: width,
    text: text.to_s,
    role: role,
  )
end

#queue_ansi_rect(row:, col:, width:, height:, role:) ⇒ Object



556
557
558
559
560
561
562
563
564
565
566
567
568
569
# File 'lib/fatty/renderer/truecolor.rb', line 556

def queue_ansi_rect(row:, col:, width:, height:, role:)
  return if height <= 0 || width <= 0

  height.times do |i|
    queue_ansi_line(
      row: row + i,
      col: col,
      width: width,
      text: " " * width,
      role: role,
    )
  end
  nil
end

#queue_ansi_segments_line(row:, col:, width:, segments:, fill_role: :output) ⇒ Object



532
533
534
535
536
537
538
539
540
541
542
# File 'lib/fatty/renderer/truecolor.rb', line 532

def queue_ansi_segments_line(row:, col:, width:, segments:, fill_role: :output)
  @pending_ansi_draws << {
    type: :segments_line,
    row: row,
    col: col,
    width: width,
    segments: segments,
    fill_role: fill_role,
  }
  nil
end

#render_alertObject

Raises:

  • (NotImplementedError)


90
91
92
# File 'lib/fatty/renderer.rb', line 90

def render_alert(...)
  raise NotImplementedError, "#{self.class} must implement #render_alert"
end

#render_input_fieldObject

Raises:

  • (NotImplementedError)


74
75
76
# File 'lib/fatty/renderer.rb', line 74

def render_input_field(...)
  raise NotImplementedError, "#{self.class} must implement #render_input_field"
end

#render_outputObject

Raises:

  • (NotImplementedError)


66
67
68
# File 'lib/fatty/renderer.rb', line 66

def render_output(...)
  raise NotImplementedError, "#{self.class} must implement #render_output"
end

#render_pager_fieldObject

Raises:

  • (NotImplementedError)


78
79
80
# File 'lib/fatty/renderer.rb', line 78

def render_pager_field(...)
  raise NotImplementedError, "#{self.class} must implement #render_pager_field"
end

#render_popupObject

Raises:

  • (NotImplementedError)


82
83
84
# File 'lib/fatty/renderer.rb', line 82

def render_popup(...)
  raise NotImplementedError, "#{self.class} must implement #render_popup"
end

#render_prompt_popupObject

Raises:

  • (NotImplementedError)


86
87
88
# File 'lib/fatty/renderer.rb', line 86

def render_prompt_popup(...)
  raise NotImplementedError, "#{self.class} must implement #render_prompt_popup"
end

#render_statusObject

Raises:

  • (NotImplementedError)


70
71
72
# File 'lib/fatty/renderer.rb', line 70

def render_status(...)
  raise NotImplementedError, "#{self.class} must implement #render_status"
end

#restore_output_cursor(field, row:) ⇒ Object

Restore cursor into the output window at a specific output-win row. row: is 0..(screen.output_rect.rows-1), NOT an absolute screen row.



502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/fatty/renderer/truecolor.rb', line 502

def restore_output_cursor(field, row:)
  cols = @screen.cols

  x = field.cursor_x.to_i
  x = x.clamp(0, [cols - 1, 0].max)

  row0 = @screen.output_rect.row
  col0 = @screen.output_rect.col
  cols = @screen.output_rect.cols

  @pending_ansi_draws << {
    type: :cursor,
    row: row0 + row,
    col: col0 + x.clamp(0, [cols - 1, 0].max),
  }
end