Skip to content
claudewheel.vertical_viewport
Edit
On this page

Places a window over a column of variable-height blocks: centred on the focused row, clamped at both ends, reporting what each edge cuts off.

#claudewheel.vertical_viewport

#claudewheel.vertical_viewport

Scroll a column of variable-height row blocks, as arithmetic over dimensions.

This is the vertical mirror of the renderer's horizontal viewport (:meth:claudewheel.renderer.Renderer._compute_viewport), and it keeps that one's rule: when the content does not fit, the window is centered on the focused item and then clamped to the content, so scrolling is a function of the focus alone and nothing has to be remembered between frames.

Two deliberate differences from the horizontal original:

  • It is pure. The horizontal version reads self.term.cols in the middle

of its arithmetic, so it cannot be exercised without a terminal object. Here every dimension -- the row heights and the window height -- is a parameter, and the result is a plain value.

  • Rows have heights. A horizontal segment occupies one screen row; a

session block occupies two or three lines collapsed and five when highlighted, so the arithmetic runs over cumulative line offsets rather than a single row index, and the focused row may itself be taller than the whole window. When it is, its top is pinned to the top of the window: centering a block that cannot fit would cut off the line carrying its name.

The result reports one :class:RowSlice per row with any visible line, including the partially visible rows at each edge, and says how many lines of each were cut. It never decides what to do about a partial row -- drawing it clipped or dropping it is the screen's choice, and both are expressible from the same result.

#RowSlice

The visible part of one row block.

screen_top is window-relative (0 is the window's first line); skip_top counts the row's own leading lines that fall above the window, and lines how many of its lines are visible.

#skip_bottom

python
def skip_bottom(self) -> int

The row's own trailing lines that fall below the window.

#clipped

python
def clipped(self) -> bool

True when some of the row's lines are outside the window.

#Viewport

Where the window sits over the content, and what it shows.

start is the first visible content line, height the window as the caller declared it, and total the summed height of every row.

#scrolling

python
def scrolling(self) -> bool

True when the content is taller than the window.

#row_tops

python
def row_tops(row_heights: Sequence[int]) -> tuple[int, ...]

The content line each row starts at, one entry per row.

#_start_line

python
def _start_line(row_heights: Sequence[int], tops: Sequence[int], focus_idx: int, window_height: int) -> int

The first visible content line, by the centering rule described above.

#compute_viewport

python
def compute_viewport(row_heights: Sequence[int], focus_idx: int, window_height: int) -> Viewport

Place a window_height-line window over rows of the given heights.

The window is centered on the row at focus_idx and clamped to the content; a focused row taller than the window has its top pinned instead. A focus_idx naming no row, and a window with no lines, both leave the window at the top. Negative heights are a caller bug and raise.

Search