diff --git a/adafruit_displayio_layout/layouts/linear_layout.py b/adafruit_displayio_layout/layouts/linear_layout.py new file mode 100644 index 0000000..3bcd6cb --- /dev/null +++ b/adafruit_displayio_layout/layouts/linear_layout.py @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: Copyright (c) 2021 Tim Cocks +# +# SPDX-License-Identifier: MIT +""" +`linear_layout` +================================================================================ + +A layout that organizes cells into a vertical or horizontal line. + + +* Author(s): Tim Cocks + +Implementation Notes +-------------------- + +**Hardware:** + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +from adafruit_displayio_layout.widgets.widget import Widget + + +class LinearLayout(Widget): + """ + LinearLayout holds multiple content elements and arranges + them in a line either horizontally or vertically. + """ + + VERTICAL_ORIENTATION = 1 + HORIZONTAL_ORIENTATION = 2 + + def __init__( + self, + x, + y, + orientation=VERTICAL_ORIENTATION, + padding=0, + ): + """ + :param int x: The horizontal position of the layout + :param int y: The vertical position of the layout + :param int orientation: The orientation of the layout. Must be VERTICAL_ORIENTATION + or HORIZONTAL_ORIENTATION + :param int padding: The padding between items in the layout + """ + + super().__init__(x=x, y=y, width=1, height=1) + + self.x = x + self.y = y + self.padding = padding + if orientation not in [self.VERTICAL_ORIENTATION, self.HORIZONTAL_ORIENTATION]: + raise ValueError( + "Orientation must be either LinearLayout.VERTICAL_ORIENTATION" + " or LinearLayout.HORIZONTAL_ORIENTATION" + ) + + self.orientation = orientation + self._content_list = [] + self._prev_content_end = 0 + + def add_content(self, content): + """Add a child to the linear layout. + + :param content: the content to add to the linear layout e.g. label, button, etc... + Group subclasses that have width and height properties can be used. + + :return: None""" + + self._content_list.append(content) + self.append(content) + self._layout() + + def _layout(self): + # pylint: disable=too-many-branches, protected-access + self._prev_content_end = 0 + + for _, content in enumerate(self._content_list): + if not hasattr(content, "anchor_point"): + if self.orientation == self.VERTICAL_ORIENTATION: + content.y = self._prev_content_end + try: + self._prev_content_end = ( + self._prev_content_end + content.height + self.padding + ) + except AttributeError as error: + print(error) + try: + self._prev_content_end = ( + self._prev_content_end + content._height + self.padding + ) + except AttributeError as inner_error: + print(inner_error) + + else: + content.x = self._prev_content_end + if not hasattr(content, "tile_width"): + self._prev_content_end = ( + content.x + content.width + (self.padding * 2) + ) + else: + self._prev_content_end = ( + content.x + + (content.width * content.tile_width) + + (self.padding * 2) + ) + else: # use anchor point + content.anchor_point = ( + 0, + content.anchor_point[1] if content.anchor_point is not None else 0, + ) + if self.orientation == self.VERTICAL_ORIENTATION: + content.anchored_position = (0, self._prev_content_end) + # self._prev_content_end = content.y + content.height + if not hasattr(content, "bounding_box"): + self._prev_content_end = ( + self._prev_content_end + content.height + self.padding + ) + else: + self._prev_content_end = ( + self._prev_content_end + + (content.bounding_box[3] * content.scale) + + self.padding + ) + + else: + original_achored_pos_y = ( + content.anchored_position[1] + if content.anchored_position is not None + else 0 + ) + + content.anchored_position = ( + self._prev_content_end, + original_achored_pos_y, + ) + if not hasattr(content, "bounding_box"): + self._prev_content_end = ( + self._prev_content_end + content.width + self.padding + ) + else: + self._prev_content_end = ( + self._prev_content_end + + (content.bounding_box[2] * content.scale) + + self.padding + ) diff --git a/examples/displayio_layout_linearlayout_simpletest.py b/examples/displayio_layout_linearlayout_simpletest.py new file mode 100644 index 0000000..4090561 --- /dev/null +++ b/examples/displayio_layout_linearlayout_simpletest.py @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2024 Tim C, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Illustrates usage of LinearLayout to display a text label to the right of +an icon. +""" +import adafruit_imageload +import board +import displayio +import terminalio +from adafruit_display_text import label +from adafruit_displayio_layout.layouts.linear_layout import LinearLayout + +# use built in display (PyPortal, PyGamer, PyBadge, CLUE, etc.) +# see guide for setting up external displays (TFT / OLED breakouts, RGB matrices, etc.) +# https://learn.adafruit.com/circuitpython-display-support-using-displayio/display-and-display-bus +display = board.DISPLAY + +# Make the display context +main_group = displayio.Group() +display.root_group = main_group + +layout = LinearLayout( + x=10, y=10, padding=4, orientation=LinearLayout.HORIZONTAL_ORIENTATION +) + +lbl = label.Label(terminalio.FONT, scale=4, x=0, y=0, text="Hello") + +icon, icon_palette = adafruit_imageload.load("icons/Play_48x48_small.bmp") +icon_tile_grid = displayio.TileGrid(icon, pixel_shader=icon_palette) +layout.add_content(icon_tile_grid) +layout.add_content(lbl) + +main_group.append(layout) +while True: + pass