diff --git a/README.rst b/README.rst index 1ad30d4..d12824c 100644 --- a/README.rst +++ b/README.rst @@ -34,10 +34,12 @@ For a board with a built-in display. import board import terminalio - from adafruit_display_text import text_area + from adafruit_display_text import label text = "Hello world" - text_area = text_area.TextArea(terminalio.FONT, text=text, width=len(text)) + text_area = label.Label(terminalio.FONT, text=text) + text_area.x = 10 + text_area.y = 10 board.DISPLAY.show(text_area) diff --git a/adafruit_display_text/text_area.py b/adafruit_display_text/label.py similarity index 61% rename from adafruit_display_text/text_area.py rename to adafruit_display_text/label.py index e0f2fbc..841d0fc 100644 --- a/adafruit_display_text/text_area.py +++ b/adafruit_display_text/label.py @@ -20,10 +20,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ -`adafruit_display_text.text_area` +`adafruit_display_text.label` ==================================================== -Displays text using CircuitPython's displayio. +Displays text labels using CircuitPython's displayio. * Author(s): Scott Shawcroft @@ -44,21 +44,23 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Text.git" -class TextArea(displayio.Group): - """An area displaying a string of textself. +class Label(displayio.Group): + """A label displaying a string of text. 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 font: A font class that has ``get_bounding_box`` and ``get_glyph`` :param str text: Text to display - :param int width: Area width in characters - :param int height: Area height in characters + :param int max_glyphs: The largest quantity of glyphs we will display :param int color: Color of all text in RGB hex""" - def __init__(self, font, *, text=None, width=None, height=1, color=0xffffff): - if not width and not text: - raise RuntimeError("Please provide a width") - if not width: - width = len(text) - super().__init__(max_size=width * height) - self.width = width + def __init__(self, font, *, text=None, max_glyphs=None, color=0xffffff, **kwargs): + if not max_glyphs and not text: + raise RuntimeError("Please provide a max size, or initial text") + if not max_glyphs: + max_glyphs = len(text) + super().__init__(max_size=max_glyphs, **kwargs) + self.width = max_glyphs self.font = font self._text = None @@ -68,36 +70,47 @@ def __init__(self, font, *, text=None, width=None, height=1, color=0xffffff): bounds = self.font.get_bounding_box() self.height = bounds[1] + self._line_spacing = 1.25 + self._boundingbox = None if text: self._update_text(text) - def _update_text(self, new_text): + def _update_text(self, new_text): # pylint: disable=too-many-locals x = 0 y = 0 i = 0 old_c = 0 + y_offset = int((self.font.get_glyph(ord('M')).height - + new_text.count('\n') * self.height * self.line_spacing) / 2) + #print("y offset from baseline", y_offset) + left = right = top = bottom = 0 for character in new_text: if character == '\n': - y += int(self.height * 1.25) + y += int(self.height * self._line_spacing) x = 0 continue glyph = self.font.get_glyph(ord(character)) if not glyph: continue - position_y = y + self.height - glyph.height - glyph.dy + right = max(right, x+glyph.width) + if y == 0: # first line, find the Ascender height + top = min(top, -glyph.height+y_offset) + bottom = max(bottom, y-glyph.dy+y_offset) + position_y = y - glyph.height - glyph.dy + y_offset + position_x = x + glyph.dx if not self._text or old_c >= len(self._text) or character != self._text[old_c]: face = displayio.TileGrid(glyph.bitmap, pixel_shader=self.palette, default_tile=glyph.tile_index, tile_width=glyph.width, tile_height=glyph.height, - position=(x, position_y)) + position=(position_x, position_y)) if i < len(self): self[i] = face else: self.append(face) elif self._text and character == self._text[old_c]: - self[i].position = (x, position_y) + self[i].position = (position_x, position_y) x += glyph.shift_x @@ -112,6 +125,23 @@ def _update_text(self, new_text): while len(self) > i: self.pop() self._text = new_text + self._boundingbox = (left, top, left+right, bottom-top) + + @property + def bounding_box(self): + """An (x, y, w, h) tuple that completely covers all glyphs. The + first two numbers are offset from the x, y origin of this group""" + return tuple(self._boundingbox) + + @property + def line_spacing(self): + """The amount of space between lines of text, in multiples of the font's + bounding-box height. (E.g. 1.0 is the bounding-box height)""" + return self._line_spacing + + @line_spacing.setter + def line_spacing(self, spacing): + self._line_spacing = spacing @property def color(self): diff --git a/docs/api.rst b/docs/api.rst index e54848c..790fedf 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -4,5 +4,5 @@ .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) .. use this format as the module name: "adafruit_foo.foo" -.. automodule:: adafruit_display_text.text_area +.. automodule:: adafruit_display_text.label :members: diff --git a/examples/pyportal.py b/examples/pyportal.py index ecfa6fd..9c1b4f9 100644 --- a/examples/pyportal.py +++ b/examples/pyportal.py @@ -6,7 +6,7 @@ import displayio from adafruit_bitmap_font import bitmap_font -from adafruit_display_text.text_area import TextArea +from adafruit_display_text.label import Label backlight = pulseio.PWMOut(microcontroller.pin.PB21) #pylint: disable=no-member @@ -31,7 +31,7 @@ for demo_text in demos: for font in fonts: print("Font load {}".format(font.name)) - area = TextArea(font, text=demo_text) + area = Label(font, text=demo_text) area.y = y splash.append(area) diff --git a/examples/textarea_boundingbox.py b/examples/textarea_boundingbox.py new file mode 100644 index 0000000..97ddac1 --- /dev/null +++ b/examples/textarea_boundingbox.py @@ -0,0 +1,61 @@ +import os +import board +import displayio +from adafruit_display_text.label import Label +from adafruit_bitmap_font import bitmap_font + +# the current working directory (where this file is) +cwd = ("/"+__file__).rsplit('/', 1)[0] +fonts = [file for file in os.listdir(cwd+"/fonts/") + if (file.endswith(".bdf") and not file.startswith("._"))] +for i, filename in enumerate(fonts): + fonts[i] = cwd+"/fonts/"+filename +print(fonts) + +########################################################################## +THE_FONT = fonts[0] +DISPLAY_STRING = "A multi-line-\nexample of\n font bounding!" +WRAP_CHARS = 40 + +########################################################################## +# Make the display context +splash = displayio.Group() +board.DISPLAY.show(splash) + +# Make a background color fill +color_bitmap = displayio.Bitmap(320, 240, 1) +color_palette = displayio.Palette(1) +color_palette[0] = 0xFFFFFF +bg_sprite = displayio.TileGrid(color_bitmap, + pixel_shader=color_palette, + position=(0, 0)) +splash.append(bg_sprite) + +# Load the font +font = bitmap_font.load_font(THE_FONT) +font.load_glyphs(DISPLAY_STRING.encode('utf-8')) + +print(DISPLAY_STRING) + +text = Label(font, text=DISPLAY_STRING) +text.x = 20 +text.y = 100 +text.color = 0x0 + +# Make a background color fill +dims = text.bounding_box +print(dims) +textbg_bitmap = displayio.Bitmap(dims[2], dims[3], 1) +textbg_palette = displayio.Palette(1) +textbg_palette[0] = 0xFF0000 +textbg_sprite = displayio.TileGrid(textbg_bitmap, + pixel_shader=textbg_palette, + position=(text.x+dims[0], text.y+dims[1])) +splash.append(textbg_sprite) +splash.append(text) +board.DISPLAY.refresh_soon() +board.DISPLAY.wait_for_frame() + + +while True: + pass