Class: Charming::Components::Viewport

Inherits:
Charming::Component show all
Includes:
KeyboardHandler
Defined in:
lib/charming/presentation/components/viewport.rb,
lib/charming/presentation/components/viewport/position.rb,
lib/charming/presentation/components/viewport/line_window.rb,
lib/charming/presentation/components/viewport/content_lines.rb

Overview

Viewport is a scrollable region over multi-line content. Supports keyboard scrolling (up/down/left/right, page up/down, home/end) and mouse interactions (scroll wheel and click-to-position). Lines are clipped with ANSI awareness via UI::ANSISlicer so styled text is preserved across horizontal scrolls. When wrap: is true, long lines are wrapped to the configured width before scrolling.

Defined Under Namespace

Classes: ContentLines, LineWindow, Position

Constant Summary collapse

KEY_ACTIONS =

Maps scroll keys to the instance methods that perform them via KeyboardHandler.

{
  up: :scroll_up,
  down: :scroll_down,
  page_up: :page_up,
  page_down: :page_down,
  home: :scroll_home,
  end: :scroll_end,
  left: :scroll_left,
  right: :scroll_right
}.freeze

Constants included from KeyboardHandler

KeyboardHandler::VIM_KEYMAP

Instance Method Summary collapse

Methods included from KeyboardHandler

#handle_key

Methods inherited from Charming::Component

#captures_text?

Methods inherited from View

#focused?, #layout_assigns

Constructor Details

#initialize(content:, width: nil, height: nil, offset: 0, column: 0, wrap: false, keymap: :vim, follow: false) ⇒ Viewport

content may be a string, an array of lines, or any object responding to render. width and height constrain the visible window; offset is the top-visible row and column is the left-visible column. wrap enables soft-wrapping of long lines. follow pins the viewport to the bottom of the content regardless of offset, so callers that rebuild each event stick to new content; pair with at_bottom? to decide whether to keep following after the user scrolls.



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/charming/presentation/components/viewport.rb', line 31

def initialize(content:, width: nil, height: nil, offset: 0, column: 0, wrap: false, keymap: :vim, follow: false)
  super()
  @content = content
  @width = width
  @height = height
  @position = Position.new(offset: offset, column: column)
  @wrap = wrap
  @keymap = keymap
  position.clamp(bounds)
  position.move_to(max_offset, bounds) if follow
end

Instance Method Details

#at_bottom?Boolean

True when the last content row is visible. Callers persisting follow mode store this after key dispatch to decide whether the next build follows.

Returns:

  • (Boolean)


60
61
62
# File 'lib/charming/presentation/components/viewport.rb', line 60

def at_bottom?
  offset >= max_offset
end

#columnObject

The current left-visible column.



54
55
56
# File 'lib/charming/presentation/components/viewport.rb', line 54

def column
  @position.column
end

#handle_mouse(event) ⇒ Object

Handles mouse events: scroll wheel adjusts the row offset, click moves the top visible row to the clicked position. Returns :handled on success.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/charming/presentation/components/viewport.rb', line 66

def handle_mouse(event)
  return nil unless height

  if event.scroll?
    scroll_delta = (event.button_name == :scroll_up) ? -1 : 1
    position.move_to(offset + scroll_delta, bounds)
    return :handled
  end

  return nil unless event.click?

  clicked_row = event.y
  return nil if clicked_row < 0 || clicked_row >= viewport_height

  position.move_to(offset + clicked_row, bounds)
  :handled
end

#offsetObject

The current top-visible row.



49
50
51
# File 'lib/charming/presentation/components/viewport.rb', line 49

def offset
  @position.offset
end

#renderObject

Renders the visible window of content as a multi-line string.



44
45
46
# File 'lib/charming/presentation/components/viewport.rb', line 44

def render
  visible_lines.map { |line| render_line(line) }.join("\n")
end