Skip to content

Commit 2d7ba2c

Browse files
authored
Merge pull request #58 from FoamyGuy/cell_anchor_point
Cell anchor point
2 parents eed7282 + 710b202 commit 2d7ba2c

File tree

1 file changed

+142
-28
lines changed

1 file changed

+142
-28
lines changed

adafruit_displayio_layout/layouts/grid_layout.py

+142-28
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,9 @@ 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. Default is (0.0, 0.0)
56+
which will anchor content to the top left of the cell.
4857
4958
"""
5059

@@ -62,6 +71,7 @@ def __init__(
6271
h_divider_line_rows=None,
6372
v_divider_line_cols=None,
6473
divider_line_color=0xFFFFFF,
74+
cell_anchor_point=(0.0, 0.0),
6575
):
6676
super().__init__(x=x, y=y)
6777
self.x = x
@@ -71,6 +81,7 @@ def __init__(
7181
self.grid_size = grid_size
7282
self.cell_padding = cell_padding
7383
self._cell_content_list = []
84+
self._cell_anchor_point = cell_anchor_point
7485

7586
self._divider_lines = []
7687
self._divider_color = divider_line_color
@@ -114,18 +125,19 @@ def _layout_cells(self):
114125
grid_position_x = cell["grid_position"][0]
115126
grid_position_y = cell["grid_position"][1]
116127

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

120131
_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)
122133
- 2 * self.cell_padding
123134
)
124135

125136
_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)
127138
- 2 * self.cell_padding
128139
)
140+
129141
if hasattr(cell["content"], "resize"):
130142
# if it has resize function
131143
cell["content"].resize(
@@ -150,18 +162,24 @@ def _layout_cells(self):
150162
cell["content"].x = (
151163
int(grid_position_x * self._width / grid_size_x)
152164
+ self.cell_padding
165+
+ int(cell["cell_anchor_point"][0] * _measured_width)
166+
- int(cell["content"].width * cell["cell_anchor_point"][0])
153167
)
154168
cell["content"].y = (
155169
int(grid_position_y * self._height / grid_size_y)
156170
+ self.cell_padding
171+
+ int(cell["cell_anchor_point"][1] * _measured_height)
172+
- int(cell["content"].height * cell["cell_anchor_point"][1])
157173
)
158174
else:
159-
cell["content"].anchor_point = (0, 0)
175+
cell["content"].anchor_point = cell["cell_anchor_point"]
160176
cell["content"].anchored_position = (
161177
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),
163180
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),
165183
)
166184

167185
self.append(cell["content"])
@@ -173,42 +191,84 @@ def _layout_cells(self):
173191

174192
if not hasattr(cell["content"], "anchor_point"):
175193
_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+
)
179199

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

183-
_right_line_loc_y = cell["content"].y - self.cell_padding
184228
_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+
)
187234
else:
188235
_bottom_line_loc_y = (
189236
cell["content"].anchored_position[1]
190237
+ _measured_height
191238
+ self.cell_padding
239+
- (cell["cell_anchor_point"][1] * _measured_height)
192240
) - 1
193241
_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)
195245
)
196246

197247
_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)
199251
)
200252
_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)
202256
)
203257

204258
_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)
206262
)
207263
_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+
)
212272

213273
_horizontal_divider_line = displayio.Shape(
214274
_measured_width + (2 * self.cell_padding),
@@ -255,31 +315,61 @@ def _layout_cells(self):
255315
for line_obj in self._divider_lines:
256316
self.remove(line_obj["tilegrid"])
257317

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
260330
):
261331
self._divider_lines.append(
262332
{
263333
"shape": _horizontal_divider_line,
264334
"tilegrid": _bottom_divider_tilegrid,
265335
}
266336
)
337+
338+
"""
339+
Every cell whose index is in h_divider_line_rows gets
340+
a top divider line.
341+
"""
267342
if grid_position_y in self.h_divider_line_rows:
268343
self._divider_lines.append(
269344
{
270345
"shape": _horizontal_divider_line,
271346
"tilegrid": _top_divider_tilegrid,
272347
}
273348
)
349+
350+
"""
351+
Every cell whose index is in v_divider_line_cols gets
352+
a left divider line.
353+
"""
274354
if grid_position_x in self.v_divider_line_cols:
275355
self._divider_lines.append(
276356
{
277357
"shape": _horizontal_divider_line,
278358
"tilegrid": _left_divider_tilegrid,
279359
}
280360
)
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
283373
):
284374
self._divider_lines.append(
285375
{
@@ -291,7 +381,9 @@ def _layout_cells(self):
291381
for line_obj in self._divider_lines:
292382
self.append(line_obj["tilegrid"])
293383

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+
):
295387
"""Add a child to the grid.
296388
297389
: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):
300392
x,y coordinates in grid cells. e.g. (1,0)
301393
:param tuple cell_size: the size and shape that the new cell should
302394
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.
303399
: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+
304406
sub_view_obj = {
407+
"cell_anchor_point": _this_cell_anchor_point,
305408
"content": cell_content,
306409
"grid_position": grid_position,
307410
"cell_size": cell_size,
@@ -326,3 +429,14 @@ def get_cell(self, cell_coordinates):
326429
cell_coordinates
327430
)
328431
)
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

Comments
 (0)