Source code for simvx.core.nodes_2d.text

"""Text2D -- screen-pinned text drawn through the one 2D text builder (P3a)."""

from ..descriptors import Property
from ..properties import Colour
from .node2d import Node2D


[docs] class Text2D(Node2D): """Screen-pinned text for HUD / UI. Works in both 2D and 3D modes. Text2D draws through the ONE 2D text builder (``renderer.draw_text``): the item path emits a native kerned MSDF ``GLYPH`` item, and the ``Draw2D`` ``_draw_recursive`` walk (editor play-mode / web) appends a matching ``TEXT`` op -- both from the same layout, so they render identically. The text is **screen-pinned**: it draws in screen space (``screen_space=True``) at its ``position`` (the inherited :class:`Node2D` ``Vec2``), so it stays fixed while a Camera2D pans the world -- the usual HUD behaviour. ``font_scale`` is a multiplier on the 16px logical em (``scale=1`` -> 16 logical px). Common port patterns are first-class node Properties: * ``outline`` / ``outline_colour`` -- a readable halo on any background. * ``rect`` -- position + align inside a box instead of at a point. * ``fit_to_width`` -- shrink ``font_scale`` so the text fits ``rect`` width. """ text = Property("", hint="Text to display") font_scale = Property(1.0, range=(0.1, 100.0), hint="Font size (multiplier on 16px em)") colour = Colour((1.0, 1.0, 1.0, 1.0)) # Horizontal anchor for ``position`` / box alignment inside ``rect``: "left" # puts position.x at the text's left edge, "centre" centres on it, "right" puts # it at the right edge. Each line is measured by the builder, so centring is # exact. align = Property("left", enum=["left", "centre", "right"], hint="Horizontal anchor") # Outline halo: a 4-direction offset copy in # ``outline_colour`` under the glyphs. ``outline`` is in glyph-em units (~0.08 # is a typical 1-2px halo); 0 disables it (the common case, zero added cost). outline = Property(0.0, range=(0.0, 0.5), hint="Outline thickness (em units; 0 = none)") outline_colour = Colour((0.0, 0.0, 0.0, 1.0)) # Box mode: when set, the text is positioned + aligned inside this rect # (x, y, w, h) in screen pixels instead of anchored at ``position``. rect = Property(None, hint="(x, y, w, h) box to align text inside (None = point at position)") # Shrink font_scale so the text fits the box (or position.x..+width) horizontally. fit_to_width = Property(False, hint="Shrink font_scale so text fits the rect/box width")
[docs] def on_draw(self, renderer) -> None: if not self.text: return ox, oy = getattr(self.tree, "overlay_offset", (0.0, 0.0)) if self.tree is not None else (0.0, 0.0) fc = self.colour if any(c > 1.0 for c in fc[:3]): # normalise 0-255 int colours fc = tuple(c / 255.0 for c in fc) oc = self.outline_colour if any(c > 1.0 for c in oc[:3]): oc = tuple(c / 255.0 for c in oc) box = self.rect if box is not None: rect = (box[0] + ox, box[1] + oy, box[2], box[3]) renderer.draw_text( self.text, rect=rect, scale=self.font_scale, colour=fc, alignment=self.align, fit_to_width=self.fit_to_width, outline=self.outline, outline_colour=oc, screen_space=True, ) else: pos = self.position renderer.draw_text( self.text, (pos.x + ox, pos.y + oy), scale=self.font_scale, colour=fc, alignment=self.align, fit_to_width=self.fit_to_width, outline=self.outline, outline_colour=oc, screen_space=True, )