Skip to content

Updated background color handling for label #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jun 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 93 additions & 28 deletions adafruit_display_text/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,19 @@ def __init__(
color=0xFFFFFF,
background_color=None,
line_spacing=1.25,
background_tight=False,
padding_top=0,
padding_bottom=0,
padding_left=0,
padding_right=0,
**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)
max_glyphs = len(text) + 1 # add one for the background bitmap tileGrid
super().__init__(max_size=max_glyphs, **kwargs)

self.width = max_glyphs
self._font = font
self._text = None
Expand All @@ -87,28 +93,94 @@ def __init__(
self.y = y

self.palette = displayio.Palette(2)
if background_color is not None:
self.palette[0] = background_color
self.palette.make_opaque(0)
self._transparent_background = False
else:
self.palette[0] = 0
self.palette.make_transparent(0)
self._transparent_background = True
self.palette[0] = 0
self.palette.make_transparent(0)
self.palette[1] = color

bounds = self._font.get_bounding_box()
self.height = bounds[1]
self.height = self._font.get_bounding_box()[1]
self._line_spacing = line_spacing
self._boundingbox = None

self._background_tight = (
background_tight # sets padding status for text background box
)

self._background_color = background_color
self._background_palette = displayio.Palette(1)
self.append(
displayio.TileGrid(
displayio.Bitmap(0, 0, 1), pixel_shader=self._background_palette
)
) # initialize with a blank tilegrid placeholder for background

self._padding_top = padding_top
self._padding_bottom = padding_bottom
self._padding_left = padding_left
self._padding_right = padding_right

if text is not None:
self._update_text(str(text))

def _create_background_box(self, lines, y_offset):

left = self._boundingbox[0]

if self._background_tight: # draw a tight bounding box
box_width = self._boundingbox[2]
box_height = self._boundingbox[3]
x_box_offset = 0
y_box_offset = self._boundingbox[1]

else: # draw a "loose" bounding box to include any ascenders/descenders.

# check a few glyphs for maximum ascender and descender height
# Enhancement: it would be preferred to access the font "FONT_ASCENT" and
# "FONT_DESCENT" in the imported BDF file
glyphs = "M j'" # choose glyphs with highest ascender and lowest
# descender, will depend upon font used
ascender_max = descender_max = 0
for char in glyphs:
this_glyph = self._font.get_glyph(ord(char))
ascender_max = max(ascender_max, this_glyph.height + this_glyph.dy)
descender_max = max(descender_max, -this_glyph.dy)

box_width = self._boundingbox[2] + self._padding_left + self._padding_right
x_box_offset = -self._padding_left
box_height = (
(ascender_max + descender_max)
+ int((lines - 1) * self.height * self._line_spacing)
+ self._padding_top
+ self._padding_bottom
)
y_box_offset = -ascender_max + y_offset - self._padding_top

self._update_background_color(self._background_color)
box_width = max(0, box_width) # remove any negative values
box_height = max(0, box_height) # remove any negative values

background_bitmap = displayio.Bitmap(box_width, box_height, 1)
tile_grid = displayio.TileGrid(
background_bitmap,
pixel_shader=self._background_palette,
x=left + x_box_offset,
y=y_box_offset,
)

return tile_grid

def _update_background_color(self, new_color):

if new_color is None:
self._background_palette.make_transparent(0)
else:
self._background_palette.make_opaque(0)
self._background_palette[0] = new_color
self._background_color = new_color

def _update_text(self, new_text): # pylint: disable=too-many-locals
x = 0
y = 0
i = 0
i = 1
old_c = 0
y_offset = int(
(
Expand All @@ -118,17 +190,19 @@ def _update_text(self, new_text): # pylint: disable=too-many-locals
/ 2
)
left = right = top = bottom = 0
lines = 1
for character in new_text:
if character == "\n":
y += int(self.height * self._line_spacing)
x = 0
lines += 1
continue
glyph = self._font.get_glyph(ord(character))
if not glyph:
continue
right = max(right, x + glyph.width + glyph.shift_x)
right = max(right, x + glyph.shift_x)
if y == 0: # first line, find the Ascender height
top = min(top, -glyph.height + y_offset)
top = min(top, -glyph.height - glyph.dy + y_offset)
bottom = max(bottom, y - glyph.dy + y_offset)
position_y = y - glyph.height - glyph.dy + y_offset
position_x = x + glyph.dx
Expand Down Expand Up @@ -161,14 +235,14 @@ def _update_text(self, new_text): # pylint: disable=too-many-locals
else:
self.append(face)
elif self._text and character == self._text[old_c]:

try:
self[i].position = (position_x, position_y)
except AttributeError:
self[i].x = position_x
self[i].y = position_y

x += glyph.shift_x

# TODO skip this for control sequences or non-printables.
i += 1
old_c += 1
Expand All @@ -187,6 +261,7 @@ def _update_text(self, new_text): # pylint: disable=too-many-locals
self.pop()
self._text = new_text
self._boundingbox = (left, top, left + right, bottom - top)
self[0] = self._create_background_box(lines, y_offset)

@property
def bounding_box(self):
Expand Down Expand Up @@ -216,20 +291,11 @@ def color(self, new_color):
@property
def background_color(self):
"""Color of the background as an RGB hex number."""
if not self._transparent_background:
return self.palette[0]
return None
return self._background_color

@background_color.setter
def background_color(self, new_color):
if new_color is not None:
self.palette[0] = new_color
self.palette.make_opaque(0)
self._transparent_background = False
else:
self.palette[0] = 0
self.palette.make_transparent(0)
self._transparent_background = True
self._update_background_color(new_color)

@property
def text(self):
Expand All @@ -253,8 +319,7 @@ def font(self, new_font):
current_anchored_position = self.anchored_position
self._text = ""
self._font = new_font
bounds = self._font.get_bounding_box()
self.height = bounds[1]
self.height = self._font.get_bounding_box()[1]
self._update_text(str(old_text))
self.anchored_position = current_anchored_position

Expand Down
125 changes: 125 additions & 0 deletions examples/display_text_background_color_padding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""
This examples shows the use color and background_color
"""
import time
import board
import displayio


# from adafruit_st7789 import ST7789
from adafruit_ili9341 import ILI9341
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font

# Setup the SPI display

print("Starting the display...") # goes to serial only
displayio.release_displays()


spi = board.SPI()
tft_cs = board.D9 # arbitrary, pin not used
tft_dc = board.D10
tft_backlight = board.D12
tft_reset = board.D11

while not spi.try_lock():
spi.configure(baudrate=32000000)
spi.unlock()

display_bus = displayio.FourWire(
spi,
command=tft_dc,
chip_select=tft_cs,
reset=tft_reset,
baudrate=32000000,
polarity=1,
phase=1,
)

print("spi.frequency: {}".format(spi.frequency))

DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240

# display = ST7789(display_bus, width=240, height=240, rotation=0, rowstart=80, colstart=0)
display = ILI9341(
display_bus,
width=DISPLAY_WIDTH,
height=DISPLAY_HEIGHT,
rotation=180,
auto_refresh=True,
)

display.show(None)

# font=terminalio.FONT # this is the Builtin fixed dimension font

font = bitmap_font.load_font("fonts/BitstreamVeraSans-Roman-24.bdf")


text = []
text.append("none") # no ascenders or descenders
text.append("pop quops") # only descenders
text.append("MONSTERs are tall") # only ascenders
text.append("MONSTERs ate pop quops") # both ascenders and descenders
text.append("MONSTER quops\nnewline quops") # with newline

display.auto_refresh = True
myGroup = displayio.Group(max_size=6)
display.show(myGroup)

text_area = []
myPadding = 4

for i, thisText in enumerate(text):
text_area.append(
label.Label(
font,
text=thisText,
color=0xFFFFFF,
background_color=None,
background_tight=False,
padding_top=myPadding,
padding_bottom=myPadding,
padding_left=myPadding,
padding_right=myPadding,
)
)

this_x = 10
this_y = 10 + i * 40
text_area[i].x = 10
text_area[i].y = 3 + i * 50
text_area[i].anchor_point = (0, 0)
text_area[i].anchored_position = (this_x, this_y)
myGroup.append(text_area[i])

print("background color is {}".format(text_area[0].background_color))


while True:
time.sleep(2)
text_area[0].text = "text" # change some text in an existing text box
# Note: changed text must fit within existing number of characters
# when the Label was created

for area in text_area:
area.background_color = 0xFF0000
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = 0x000088
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = 0x00FF00
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = 0xFF0000
print("background color is {:06x}".format(text_area[0].background_color))
time.sleep(2)
for area in text_area:
area.background_color = None
print("background color is {}".format(text_area[0].background_color))