Source code for tessella.widgets.positioned

import pygame

from tessella.constraints import WidgetConstraints
from . import SingleChildWidget
from . import Widget

[docs] class Positioned(SingleChildWidget): """ A widget capable of manually positioning its children in regards to the edges of the space available, usually inside a `Stack` widget. Attributes: left (int | None): The left side offset, in pixels. right (int | None): The right side offset, in pixels. top (int | None): The top side offset, in pixels. bottom (int | None): The bottom side offset, in pixels. NOTE: `left` and `right` cannot be both set at once. The same applies for `top` and `bottom`. """ def __init__( self, child: Widget, left: int | None=None, right: int | None=None, top: int | None=None, bottom: int | None=None, ): """ Initializes a new Positioned object. Args: child (Widget): The child of this widget. left (int | None): The left side offset, in pixels. If both \ `left` and `right` are None, defaults to 0. right (int | None): The right side offset, in pixels. Defaults to None. top (int | None): The top side offset, in pixels. If both \ `top` and `bottom` are None, defaults to 0. bottom (int | None): The bottom side offset, in pixels. Defaults to None. Raises: AssertionError: If both `left` and `right` are set, or if \ both `top` and `bottom` are set. """ assert (not (left != None and right != None)), "Positioned widget cannot have both 'left' and 'right' properties at the same time." assert (not (top != None and bottom != None)), "Positioned widget cannot have both 'top' and 'bottom' properties at the same time." super().__init__(child) # Aligns to topleft by default if (left is None and right is None): left = 0 if (top is None and bottom is None): top = 0 self.left: int | None = left self.right: int | None = right self.top: int | None = top self.bottom: int | None = bottom def _align_child(self) -> None: self.child.set_position(self.bounds.topleft)
[docs] def set_position(self, new_position: tuple[int, int]) -> None: """ Ignores `new_position` and instead derives this widget's \ position from its parent's bounds and its `left`/`right`/`top`/ \ `bottom` offsets, so that it stays correctly positioned \ regardless of layout changes elsewhere in the tree. Also \ repositions the child accordingly. Args: new_position (tuple[int, int]): Unused; kept for signature \ compatibility with `Widget.set_position`. """ # This override is meant to correctly work out # the position of this widget regardless of # external conditions if self.parent is not None: parent_bounds = self.parent.get_bounds() else: parent_bounds = self.bounds bounds = self.get_bounds() if self.left is not None: bounds.left = parent_bounds.left + self.left else: bounds.right = parent_bounds.right - self.right if self.top is not None: bounds.top = parent_bounds.top + self.top else: bounds.bottom = parent_bounds.bottom - self.bottom # Ensures we also update the child position super().set_position(bounds.topleft)
def _debug_draw(self) -> pygame.Surface | None: surface = pygame.Surface(self.bounds.size, pygame.SRCALPHA) surface_rect = surface.get_rect() pygame.draw.rect(surface, "#C9E83D", surface_rect, 1) return surface def _draw(self) -> pygame.Surface | None: return None
[docs] def calculate_size(self, constraints: WidgetConstraints) -> tuple[int, int]: """ Shrinks the available constraints by whichever horizontal and \ vertical offsets are set, then lays out the child within what's \ left. Args: constraints (WidgetConstraints): The constraints to be used for \ determining the sizing of this widget. Returns: tuple[int, int]: The calculated size for this widget. """ if self.left != None: constraints.max_width -= self.left else: constraints.max_width -= self.right if self.top != None: constraints.max_height -= self.top else: constraints.max_height -= self.bottom return self.child.apply_layout_constraints(constraints)
[docs] def depends_on_child_size(self) -> bool: return True
[docs] def depends_on_parent_size(self) -> bool: return True