Files
2025-08-10 14:14:04 -05:00

43 lines
5.5 KiB
Markdown

# 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 its `Transform` 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` and `Position::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:
1. **Clear Scope**: The `RenderScope` is cleared of any previous draw instructions, ensuring a clean slate for the current widget.
2. **Apply Style (if present)**: If the widget has a `Style` component, it's applied to the `RenderScope`.
3. **Apply Transform (Dimensions First)**: If the widget has a `Transform` component, its `use_dimensions` method is called. This resolves `Dimension` rules (`Full`, `Const`, `Content`) into the `RawTransform` based on the parent's size.
4. **Element `render` Call**: The widget's `Element::render` method is invoked.
* Here, the `Element` issues its direct drawing commands (e.g., `scope.draw_text(...)`, `scope.draw_rect(...)`).
* Crucially, if the `Dimension` was `Content`, the `Element` is responsible for updating the `RenderScope`'s `RawTransform` width/height to enclose its drawn content (e.g., `scope.use_area`).
5. **Extension `render_widget` Calls**: Any registered extensions have their `render_widget` hook called. Extensions can observe or modify the `RenderScope` or widget state at this point.
6. **Re-Apply Transform (Positions Second)**: After the `Element` and extensions have had a chance to determine the *content size* (for `Dimension::Content`), the widget's `Transform::use_position` is called. This resolves `Position` rules (`Const`, `Center`, `End`) into the `RawTransform` using the now-finalized dimensions. Margins are also applied here.
7. **`ElementRenderer::before_draw` Call**: An `ElementRenderer` trait hook is called. This is specifically used by "ghost" elements (like `Div` or `FlexRow`) to adjust the `RenderScope`'s transform *for their children*. For example, a `FlexRow` would update the `x`/`y` of the `RenderScope`'s internal `RawTransform` so that the next child starts at the correct position within the row.
8. **`RenderScope::draw`**: The accumulated `RenderMethod` instructions within the `RenderScope`'s `render_stack` are flushed to the terminal. This involves:
* Drawing the background (if `Style::background` is `Solid`, `Outline`, or `RoundedOutline`).
* Iterating through `render_stack` and printing text or drawing rectangles at their resolved coordinates, applying foreground colors from `Style` or specific `TextColored` commands.
9. **Element `after_render` Call**: The widget's `Element::after_render` method is invoked.
* This is typically where container elements (`Div`, `FlexRow`, `Paginator`) recursively trigger `scope.render_widget` for their children. They pass a *new* or modified `RenderScope` to their children, setting the `parent_width` and `parent_height` appropriately (e.g., the parent's own *content area*).
10. **Extension `after_render_widget` Calls**: Any registered extensions have their `after_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).
11. **`widget.auto_refresh()`**: For `DynWidget`s, this checks if any registered dependencies have changed and, if so, triggers a `refresh()` 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 on `RenderScope` to add `RenderMethod`s to the `render_stack`. These methods also update the `RenderScope`'s internal `RawTransform` to account for the content's size, enabling `Dimension::Content` to work.
* **`use_area(width, height)`**: Allows an `Element` to explicitly declare a minimum required width and height, which is useful when the content itself doesn't automatically dictate a size (e.g., a `Div` that just reserves space).
* **Coordinate System**: All coordinates `(x, y)` are relative to the *current* `RenderScope`'s top-left corner. When `RenderScope::draw` is called, these relative coordinates are offset by the `RenderScope`'s absolute `transform.x` and `transform.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.