From 558c4399773feee5c3c0ae4d0cf6381e8817b58a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 8 Nov 2024 18:03:52 -0600 Subject: [PATCH 1/7] starting TextBox --- adafruit_display_text/text_box.py | 619 +++++++++++++++++++ examples/display_text_text_box_simpletest.py | 48 ++ 2 files changed, 667 insertions(+) create mode 100644 adafruit_display_text/text_box.py create mode 100644 examples/display_text_text_box_simpletest.py diff --git a/adafruit_display_text/text_box.py b/adafruit_display_text/text_box.py new file mode 100644 index 0000000..19a4397 --- /dev/null +++ b/adafruit_display_text/text_box.py @@ -0,0 +1,619 @@ +# SPDX-FileCopyrightText: 2020 Kevin Matocha +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_display_text.bitmap_label` +================================================================================ + +Text graphics handling for CircuitPython, including text boxes + + +* Author(s): Kevin Matocha + +Implementation Notes +-------------------- + +**Hardware:** + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Text.git" + +import displayio +from micropython import const + +from adafruit_display_text import LabelBase, wrap_text_to_pixels + +try: + import bitmaptools +except ImportError: + # We have a slower fallback for bitmaptools + pass + +try: + from typing import Optional, Tuple + from fontio import FontProtocol +except ImportError: + pass + + +# pylint: disable=too-many-instance-attributes +class TextBox(LabelBase): + """A label displaying a string of text that is stored in a bitmap. + Note: This ``bitmap_label.py`` library utilizes a :py:class:`~displayio.Bitmap` + to display the text. This method is memory-conserving relative to ``label.py``. + + For further reduction in memory usage, set ``save_text=False`` (text string will not + be stored and ``line_spacing`` and ``font`` are immutable with ``save_text`` + set to ``False``). + + The origin point set by ``x`` and ``y`` + properties will be the left edge of the bounding box, and in the center of a M + glyph (if its one line), or the (number of lines * linespacing + M)/2. That is, + it will try to have it be center-left as close as possible. + + :param font: A font class that has ``get_bounding_box`` and ``get_glyph``. + Must include a capital M for measuring character size. + :type font: ~fontio.FontProtocol + :param str text: Text to display + :param int|Tuple(int, int, int) color: Color of all text in HEX or RGB + :param int|Tuple(int, int, int)|None background_color: Color of the background, use `None` + for transparent + :param float line_spacing: Line spacing of text to display + :param bool background_tight: Set `True` only if you want background box to tightly + surround text. When set to 'True' Padding parameters will be ignored. + :param int padding_top: Additional pixels added to background bounding box at top + :param int padding_bottom: Additional pixels added to background bounding box at bottom + :param int padding_left: Additional pixels added to background bounding box at left + :param int padding_right: Additional pixels added to background bounding box at right + :param Tuple(float, float) anchor_point: Point that anchored_position moves relative to. + Tuple with decimal percentage of width and height. + (E.g. (0,0) is top left, (1.0, 0.5): is middle right.) + :param Tuple(int, int) anchored_position: Position relative to the anchor_point. Tuple + containing x,y pixel coordinates. + :param int scale: Integer value of the pixel scaling + :param bool save_text: Set True to save the text string as a constant in the + label structure. Set False to reduce memory use. + :param bool base_alignment: when True allows to align text label to the baseline. + This is helpful when two or more labels need to be aligned to the same baseline + :param Tuple(int, str) tab_replacement: tuple with tab character replace information. When + (4, " ") will indicate a tab replacement of 4 spaces, defaults to 4 spaces by + tab character + :param str label_direction: string defining the label text orientation. There are 5 + configurations possibles ``LTR``-Left-To-Right ``RTL``-Right-To-Left + ``UPD``-Upside Down ``UPR``-Upwards ``DWR``-Downwards. It defaults to ``LTR`` + :param bool verbose: print debugging information in some internal functions. Default to False + + """ + + ALIGN_LEFT = const(0) + ALIGN_CENTER = const(1) + ALIGN_RIGHT = const(2) + + DYNAMIC_HEIGHT = const(-1) + + def __init__(self, font: FontProtocol, width: int, height: int, align=ALIGN_LEFT, **kwargs) -> None: + self._bitmap = None + self._tilegrid = None + self._prev_label_direction = None + self._width = width + + self._height = height + self.align = align + + super().__init__(font, **kwargs) + + self._text = self._replace_tabs(self._text) + self._original_text = self._text + + self.lines = wrap_text_to_pixels(self._text, self._width - self._padding_left - self._padding_right, font) + self._text = "\n".join(self.lines) + + # call the text updater with all the arguments. + self._reset_text( + font=font, + text=self._text, + line_spacing=self._line_spacing, + scale=self.scale, + ) + + def _reset_text( + self, + font: Optional[FontProtocol] = None, + text: Optional[str] = None, + line_spacing: Optional[float] = None, + scale: Optional[int] = None, + ) -> None: + # pylint: disable=too-many-branches, too-many-statements, too-many-locals + + # Store all the instance variables + if font is not None: + self._font = font + if line_spacing is not None: + self._line_spacing = line_spacing + + # if text is not provided as a parameter (text is None), use the previous value. + if text is None: + text = self._text + + self._text = self._replace_tabs(text) + + # Check for empty string + if (text == "") or ( + text is None + ): # If empty string, just create a zero-sized bounding box and that's it. + self._bounding_box = ( + 0, + 0, + 0, # zero width with text == "" + 0, # zero height with text == "" + ) + # Clear out any items in the self._local_group Group, in case this is an + # update to the bitmap_label + for _ in self._local_group: + self._local_group.pop(0) + + # Free the bitmap and tilegrid since they are removed + self._bitmap = None + self._tilegrid = None + + else: # The text string is not empty, so create the Bitmap and TileGrid and + # append to the self Group + + # Calculate the text bounding box + + # Calculate both "tight" and "loose" bounding box dimensions to match label for + # anchor_position calculations + ( + box_x, + tight_box_y, + x_offset, + tight_y_offset, + loose_box_y, + loose_y_offset, + ) = self._text_bounding_box( + text, + self._font, + ) # calculate the box size for a tight and loose backgrounds + + if self._background_tight: + box_y = tight_box_y + y_offset = tight_y_offset + self._padding_left = 0 + self._padding_right = 0 + self._padding_top = 0 + self._padding_bottom = 0 + + else: # calculate the box size for a loose background + box_y = loose_box_y + y_offset = loose_y_offset + + # Calculate the background size including padding + tight_box_x = box_x + box_x = box_x + self._padding_left + self._padding_right + box_y = box_y + self._padding_top + self._padding_bottom + + if self._height == self.DYNAMIC_HEIGHT: + self._height = box_y + + # Create the Bitmap unless it can be reused + new_bitmap = None + if ( + self._bitmap is None + or self._bitmap.width != self._width + or self._bitmap.height != self._height + ): + new_bitmap = displayio.Bitmap(self._width, self._height, len(self._palette)) + self._bitmap = new_bitmap + else: + self._bitmap.fill(0) + + # Place the text into the Bitmap + self._place_text( + self._bitmap, + text, + self._font, + self._padding_left - x_offset, + self._padding_top + y_offset, + ) + + if self._base_alignment: + label_position_yoffset = 0 + else: + label_position_yoffset = self._ascent // 2 + + # Create the TileGrid if not created bitmap unchanged + if self._tilegrid is None or new_bitmap: + self._tilegrid = displayio.TileGrid( + self._bitmap, + pixel_shader=self._palette, + width=1, + height=1, + tile_width=self._width, + tile_height=self._height, + default_tile=0, + x=-self._padding_left + x_offset, + y=label_position_yoffset - y_offset - self._padding_top, + ) + # Clear out any items in the local_group Group, in case this is an update to + # the bitmap_label + for _ in self._local_group: + self._local_group.pop(0) + self._local_group.append( + self._tilegrid + ) # add the bitmap's tilegrid to the group + + self._bounding_box = ( + self._tilegrid.x + self._padding_left, + self._tilegrid.y + self._padding_top, + tight_box_x, + tight_box_y, + ) + + if ( + scale is not None + ): # Scale will be defined in local_group (Note: self should have scale=1) + self.scale = scale # call the setter + + # set the anchored_position with setter after bitmap is created, sets the + # x,y positions of the label + self.anchored_position = self._anchored_position + + + @property + def height(self) -> int: + """The height of the label determined from the bounding box.""" + return self._height + + @property + def width(self) -> int: + """The width of the label determined from the bounding box.""" + return self._width + + @staticmethod + def _line_spacing_ypixels(font: FontProtocol, line_spacing: float) -> int: + # Note: Scaling is provided at the Group level + return_value = int(line_spacing * font.get_bounding_box()[1]) + return return_value + + def _text_bounding_box( + self, text: str, font: FontProtocol + ) -> Tuple[int, int, int, int, int, int]: + # pylint: disable=too-many-locals + + ascender_max, descender_max = self._ascent, self._descent + + lines = 1 + + xposition = ( + x_start + ) = yposition = y_start = 0 # starting x and y position (left margin) + + left = None + right = x_start + top = bottom = y_start + + y_offset_tight = self._ascent // 2 + + newlines = 0 + line_spacing = self._line_spacing + + for char in text: + if char == "\n": # newline + newlines += 1 + + else: + my_glyph = font.get_glyph(ord(char)) + + if my_glyph is None: # Error checking: no glyph found + print("Glyph not found: {}".format(repr(char))) + else: + if newlines: + xposition = x_start # reset to left column + yposition += ( + self._line_spacing_ypixels(font, line_spacing) * newlines + ) # Add the newline(s) + lines += newlines + newlines = 0 + if xposition == x_start: + if left is None: + left = 0 + else: + left = min(left, my_glyph.dx) + xright = xposition + my_glyph.width + my_glyph.dx + xposition += my_glyph.shift_x + + right = max(right, xposition, xright) + + if yposition == y_start: # first line, find the Ascender height + top = min(top, -my_glyph.height - my_glyph.dy + y_offset_tight) + bottom = max(bottom, yposition - my_glyph.dy + y_offset_tight) + + if left is None: + left = 0 + + final_box_width = right - left + + final_box_height_tight = bottom - top + final_y_offset_tight = -top + y_offset_tight + + final_box_height_loose = (lines - 1) * self._line_spacing_ypixels( + font, line_spacing + ) + (ascender_max + descender_max) + final_y_offset_loose = ascender_max + + # return (final_box_width, final_box_height, left, final_y_offset) + + return ( + final_box_width, + final_box_height_tight, + left, + final_y_offset_tight, + final_box_height_loose, + final_y_offset_loose, + ) + + # pylint: disable = too-many-branches + def _place_text( + self, + bitmap: displayio.Bitmap, + text: str, + font: FontProtocol, + xposition: int, + yposition: int, + skip_index: int = 0, # set to None to write all pixels, other wise skip this palette index + # when copying glyph bitmaps (this is important for slanted text + # where rectangular glyph boxes overlap) + ) -> Tuple[int, int, int, int]: + # pylint: disable=too-many-arguments, too-many-locals + + # placeText - Writes text into a bitmap at the specified location. + # + # Note: scale is pushed up to Group level + original_xposition = xposition + cur_line_index = 0 + cur_line_width = self._text_bounding_box(self.lines[0], self.font)[0] + + if self.align == self.ALIGN_LEFT: + x_start = original_xposition # starting x position (left margin) + if self.align == self.ALIGN_CENTER: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space // 2 + if self.align == self.ALIGN_RIGHT: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space - self._padding_right + xposition = x_start + + y_start = yposition + #print(f"start loc {x_start}, {y_start}") + + left = None + right = x_start + top = bottom = y_start + line_spacing = self._line_spacing + + #print(f"cur_line width: {cur_line_width}") + for char in text: + if char == "\n": # newline + cur_line_index += 1 + cur_line_width = self._text_bounding_box(self.lines[cur_line_index], self.font)[0] + #print(f"cur_line width: {cur_line_width}") + if self.align == self.ALIGN_LEFT: + x_start = original_xposition # starting x position (left margin) + if self.align == self.ALIGN_CENTER: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space // 2 + if self.align == self.ALIGN_RIGHT: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space - self._padding_right + xposition = x_start + + yposition = yposition + self._line_spacing_ypixels( + font, line_spacing + ) # Add a newline + + else: + my_glyph = font.get_glyph(ord(char)) + + if my_glyph is None: # Error checking: no glyph found + print("Glyph not found: {}".format(repr(char))) + else: + if xposition == x_start: + if left is None: + left = 0 + else: + left = min(left, my_glyph.dx) + + right = max( + right, + xposition + my_glyph.shift_x, + xposition + my_glyph.width + my_glyph.dx, + ) + if yposition == y_start: # first line, find the Ascender height + top = min(top, -my_glyph.height - my_glyph.dy) + bottom = max(bottom, yposition - my_glyph.dy) + + glyph_offset_x = ( + my_glyph.tile_index * my_glyph.width + ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. + # for BDF loaded fonts, this should equal 0 + + y_blit_target = yposition - my_glyph.height - my_glyph.dy + + # Clip glyph y-direction if outside the font ascent/descent metrics. + # Note: bitmap.blit will automatically clip the bottom of the glyph. + y_clip = 0 + if y_blit_target < 0: + y_clip = -y_blit_target # clip this amount from top of bitmap + y_blit_target = 0 # draw the clipped bitmap at y=0 + if self._verbose: + print( + 'Warning: Glyph clipped, exceeds Ascent property: "{}"'.format( + char + ) + ) + + if (y_blit_target + my_glyph.height) > bitmap.height: + if self._verbose: + print( + 'Warning: Glyph clipped, exceeds descent property: "{}"'.format( + char + ) + ) + try: + self._blit( + bitmap, + max(xposition + my_glyph.dx, 0), + y_blit_target, + my_glyph.bitmap, + x_1=glyph_offset_x, + y_1=y_clip, + x_2=glyph_offset_x + my_glyph.width, + y_2=my_glyph.height, + skip_index=skip_index, # do not copy over any 0 background pixels + ) + except ValueError: + # ignore index out of bounds error + break + + xposition = xposition + my_glyph.shift_x + + # bounding_box + return left, top, right - left, bottom - top + + def _blit( + self, + bitmap: displayio.Bitmap, # target bitmap + x: int, # target x upper left corner + y: int, # target y upper left corner + source_bitmap: displayio.Bitmap, # source bitmap + x_1: int = 0, # source x start + y_1: int = 0, # source y start + x_2: int = None, # source x end + y_2: int = None, # source y end + skip_index: int = None, # palette index that will not be copied + # (for example: the background color of a glyph) + ) -> None: + # pylint: disable=no-self-use, too-many-arguments + + if hasattr(bitmap, "blit"): # if bitmap has a built-in blit function, call it + # this function should perform its own input checks + bitmap.blit( + x, + y, + source_bitmap, + x1=x_1, + y1=y_1, + x2=x_2, + y2=y_2, + skip_index=skip_index, + ) + + elif hasattr(bitmaptools, "blit"): + bitmaptools.blit( + bitmap, + source_bitmap, + x, + y, + x1=x_1, + y1=y_1, + x2=x_2, + y2=y_2, + skip_source_index=skip_index, + ) + + + else: # perform pixel by pixel copy of the bitmap + # Perform input checks + + if x_2 is None: + x_2 = source_bitmap.width + if y_2 is None: + y_2 = source_bitmap.height + + # Rearrange so that x_1 < x_2 and y1 < y2 + if x_1 > x_2: + x_1, x_2 = x_2, x_1 + if y_1 > y_2: + y_1, y_2 = y_2, y_1 + + # Ensure that x2 and y2 are within source bitmap size + x_2 = min(x_2, source_bitmap.width) + y_2 = min(y_2, source_bitmap.height) + + for y_count in range(y_2 - y_1): + for x_count in range(x_2 - x_1): + x_placement = x + x_count + y_placement = y + y_count + + if (bitmap.width > x_placement >= 0) and ( + bitmap.height > y_placement >= 0 + ): # ensure placement is within target bitmap + # get the palette index from the source bitmap + this_pixel_color = source_bitmap[ + y_1 + + ( + y_count * source_bitmap.width + ) # Direct index into a bitmap array is speedier than [x,y] tuple + + x_1 + + x_count + ] + + if (skip_index is None) or (this_pixel_color != skip_index): + bitmap[ # Direct index into a bitmap array is speedier than [x,y] tuple + y_placement * bitmap.width + x_placement + ] = this_pixel_color + elif y_placement > bitmap.height: + break + + def _set_line_spacing(self, new_line_spacing: float) -> None: + if self._save_text: + self._reset_text(line_spacing=new_line_spacing, scale=self.scale) + else: + raise RuntimeError("line_spacing is immutable when save_text is False") + + def _set_font(self, new_font: FontProtocol) -> None: + self._font = new_font + if self._save_text: + self._reset_text(font=new_font, scale=self.scale) + else: + raise RuntimeError("font is immutable when save_text is False") + + def _set_text(self, new_text: str, scale: int) -> None: + self._reset_text(text=self._replace_tabs(new_text), scale=self.scale) + + def _set_background_color(self, new_color: Optional[int]): + self._background_color = new_color + if new_color is not None: + self._palette[0] = new_color + self._palette.make_opaque(0) + else: + self._palette[0] = 0 + self._palette.make_transparent(0) + + def _set_label_direction(self, new_label_direction: str) -> None: + # Only make changes if new direction is different + # to prevent errors in the _reset_text() direction checks + if self._label_direction != new_label_direction: + self._prev_label_direction = self._label_direction + self._label_direction = new_label_direction + self._reset_text(text=str(self._text)) # Force a recalculation + + def _get_valid_label_directions(self) -> Tuple[str, ...]: + return "LTR", "RTL", "UPD", "UPR", "DWR" + + @property + def bitmap(self) -> displayio.Bitmap: + """ + The Bitmap object that the text and background are drawn into. + + :rtype: displayio.Bitmap + """ + return self._bitmap diff --git a/examples/display_text_text_box_simpletest.py b/examples/display_text_text_box_simpletest.py new file mode 100644 index 0000000..ecc71b0 --- /dev/null +++ b/examples/display_text_text_box_simpletest.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2024 foamyguy for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board +import displayio +import terminalio +from adafruit_display_text.text_box import TextBox + +main_group = displayio.Group() + +left_text = ("Left left left left " * 2).rstrip() +left_text_area = TextBox(terminalio.FONT, 190, TextBox.DYNAMIC_HEIGHT, + align=TextBox.ALIGN_LEFT, + text=left_text, + background_color=0xff00ff, + color=0x000000, scale=1) + +left_text_area.x = 10 +left_text_area.y = 10 +main_group.append(left_text_area) + + +center_text = ("center center center " * 2).rstrip() +center_text_area = TextBox(terminalio.FONT, 190, TextBox.DYNAMIC_HEIGHT, + align=TextBox.ALIGN_CENTER, + text=center_text, + background_color=0x00ff00, + color=0x000000, scale=1) + +center_text_area.x = 10 +center_text_area.y = 10 + left_text_area.height + 10 +main_group.append(center_text_area) + + +right_text = ("right right right right " * 2).rstrip() +right_text_area = TextBox(terminalio.FONT, 190, TextBox.DYNAMIC_HEIGHT, + align=TextBox.ALIGN_RIGHT, + text=right_text, + background_color=0xffff00, + color=0x000000, scale=1) + +right_text_area.x = 10 +right_text_area.y = center_text_area.y + center_text_area.height + 10 +main_group.append(right_text_area) + +board.DISPLAY.root_group = main_group +while True: + pass From 203eac5efb643351a8915004cd73bd58909757b0 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 15 Nov 2024 17:56:24 -0600 Subject: [PATCH 2/7] change to be subclass of bitmap_label --- adafruit_display_text/text_box.py | 571 ++++++++++-------------------- 1 file changed, 179 insertions(+), 392 deletions(-) diff --git a/adafruit_display_text/text_box.py b/adafruit_display_text/text_box.py index 19a4397..1a3b4f7 100644 --- a/adafruit_display_text/text_box.py +++ b/adafruit_display_text/text_box.py @@ -29,7 +29,10 @@ import displayio from micropython import const -from adafruit_display_text import LabelBase, wrap_text_to_pixels +from adafruit_display_text import wrap_text_to_pixels, LabelBase +from adafruit_display_text import bitmap_label + + try: import bitmaptools @@ -45,54 +48,7 @@ # pylint: disable=too-many-instance-attributes -class TextBox(LabelBase): - """A label displaying a string of text that is stored in a bitmap. - Note: This ``bitmap_label.py`` library utilizes a :py:class:`~displayio.Bitmap` - to display the text. This method is memory-conserving relative to ``label.py``. - - For further reduction in memory usage, set ``save_text=False`` (text string will not - be stored and ``line_spacing`` and ``font`` are immutable with ``save_text`` - set to ``False``). - - The origin point set by ``x`` and ``y`` - properties will be the left edge of the bounding box, and in the center of a M - glyph (if its one line), or the (number of lines * linespacing + M)/2. That is, - it will try to have it be center-left as close as possible. - - :param font: A font class that has ``get_bounding_box`` and ``get_glyph``. - Must include a capital M for measuring character size. - :type font: ~fontio.FontProtocol - :param str text: Text to display - :param int|Tuple(int, int, int) color: Color of all text in HEX or RGB - :param int|Tuple(int, int, int)|None background_color: Color of the background, use `None` - for transparent - :param float line_spacing: Line spacing of text to display - :param bool background_tight: Set `True` only if you want background box to tightly - surround text. When set to 'True' Padding parameters will be ignored. - :param int padding_top: Additional pixels added to background bounding box at top - :param int padding_bottom: Additional pixels added to background bounding box at bottom - :param int padding_left: Additional pixels added to background bounding box at left - :param int padding_right: Additional pixels added to background bounding box at right - :param Tuple(float, float) anchor_point: Point that anchored_position moves relative to. - Tuple with decimal percentage of width and height. - (E.g. (0,0) is top left, (1.0, 0.5): is middle right.) - :param Tuple(int, int) anchored_position: Position relative to the anchor_point. Tuple - containing x,y pixel coordinates. - :param int scale: Integer value of the pixel scaling - :param bool save_text: Set True to save the text string as a constant in the - label structure. Set False to reduce memory use. - :param bool base_alignment: when True allows to align text label to the baseline. - This is helpful when two or more labels need to be aligned to the same baseline - :param Tuple(int, str) tab_replacement: tuple with tab character replace information. When - (4, " ") will indicate a tab replacement of 4 spaces, defaults to 4 spaces by - tab character - :param str label_direction: string defining the label text orientation. There are 5 - configurations possibles ``LTR``-Left-To-Right ``RTL``-Right-To-Left - ``UPD``-Upside Down ``UPR``-Upwards ``DWR``-Downwards. It defaults to ``LTR`` - :param bool verbose: print debugging information in some internal functions. Default to False - - """ - +class TextBox(bitmap_label.Label): ALIGN_LEFT = const(0) ALIGN_CENTER = const(1) ALIGN_RIGHT = const(2) @@ -100,21 +56,39 @@ class TextBox(LabelBase): DYNAMIC_HEIGHT = const(-1) def __init__(self, font: FontProtocol, width: int, height: int, align=ALIGN_LEFT, **kwargs) -> None: + """ + + :param font: + :param width: + :param height: + :param align: + :param kwargs: + """ self._bitmap = None self._tilegrid = None self._prev_label_direction = None self._width = width - self._height = height + if height != TextBox.DYNAMIC_HEIGHT: + self._height = height + self.dynamic_height = False + else: + self.dynamic_height = True + self.align = align - super().__init__(font, **kwargs) + self._padding_left = kwargs.get("padding_left", 0) + self._padding_right = kwargs.get("padding_right", 0) - self._text = self._replace_tabs(self._text) - self._original_text = self._text + self.lines = wrap_text_to_pixels(kwargs.get("text", ""), self._width - self._padding_left - self._padding_right, font) + + super(bitmap_label.Label, self).__init__(font, **kwargs) + + print(f"before reset: {self._text}") - self.lines = wrap_text_to_pixels(self._text, self._width - self._padding_left - self._padding_right, font) self._text = "\n".join(self.lines) + self._text = self._replace_tabs(self._text) + self._original_text = self._text # call the text updater with all the arguments. self._reset_text( @@ -123,7 +97,135 @@ def __init__(self, font: FontProtocol, width: int, height: int, align=ALIGN_LEFT line_spacing=self._line_spacing, scale=self.scale, ) + print(f"after reset: {self._text}") + + def _place_text( + self, + bitmap: displayio.Bitmap, + text: str, + font: FontProtocol, + xposition: int, + yposition: int, + skip_index: int = 0, # set to None to write all pixels, other wise skip this palette index + # when copying glyph bitmaps (this is important for slanted text + # where rectangular glyph boxes overlap) + ) -> Tuple[int, int, int, int]: + # pylint: disable=too-many-arguments, too-many-locals + + # placeText - Writes text into a bitmap at the specified location. + # + # Note: scale is pushed up to Group level + original_xposition = xposition + cur_line_index = 0 + cur_line_width = self._text_bounding_box(self.lines[0], self.font)[0] + + if self.align == self.ALIGN_LEFT: + x_start = original_xposition # starting x position (left margin) + if self.align == self.ALIGN_CENTER: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space // 2 + if self.align == self.ALIGN_RIGHT: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space - self._padding_right + xposition = x_start + + y_start = yposition + #print(f"start loc {x_start}, {y_start}") + + left = None + right = x_start + top = bottom = y_start + line_spacing = self._line_spacing + + #print(f"cur_line width: {cur_line_width}") + for char in text: + if char == "\n": # newline + cur_line_index += 1 + cur_line_width = self._text_bounding_box(self.lines[cur_line_index], self.font)[0] + #print(f"cur_line width: {cur_line_width}") + if self.align == self.ALIGN_LEFT: + x_start = original_xposition # starting x position (left margin) + if self.align == self.ALIGN_CENTER: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space // 2 + if self.align == self.ALIGN_RIGHT: + unused_space = self._width - cur_line_width + x_start = original_xposition + unused_space - self._padding_right + xposition = x_start + + yposition = yposition + self._line_spacing_ypixels( + font, line_spacing + ) # Add a newline + + else: + my_glyph = font.get_glyph(ord(char)) + + if my_glyph is None: # Error checking: no glyph found + print("Glyph not found: {}".format(repr(char))) + else: + if xposition == x_start: + if left is None: + left = 0 + else: + left = min(left, my_glyph.dx) + + right = max( + right, + xposition + my_glyph.shift_x, + xposition + my_glyph.width + my_glyph.dx, + ) + if yposition == y_start: # first line, find the Ascender height + top = min(top, -my_glyph.height - my_glyph.dy) + bottom = max(bottom, yposition - my_glyph.dy) + + glyph_offset_x = ( + my_glyph.tile_index * my_glyph.width + ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. + # for BDF loaded fonts, this should equal 0 + + y_blit_target = yposition - my_glyph.height - my_glyph.dy + + # Clip glyph y-direction if outside the font ascent/descent metrics. + # Note: bitmap.blit will automatically clip the bottom of the glyph. + y_clip = 0 + if y_blit_target < 0: + y_clip = -y_blit_target # clip this amount from top of bitmap + y_blit_target = 0 # draw the clipped bitmap at y=0 + if self._verbose: + print( + 'Warning: Glyph clipped, exceeds Ascent property: "{}"'.format( + char + ) + ) + + if (y_blit_target + my_glyph.height) > bitmap.height: + if self._verbose: + print( + 'Warning: Glyph clipped, exceeds descent property: "{}"'.format( + char + ) + ) + try: + self._blit( + bitmap, + max(xposition + my_glyph.dx, 0), + y_blit_target, + my_glyph.bitmap, + x_1=glyph_offset_x, + y_1=y_clip, + x_2=glyph_offset_x + my_glyph.width, + y_2=my_glyph.height, + skip_index=skip_index, # do not copy over any 0 background pixels + ) + except ValueError: + # ignore index out of bounds error + break + xposition = xposition + my_glyph.shift_x + + # bounding_box + return left, top, right - left, bottom - top + def _reset_text( self, font: Optional[FontProtocol] = None, @@ -144,6 +246,7 @@ def _reset_text( text = self._text self._text = self._replace_tabs(text) + print(f"inside reset_text text: {text}") # Check for empty string if (text == "") or ( @@ -200,7 +303,8 @@ def _reset_text( box_x = box_x + self._padding_left + self._padding_right box_y = box_y + self._padding_top + self._padding_bottom - if self._height == self.DYNAMIC_HEIGHT: + if self.dynamic_height: + print(f"dynamic height, box_y: {box_y}") self._height = box_y # Create the Bitmap unless it can be reused @@ -256,6 +360,7 @@ def _reset_text( tight_box_x, tight_box_y, ) + print(f"end of reset_text bounding box: {self._bounding_box}") if ( scale is not None @@ -266,7 +371,6 @@ def _reset_text( # x,y positions of the label self.anchored_position = self._anchored_position - @property def height(self) -> int: """The height of the label determined from the bounding box.""" @@ -277,343 +381,26 @@ def width(self) -> int: """The width of the label determined from the bounding box.""" return self._width - @staticmethod - def _line_spacing_ypixels(font: FontProtocol, line_spacing: float) -> int: - # Note: Scaling is provided at the Group level - return_value = int(line_spacing * font.get_bounding_box()[1]) - return return_value - - def _text_bounding_box( - self, text: str, font: FontProtocol - ) -> Tuple[int, int, int, int, int, int]: - # pylint: disable=too-many-locals - - ascender_max, descender_max = self._ascent, self._descent - - lines = 1 - - xposition = ( - x_start - ) = yposition = y_start = 0 # starting x and y position (left margin) - - left = None - right = x_start - top = bottom = y_start - - y_offset_tight = self._ascent // 2 - - newlines = 0 - line_spacing = self._line_spacing - - for char in text: - if char == "\n": # newline - newlines += 1 - - else: - my_glyph = font.get_glyph(ord(char)) - - if my_glyph is None: # Error checking: no glyph found - print("Glyph not found: {}".format(repr(char))) - else: - if newlines: - xposition = x_start # reset to left column - yposition += ( - self._line_spacing_ypixels(font, line_spacing) * newlines - ) # Add the newline(s) - lines += newlines - newlines = 0 - if xposition == x_start: - if left is None: - left = 0 - else: - left = min(left, my_glyph.dx) - xright = xposition + my_glyph.width + my_glyph.dx - xposition += my_glyph.shift_x - - right = max(right, xposition, xright) - - if yposition == y_start: # first line, find the Ascender height - top = min(top, -my_glyph.height - my_glyph.dy + y_offset_tight) - bottom = max(bottom, yposition - my_glyph.dy + y_offset_tight) - - if left is None: - left = 0 - - final_box_width = right - left - - final_box_height_tight = bottom - top - final_y_offset_tight = -top + y_offset_tight - - final_box_height_loose = (lines - 1) * self._line_spacing_ypixels( - font, line_spacing - ) + (ascender_max + descender_max) - final_y_offset_loose = ascender_max - - # return (final_box_width, final_box_height, left, final_y_offset) - - return ( - final_box_width, - final_box_height_tight, - left, - final_y_offset_tight, - final_box_height_loose, - final_y_offset_loose, - ) - - # pylint: disable = too-many-branches - def _place_text( - self, - bitmap: displayio.Bitmap, - text: str, - font: FontProtocol, - xposition: int, - yposition: int, - skip_index: int = 0, # set to None to write all pixels, other wise skip this palette index - # when copying glyph bitmaps (this is important for slanted text - # where rectangular glyph boxes overlap) - ) -> Tuple[int, int, int, int]: - # pylint: disable=too-many-arguments, too-many-locals - - # placeText - Writes text into a bitmap at the specified location. - # - # Note: scale is pushed up to Group level - original_xposition = xposition - cur_line_index = 0 - cur_line_width = self._text_bounding_box(self.lines[0], self.font)[0] - - if self.align == self.ALIGN_LEFT: - x_start = original_xposition # starting x position (left margin) - if self.align == self.ALIGN_CENTER: - unused_space = self._width - cur_line_width - x_start = original_xposition + unused_space // 2 - if self.align == self.ALIGN_RIGHT: - unused_space = self._width - cur_line_width - x_start = original_xposition + unused_space - self._padding_right - xposition = x_start - - y_start = yposition - #print(f"start loc {x_start}, {y_start}") - - left = None - right = x_start - top = bottom = y_start - line_spacing = self._line_spacing - - #print(f"cur_line width: {cur_line_width}") - for char in text: - if char == "\n": # newline - cur_line_index += 1 - cur_line_width = self._text_bounding_box(self.lines[cur_line_index], self.font)[0] - #print(f"cur_line width: {cur_line_width}") - if self.align == self.ALIGN_LEFT: - x_start = original_xposition # starting x position (left margin) - if self.align == self.ALIGN_CENTER: - unused_space = self._width - cur_line_width - x_start = original_xposition + unused_space // 2 - if self.align == self.ALIGN_RIGHT: - unused_space = self._width - cur_line_width - x_start = original_xposition + unused_space - self._padding_right - xposition = x_start - - yposition = yposition + self._line_spacing_ypixels( - font, line_spacing - ) # Add a newline - - else: - my_glyph = font.get_glyph(ord(char)) - - if my_glyph is None: # Error checking: no glyph found - print("Glyph not found: {}".format(repr(char))) - else: - if xposition == x_start: - if left is None: - left = 0 - else: - left = min(left, my_glyph.dx) - - right = max( - right, - xposition + my_glyph.shift_x, - xposition + my_glyph.width + my_glyph.dx, - ) - if yposition == y_start: # first line, find the Ascender height - top = min(top, -my_glyph.height - my_glyph.dy) - bottom = max(bottom, yposition - my_glyph.dy) - - glyph_offset_x = ( - my_glyph.tile_index * my_glyph.width - ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. - # for BDF loaded fonts, this should equal 0 - - y_blit_target = yposition - my_glyph.height - my_glyph.dy - - # Clip glyph y-direction if outside the font ascent/descent metrics. - # Note: bitmap.blit will automatically clip the bottom of the glyph. - y_clip = 0 - if y_blit_target < 0: - y_clip = -y_blit_target # clip this amount from top of bitmap - y_blit_target = 0 # draw the clipped bitmap at y=0 - if self._verbose: - print( - 'Warning: Glyph clipped, exceeds Ascent property: "{}"'.format( - char - ) - ) - - if (y_blit_target + my_glyph.height) > bitmap.height: - if self._verbose: - print( - 'Warning: Glyph clipped, exceeds descent property: "{}"'.format( - char - ) - ) - try: - self._blit( - bitmap, - max(xposition + my_glyph.dx, 0), - y_blit_target, - my_glyph.bitmap, - x_1=glyph_offset_x, - y_1=y_clip, - x_2=glyph_offset_x + my_glyph.width, - y_2=my_glyph.height, - skip_index=skip_index, # do not copy over any 0 background pixels - ) - except ValueError: - # ignore index out of bounds error - break - - xposition = xposition + my_glyph.shift_x - - # bounding_box - return left, top, right - left, bottom - top - - def _blit( - self, - bitmap: displayio.Bitmap, # target bitmap - x: int, # target x upper left corner - y: int, # target y upper left corner - source_bitmap: displayio.Bitmap, # source bitmap - x_1: int = 0, # source x start - y_1: int = 0, # source y start - x_2: int = None, # source x end - y_2: int = None, # source y end - skip_index: int = None, # palette index that will not be copied - # (for example: the background color of a glyph) - ) -> None: - # pylint: disable=no-self-use, too-many-arguments - - if hasattr(bitmap, "blit"): # if bitmap has a built-in blit function, call it - # this function should perform its own input checks - bitmap.blit( - x, - y, - source_bitmap, - x1=x_1, - y1=y_1, - x2=x_2, - y2=y_2, - skip_index=skip_index, - ) - - elif hasattr(bitmaptools, "blit"): - bitmaptools.blit( - bitmap, - source_bitmap, - x, - y, - x1=x_1, - y1=y_1, - x2=x_2, - y2=y_2, - skip_source_index=skip_index, - ) - - - else: # perform pixel by pixel copy of the bitmap - # Perform input checks - - if x_2 is None: - x_2 = source_bitmap.width - if y_2 is None: - y_2 = source_bitmap.height - - # Rearrange so that x_1 < x_2 and y1 < y2 - if x_1 > x_2: - x_1, x_2 = x_2, x_1 - if y_1 > y_2: - y_1, y_2 = y_2, y_1 - - # Ensure that x2 and y2 are within source bitmap size - x_2 = min(x_2, source_bitmap.width) - y_2 = min(y_2, source_bitmap.height) - - for y_count in range(y_2 - y_1): - for x_count in range(x_2 - x_1): - x_placement = x + x_count - y_placement = y + y_count - - if (bitmap.width > x_placement >= 0) and ( - bitmap.height > y_placement >= 0 - ): # ensure placement is within target bitmap - # get the palette index from the source bitmap - this_pixel_color = source_bitmap[ - y_1 - + ( - y_count * source_bitmap.width - ) # Direct index into a bitmap array is speedier than [x,y] tuple - + x_1 - + x_count - ] - - if (skip_index is None) or (this_pixel_color != skip_index): - bitmap[ # Direct index into a bitmap array is speedier than [x,y] tuple - y_placement * bitmap.width + x_placement - ] = this_pixel_color - elif y_placement > bitmap.height: - break - - def _set_line_spacing(self, new_line_spacing: float) -> None: - if self._save_text: - self._reset_text(line_spacing=new_line_spacing, scale=self.scale) - else: - raise RuntimeError("line_spacing is immutable when save_text is False") - - def _set_font(self, new_font: FontProtocol) -> None: - self._font = new_font - if self._save_text: - self._reset_text(font=new_font, scale=self.scale) - else: - raise RuntimeError("font is immutable when save_text is False") - - def _set_text(self, new_text: str, scale: int) -> None: - self._reset_text(text=self._replace_tabs(new_text), scale=self.scale) + @width.setter + def width(self, width: int) -> None: + self._width = width + self.text = self._text - def _set_background_color(self, new_color: Optional[int]): - self._background_color = new_color - if new_color is not None: - self._palette[0] = new_color - self._palette.make_opaque(0) + @height.setter + def height(self, height: int) -> None: + if height != TextBox.DYNAMIC_HEIGHT: + self._height = height + self.dynamic_height = False else: - self._palette[0] = 0 - self._palette.make_transparent(0) - - def _set_label_direction(self, new_label_direction: str) -> None: - # Only make changes if new direction is different - # to prevent errors in the _reset_text() direction checks - if self._label_direction != new_label_direction: - self._prev_label_direction = self._label_direction - self._label_direction = new_label_direction - self._reset_text(text=str(self._text)) # Force a recalculation + self.dynamic_height = True + self.text = self._text - def _get_valid_label_directions(self) -> Tuple[str, ...]: - return "LTR", "RTL", "UPD", "UPR", "DWR" - - @property - def bitmap(self) -> displayio.Bitmap: - """ - The Bitmap object that the text and background are drawn into. + @bitmap_label.Label.text.setter + def text(self, text: str) -> None: + self.lines = wrap_text_to_pixels(text, self._width - self._padding_left - self._padding_right, + self.font) + self._text = self._replace_tabs(text) + self._original_text = self._text + self._text = "\n".join(self.lines) - :rtype: displayio.Bitmap - """ - return self._bitmap + self._set_text(self._text, self.scale) From e6040cf657934887c66bde6786cc04723cf1dc1c Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 16 Nov 2024 09:11:38 -0600 Subject: [PATCH 3/7] format and lint --- adafruit_display_text/text_box.py | 87 +++++++++++++------- examples/display_text_text_box_simpletest.py | 45 ++++++---- 2 files changed, 88 insertions(+), 44 deletions(-) diff --git a/adafruit_display_text/text_box.py b/adafruit_display_text/text_box.py index 1a3b4f7..9abe504 100644 --- a/adafruit_display_text/text_box.py +++ b/adafruit_display_text/text_box.py @@ -29,17 +29,9 @@ import displayio from micropython import const -from adafruit_display_text import wrap_text_to_pixels, LabelBase +from adafruit_display_text import wrap_text_to_pixels from adafruit_display_text import bitmap_label - - -try: - import bitmaptools -except ImportError: - # We have a slower fallback for bitmaptools - pass - try: from typing import Optional, Tuple from fontio import FontProtocol @@ -49,21 +41,31 @@ # pylint: disable=too-many-instance-attributes class TextBox(bitmap_label.Label): + """ + TextBox has a constrained width and optionally height. + You set the desired size when it's initialized it + will automatically wrap text to fit it within the allotted + size. + + Left, Right, and Center alignment of the text within the + box are supported. + + :param font: The font to use for the TextBox. + :param width: The width of the TextBox in pixels. + :param height: The height of the TextBox in pixels. + :param align: How to align the text within the box, + valid values are `ALIGN_LEFT`, `ALIGN_CENTER`, `ALIGN_RIGHT`. + """ + ALIGN_LEFT = const(0) ALIGN_CENTER = const(1) ALIGN_RIGHT = const(2) DYNAMIC_HEIGHT = const(-1) - def __init__(self, font: FontProtocol, width: int, height: int, align=ALIGN_LEFT, **kwargs) -> None: - """ - - :param font: - :param width: - :param height: - :param align: - :param kwargs: - """ + def __init__( + self, font: FontProtocol, width: int, height: int, align=ALIGN_LEFT, **kwargs + ) -> None: self._bitmap = None self._tilegrid = None self._prev_label_direction = None @@ -75,12 +77,20 @@ def __init__(self, font: FontProtocol, width: int, height: int, align=ALIGN_LEFT else: self.dynamic_height = True - self.align = align + if align not in (TextBox.ALIGN_LEFT, TextBox.ALIGN_CENTER, TextBox.ALIGN_RIGHT): + raise ValueError( + "Align must be one of: ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT" + ) + self._align = align self._padding_left = kwargs.get("padding_left", 0) self._padding_right = kwargs.get("padding_right", 0) - self.lines = wrap_text_to_pixels(kwargs.get("text", ""), self._width - self._padding_left - self._padding_right, font) + self.lines = wrap_text_to_pixels( + kwargs.get("text", ""), + self._width - self._padding_left - self._padding_right, + font, + ) super(bitmap_label.Label, self).__init__(font, **kwargs) @@ -127,22 +137,25 @@ def _place_text( if self.align == self.ALIGN_RIGHT: unused_space = self._width - cur_line_width x_start = original_xposition + unused_space - self._padding_right - xposition = x_start + + xposition = x_start # pylint: disable=used-before-assignment y_start = yposition - #print(f"start loc {x_start}, {y_start}") + # print(f"start loc {x_start}, {y_start}") left = None right = x_start top = bottom = y_start line_spacing = self._line_spacing - #print(f"cur_line width: {cur_line_width}") + # print(f"cur_line width: {cur_line_width}") for char in text: if char == "\n": # newline cur_line_index += 1 - cur_line_width = self._text_bounding_box(self.lines[cur_line_index], self.font)[0] - #print(f"cur_line width: {cur_line_width}") + cur_line_width = self._text_bounding_box( + self.lines[cur_line_index], self.font + )[0] + # print(f"cur_line width: {cur_line_width}") if self.align == self.ALIGN_LEFT: x_start = original_xposition # starting x position (left margin) if self.align == self.ALIGN_CENTER: @@ -225,7 +238,7 @@ def _place_text( # bounding_box return left, top, right - left, bottom - top - + def _reset_text( self, font: Optional[FontProtocol] = None, @@ -314,7 +327,9 @@ def _reset_text( or self._bitmap.width != self._width or self._bitmap.height != self._height ): - new_bitmap = displayio.Bitmap(self._width, self._height, len(self._palette)) + new_bitmap = displayio.Bitmap( + self._width, self._height, len(self._palette) + ) self._bitmap = new_bitmap else: self._bitmap.fill(0) @@ -397,10 +412,24 @@ def height(self, height: int) -> None: @bitmap_label.Label.text.setter def text(self, text: str) -> None: - self.lines = wrap_text_to_pixels(text, self._width - self._padding_left - self._padding_right, - self.font) + self.lines = wrap_text_to_pixels( + text, self._width - self._padding_left - self._padding_right, self.font + ) self._text = self._replace_tabs(text) self._original_text = self._text self._text = "\n".join(self.lines) self._set_text(self._text, self.scale) + + @property + def align(self): + """Alignment of the text within the TextBox""" + return self._align + + @align.setter + def align(self, align: int) -> None: + if align not in (TextBox.ALIGN_LEFT, TextBox.ALIGN_CENTER, TextBox.ALIGN_RIGHT): + raise ValueError( + "Align must be one of: ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT" + ) + self._align = align diff --git a/examples/display_text_text_box_simpletest.py b/examples/display_text_text_box_simpletest.py index ecc71b0..1512f7c 100644 --- a/examples/display_text_text_box_simpletest.py +++ b/examples/display_text_text_box_simpletest.py @@ -9,11 +9,16 @@ main_group = displayio.Group() left_text = ("Left left left left " * 2).rstrip() -left_text_area = TextBox(terminalio.FONT, 190, TextBox.DYNAMIC_HEIGHT, - align=TextBox.ALIGN_LEFT, - text=left_text, - background_color=0xff00ff, - color=0x000000, scale=1) +left_text_area = TextBox( + terminalio.FONT, + 190, + TextBox.DYNAMIC_HEIGHT, + align=TextBox.ALIGN_LEFT, + text=left_text, + background_color=0xFF00FF, + color=0x000000, + scale=1, +) left_text_area.x = 10 left_text_area.y = 10 @@ -21,11 +26,16 @@ center_text = ("center center center " * 2).rstrip() -center_text_area = TextBox(terminalio.FONT, 190, TextBox.DYNAMIC_HEIGHT, - align=TextBox.ALIGN_CENTER, - text=center_text, - background_color=0x00ff00, - color=0x000000, scale=1) +center_text_area = TextBox( + terminalio.FONT, + 190, + TextBox.DYNAMIC_HEIGHT, + align=TextBox.ALIGN_CENTER, + text=center_text, + background_color=0x00FF00, + color=0x000000, + scale=1, +) center_text_area.x = 10 center_text_area.y = 10 + left_text_area.height + 10 @@ -33,11 +43,16 @@ right_text = ("right right right right " * 2).rstrip() -right_text_area = TextBox(terminalio.FONT, 190, TextBox.DYNAMIC_HEIGHT, - align=TextBox.ALIGN_RIGHT, - text=right_text, - background_color=0xffff00, - color=0x000000, scale=1) +right_text_area = TextBox( + terminalio.FONT, + 190, + TextBox.DYNAMIC_HEIGHT, + align=TextBox.ALIGN_RIGHT, + text=right_text, + background_color=0xFFFF00, + color=0x000000, + scale=1, +) right_text_area.x = 10 right_text_area.y = center_text_area.y + center_text_area.height + 10 From c7b667b43d57419f6758d2f0d3f8c26f0170a760 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 16 Nov 2024 09:14:36 -0600 Subject: [PATCH 4/7] disable some lint checks --- adafruit_display_text/text_box.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_display_text/text_box.py b/adafruit_display_text/text_box.py index 9abe504..7a5294a 100644 --- a/adafruit_display_text/text_box.py +++ b/adafruit_display_text/text_box.py @@ -39,7 +39,7 @@ pass -# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-instance-attributes, duplicate-code class TextBox(bitmap_label.Label): """ TextBox has a constrained width and optionally height. @@ -120,7 +120,7 @@ def _place_text( # when copying glyph bitmaps (this is important for slanted text # where rectangular glyph boxes overlap) ) -> Tuple[int, int, int, int]: - # pylint: disable=too-many-arguments, too-many-locals + # pylint: disable=too-many-arguments, too-many-locals, too-many-statements, too-many-branches # placeText - Writes text into a bitmap at the specified location. # From 6d883bbdef7701fe87e95f19d11d59977b4d00b8 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 16 Nov 2024 09:20:53 -0600 Subject: [PATCH 5/7] add text_box to docs --- adafruit_display_text/text_box.py | 2 +- docs/api.rst | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/text_box.py b/adafruit_display_text/text_box.py index 7a5294a..b3bfb86 100644 --- a/adafruit_display_text/text_box.py +++ b/adafruit_display_text/text_box.py @@ -54,7 +54,7 @@ class TextBox(bitmap_label.Label): :param width: The width of the TextBox in pixels. :param height: The height of the TextBox in pixels. :param align: How to align the text within the box, - valid values are `ALIGN_LEFT`, `ALIGN_CENTER`, `ALIGN_RIGHT`. + valid values are ``ALIGN_LEFT``, ``ALIGN_CENTER``, ``ALIGN_RIGHT``. """ ALIGN_LEFT = const(0) diff --git a/docs/api.rst b/docs/api.rst index 00350e7..24335e0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -19,3 +19,6 @@ .. automodule:: adafruit_display_text.outlined_label :members: + +.. automodule:: adafruit_display_text.text_box + :members: \ No newline at end of file From eb6b29fde8368cc0c10e1f42901de2778d1fed19 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 17 Nov 2024 10:27:23 -0600 Subject: [PATCH 6/7] eol newline --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 24335e0..bb959a9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -21,4 +21,4 @@ :members: .. automodule:: adafruit_display_text.text_box - :members: \ No newline at end of file + :members: From 9a01b34c7c1744929799ff97821dad3ba2e1060e Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 17 Nov 2024 10:42:06 -0600 Subject: [PATCH 7/7] fix copyright and file declaration --- adafruit_display_text/text_box.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_display_text/text_box.py b/adafruit_display_text/text_box.py index b3bfb86..7f3d39d 100644 --- a/adafruit_display_text/text_box.py +++ b/adafruit_display_text/text_box.py @@ -1,15 +1,15 @@ -# SPDX-FileCopyrightText: 2020 Kevin Matocha +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries # # SPDX-License-Identifier: MIT """ -`adafruit_display_text.bitmap_label` +`adafruit_display_text.text_box` ================================================================================ Text graphics handling for CircuitPython, including text boxes -* Author(s): Kevin Matocha +* Author(s): Tim Cocks Implementation Notes --------------------