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 -------------------- pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy