@@ -66,7 +66,10 @@ class Label(LabelBase):
66
66
This is helpful when two or more labels need to be aligned to the same baseline
67
67
:param (int,str) tab_replacement: tuple with tab character replace information. When
68
68
(4, " ") will indicate a tab replacement of 4 spaces, defaults to 4 spaces by
69
- tab character"""
69
+ tab character
70
+ :param str label_direction: string defining the label text orientation. There are 5
71
+ configurations possibles ``LTR``-Left-To-Right ``RTL``-Right-To-Left
72
+ ``TTB``-Top-To-Bottom ``UPR``-Upwards ``DWR``-Downwards. It defaults to ``LTR``"""
70
73
71
74
# pylint: disable=too-many-instance-attributes, too-many-locals
72
75
# This has a lot of getters/setters, maybe it needs cleanup.
@@ -122,6 +125,7 @@ def __init__(self, font, **kwargs) -> None:
122
125
self ._padding_left = kwargs .get ("padding_left" , 0 )
123
126
self ._padding_right = kwargs .get ("padding_right" , 0 )
124
127
self .base_alignment = kwargs .get ("base_alignment" , False )
128
+ self ._label_direction = kwargs .get ("label_direction" , "LTR" )
125
129
126
130
if text is not None :
127
131
self ._update_text (str (text ))
@@ -136,7 +140,6 @@ def _create_background_box(self, lines: int, y_offset: int) -> None:
136
140
:param y_offset: int y pixel bottom coordinate for the background_box"""
137
141
138
142
left = self ._bounding_box [0 ]
139
-
140
143
if self ._background_tight : # draw a tight bounding box
141
144
box_width = self ._bounding_box [2 ]
142
145
box_height = self ._bounding_box [3 ]
@@ -146,14 +149,33 @@ def _create_background_box(self, lines: int, y_offset: int) -> None:
146
149
else : # draw a "loose" bounding box to include any ascenders/descenders.
147
150
ascent , descent = self ._get_ascent_descent ()
148
151
149
- box_width = self ._bounding_box [2 ] + self ._padding_left + self ._padding_right
150
- x_box_offset = - self ._padding_left
151
- box_height = (
152
- (ascent + descent )
153
- + int ((lines - 1 ) * self .height * self ._line_spacing )
154
- + self ._padding_top
155
- + self ._padding_bottom
156
- )
152
+ if (
153
+ self ._label_direction == "UPR"
154
+ or self ._label_direction == "DWR"
155
+ or self ._label_direction == "TTB"
156
+ ):
157
+ box_height = (
158
+ self ._bounding_box [3 ] + self ._padding_top + self ._padding_bottom
159
+ )
160
+ x_box_offset = - self ._padding_bottom
161
+ box_width = (
162
+ (ascent + descent )
163
+ + int ((lines - 1 ) * self .width * self ._line_spacing )
164
+ + self ._padding_left
165
+ + self ._padding_right
166
+ )
167
+ else :
168
+ box_width = (
169
+ self ._bounding_box [2 ] + self ._padding_left + self ._padding_right
170
+ )
171
+ x_box_offset = - self ._padding_left
172
+ box_height = (
173
+ (ascent + descent )
174
+ + int ((lines - 1 ) * self .height * self ._line_spacing )
175
+ + self ._padding_top
176
+ + self ._padding_bottom
177
+ )
178
+
157
179
if self .base_alignment :
158
180
y_box_offset = - ascent - self ._padding_top
159
181
else :
@@ -162,12 +184,25 @@ def _create_background_box(self, lines: int, y_offset: int) -> None:
162
184
box_width = max (0 , box_width ) # remove any negative values
163
185
box_height = max (0 , box_height ) # remove any negative values
164
186
187
+ if self ._label_direction == "UPR" :
188
+ movx = left + x_box_offset
189
+ movy = - box_height - x_box_offset
190
+ elif self ._label_direction == "DWR" :
191
+ movx = left + x_box_offset
192
+ movy = x_box_offset
193
+ elif self ._label_direction == "TTB" :
194
+ movx = left + x_box_offset
195
+ movy = x_box_offset
196
+ else :
197
+ movx = left + x_box_offset
198
+ movy = y_box_offset
199
+
165
200
background_bitmap = displayio .Bitmap (box_width , box_height , 1 )
166
201
tile_grid = displayio .TileGrid (
167
202
background_bitmap ,
168
203
pixel_shader = self ._background_palette ,
169
- x = left + x_box_offset ,
170
- y = y_box_offset ,
204
+ x = movx ,
205
+ y = movy ,
171
206
)
172
207
173
208
return tile_grid
@@ -222,7 +257,7 @@ def _update_background_color(self, new_color: int) -> None:
222
257
self ._bounding_box [3 ] + self ._padding_top + self ._padding_bottom > 0
223
258
)
224
259
):
225
- self .local_group [0 ] = self ._create_background_box (lines , y_offset )
260
+ self .local_group [0 ] = self ._create_background_box (lines , self . _y_offset )
226
261
else : # delete the existing bitmap
227
262
self .local_group .pop (0 )
228
263
self ._added_background_tilegrid = False
@@ -243,8 +278,15 @@ def _update_text(
243
278
else :
244
279
self ._y_offset = self ._get_ascent () // 2
245
280
246
- right = top = bottom = 0
247
- left = None
281
+ if self ._label_direction == "RTL" :
282
+ left = top = bottom = 0
283
+ right = None
284
+ elif self ._label_direction == "LTR" :
285
+ right = top = bottom = 0
286
+ left = None
287
+ else :
288
+ top = right = left = 0
289
+ bottom = 0
248
290
249
291
for character in new_text :
250
292
if character == "\n " :
@@ -254,17 +296,74 @@ def _update_text(
254
296
glyph = self ._font .get_glyph (ord (character ))
255
297
if not glyph :
256
298
continue
257
- right = max (right , x + glyph .shift_x , x + glyph .width + glyph .dx )
258
- if x == 0 :
259
- if left is None :
260
- left = glyph .dx
299
+
300
+ if self ._label_direction == "LTR" or self ._label_direction == "RTL" :
301
+ bottom = max (bottom , y - glyph .dy + self ._y_offset )
302
+ if y == 0 : # first line, find the Ascender height
303
+ top = min (top , - glyph .height - glyph .dy + self ._y_offset )
304
+ position_y = y - glyph .height - glyph .dy + self ._y_offset
305
+
306
+ if self ._label_direction == "LTR" :
307
+ right = max (right , x + glyph .shift_x , x + glyph .width + glyph .dx )
308
+ if x == 0 :
309
+ if left is None :
310
+ left = glyph .dx
311
+ else :
312
+ left = min (left , glyph .dx )
313
+ position_x = x + glyph .dx
261
314
else :
262
- left = min (left , glyph .dx )
263
- if y == 0 : # first line, find the Ascender height
264
- top = min (top , - glyph .height - glyph .dy + self ._y_offset )
265
- bottom = max (bottom , y - glyph .dy + self ._y_offset )
266
- position_y = y - glyph .height - glyph .dy + self ._y_offset
267
- position_x = x + glyph .dx
315
+ left = max (
316
+ left , abs (x ) + glyph .shift_x , abs (x ) + glyph .width + glyph .dx
317
+ )
318
+ if x == 0 :
319
+ if right is None :
320
+ right = glyph .dx
321
+ else :
322
+ right = max (right , glyph .dx )
323
+ position_x = x - glyph .width
324
+
325
+ if self ._label_direction == "TTB" :
326
+ if x == 0 :
327
+ if left is None :
328
+ left = glyph .dx
329
+ else :
330
+ left = min (left , glyph .dx )
331
+ if y == 0 :
332
+ top = min (top , - glyph .dy )
333
+
334
+ bottom = max (bottom , y + glyph .height , y + glyph .height + glyph .dy )
335
+ right = max (
336
+ right , x + glyph .width + glyph .dx , x + glyph .shift_x + glyph .dx
337
+ )
338
+ position_y = y + glyph .dy
339
+ position_x = x - glyph .width // 2 + self ._y_offset
340
+
341
+ if self ._label_direction == "UPR" :
342
+ if x == 0 :
343
+ if bottom is None :
344
+ bottom = - glyph .dx
345
+
346
+ if y == 0 : # first line, find the Ascender height
347
+ bottom = min (bottom , - glyph .dy )
348
+ left = min (left , x - glyph .height + self ._y_offset )
349
+ top = min (top , y - glyph .width - glyph .dx , y - glyph .shift_x )
350
+ right = max (right , x + glyph .height , x + glyph .height - glyph .dy )
351
+ position_y = y - glyph .width - glyph .dx
352
+ position_x = x - glyph .height - glyph .dy + self ._y_offset
353
+
354
+ if self ._label_direction == "DWR" :
355
+ if y == 0 :
356
+ if top is None :
357
+ top = - glyph .dx
358
+ top = min (top , - glyph .dx )
359
+ if x == 0 :
360
+ left = min (left , - glyph .dy )
361
+ left = min (left , x , x - glyph .dy - self ._y_offset )
362
+ bottom = max (bottom , y + glyph .width + glyph .dx , y + glyph .shift_x )
363
+ right = max (right , x + glyph .height )
364
+ position_y = y + glyph .dx
365
+ position_x = x + glyph .dy - self ._y_offset
366
+
268
367
if glyph .width > 0 and glyph .height > 0 :
269
368
try :
270
369
# pylint: disable=unexpected-keyword-arg
@@ -286,22 +385,58 @@ def _update_text(
286
385
x = position_x ,
287
386
y = position_y ,
288
387
)
388
+
389
+ if self ._label_direction == "UPR" :
390
+ face .transpose_xy = True
391
+ face .flip_x = True
392
+ if self ._label_direction == "DWR" :
393
+ face .transpose_xy = True
394
+ face .flip_y = True
395
+
289
396
if tilegrid_count < len (self .local_group ):
290
397
self .local_group [tilegrid_count ] = face
291
398
else :
292
399
self .local_group .append (face )
293
400
tilegrid_count += 1
294
- x += glyph .shift_x
401
+
402
+ if self ._label_direction == "RTL" :
403
+ x = x - glyph .shift_x
404
+ if self ._label_direction == "TTB" :
405
+ if glyph .height < 2 :
406
+ y = y + glyph .shift_x
407
+ else :
408
+ y = y + glyph .height + 1
409
+ if self ._label_direction == "UPR" :
410
+ y = y - glyph .shift_x
411
+ if self ._label_direction == "DWR" :
412
+ y = y + glyph .shift_x
413
+ if self ._label_direction == "LTR" :
414
+ x = x + glyph .shift_x
415
+
295
416
i += 1
296
- # Remove the rest
297
417
298
- if left is None :
418
+ if self . _label_direction == "LTR" and left is None :
299
419
left = 0
420
+ if self ._label_direction == "RTL" and right is None :
421
+ right = 0
422
+ if self ._label_direction == "TTB" and top is None :
423
+ top = 0
300
424
301
425
while len (self .local_group ) > tilegrid_count : # i:
302
426
self .local_group .pop ()
427
+ # pylint: disable=invalid-unary-operand-type
428
+ if self ._label_direction == "RTL" :
429
+ self ._bounding_box = (- left , top , left - right , bottom - top )
430
+ if self ._label_direction == "TTB" :
431
+ self ._bounding_box = (left , top , right - left , bottom - top )
432
+ if self ._label_direction == "UPR" :
433
+ self ._bounding_box = (left , top , right , bottom - top )
434
+ if self ._label_direction == "DWR" :
435
+ self ._bounding_box = (left , top , right , bottom - top )
436
+ if self ._label_direction == "LTR" :
437
+ self ._bounding_box = (left , top , right - left , bottom - top )
438
+
303
439
self ._text = new_text
304
- self ._bounding_box = (left , top , right - left , bottom - top )
305
440
306
441
if self .background_color is not None :
307
442
self ._update_background_color (self ._background_color )
@@ -331,5 +466,10 @@ def _set_line_spacing(self, new_line_spacing: float) -> None:
331
466
def _set_text (self , new_text : str , scale : int ) -> None :
332
467
self ._reset_text (new_text )
333
468
334
- def _set_background_color (self , new_color ) :
469
+ def _set_background_color (self , new_color : int ) -> None :
335
470
self ._update_background_color (new_color )
471
+
472
+ def _set_label_direction (self , new_label_direction : str ) -> None :
473
+ self ._label_direction = new_label_direction
474
+ old_text = self ._text
475
+ self ._update_text (str (old_text ))
0 commit comments