#----------------------------------------------------------------------------- # Copyright (c) Anaconda, Inc., and Bokeh Contributors. # All rights reserved. # # The full license is in the file LICENSE.txt, distributed with this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Boilerplate #----------------------------------------------------------------------------- from __future__ import annotations import logging # isort:skip log = logging.getLogger(__name__) #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- # Standard library imports from typing import Any, ClassVar, Literal # Bokeh imports from ..core.properties import ( Either, Enum, Instance, Int, Required, String, ) from ..core.property.aliases import CoordinateLike from ..model import Model #----------------------------------------------------------------------------- # Globals and constants #----------------------------------------------------------------------------- __all__ = ( "Indexed", "Node", "XY", ) ImplicitTarget = Literal["viewport", "canvas", "plot", "frame", "parent"] #----------------------------------------------------------------------------- # Dev API #----------------------------------------------------------------------------- class BoxNodes: """ Provider of box nodes for box-like models. """ def __init__(self, target: Model | ImplicitTarget) -> None: self.target = target def _node(self, symbol: str) -> Node: return Node(target=self.target, symbol=symbol) @property def left(self) -> Node: return self._node("left") @property def right(self) -> Node: return self._node("right") @property def top(self) -> Node: return self._node("top") @property def bottom(self) -> Node: return self._node("bottom") @property def top_left(self) -> Node: return self._node("top_left") @property def top_center(self) -> Node: return self._node("top_center") @property def top_right(self) -> Node: return self._node("top_right") @property def center_left(self) -> Node: return self._node("center_left") @property def center(self) -> Node: return self._node("center") @property def center_right(self) -> Node: return self._node("center_right") @property def bottom_left(self) -> Node: return self._node("bottom_left") @property def bottom_center(self) -> Node: return self._node("bottom_center") @property def bottom_right(self) -> Node: return self._node("bottom_right") @property def width(self) -> Node: return self._node("width") @property def height(self) -> Node: return self._node("height") #----------------------------------------------------------------------------- # General API #----------------------------------------------------------------------------- class Coordinate(Model): """ A base class for various types of coordinate specifications. """ # explicit __init__ to support Init signatures def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) class XY(Coordinate): """ A point in a Cartesian coordinate system. .. note:: This model is experimental and may change at any point. """ # explicit __init__ to support Init signatures def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) x = Required(CoordinateLike, help=""" The x component. """) y = Required(CoordinateLike, help=""" The y component. """) class Indexed(Coordinate): """ A coordinate computed given an index into a renderer's data. .. note:: This model is experimental and may change at any point. """ # explicit __init__ to support Init signatures def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) index = Required(Int, help=""" An index into the data. """) renderer = Instance(".models.renderers.GlyphRenderer", help=""" A renderer that is the provider of the data. """) class Node(Coordinate): """ Represents a symbolic coordinate (by name). .. note:: This model is experimental and may change at any point. """ # explicit __init__ to support Init signatures def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) target = Required(Either(Instance(Model), Enum(ImplicitTarget)), help=""" The provider of coordinates for this node. This can be either a concrete model that can provide its coordinates (e.g. a renderer, a frame or a canvas) or an implicit target defined by the enum, which is resolved as the nearest parent of the given type. If the provider cannot be determined or it isn't able to provide coordinates, then the node resolved to an invalid coordinate (with x and y components being ``NaN``). """) symbol = Required(String, help=""" A symbolic name of a coordinate to provide. The allowed terms are dependent on the target of this node. For example, for box-like targets this will comprise of box anchors (e.g. center, top left) and box edges (e.g. top, left). """) offset = Int(default=0, help=""" Optional pixel offset for the computed coordinate. """) canvas: ClassVar[BoxNodes] = BoxNodes("canvas") plot: ClassVar[BoxNodes] = BoxNodes("plot") frame: ClassVar[BoxNodes] = BoxNodes("frame") parent: ClassVar[BoxNodes] = BoxNodes("parent") #----------------------------------------------------------------------------- # Private API #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Code #-----------------------------------------------------------------------------