22
22
https://github.com/adafruit/circuitpython/releases
23
23
24
24
"""
25
+ try :
26
+ # Used only for typing
27
+ from typing import Tuple
28
+ except ImportError :
29
+ pass
30
+
25
31
import math
26
32
import displayio
27
33
@@ -45,6 +51,9 @@ class GridLayout(displayio.Group):
45
51
:param Union[tuple, list] v_divider_line_cols: Column indexes to draw divider
46
52
lines before. Column indexes are 0 based.
47
53
:param divider_line_color: The color of the divider lines (in hexadecimal)
54
+ :param tuple cell_anchor_point: Anchor point used within every cell. Needs to
55
+ be a tuple containing two floats between 0.0 and 1.0. Default is (0.0, 0.0)
56
+ which will anchor content to the top left of the cell.
48
57
49
58
"""
50
59
@@ -62,6 +71,7 @@ def __init__(
62
71
h_divider_line_rows = None ,
63
72
v_divider_line_cols = None ,
64
73
divider_line_color = 0xFFFFFF ,
74
+ cell_anchor_point = (0.0 , 0.0 ),
65
75
):
66
76
super ().__init__ (x = x , y = y )
67
77
self .x = x
@@ -71,6 +81,7 @@ def __init__(
71
81
self .grid_size = grid_size
72
82
self .cell_padding = cell_padding
73
83
self ._cell_content_list = []
84
+ self ._cell_anchor_point = cell_anchor_point
74
85
75
86
self ._divider_lines = []
76
87
self ._divider_color = divider_line_color
@@ -114,18 +125,19 @@ def _layout_cells(self):
114
125
grid_position_x = cell ["grid_position" ][0 ]
115
126
grid_position_y = cell ["grid_position" ][1 ]
116
127
117
- button_size_x = cell ["cell_size" ][0 ]
118
- button_size_y = cell ["cell_size" ][1 ]
128
+ content_cell_size_x = cell ["cell_size" ][0 ]
129
+ content_cell_size_y = cell ["cell_size" ][1 ]
119
130
120
131
_measured_width = (
121
- math .ceil (button_size_x * self ._width / grid_size_x )
132
+ math .ceil (content_cell_size_x * self ._width / grid_size_x )
122
133
- 2 * self .cell_padding
123
134
)
124
135
125
136
_measured_height = (
126
- math .ceil (button_size_y * self ._height / grid_size_y )
137
+ math .ceil (content_cell_size_y * self ._height / grid_size_y )
127
138
- 2 * self .cell_padding
128
139
)
140
+
129
141
if hasattr (cell ["content" ], "resize" ):
130
142
# if it has resize function
131
143
cell ["content" ].resize (
@@ -150,18 +162,24 @@ def _layout_cells(self):
150
162
cell ["content" ].x = (
151
163
int (grid_position_x * self ._width / grid_size_x )
152
164
+ self .cell_padding
165
+ + int (cell ["cell_anchor_point" ][0 ] * _measured_width )
166
+ - int (cell ["content" ].width * cell ["cell_anchor_point" ][0 ])
153
167
)
154
168
cell ["content" ].y = (
155
169
int (grid_position_y * self ._height / grid_size_y )
156
170
+ self .cell_padding
171
+ + int (cell ["cell_anchor_point" ][1 ] * _measured_height )
172
+ - int (cell ["content" ].height * cell ["cell_anchor_point" ][1 ])
157
173
)
158
174
else :
159
- cell ["content" ].anchor_point = ( 0 , 0 )
175
+ cell ["content" ].anchor_point = cell [ "cell_anchor_point" ]
160
176
cell ["content" ].anchored_position = (
161
177
int (grid_position_x * self ._width / grid_size_x )
162
- + self .cell_padding ,
178
+ + self .cell_padding
179
+ + (cell ["cell_anchor_point" ][0 ] * _measured_width ),
163
180
int (grid_position_y * self ._height / grid_size_y )
164
- + self .cell_padding ,
181
+ + self .cell_padding
182
+ + (cell ["cell_anchor_point" ][1 ] * _measured_height ),
165
183
)
166
184
167
185
self .append (cell ["content" ])
@@ -173,42 +191,84 @@ def _layout_cells(self):
173
191
174
192
if not hasattr (cell ["content" ], "anchor_point" ):
175
193
_bottom_line_loc_y = (
176
- cell ["content" ].y + _measured_height + self .cell_padding
177
- ) - 1
178
- _bottom_line_loc_x = cell ["content" ].x - self .cell_padding
194
+ (cell ["content" ].y + _measured_height + self .cell_padding )
195
+ - 1
196
+ - int (cell ["cell_anchor_point" ][1 ] * _measured_height )
197
+ + int (cell ["content" ].height * cell ["cell_anchor_point" ][1 ])
198
+ )
179
199
180
- _top_line_loc_y = cell ["content" ].y - self .cell_padding
181
- _top_line_loc_x = cell ["content" ].x - self .cell_padding
200
+ _bottom_line_loc_x = (
201
+ cell ["content" ].x
202
+ - self .cell_padding
203
+ - int (cell ["cell_anchor_point" ][0 ] * _measured_width )
204
+ + int (cell ["content" ].width * cell ["cell_anchor_point" ][0 ])
205
+ )
206
+
207
+ _top_line_loc_y = (
208
+ cell ["content" ].y
209
+ - self .cell_padding
210
+ - int (cell ["cell_anchor_point" ][1 ] * _measured_height )
211
+ + int (cell ["content" ].height * cell ["cell_anchor_point" ][1 ])
212
+ )
213
+
214
+ _top_line_loc_x = (
215
+ cell ["content" ].x
216
+ - self .cell_padding
217
+ - int (cell ["cell_anchor_point" ][0 ] * _measured_width )
218
+ + int (cell ["content" ].width * cell ["cell_anchor_point" ][0 ])
219
+ )
220
+
221
+ _right_line_loc_y = (
222
+ cell ["content" ].y
223
+ - self .cell_padding
224
+ - int (cell ["cell_anchor_point" ][1 ] * _measured_height )
225
+ + int (cell ["content" ].height * cell ["cell_anchor_point" ][1 ])
226
+ )
182
227
183
- _right_line_loc_y = cell ["content" ].y - self .cell_padding
184
228
_right_line_loc_x = (
185
- cell ["content" ].x + _measured_width + self .cell_padding
186
- ) - 1
229
+ (cell ["content" ].x + _measured_width + self .cell_padding )
230
+ - 1
231
+ - int (cell ["cell_anchor_point" ][0 ] * _measured_width )
232
+ + int (cell ["content" ].width * cell ["cell_anchor_point" ][0 ])
233
+ )
187
234
else :
188
235
_bottom_line_loc_y = (
189
236
cell ["content" ].anchored_position [1 ]
190
237
+ _measured_height
191
238
+ self .cell_padding
239
+ - (cell ["cell_anchor_point" ][1 ] * _measured_height )
192
240
) - 1
193
241
_bottom_line_loc_x = (
194
- cell ["content" ].anchored_position [0 ] - self .cell_padding
242
+ cell ["content" ].anchored_position [0 ]
243
+ - self .cell_padding
244
+ - (cell ["cell_anchor_point" ][0 ] * _measured_width )
195
245
)
196
246
197
247
_top_line_loc_y = (
198
- cell ["content" ].anchored_position [1 ] - self .cell_padding
248
+ cell ["content" ].anchored_position [1 ]
249
+ - self .cell_padding
250
+ - (cell ["cell_anchor_point" ][1 ] * _measured_height )
199
251
)
200
252
_top_line_loc_x = (
201
- cell ["content" ].anchored_position [0 ] - self .cell_padding
253
+ cell ["content" ].anchored_position [0 ]
254
+ - self .cell_padding
255
+ - (cell ["cell_anchor_point" ][0 ] * _measured_width )
202
256
)
203
257
204
258
_right_line_loc_y = (
205
- cell ["content" ].anchored_position [1 ] - self .cell_padding
259
+ cell ["content" ].anchored_position [1 ]
260
+ - self .cell_padding
261
+ - (cell ["cell_anchor_point" ][1 ] * _measured_height )
206
262
)
207
263
_right_line_loc_x = (
208
- cell ["content" ].anchored_position [0 ]
209
- + _measured_width
210
- + self .cell_padding
211
- ) - 1
264
+ (
265
+ cell ["content" ].anchored_position [0 ]
266
+ + _measured_width
267
+ + self .cell_padding
268
+ )
269
+ - 1
270
+ - (cell ["cell_anchor_point" ][0 ] * _measured_width )
271
+ )
212
272
213
273
_horizontal_divider_line = displayio .Shape (
214
274
_measured_width + (2 * self .cell_padding ),
@@ -255,31 +315,61 @@ def _layout_cells(self):
255
315
for line_obj in self ._divider_lines :
256
316
self .remove (line_obj ["tilegrid" ])
257
317
258
- if grid_position_y == grid_size_y - 1 and (
259
- grid_position_y + 1 in self .h_divider_line_rows
318
+ """
319
+ Only use bottom divider lines on the bottom row. All
320
+ other rows rely on top divder lines of the row beneath them.
321
+ Add the content_cell_size to the grid_position to account for
322
+ areas larger than 1x1 cells. For 1x1 cells this will equal zero
323
+ and not change anything.
324
+ """
325
+ if (
326
+ grid_position_y + content_cell_size_y - 1
327
+ ) == grid_size_y - 1 and (
328
+ (grid_position_y + content_cell_size_y - 1 ) + 1
329
+ in self .h_divider_line_rows
260
330
):
261
331
self ._divider_lines .append (
262
332
{
263
333
"shape" : _horizontal_divider_line ,
264
334
"tilegrid" : _bottom_divider_tilegrid ,
265
335
}
266
336
)
337
+
338
+ """
339
+ Every cell whose index is in h_divider_line_rows gets
340
+ a top divider line.
341
+ """
267
342
if grid_position_y in self .h_divider_line_rows :
268
343
self ._divider_lines .append (
269
344
{
270
345
"shape" : _horizontal_divider_line ,
271
346
"tilegrid" : _top_divider_tilegrid ,
272
347
}
273
348
)
349
+
350
+ """
351
+ Every cell whose index is in v_divider_line_cols gets
352
+ a left divider line.
353
+ """
274
354
if grid_position_x in self .v_divider_line_cols :
275
355
self ._divider_lines .append (
276
356
{
277
357
"shape" : _horizontal_divider_line ,
278
358
"tilegrid" : _left_divider_tilegrid ,
279
359
}
280
360
)
281
- if grid_position_x == grid_size_x - 1 and (
282
- grid_position_x + 1 in self .v_divider_line_cols
361
+ """
362
+ Only use right divider lines on the right-most column. All
363
+ other columns rely on left divider lines of the column to their
364
+ left. Add the content_cell_size to the grid_position to account for
365
+ areas larger than 1x1 cells. For 1x1 cells this will equal zero
366
+ and not change anything.
367
+ """
368
+ if (
369
+ grid_position_x + content_cell_size_x - 1
370
+ ) == grid_size_x - 1 and (
371
+ (grid_position_x + content_cell_size_x - 1 ) + 1
372
+ in self .v_divider_line_cols
283
373
):
284
374
self ._divider_lines .append (
285
375
{
@@ -291,7 +381,9 @@ def _layout_cells(self):
291
381
for line_obj in self ._divider_lines :
292
382
self .append (line_obj ["tilegrid" ])
293
383
294
- def add_content (self , cell_content , grid_position , cell_size ):
384
+ def add_content (
385
+ self , cell_content , grid_position , cell_size , cell_anchor_point = None
386
+ ):
295
387
"""Add a child to the grid.
296
388
297
389
:param cell_content: the content to add to this cell e.g. label, button, etc...
@@ -300,8 +392,19 @@ def add_content(self, cell_content, grid_position, cell_size):
300
392
x,y coordinates in grid cells. e.g. (1,0)
301
393
:param tuple cell_size: the size and shape that the new cell should
302
394
occupy. Width and height in cells inside a tuple e.g. (1, 1)
395
+ :param tuple cell_anchor_point: a tuple of floats between 0.0 and 1.0.
396
+ If passed, this value will override the cell_anchor_point of the GridLayout
397
+ for the single cell having it's content added with this function call. If omitted
398
+ then the cell_anchor_point from the GridLayout will be used.
303
399
:return: None"""
400
+
401
+ if cell_anchor_point :
402
+ _this_cell_anchor_point = cell_anchor_point
403
+ else :
404
+ _this_cell_anchor_point = self ._cell_anchor_point
405
+
304
406
sub_view_obj = {
407
+ "cell_anchor_point" : _this_cell_anchor_point ,
305
408
"content" : cell_content ,
306
409
"grid_position" : grid_position ,
307
410
"cell_size" : cell_size ,
@@ -326,3 +429,14 @@ def get_cell(self, cell_coordinates):
326
429
cell_coordinates
327
430
)
328
431
)
432
+
433
+ @property
434
+ def cell_size_pixels (self ) -> Tuple [int , int ]:
435
+ """
436
+ Get the size of a 1x1 cell in pixels. Can be useful for manually
437
+ re-positioning content within cells.
438
+
439
+ :return Tuple[int, int]: A tuple containing the (x, y) size in
440
+ pixels of a 1x1 cell in the GridLayout
441
+ """
442
+ return (self ._width // self .grid_size [0 ], self ._height // self .grid_size [1 ])
0 commit comments