# SPDX-FileCopyrightText: 2021 Kevin Matocha # # SPDX-License-Identifier: MIT """ `displayio_annotation` ================================================================================ A widget for annotating other widgets or freeform positions. * Author(s): Kevin Matocha Implementation Notes -------------------- **Hardware:** **Software and Dependencies:** * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases """ from adafruit_display_shapes.line import Line from adafruit_display_text import bitmap_label from adafruit_displayio_layout.widgets.widget import Widget from terminalio import FONT class Annotation(Widget): """A widget to be used to annotate other widgets with text and lines, but can also be used freeform by using ``(x,y)`` parameter. :param int x: x-direction pixel position for the end of the annotation line for freeform positioning, ``(x,y)`` will be ignored if a ``widget`` and ``anchor_point`` and/or ``anchored_position`` are provided. :param int y: y-direction pixel position for the end of the annotation line for freeform positioning. :param Widget widget: the widget to be annotated, all dimensions are relative to this widget. The annotation line position will be defined by either the ``anchor_point`` (in relative dimensions of the size of the widget) or the ``anchored_position`` (in raw pixel dimensions relative to the origin of the widget). :param str text: text to be displayed in the annotation. :param Font font: font to be used for the text. :param anchor_point: starting point for the annotation line, where ``anchor_point`` is an (A,B) tuple in relative units of the size of the widget, for example (0.0, 0.0) is the upper left corner, and (1.0, 1.0) is the lower right corner of the widget. If ``anchor_point`` is `None`, then ``anchored_position`` is used to set the annotation line starting point, in widget size relative units (default is (0.0, 0.0)). :type anchor_point: Tuple[float, float] :param anchored_position: pixel position starting point for the annotation line where ``anchored_position`` is an (x,y) tuple in pixel units relative to the upper left corner of the widget, in pixel units (default is None). :type anchored_position: Tuple[int, int] :param position_offset: Used to *nudge* the line position to where you want, this is an (x,y) pixel offset added to the annotation line starting point, either set by ``anchor_point`` or ``anchored_position`` (in pixel units). :type position_offset: Tuple[int, int] :param int delta_x: the pixel x-offset for the second end of the line where the text will reside, in pixel units (default: -15). :param int delta_y: the pixel y-offset for the second end of the line where the text will reside, in pixel units (default: -10). :param int stroke: the annotation line width (in pixels). [NOT currently implemented] :param int line_color: the color of the annotation line (default: 0xFFFFFF). :param int text_color: the color of the text, if set to `None` color will be set to ``line_color`` (default: same as ``line_color``). :param text_offset: a (x,y) pixel offset to adjust text position relative to annotation line, in pixel units (default: (0,-1)). :type text_offset: Tuple[int, int] :param Boolean text_under: set `True` for text to be placed below the annotation line (default: False). .. figure:: annotation_example.png :scale: 125 % :align: center :alt: Example of the annotation widget. Example of the annotation widget showing two widget annotations (using ``widget`` and ``anchor_point`` input parameters) and a freeform annotation (using ``x`` and ``y`` input parameters). File location: *examples/displayio_annotation_simpletest.py* """ def __init__( # noqa: PLR0913, PLR0917, too many args, too many positional args self, x=None, y=None, text=None, font=FONT, delta_x=-15, delta_y=-10, widget=None, anchor_point=(0.0, 0.0), anchored_position=None, position_offset=(0, 0), stroke=3, # Not currently implemented in adafruit_display_shapes/line.py line_color=0xFFFFFF, text_color=None, text_offset=(0, -1), text_under=False, ): if widget: if (x is not None) or (y is not None): print( "Note: Overriding (x,y) values with widget, anchor_point" " and/or anchored_position" ) widget_width = widget.bounding_box[2] widget_height = widget.bounding_box[3] if anchor_point is not None: line_x0 = widget.x + round(widget_width * anchor_point[0]) + position_offset[0] line_y0 = widget.y + round(widget_height * anchor_point[1]) + position_offset[1] elif anchored_position is not None: line_x0 = widget.x + anchored_position[0] + position_offset[0] line_y0 = widget.y + anchored_position[1] + position_offset[1] else: raise ValueError("Must supply either anchor_point or anchored_position") elif (x is not None) and (y is not None): line_x0 = x line_y0 = y else: raise ValueError( "Must supply either (x,y) or widget and anchor_point or anchored_position" ) line_x1 = line_x0 + delta_x line_y1 = line_y0 + delta_y text_anchor_point = (0.0, 1.0) # default: set text anchor to left corner underline_x_multiplier = 1 if delta_x < 0: # line is heading to the left, set text anchor to right corner text_anchor_point = (1.0, 1.0) underline_x_multiplier = -1 if text_under: # if text is under the line, set to text_anchor_point to upper edge text_anchor_point = (text_anchor_point[0], 0.0) if text_color is None: text_color = line_color self._label = bitmap_label.Label( text=text, font=font, color=text_color, anchor_point=text_anchor_point, anchored_position=(line_x1 + text_offset[0], line_y1 + text_offset[1]), ) label_width = self._label.bounding_box[2] line_x2 = line_x1 + label_width * underline_x_multiplier + text_offset[0] # lengthen the line if the text is offset line_y2 = line_y1 self._line0 = Line(line_x0, line_y0, line_x1, line_y1, color=line_color) self._line1 = Line(line_x1, line_y1, line_x2, line_y2, color=line_color) super().__init__() # Group elements: # 0. Line0 - from (x,y) to (x+delta_x, y+delta_y) # 1. Line1 - horizontal line for text # 2. Label self.append(self._line0) self.append(self._line1) self.append(self._label)