5151ACCENT_END = const (1 )
5252ACCENT_FG = const (2 )
5353ACCENT_BG = const (3 )
54+ ACCENT_TYPE = const (4 )
5455
5556
5657class Label (LabelBase ):
@@ -191,8 +192,7 @@ def __init__(
191192 # outline handling vars
192193 self ._outline_size = outline_size
193194 self ._outline_color = outline_color
194- if self ._outline_color is not None :
195- self ._init_outline_stamp (outline_size )
195+ self ._init_outline_stamp (outline_size )
196196
197197 self ._save_text = save_text
198198 self ._text = self ._replace_tabs (self ._text )
@@ -290,6 +290,10 @@ def _reset_text(
290290 box_x = box_x + self ._padding_left + self ._padding_right
291291 box_y = box_y + self ._padding_top + self ._padding_bottom
292292
293+ if self ._outline_color is not None :
294+ box_x += self ._outline_size * 2
295+ box_y += self ._outline_size * 2
296+
293297 # Create the Bitmap unless it can be reused
294298 new_bitmap = None
295299 if self ._bitmap is None or self ._bitmap .width != box_x or self ._bitmap .height != box_y :
@@ -487,7 +491,9 @@ def _place_text(
487491 my_glyph = font .get_glyph (ord (char ))
488492 if self ._tmp_glyph_bitmap is None and len (self ._accent_ranges ) > 0 :
489493 self ._tmp_glyph_bitmap = displayio .Bitmap (
490- my_glyph .bitmap .width , my_glyph .bitmap .height , len (self ._palette )
494+ my_glyph .width + self .outline_size * 2 ,
495+ my_glyph .height + self .outline_size * 2 ,
496+ len (self ._palette ),
491497 )
492498
493499 if my_glyph is None : # Error checking: no glyph found
@@ -529,58 +535,110 @@ def _place_text(
529535 print (f'Warning: Glyph clipped, exceeds descent property: "{ char } "' )
530536
531537 accented = False
538+ accent_type = "foreground_background"
532539 if len (self ._accent_ranges ) > 0 :
533540 for accent_range in self ._accent_ranges :
534- if accent_range [ACCENT_START ] <= char_idx < accent_range [ACCENT_END ]:
535- self ._tmp_glyph_bitmap .fill (accent_range [ACCENT_BG ])
536-
537- bitmaptools .blit (
538- self ._tmp_glyph_bitmap ,
539- my_glyph .bitmap ,
540- 0 ,
541- 0 ,
542- skip_source_index = 0 ,
543- )
544- bitmaptools .replace_color (
545- self ._tmp_glyph_bitmap , 1 , accent_range [ACCENT_FG ]
546- )
547- accented = True
541+ if (
542+ accent_range [ACCENT_START ]
543+ <= (self .current_index + char_idx ) % len (self ._full_text )
544+ < accent_range [ACCENT_END ]
545+ ):
546+ accent_type = accent_range [ACCENT_TYPE ]
547+ if accent_range [ACCENT_TYPE ] == "foreground_background" :
548+ self ._tmp_glyph_bitmap .fill (accent_range [ACCENT_BG ])
549+
550+ bitmaptools .blit (
551+ self ._tmp_glyph_bitmap ,
552+ my_glyph .bitmap ,
553+ 0 ,
554+ 0 ,
555+ x1 = glyph_offset_x ,
556+ y1 = y_clip ,
557+ x2 = glyph_offset_x + my_glyph .width ,
558+ y2 = my_glyph .height ,
559+ skip_source_index = 0 ,
560+ )
561+ bitmaptools .replace_color (
562+ self ._tmp_glyph_bitmap , 1 , accent_range [ACCENT_FG ]
563+ )
564+ accented = True
565+ elif accent_range [ACCENT_TYPE ] == "outline" :
566+ self ._tmp_glyph_bitmap .fill (0 )
567+ bitmaptools .blit (
568+ self ._tmp_glyph_bitmap ,
569+ my_glyph .bitmap ,
570+ self ._outline_size ,
571+ self ._outline_size ,
572+ x1 = glyph_offset_x ,
573+ y1 = y_clip ,
574+ x2 = glyph_offset_x + my_glyph .width ,
575+ y2 = my_glyph .height ,
576+ skip_source_index = 0 ,
577+ )
578+ self ._add_outline (self ._tmp_glyph_bitmap )
579+ bitmaptools .replace_color (
580+ self ._tmp_glyph_bitmap , 1 , accent_range [ACCENT_FG ]
581+ )
582+ bitmaptools .replace_color (
583+ self ._tmp_glyph_bitmap , 2 , accent_range [ACCENT_BG ]
584+ )
585+ accented = True
586+
587+ # only one accent range can effect a given character
548588 break
549589
550- self ._blit (
551- bitmap ,
552- max (xposition + my_glyph .dx , 0 ),
553- y_blit_target ,
554- my_glyph .bitmap if not accented else self ._tmp_glyph_bitmap ,
555- x_1 = glyph_offset_x ,
556- y_1 = y_clip ,
557- x_2 = glyph_offset_x + my_glyph .width ,
558- y_2 = my_glyph .height ,
559- skip_index = skip_index
560- if not accented
561- else None , # do not copy over any 0 background pixels if not accented
562- )
563-
564- xposition = xposition + my_glyph .shift_x
565-
566- self ._add_outline ()
590+ if accented :
591+ bitmaptools .blit (
592+ bitmap ,
593+ self ._tmp_glyph_bitmap ,
594+ max (xposition + my_glyph .dx , 0 ),
595+ y_blit_target ,
596+ )
597+ else :
598+ try :
599+ self ._blit (
600+ bitmap ,
601+ max (xposition + my_glyph .dx , 0 ),
602+ y_blit_target ,
603+ my_glyph .bitmap if not accented else self ._tmp_glyph_bitmap ,
604+ x_1 = glyph_offset_x ,
605+ y_1 = y_clip ,
606+ x_2 = glyph_offset_x + my_glyph .width ,
607+ y_2 = my_glyph .height ,
608+ skip_index = skip_index
609+ if not accented
610+ else None , # do not copy any 0 background pixels if not accented
611+ )
612+ except ValueError :
613+ # It's possible to overshoot the width of the bitmap if max_characters
614+ # is enabled and outline is used on at least some of the text.
615+ # In this case just skip any characters that fall outside the
616+ # max_characters box size without accounting for outline size.
617+ pass
618+
619+ if accented and accent_type == "outline" :
620+ xposition = xposition + my_glyph .shift_x + self ._outline_size
621+ else :
622+ xposition = xposition + my_glyph .shift_x
623+
624+ self ._add_outline (self .bitmap )
567625 # bounding_box
568626 return left , top , right - left , bottom - top
569627
570- def _add_outline (self ):
628+ def _add_outline (self , bitmap ):
571629 """
572630 Blit the outline into the labels Bitmap. Will blit self._stamp_source for each
573631 pixel of the foreground color but skip the foreground color when we blit,
574632 creating an outline.
575633 :return: None
576634 """
577- if self ._outline_color is not None :
578- for y in range (self . bitmap .height ):
579- for x in range (self . bitmap .width ):
580- if self . bitmap [x , y ] == 1 :
635+ if bitmap is not self . bitmap or self ._outline_color is not None :
636+ for y in range (bitmap .height ):
637+ for x in range (bitmap .width ):
638+ if bitmap [x , y ] == 1 :
581639 try :
582640 bitmaptools .blit (
583- self . bitmap ,
641+ bitmap ,
584642 self ._stamp_source ,
585643 x - self ._outline_size ,
586644 y - self ._outline_size ,
@@ -855,7 +913,9 @@ def outline_size(self, new_outline_size):
855913 scale = self .scale ,
856914 )
857915
858- def add_accent_range (self , start , end , foreground_color , background_color ):
916+ def add_accent_range (
917+ self , start , end , foreground_color , background_color , accent_type = "foreground_background"
918+ ):
859919 """
860920 Set a range of text to get accented with the specified colors.
861921
@@ -865,9 +925,12 @@ def add_accent_range(self, start, end, foreground_color, background_color):
865925 the accent foreground color.
866926 :param background_color: The color index within ``color_palette`` to use for
867927 the accent background color.
928+ :param accent_type: The type of accent to use, either "foreground_background" or "outline"
868929 :return: None
869930 """
870- self ._accent_ranges .append ((start , end , foreground_color , background_color ))
931+ if accent_type not in {"foreground_background" , "outline" }:
932+ raise ValueError ("accent_type must be either 'foreground_background' or 'outline'" )
933+ self ._accent_ranges .append ((start , end , foreground_color , background_color , accent_type ))
871934 self ._reset_text (text = str (self ._text ))
872935
873936 def remove_accent_range (self , start ):
@@ -882,7 +945,14 @@ def remove_accent_range(self, start):
882945 self ._accent_ranges .remove (accent_range )
883946 self ._reset_text (text = str (self ._text ))
884947
885- def add_accent_to_substring (self , substring , foreground_color , background_color , start = 0 ):
948+ def add_accent_to_substring (
949+ self ,
950+ substring ,
951+ foreground_color ,
952+ background_color ,
953+ accent_type = "foreground_background" ,
954+ start = 0 ,
955+ ):
886956 """
887957 Add accent to the first occurrence of ``substring`` found in the labels text,
888958 starting from ``start``.
@@ -892,14 +962,18 @@ def add_accent_to_substring(self, substring, foreground_color, background_color,
892962 the accent foreground color.
893963 :param background_color: The color index within ``color_palette`` to use for
894964 the accent background color.
965+ :param accent_type: The type of accent to use, either "foreground_background" or "outline"
895966 :param start: The index within text to start searching for the substring.
896967 Defaults is 0 to search the whole text.
897968 :return: True if the substring was found, False otherwise.
898969 """
899-
900- index = self ._text .find (substring , start )
970+ if accent_type not in {"foreground_background" , "outline" }:
971+ raise ValueError ("accent_type must be either 'foreground_background' or 'outline'" )
972+ index = self ._full_text .find (substring , start )
901973 if index != - 1 :
902- self .add_accent_range (index , index + len (substring ), foreground_color , background_color )
974+ self .add_accent_range (
975+ index , index + len (substring ), foreground_color , background_color , accent_type
976+ )
903977 return True
904978 else :
905979 return False
@@ -915,7 +989,7 @@ def remove_accent_from_substring(self, substring, start=0):
915989 :return: True if the substring was found, False otherwise.
916990 """
917991
918- index = self ._text .find (substring , start )
992+ index = self ._full_text .find (substring , start )
919993 if index != - 1 :
920994 self .remove_accent_range (index )
921995 return True
0 commit comments