render method
Implementation
@override
void render(Rect area, Buffer buffer, RenderContext ctx) {
if (area.isEmpty) return;
state.contentHeight = contentHeight;
if (contentWidth > 0) state.contentWidth = contentWidth;
final hasVScroll = showScrollbar && contentHeight > area.height;
final viewportWidth =
(area.width - (hasVScroll ? 1 : 0)).clamp(0, area.width);
final viewportHeight = area.height;
if (viewportWidth <= 0 || viewportHeight <= 0) return;
final maxY = _maxOffsetY(viewportHeight);
state.offsetY = state.offsetY.clamp(0, maxY);
if (horizontal) {
final maxX = _maxOffsetX(viewportWidth);
state.offsetX = state.offsetX.clamp(0, maxX);
} else {
state.offsetX = 0;
}
final renderWidth = contentWidth > 0 ? contentWidth : viewportWidth;
final renderHeight = contentHeight > 0 ? contentHeight : viewportHeight;
// Render into a virtual buffer at origin (0,0), then blit window.
final inner = Buffer(
Size(renderWidth.clamp(1, 1 << 16), renderHeight.clamp(1, 1 << 16)));
child.render(Rect(0, 0, inner.width, inner.height), inner, ctx);
final srcX = state.offsetX;
final srcY = state.offsetY;
for (var dy = 0; dy < viewportHeight; dy++) {
for (var dx = 0; dx < viewportWidth; dx++) {
final cell = inner.get(srcX + dx, srcY + dy);
buffer.set(area.x + dx, area.y + dy, cell);
}
}
if (hasVScroll) {
final track = scrollbarStyle ?? Style(fg: ctx.theme.colors.muted);
final thumb = scrollbarThumbStyle ??
Style(fg: ctx.theme.colors.primary, bold: true);
final trackX = area.x + area.width - 1;
final ratio = contentHeight == 0
? 0.0
: (viewportHeight / contentHeight).clamp(0.0, 1.0);
final thumbLen =
(ratio * viewportHeight).round().clamp(1, viewportHeight);
final posRatio = maxY == 0 ? 0.0 : state.offsetY / maxY;
final thumbStart = (posRatio * (viewportHeight - thumbLen))
.round()
.clamp(0, viewportHeight - thumbLen);
for (var y = 0; y < viewportHeight; y++) {
final isThumb = y >= thumbStart && y < thumbStart + thumbLen;
buffer.setChar(trackX, area.y + y, isThumb ? '█' : '│',
style: isThumb ? thumb : track);
}
}
}