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.colsin 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
def skip_bottom(self) -> intThe row's own trailing lines that fall below the window.
#clipped
def clipped(self) -> boolTrue 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
def scrolling(self) -> boolTrue when the content is taller than the window.
#row_tops
def row_tops(row_heights: Sequence[int]) -> tuple[int, ...]The content line each row starts at, one entry per row.
#_start_line
def _start_line(row_heights: Sequence[int], tops: Sequence[int], focus_idx: int, window_height: int) -> intThe first visible content line, by the centering rule described above.
#compute_viewport
def compute_viewport(row_heights: Sequence[int], focus_idx: int, window_height: int) -> ViewportPlace 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.