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