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 itsTransformcomponent and its parent's layout. - Drawing Command Accumulation: It acts as a temporary buffer for
RenderMethodinstructions (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::FullandPosition::Center/Endrules. - Style Application: It carries the
Stylecomponent 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
RenderScopeis cleared of any previous draw instructions, ensuring a clean slate for the current widget. - Apply Style (if present): If the widget has a
Stylecomponent, it's applied to theRenderScope. - Apply Transform (Dimensions First): If the widget has a
Transformcomponent, itsuse_dimensionsmethod is called. This resolvesDimensionrules (Full,Const,Content) into theRawTransformbased on the parent's size. - Element
renderCall: The widget'sElement::rendermethod is invoked.- Here, the
Elementissues its direct drawing commands (e.g.,scope.draw_text(...),scope.draw_rect(...)). - Crucially, if the
DimensionwasContent, theElementis responsible for updating theRenderScope'sRawTransformwidth/height to enclose its drawn content (e.g.,scope.use_area).
- Here, the
- Extension
render_widgetCalls: Any registered extensions have theirrender_widgethook called. Extensions can observe or modify theRenderScopeor widget state at this point. - Re-Apply Transform (Positions Second): After the
Elementand extensions have had a chance to determine the content size (forDimension::Content), the widget'sTransform::use_positionis called. This resolvesPositionrules (Const,Center,End) into theRawTransformusing the now-finalized dimensions. Margins are also applied here. ElementRenderer::before_drawCall: AnElementRenderertrait hook is called. This is specifically used by "ghost" elements (likeDivorFlexRow) to adjust theRenderScope's transform for their children. For example, aFlexRowwould update thex/yof theRenderScope's internalRawTransformso that the next child starts at the correct position within the row.RenderScope::draw: The accumulatedRenderMethodinstructions within theRenderScope'srender_stackare flushed to the terminal. This involves:- Drawing the background (if
Style::backgroundisSolid,Outline, orRoundedOutline). - Iterating through
render_stackand printing text or drawing rectangles at their resolved coordinates, applying foreground colors fromStyleor specificTextColoredcommands.
- Drawing the background (if
- Element
after_renderCall: The widget'sElement::after_rendermethod is invoked.- This is typically where container elements (
Div,FlexRow,Paginator) recursively triggerscope.render_widgetfor their children. They pass a new or modifiedRenderScopeto their children, setting theparent_widthandparent_heightappropriately (e.g., the parent's own content area).
- This is typically where container elements (
- Extension
after_render_widgetCalls: Any registered extensions have theirafter_render_widgethook called. This allows extensions to perform post-rendering logic, such as updating internal state based on the final rendered transform (e.g.,RelativeFocusExtensionrecords widget positions). widget.auto_refresh(): ForDynWidgets, 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 onRenderScopeto addRenderMethods to therender_stack. These methods also update theRenderScope's internalRawTransformto account for the content's size, enablingDimension::Contentto work.use_area(width, height): Allows anElementto explicitly declare a minimum required width and height, which is useful when the content itself doesn't automatically dictate a size (e.g., aDivthat just reserves space).- Coordinate System: All coordinates
(x, y)are relative to the currentRenderScope's top-left corner. WhenRenderScope::drawis called, these relative coordinates are offset by theRenderScope's absolutetransform.xandtransform.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.