Rendering Pipeline and RenderScope
OSUI's rendering process is a structured sequence of operations managed by the RenderScope
. This module centralizes the accumulation of drawing commands and their eventual flush to the terminal.
The Role of RenderScope
RenderScope
is a mutable context passed through the rendering process for each widget. It serves several key purposes:
- Layout Context: It holds the current
RawTransform
(resolved position and dimensions) for the widget being rendered, derived from itsTransform
component and its parent's layout. - Drawing Command Accumulation: It acts as a temporary buffer for
RenderMethod
instructions (text, rectangles). Widgets and extensions add commands to this stack. - Parent Size Tracking: It keeps track of the available dimensions from the current widget's parent, crucial for resolving
Dimension::Full
andPosition::Center
/End
rules. - Style Application: It carries the
Style
component for the current widget, influencing how drawing commands are eventually rendered (e.g., background fill, foreground color).
The Rendering Sequence
When Screen::render_widget
is called for a given Arc<Widget>
, the following general pipeline is executed:
- Clear Scope: The
RenderScope
is cleared of any previous draw instructions, ensuring a clean slate for the current widget. - Apply Style (if present): If the widget has a
Style
component, it's applied to theRenderScope
. - Apply Transform (Dimensions First): If the widget has a
Transform
component, itsuse_dimensions
method is called. This resolvesDimension
rules (Full
,Const
,Content
) into theRawTransform
based on the parent's size. - Element
render
Call: The widget'sElement::render
method is invoked.- Here, the
Element
issues its direct drawing commands (e.g.,scope.draw_text(...)
,scope.draw_rect(...)
). - Crucially, if the
Dimension
wasContent
, theElement
is responsible for updating theRenderScope
'sRawTransform
width/height to enclose its drawn content (e.g.,scope.use_area
).
- Here, the
- Extension
render_widget
Calls: Any registered extensions have theirrender_widget
hook called. Extensions can observe or modify theRenderScope
or widget state at this point. - Re-Apply Transform (Positions Second): After the
Element
and extensions have had a chance to determine the content size (forDimension::Content
), the widget'sTransform::use_position
is called. This resolvesPosition
rules (Const
,Center
,End
) into theRawTransform
using the now-finalized dimensions. Margins are also applied here. ElementRenderer::before_draw
Call: AnElementRenderer
trait hook is called. This is specifically used by "ghost" elements (likeDiv
orFlexRow
) to adjust theRenderScope
's transform for their children. For example, aFlexRow
would update thex
/y
of theRenderScope
's internalRawTransform
so that the next child starts at the correct position within the row.RenderScope::draw
: The accumulatedRenderMethod
instructions within theRenderScope
'srender_stack
are flushed to the terminal. This involves:- Drawing the background (if
Style::background
isSolid
,Outline
, orRoundedOutline
). - Iterating through
render_stack
and printing text or drawing rectangles at their resolved coordinates, applying foreground colors fromStyle
or specificTextColored
commands.
- Drawing the background (if
- Element
after_render
Call: The widget'sElement::after_render
method is invoked.- This is typically where container elements (
Div
,FlexRow
,Paginator
) recursively triggerscope.render_widget
for their children. They pass a new or modifiedRenderScope
to their children, setting theparent_width
andparent_height
appropriately (e.g., the parent's own content area).
- This is typically where container elements (
- Extension
after_render_widget
Calls: Any registered extensions have theirafter_render_widget
hook called. This allows extensions to perform post-rendering logic, such as updating internal state based on the final rendered transform (e.g.,RelativeFocusExtension
records widget positions). widget.auto_refresh()
: ForDynWidget
s, this checks if any registered dependencies have changed and, if so, triggers arefresh()
to rebuild the widget for the next frame.
Key Concepts in RenderScope
RenderMethod
: An internal enum representing a single primitive drawing operation (Text, TextInverted, TextColored, Rectangle).draw_text
,draw_rect
, etc.: Methods onRenderScope
to addRenderMethod
s to therender_stack
. These methods also update theRenderScope
's internalRawTransform
to account for the content's size, enablingDimension::Content
to work.use_area(width, height)
: Allows anElement
to explicitly declare a minimum required width and height, which is useful when the content itself doesn't automatically dictate a size (e.g., aDiv
that just reserves space).- Coordinate System: All coordinates
(x, y)
are relative to the currentRenderScope
's top-left corner. WhenRenderScope::draw
is called, these relative coordinates are offset by theRenderScope
's absolutetransform.x
andtransform.y
. Padding (px
,py
) is also applied as an offset for content rendering.
This pipeline ensures that layout calculations are performed efficiently, drawing commands are batched, and elements are rendered within their correctly determined bounds, respecting both parent constraints and self-determined content sizes.