@@ -72,13 +72,19 @@ def __init__(
7272 color = 0xFFFFFF ,
7373 background_color = None ,
7474 line_spacing = 1.25 ,
75+ background_tight = False ,
76+ padding_top = 0 ,
77+ padding_bottom = 0 ,
78+ padding_left = 0 ,
79+ padding_right = 0 ,
7580 ** kwargs
7681 ):
7782 if not max_glyphs and not text :
7883 raise RuntimeError ("Please provide a max size, or initial text" )
7984 if not max_glyphs :
80- max_glyphs = len (text )
85+ max_glyphs = len (text ) + 1 # add one for the background bitmap tileGrid
8186 super ().__init__ (max_size = max_glyphs , ** kwargs )
87+
8288 self .width = max_glyphs
8389 self ._font = font
8490 self ._text = None
@@ -87,28 +93,94 @@ def __init__(
8793 self .y = y
8894
8995 self .palette = displayio .Palette (2 )
90- if background_color is not None :
91- self .palette [0 ] = background_color
92- self .palette .make_opaque (0 )
93- self ._transparent_background = False
94- else :
95- self .palette [0 ] = 0
96- self .palette .make_transparent (0 )
97- self ._transparent_background = True
96+ self .palette [0 ] = 0
97+ self .palette .make_transparent (0 )
9898 self .palette [1 ] = color
9999
100- bounds = self ._font .get_bounding_box ()
101- self .height = bounds [1 ]
100+ self .height = self ._font .get_bounding_box ()[1 ]
102101 self ._line_spacing = line_spacing
103102 self ._boundingbox = None
104103
104+ self ._background_tight = (
105+ background_tight # sets padding status for text background box
106+ )
107+
108+ self ._background_color = background_color
109+ self ._background_palette = displayio .Palette (1 )
110+ self .append (
111+ displayio .TileGrid (
112+ displayio .Bitmap (0 , 0 , 1 ), pixel_shader = self ._background_palette
113+ )
114+ ) # initialize with a blank tilegrid placeholder for background
115+
116+ self ._padding_top = padding_top
117+ self ._padding_bottom = padding_bottom
118+ self ._padding_left = padding_left
119+ self ._padding_right = padding_right
120+
105121 if text is not None :
106122 self ._update_text (str (text ))
107123
124+ def _create_background_box (self , lines , y_offset ):
125+
126+ left = self ._boundingbox [0 ]
127+
128+ if self ._background_tight : # draw a tight bounding box
129+ box_width = self ._boundingbox [2 ]
130+ box_height = self ._boundingbox [3 ]
131+ x_box_offset = 0
132+ y_box_offset = self ._boundingbox [1 ]
133+
134+ else : # draw a "loose" bounding box to include any ascenders/descenders.
135+
136+ # check a few glyphs for maximum ascender and descender height
137+ # Enhancement: it would be preferred to access the font "FONT_ASCENT" and
138+ # "FONT_DESCENT" in the imported BDF file
139+ glyphs = "M j'" # choose glyphs with highest ascender and lowest
140+ # descender, will depend upon font used
141+ ascender_max = descender_max = 0
142+ for char in glyphs :
143+ this_glyph = self ._font .get_glyph (ord (char ))
144+ ascender_max = max (ascender_max , this_glyph .height + this_glyph .dy )
145+ descender_max = max (descender_max , - this_glyph .dy )
146+
147+ box_width = self ._boundingbox [2 ] + self ._padding_left + self ._padding_right
148+ x_box_offset = - self ._padding_left
149+ box_height = (
150+ (ascender_max + descender_max )
151+ + int ((lines - 1 ) * self .height * self ._line_spacing )
152+ + self ._padding_top
153+ + self ._padding_bottom
154+ )
155+ y_box_offset = - ascender_max + y_offset - self ._padding_top
156+
157+ self ._update_background_color (self ._background_color )
158+ box_width = max (0 , box_width ) # remove any negative values
159+ box_height = max (0 , box_height ) # remove any negative values
160+
161+ background_bitmap = displayio .Bitmap (box_width , box_height , 1 )
162+ tile_grid = displayio .TileGrid (
163+ background_bitmap ,
164+ pixel_shader = self ._background_palette ,
165+ x = left + x_box_offset ,
166+ y = y_box_offset ,
167+ )
168+
169+ return tile_grid
170+
171+ def _update_background_color (self , new_color ):
172+
173+ if new_color is None :
174+ self ._background_palette .make_transparent (0 )
175+ else :
176+ self ._background_palette .make_opaque (0 )
177+ self ._background_palette [0 ] = new_color
178+ self ._background_color = new_color
179+
108180 def _update_text (self , new_text ): # pylint: disable=too-many-locals
109181 x = 0
110182 y = 0
111- i = 0
183+ i = 1
112184 old_c = 0
113185 y_offset = int (
114186 (
@@ -118,17 +190,19 @@ def _update_text(self, new_text): # pylint: disable=too-many-locals
118190 / 2
119191 )
120192 left = right = top = bottom = 0
193+ lines = 1
121194 for character in new_text :
122195 if character == "\n " :
123196 y += int (self .height * self ._line_spacing )
124197 x = 0
198+ lines += 1
125199 continue
126200 glyph = self ._font .get_glyph (ord (character ))
127201 if not glyph :
128202 continue
129- right = max (right , x + glyph .width + glyph . shift_x )
203+ right = max (right , x + glyph .shift_x )
130204 if y == 0 : # first line, find the Ascender height
131- top = min (top , - glyph .height + y_offset )
205+ top = min (top , - glyph .height - glyph . dy + y_offset )
132206 bottom = max (bottom , y - glyph .dy + y_offset )
133207 position_y = y - glyph .height - glyph .dy + y_offset
134208 position_x = x + glyph .dx
@@ -161,14 +235,14 @@ def _update_text(self, new_text): # pylint: disable=too-many-locals
161235 else :
162236 self .append (face )
163237 elif self ._text and character == self ._text [old_c ]:
238+
164239 try :
165240 self [i ].position = (position_x , position_y )
166241 except AttributeError :
167242 self [i ].x = position_x
168243 self [i ].y = position_y
169244
170245 x += glyph .shift_x
171-
172246 # TODO skip this for control sequences or non-printables.
173247 i += 1
174248 old_c += 1
@@ -187,6 +261,7 @@ def _update_text(self, new_text): # pylint: disable=too-many-locals
187261 self .pop ()
188262 self ._text = new_text
189263 self ._boundingbox = (left , top , left + right , bottom - top )
264+ self [0 ] = self ._create_background_box (lines , y_offset )
190265
191266 @property
192267 def bounding_box (self ):
@@ -216,20 +291,11 @@ def color(self, new_color):
216291 @property
217292 def background_color (self ):
218293 """Color of the background as an RGB hex number."""
219- if not self ._transparent_background :
220- return self .palette [0 ]
221- return None
294+ return self ._background_color
222295
223296 @background_color .setter
224297 def background_color (self , new_color ):
225- if new_color is not None :
226- self .palette [0 ] = new_color
227- self .palette .make_opaque (0 )
228- self ._transparent_background = False
229- else :
230- self .palette [0 ] = 0
231- self .palette .make_transparent (0 )
232- self ._transparent_background = True
298+ self ._update_background_color (new_color )
233299
234300 @property
235301 def text (self ):
@@ -256,8 +322,7 @@ def font(self, new_font):
256322 current_anchored_position = self .anchored_position
257323 self ._text = ""
258324 self ._font = new_font
259- bounds = self ._font .get_bounding_box ()
260- self .height = bounds [1 ]
325+ self .height = self ._font .get_bounding_box ()[1 ]
261326 self ._update_text (str (old_text ))
262327 self .anchored_position = current_anchored_position
263328
0 commit comments