wijjit.rendering.paint_context.PaintContext
- class wijjit.rendering.paint_context.PaintContext(buffer, style_resolver, bounds, clip_region=None)[source]
Rendering context for cell-based element painting.
This class provides the environment and helper methods for elements to render themselves to a cell-based screen buffer using theme styles.
- Parameters:
buffer (
ScreenBuffer) – Target screen buffer for renderingstyle_resolver (
StyleResolver) – Style resolver for applying theme stylesbounds (
Bounds) – Rendering bounds for the element (position and size)clip_region (
Bounds, optional) – Clip region to restrict rendering. Content outside this region is not drawn. If None, defaults to bounds. Used for scrollable frames to prevent children from drawing outside the frame’s visible content area.
- Variables:
buffer (
ScreenBuffer) – Target screen bufferstyle_resolver (
StyleResolver) – Style resolverbounds (
Bounds) – Element boundsclip_region (
Bounds) – Effective clip region for rendering
Examples
Create a paint context and write styled text:
>>> from wijjit.terminal.screen_buffer import ScreenBuffer >>> from wijjit.styling.resolver import StyleResolver >>> from wijjit.styling.theme import DefaultTheme >>> from wijjit.layout.bounds import Bounds >>> buffer = ScreenBuffer(80, 24) >>> resolver = StyleResolver(DefaultTheme()) >>> bounds = Bounds(x=0, y=0, width=10, height=1) >>> ctx = PaintContext(buffer, resolver, bounds) >>> style = resolver.resolve_style_by_class('button') >>> ctx.write_text(0, 0, 'Click', style)
Fill a rectangular region:
>>> ctx.fill_rect(0, 0, 10, 5, ' ', style)
- __init__(buffer, style_resolver, bounds, clip_region=None)[source]
- Parameters:
buffer (ScreenBuffer)
style_resolver (StyleResolver)
bounds (Bounds)
clip_region (Bounds | None)
- Return type:
None
Methods
__init__(buffer, style_resolver, bounds[, ...])clear([style])Clear the element's bounds region.
cursor_anchor(x, y)Absolute screen coordinates for a caret at a relative position.
draw_border(x, y, width, height, style[, ...])Draw a border around a rectangular region.
fill_rect(x, y, width, height, char, style)Fill a rectangular region with a styled character.
sub_context(x, y, width, height)Create a sub-context with relative bounds.
write_cell(x, y, cell)Write a single cell to the buffer with clipping.
write_cells(x, y, cells)Write a horizontal run of pre-built cells with clipping.
write_cells_vertical(x, y, cells)Write a vertical run of pre-built cells with clipping.
write_text(x, y, text, style[, clip])Write styled text to the buffer at specified position.
write_text_wrapped(x, y, text, style, max_width)Write text with word wrapping to buffer.
- write_text(x, y, text, style, clip=True)[source]
Write styled text to the buffer at specified position.
- Parameters:
- Return type:
None
Notes
Coordinates are relative to the element’s bounds. The method automatically translates to absolute screen coordinates.
Text is written cluster by cluster and the width budget is spent in terminal columns, not Python characters. A width-2 glyph (most CJK, many emoji) is written as a head Cell holding the glyph plus a continuation Cell (
char == CONTINUATION_CHAR) in the next column carrying the same style attributes; seewijjit.terminal.cell.is_continuation(). Zero-width combining marks are folded onto the preceding base glyph (one Cell), and control characters are dropped.If
clip=True, text extending beyond the element bounds is truncated in columns and rendering is also clipped toclip_region. When only one column of a width-2 glyph is inside the budget or clip region, a styled space is written in the visible column so a half glyph never appears.ANSI escape sequences embedded in
textare stripped (whole sequences, viawijjit.terminal.ansi.strip_ansi()) rather than honored: this method takes plain text, and the supported path for pre-styled ANSI content iscontent_type="ansi". Stripping keeps unsupported input degrading to clean uncolored text instead of leaving visible sequence remnants after the ESC byte is dropped.Examples
Write text at element’s origin:
>>> ctx.write_text(0, 0, 'Hello', style)
Write text at offset within element:
>>> ctx.write_text(5, 2, 'World', style)
Write without clipping (may overflow bounds):
>>> ctx.write_text(0, 0, 'Very long text', style, clip=False)
- write_text_wrapped(x, y, text, style, max_width)[source]
Write text with word wrapping to buffer.
- Parameters:
- Returns:
Number of lines written
- Return type:
Notes
This method wraps text at word boundaries where possible, falling back to character wrapping for long words.
Existing newlines in the text are preserved as line breaks. Rendering is clipped to the clip_region if set.
Examples
Write wrapped text:
>>> lines = ctx.write_text_wrapped(0, 0, 'Long text here', style, 10) >>> lines # Number of lines used 2
- fill_rect(x, y, width, height, char, style)[source]
Fill a rectangular region with a styled character.
- Parameters:
- Return type:
None
Notes
Coordinates are relative to element bounds. The rectangle is automatically clipped to element bounds and clip_region.
Useful for backgrounds, borders, or clearing regions.
Examples
Fill background with spaces:
>>> ctx.fill_rect(0, 0, 10, 5, ' ', style)
Draw a filled box with a character:
>>> ctx.fill_rect(2, 2, 5, 3, '#', style)
- write_cell(x, y, cell)[source]
Write a single cell to the buffer with clipping.
- Parameters:
- Returns:
Number of terminal columns consumed by the cell (1 or 2), regardless of whether anything was actually written. Per-cluster render loops advance their column counter by this value.
- Return type:
Notes
Coordinates are relative to element bounds. The cell is only written where it falls within the clip_region.
A cell whose
charhas display width 2 (most CJK, many emoji) is written as a head cell plus a continuation cell (char == CONTINUATION_CHAR) in the next column carrying the same style attributes, matchingwrite_text(). When only one of the two columns is inside the clip region, a styled space is written in the visible column so a half glyph never appears. A cell that is itself a continuation cell, or whose char is width <= 1, is written as a plain single clipped cell.
- write_cells(x, y, cells)[source]
Write a horizontal run of pre-built cells with clipping.
Fast path for callers that assemble whole rows of
Cellobjects: the run is sliced to the intersection of the row with the clip region and written with a singleset_cells_horizontal()call instead of per-cell writes.- Parameters:
- Return type:
None
Notes
Wide glyphs severed at the slice edges are guarded: if the first visible cell is a continuation cell (its head fell outside the clip region), or the last visible cell is a width-2 head (its continuation fell outside), a styled space is written in that column instead so a half glyph never appears.
- write_cells_vertical(x, y, cells)[source]
Write a vertical run of pre-built cells with clipping.
Fast path for callers that assemble whole columns of
Cellobjects (borders, scrollbars): the run is sliced to the intersection of the column with the clip region and written with a singleset_cells_vertical()call.- Parameters:
- Return type:
None
Notes
Cells in a vertical run should be single-column glyphs; width-2 glyphs are not expanded into continuation cells here (a vertical run cannot place the continuation column).
- cursor_anchor(x, y)[source]
Absolute screen coordinates for a caret at a relative position.
- Parameters:
- Returns:
Absolute
(x, y)screen coordinates, or None if the position falls outside the clip region (e.g. a caret scrolled out of a frame’s visible interior).- Return type:
Notes
Elements that show a text caret call this where they paint it so the application can park the hardware terminal cursor on the same cell. Clip awareness comes for free: a caret outside the visible region yields None and the hardware cursor stays hidden.
- draw_border(x, y, width, height, style, border_chars=None)[source]
Draw a border around a rectangular region.
- Parameters:
x (
int) – X coordinate (column) relative to element boundsy (
int) – Y coordinate (row) relative to element boundswidth (
int) – Border width (including border characters)height (
int) – Border height (including border characters)style (
Style) – Style to apply to borderborder_chars (
dict, optional) – Border characters dict with keys: ‘tl’, ‘tr’, ‘bl’, ‘br’, ‘h’, ‘v’ (top-left, top-right, etc.). If None, uses single line box drawing characters.
- Return type:
None
Notes
Draws box-drawing characters around the specified rectangle. The rectangle dimensions include the border itself. Rendering is clipped to the clip_region if set.
Minimum size is 2x2 (for corners only).
Examples
Draw default single-line border:
>>> ctx.draw_border(0, 0, 20, 10, style)
Draw with custom characters:
>>> custom = {'tl': '+', 'tr': '+', 'bl': '+', 'br': '+', ... 'h': '-', 'v': '|'} >>> ctx.draw_border(0, 0, 20, 10, style, custom)
- clear(style=None)[source]
Clear the element’s bounds region.
- Parameters:
style (
Style, optional) – Style to apply to cleared cells (for background color). If None, uses default empty style.- Return type:
None
Notes
Fills the entire element bounds with space characters. Useful for clearing before rendering new content.
Examples
Clear with default style:
>>> ctx.clear()
Clear with background color:
>>> bg_style = Style(bg_color=(40, 40, 40)) >>> ctx.clear(bg_style)
- sub_context(x, y, width, height)[source]
Create a sub-context with relative bounds.
- Parameters:
- Returns:
New context with adjusted bounds
- Return type:
Notes
This is useful for nested rendering where a child element needs its own coordinate system within a parent’s bounds.
The new context shares the same buffer, style resolver, and clip_region (inherited from parent).
Examples
Create sub-context for nested element:
>>> sub_ctx = ctx.sub_context(10, 5, 20, 10) >>> # Now (0, 0) in sub_ctx is (10, 5) in parent context