Skip to content

Commit 7e75c94

Browse files
committed
add cell_anchor_point feature
1 parent eed7282 commit 7e75c94

File tree

1 file changed

+137
-28
lines changed

1 file changed

+137
-28
lines changed

adafruit_displayio_layout/layouts/grid_layout.py

Lines changed: 137 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
https://github.com/adafruit/circuitpython/releases
2323
2424
"""
25+
try:
26+
# Used only for typing
27+
from typing import Tuple
28+
except ImportError:
29+
pass
30+
2531
import math
2632
import displayio
2733

@@ -45,6 +51,8 @@ class GridLayout(displayio.Group):
4551
:param Union[tuple, list] v_divider_line_cols: Column indexes to draw divider
4652
lines before. Column indexes are 0 based.
4753
: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
4856
4957
"""
5058

@@ -62,6 +70,7 @@ def __init__(
6270
h_divider_line_rows=None,
6371
v_divider_line_cols=None,
6472
divider_line_color=0xFFFFFF,
73+
cell_anchor_point=(0.0, 0.0),
6574
):
6675
super().__init__(x=x, y=y)
6776
self.x = x
@@ -71,6 +80,7 @@ def __init__(
7180
self.grid_size = grid_size
7281
self.cell_padding = cell_padding
7382
self._cell_content_list = []
83+
self._cell_anchor_point = cell_anchor_point
7484

7585
self._divider_lines = []
7686
self._divider_color = divider_line_color
@@ -114,18 +124,19 @@ def _layout_cells(self):
114124
grid_position_x = cell["grid_position"][0]
115125
grid_position_y = cell["grid_position"][1]
116126

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]
119129

120130
_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)
122132
- 2 * self.cell_padding
123133
)
124134

125135
_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)
127137
- 2 * self.cell_padding
128138
)
139+
129140
if hasattr(cell["content"], "resize"):
130141
# if it has resize function
131142
cell["content"].resize(
@@ -150,18 +161,24 @@ def _layout_cells(self):
150161
cell["content"].x = (
151162
int(grid_position_x * self._width / grid_size_x)
152163
+ self.cell_padding
164+
+ int(cell["cell_anchor_point"][0] * _measured_width)
165+
- int(cell["content"].width * cell["cell_anchor_point"][0])
153166
)
154167
cell["content"].y = (
155168
int(grid_position_y * self._height / grid_size_y)
156169
+ self.cell_padding
170+
+ int(cell["cell_anchor_point"][1] * _measured_height)
171+
- int(cell["content"].height * cell["cell_anchor_point"][1])
157172
)
158173
else:
159-
cell["content"].anchor_point = (0, 0)
174+
cell["content"].anchor_point = cell["cell_anchor_point"]
160175
cell["content"].anchored_position = (
161176
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),
163179
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),
165182
)
166183

167184
self.append(cell["content"])
@@ -173,42 +190,84 @@ def _layout_cells(self):
173190

174191
if not hasattr(cell["content"], "anchor_point"):
175192
_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+
)
179198

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+
)
182226

183-
_right_line_loc_y = cell["content"].y - self.cell_padding
184227
_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+
)
187233
else:
188234
_bottom_line_loc_y = (
189235
cell["content"].anchored_position[1]
190236
+ _measured_height
191237
+ self.cell_padding
238+
- (cell["cell_anchor_point"][1] * _measured_height)
192239
) - 1
193240
_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)
195244
)
196245

197246
_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)
199250
)
200251
_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)
202255
)
203256

204257
_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)
206261
)
207262
_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+
)
212271

213272
_horizontal_divider_line = displayio.Shape(
214273
_measured_width + (2 * self.cell_padding),
@@ -255,31 +314,61 @@ def _layout_cells(self):
255314
for line_obj in self._divider_lines:
256315
self.remove(line_obj["tilegrid"])
257316

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
260329
):
261330
self._divider_lines.append(
262331
{
263332
"shape": _horizontal_divider_line,
264333
"tilegrid": _bottom_divider_tilegrid,
265334
}
266335
)
336+
337+
"""
338+
Every cell whose index is in h_divider_line_rows gets
339+
a top divider line.
340+
"""
267341
if grid_position_y in self.h_divider_line_rows:
268342
self._divider_lines.append(
269343
{
270344
"shape": _horizontal_divider_line,
271345
"tilegrid": _top_divider_tilegrid,
272346
}
273347
)
348+
349+
"""
350+
Every cell whose index is in v_divider_line_cols gets
351+
a left divider line.
352+
"""
274353
if grid_position_x in self.v_divider_line_cols:
275354
self._divider_lines.append(
276355
{
277356
"shape": _horizontal_divider_line,
278357
"tilegrid": _left_divider_tilegrid,
279358
}
280359
)
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
283372
):
284373
self._divider_lines.append(
285374
{
@@ -291,7 +380,9 @@ def _layout_cells(self):
291380
for line_obj in self._divider_lines:
292381
self.append(line_obj["tilegrid"])
293382

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+
):
295386
"""Add a child to the grid.
296387
297388
: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):
301392
:param tuple cell_size: the size and shape that the new cell should
302393
occupy. Width and height in cells inside a tuple e.g. (1, 1)
303394
: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+
304401
sub_view_obj = {
402+
"cell_anchor_point": _this_cell_anchor_point,
305403
"content": cell_content,
306404
"grid_position": grid_position,
307405
"cell_size": cell_size,
@@ -326,3 +424,14 @@ def get_cell(self, cell_coordinates):
326424
cell_coordinates
327425
)
328426
)
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

Comments
 (0)