Source code for simvx.core._draw_context
"""Which ambient values a node's ``on_draw`` read, so a change to one re-captures it.
A retained ``on_draw`` re-runs only when something marks it stale. A ``Property``
write marks its own node, but a draw that lays itself out from the render
target (``viewport_size``, ``viewport_insets``, ``tree.screen_size``,
``tree.screen_insets``, ``tree.ui_scale``) depends on a value that belongs to
nobody's properties. So the getters of those values record the read on the node
being captured, and whoever changes one marks exactly the nodes that read it.
``draw_context.node`` is the node whose ``on_draw`` is running, set by
``Node._draw_dispatch`` for the duration of one capture and ``None`` otherwise.
It is a module instance rather than a ``Node`` class attribute because writing
a class attribute bumps the type's version and flushes the attribute cache of
every subclass, twice per capture.
Each node carries its reads as an int, ``_ambient_reads``, cleared when its
capture starts, so the mask always describes the draw currently retained. The
getters OR plain ints into it: the ``AmbientRead`` members are the names, and
the module-level ints are what the hot path uses, because an ``IntFlag``
operation goes through the enum machinery.
"""
from __future__ import annotations
from enum import IntFlag
from typing import Any
[docs]
class AmbientRead(IntFlag):
"""The ambient values a draw can depend on."""
VIEWPORT_SIZE = 1
VIEWPORT_INSETS = 2
UI_SCALE = 4
READS_VIEWPORT_SIZE = int(AmbientRead.VIEWPORT_SIZE)
READS_VIEWPORT_INSETS = int(AmbientRead.VIEWPORT_INSETS)
READS_UI_SCALE = int(AmbientRead.UI_SCALE)
class _DrawContext:
__slots__ = ("node",)
def __init__(self) -> None:
self.node: Any = None
draw_context = _DrawContext()
[docs]
def redraw_readers(root: Any, flags: int) -> None:
"""``queue_redraw()`` every node under *root*, *root* included, whose last draw read one of *flags*.
*root* is a render target, the tree's top node or a ``SubViewport``. A
``SubViewport`` below it is a different target, so the walk does not enter
one: its nodes read that viewport's size, not *root*'s.
"""
flags = int(flags)
stack = [root]
while stack:
node = stack.pop()
if node._ambient_reads & flags:
node.queue_redraw()
for child in node.children:
if not getattr(child, "_is_subviewport", False):
stack.append(child)