Skip to content

Commit e5e8b5a

Browse files
authored
Merge pull request #48 from FoamyGuy/divider_lines
Divider lines for GirdLayout
2 parents 51c7a9e + 5a79400 commit e5e8b5a

File tree

3 files changed

+304
-27
lines changed

3 files changed

+304
-27
lines changed

adafruit_displayio_layout/layouts/grid_layout.py

Lines changed: 210 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@
2222
https://github.com/adafruit/circuitpython/releases
2323
2424
"""
25-
25+
import math
2626
import displayio
2727

2828
__version__ = "0.0.0-auto.0"
2929
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_Layout.git"
3030

3131

3232
class GridLayout(displayio.Group):
33-
3433
"""
3534
A layout that organizes children into a grid table structure.
3635
@@ -40,10 +39,27 @@ class GridLayout(displayio.Group):
4039
:param int height: Height of the layout in pixels.
4140
:param tuple grid_size: Size in cells as two ints in a tuple e.g. (2, 2)
4241
:param int cell_padding: Extra padding space inside each cell. In pixels.
42+
:param bool divider_lines: Whether or not to draw lines between the cells.
43+
:param Union[tuple, list] h_divider_line_rows: Row indexes to draw divider
44+
lines above. Row indexes are 0 based.
45+
:param Union[tuple, list] v_divider_line_cols: Column indexes to draw divider
46+
lines before. Column indexes are 0 based.
47+
4348
"""
4449

4550
# pylint: disable=too-many-arguments
46-
def __init__(self, x, y, width, height, grid_size, cell_padding):
51+
def __init__(
52+
self,
53+
x,
54+
y,
55+
width,
56+
height,
57+
grid_size,
58+
cell_padding=0,
59+
divider_lines=False,
60+
h_divider_line_rows=None,
61+
v_divider_line_cols=None,
62+
):
4763
super().__init__(x=x, y=y)
4864
self.x = x
4965
self.y = y
@@ -53,8 +69,39 @@ def __init__(self, x, y, width, height, grid_size, cell_padding):
5369
self.cell_padding = cell_padding
5470
self._cell_content_list = []
5571

56-
def _layout_cells(self):
72+
self._divider_lines = []
73+
self.h_divider_line_rows = h_divider_line_rows
74+
self.v_divider_line_cols = v_divider_line_cols
75+
76+
self._divider_lines_enabled = (
77+
(divider_lines is True)
78+
or (h_divider_line_rows is not None)
79+
or (v_divider_line_cols is not None)
80+
)
81+
82+
if divider_lines:
83+
if self.h_divider_line_rows is None:
84+
self.h_divider_line_rows = []
85+
for _y in range(self.grid_size[1] + 1):
86+
self.h_divider_line_rows.append(_y)
87+
if self.v_divider_line_cols is None:
88+
self.v_divider_line_cols = []
89+
for _x in range(self.grid_size[0] + 1):
90+
self.v_divider_line_cols.append(_x)
91+
else:
92+
if not h_divider_line_rows:
93+
self.h_divider_line_rows = tuple()
94+
if not v_divider_line_cols:
95+
self.v_divider_line_cols = tuple()
5796

97+
# use at least 1 padding so that content is inside the divider lines
98+
if cell_padding == 0 and (
99+
divider_lines or h_divider_line_rows or v_divider_line_cols
100+
):
101+
self.cell_padding = 1
102+
103+
def _layout_cells(self):
104+
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
58105
for cell in self._cell_content_list:
59106
if cell["content"] not in self:
60107
grid_size_x = self.grid_size[0]
@@ -66,44 +113,180 @@ def _layout_cells(self):
66113
button_size_x = cell["cell_size"][0]
67114
button_size_y = cell["cell_size"][1]
68115

116+
_measured_width = (
117+
math.ceil(button_size_x * self._width / grid_size_x)
118+
- 2 * self.cell_padding
119+
)
120+
121+
_measured_height = (
122+
math.ceil(button_size_y * self._height / grid_size_y)
123+
- 2 * self.cell_padding
124+
)
69125
if hasattr(cell["content"], "resize"):
70126
# if it has resize function
71127
cell["content"].resize(
72-
(
73-
int(button_size_x * self._width / grid_size_x)
74-
- 2 * self.cell_padding
75-
),
76-
(
77-
int(button_size_y * self._height / grid_size_y)
78-
- 2 * self.cell_padding
79-
),
128+
_measured_width,
129+
_measured_height,
80130
)
81131
else:
82132
try:
83133
# try width and height properties.
84-
cell["content"].width = (
85-
int(button_size_x * self._width / grid_size_x)
86-
- 2 * self.cell_padding
87-
)
88-
cell["content"].height = (
89-
int(button_size_y * self._height / grid_size_y)
90-
- 2 * self.cell_padding
91-
)
134+
cell["content"].width = _measured_width
135+
cell["content"].height = _measured_height
92136
except AttributeError:
93137
# This element does not allow setting width and height.
94138
# No problem, we'll use whatever size it already is.
139+
# _measured_width = cell["content"].width
140+
# _measured_height = cell["content"].height
141+
95142
pass
96143

97-
cell["content"].x = (
98-
int(grid_position_x * self._width / grid_size_x) + self.cell_padding
99-
)
100-
cell["content"].y = (
101-
int(grid_position_y * self._height / grid_size_y)
102-
+ self.cell_padding
103-
)
144+
if not hasattr(cell["content"], "anchor_point"):
145+
146+
cell["content"].x = (
147+
int(grid_position_x * self._width / grid_size_x)
148+
+ self.cell_padding
149+
)
150+
cell["content"].y = (
151+
int(grid_position_y * self._height / grid_size_y)
152+
+ self.cell_padding
153+
)
154+
else:
155+
cell["content"].anchor_point = (0, 0)
156+
cell["content"].anchored_position = (
157+
int(grid_position_x * self._width / grid_size_x)
158+
+ self.cell_padding,
159+
int(grid_position_y * self._height / grid_size_y)
160+
+ self.cell_padding,
161+
)
104162

105163
self.append(cell["content"])
106164

165+
if self._divider_lines_enabled:
166+
palette = displayio.Palette(2)
167+
palette[0] = 0xFFFFFF
168+
palette[1] = 0xFFFFFF
169+
170+
if not hasattr(cell["content"], "anchor_point"):
171+
_bottom_line_loc_y = (
172+
cell["content"].y + _measured_height + self.cell_padding
173+
) - 1
174+
_bottom_line_loc_x = cell["content"].x - self.cell_padding
175+
176+
_top_line_loc_y = cell["content"].y - self.cell_padding
177+
_top_line_loc_x = cell["content"].x - self.cell_padding
178+
179+
_right_line_loc_y = cell["content"].y - self.cell_padding
180+
_right_line_loc_x = (
181+
cell["content"].x + _measured_width + self.cell_padding
182+
) - 1
183+
else:
184+
_bottom_line_loc_y = (
185+
cell["content"].anchored_position[1]
186+
+ _measured_height
187+
+ self.cell_padding
188+
) - 1
189+
_bottom_line_loc_x = (
190+
cell["content"].anchored_position[0] - self.cell_padding
191+
)
192+
193+
_top_line_loc_y = (
194+
cell["content"].anchored_position[1] - self.cell_padding
195+
)
196+
_top_line_loc_x = (
197+
cell["content"].anchored_position[0] - self.cell_padding
198+
)
199+
200+
_right_line_loc_y = (
201+
cell["content"].anchored_position[1] - self.cell_padding
202+
)
203+
_right_line_loc_x = (
204+
cell["content"].anchored_position[0]
205+
+ _measured_width
206+
+ self.cell_padding
207+
) - 1
208+
209+
_horizontal_divider_line = displayio.Shape(
210+
_measured_width + (2 * self.cell_padding),
211+
1,
212+
mirror_x=False,
213+
mirror_y=False,
214+
)
215+
216+
_bottom_divider_tilegrid = displayio.TileGrid(
217+
_horizontal_divider_line,
218+
pixel_shader=palette,
219+
y=_bottom_line_loc_y,
220+
x=_bottom_line_loc_x,
221+
)
222+
223+
_top_divider_tilegrid = displayio.TileGrid(
224+
_horizontal_divider_line,
225+
pixel_shader=palette,
226+
y=_top_line_loc_y,
227+
x=_top_line_loc_x,
228+
)
229+
230+
_vertical_divider_line = displayio.Shape(
231+
1,
232+
_measured_height + (2 * self.cell_padding),
233+
mirror_x=False,
234+
mirror_y=False,
235+
)
236+
237+
_left_divider_tilegrid = displayio.TileGrid(
238+
_vertical_divider_line,
239+
pixel_shader=palette,
240+
y=_top_line_loc_y,
241+
x=_top_line_loc_x,
242+
)
243+
244+
_right_divider_tilegrid = displayio.TileGrid(
245+
_vertical_divider_line,
246+
pixel_shader=palette,
247+
y=_right_line_loc_y,
248+
x=_right_line_loc_x,
249+
)
250+
251+
for line_obj in self._divider_lines:
252+
self.remove(line_obj["tilegrid"])
253+
254+
if grid_position_y == grid_size_y - 1 and (
255+
grid_position_y + 1 in self.h_divider_line_rows
256+
):
257+
self._divider_lines.append(
258+
{
259+
"shape": _horizontal_divider_line,
260+
"tilegrid": _bottom_divider_tilegrid,
261+
}
262+
)
263+
if grid_position_y in self.h_divider_line_rows:
264+
self._divider_lines.append(
265+
{
266+
"shape": _horizontal_divider_line,
267+
"tilegrid": _top_divider_tilegrid,
268+
}
269+
)
270+
if grid_position_x in self.v_divider_line_cols:
271+
self._divider_lines.append(
272+
{
273+
"shape": _horizontal_divider_line,
274+
"tilegrid": _left_divider_tilegrid,
275+
}
276+
)
277+
if grid_position_x == grid_size_x - 1 and (
278+
grid_position_x + 1 in self.v_divider_line_cols
279+
):
280+
self._divider_lines.append(
281+
{
282+
"shape": _vertical_divider_line,
283+
"tilegrid": _right_divider_tilegrid,
284+
}
285+
)
286+
287+
for line_obj in self._divider_lines:
288+
self.append(line_obj["tilegrid"])
289+
107290
def add_content(self, cell_content, grid_position, cell_size):
108291
"""Add a child to the grid.
109292

docs/examples.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ Ensure your device works with this simple test.
77
:caption: examples/displayio_layout_simpletest.py
88
:linenos:
99

10+
GridLayout divider lines example
11+
--------------------------------
12+
13+
Create GridLayouts with divider lines.
14+
15+
.. literalinclude:: ../examples/displayio_layout_gridlayout_dividers.py
16+
:caption: examples/displayio_layout_gridlayout_dividers.py
17+
:linenos:
18+
1019
Pygame simple test
1120
------------------
1221

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# SPDX-FileCopyrightText: 2021 Tim C, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""
5+
Illustrate how to use divider lines with GridLayout
6+
"""
7+
import board
8+
import displayio
9+
import terminalio
10+
from adafruit_display_text import label
11+
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
12+
13+
# use built in display (PyPortal, PyGamer, PyBadge, CLUE, etc.)
14+
# see guide for setting up external displays (TFT / OLED breakouts, RGB matrices, etc.)
15+
# https://learn.adafruit.com/circuitpython-display-support-using-displayio/display-and-display-bus
16+
display = board.DISPLAY
17+
18+
# Make the display context
19+
main_group = displayio.Group()
20+
display.show(main_group)
21+
22+
layout = GridLayout(
23+
x=10,
24+
y=10,
25+
width=200,
26+
height=100,
27+
grid_size=(2, 2),
28+
cell_padding=8,
29+
divider_lines=True, # divider lines around every cell
30+
)
31+
_labels = []
32+
33+
_labels.append(
34+
label.Label(
35+
terminalio.FONT, scale=2, x=0, y=0, text="Hello", background_color=0x770077
36+
)
37+
)
38+
layout.add_content(_labels[0], grid_position=(0, 0), cell_size=(1, 1))
39+
_labels.append(
40+
label.Label(
41+
terminalio.FONT, scale=2, x=0, y=0, text="World", background_color=0x007700
42+
)
43+
)
44+
layout.add_content(_labels[1], grid_position=(1, 0), cell_size=(1, 1))
45+
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Hello"))
46+
layout.add_content(_labels[2], grid_position=(0, 1), cell_size=(1, 1))
47+
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Grid"))
48+
layout.add_content(_labels[3], grid_position=(1, 1), cell_size=(1, 1))
49+
50+
main_group.append(layout)
51+
52+
other_layout = GridLayout(
53+
x=10,
54+
y=120,
55+
width=140,
56+
height=80,
57+
grid_size=(2, 3),
58+
cell_padding=4,
59+
h_divider_line_rows=(1, 2), # Lines before rows 1 and 2
60+
)
61+
62+
other_layout.add_content(
63+
label.Label(terminalio.FONT, text="0x0"), grid_position=(0, 0), cell_size=(1, 1)
64+
)
65+
other_layout.add_content(
66+
label.Label(terminalio.FONT, text="0x1"), grid_position=(0, 1), cell_size=(1, 1)
67+
)
68+
other_layout.add_content(
69+
label.Label(terminalio.FONT, text="0x2"), grid_position=(0, 2), cell_size=(1, 1)
70+
)
71+
72+
other_layout.add_content(
73+
label.Label(terminalio.FONT, text="1x0"), grid_position=(1, 0), cell_size=(1, 1)
74+
)
75+
other_layout.add_content(
76+
label.Label(terminalio.FONT, text="1x1"), grid_position=(1, 1), cell_size=(1, 1)
77+
)
78+
other_layout.add_content(
79+
label.Label(terminalio.FONT, text="1x2"), grid_position=(1, 2), cell_size=(1, 1)
80+
)
81+
82+
main_group.append(other_layout)
83+
84+
while True:
85+
pass

0 commit comments

Comments
 (0)