Skip to content

Divider lines for GirdLayout #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 210 additions & 27 deletions adafruit_displayio_layout/layouts/grid_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@
https://github.com/adafruit/circuitpython/releases

"""

import math
import displayio

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


class GridLayout(displayio.Group):

"""
A layout that organizes children into a grid table structure.

Expand All @@ -40,10 +39,27 @@ class GridLayout(displayio.Group):
:param int height: Height of the layout in pixels.
:param tuple grid_size: Size in cells as two ints in a tuple e.g. (2, 2)
:param int cell_padding: Extra padding space inside each cell. In pixels.
:param bool divider_lines: Whether or not to draw lines between the cells.
:param Union[tuple, list] h_divider_line_rows: Row indexes to draw divider
lines above. Row indexes are 0 based.
:param Union[tuple, list] v_divider_line_cols: Column indexes to draw divider
lines before. Column indexes are 0 based.

"""

# pylint: disable=too-many-arguments
def __init__(self, x, y, width, height, grid_size, cell_padding):
def __init__(
self,
x,
y,
width,
height,
grid_size,
cell_padding=0,
divider_lines=False,
h_divider_line_rows=None,
v_divider_line_cols=None,
):
super().__init__(x=x, y=y)
self.x = x
self.y = y
Expand All @@ -53,8 +69,39 @@ def __init__(self, x, y, width, height, grid_size, cell_padding):
self.cell_padding = cell_padding
self._cell_content_list = []

def _layout_cells(self):
self._divider_lines = []
self.h_divider_line_rows = h_divider_line_rows
self.v_divider_line_cols = v_divider_line_cols

self._divider_lines_enabled = (
(divider_lines is True)
or (h_divider_line_rows is not None)
or (v_divider_line_cols is not None)
)

if divider_lines:
if self.h_divider_line_rows is None:
self.h_divider_line_rows = []
for _y in range(self.grid_size[1] + 1):
self.h_divider_line_rows.append(_y)
if self.v_divider_line_cols is None:
self.v_divider_line_cols = []
for _x in range(self.grid_size[0] + 1):
self.v_divider_line_cols.append(_x)
else:
if not h_divider_line_rows:
self.h_divider_line_rows = tuple()
if not v_divider_line_cols:
self.v_divider_line_cols = tuple()

# use at least 1 padding so that content is inside the divider lines
if cell_padding == 0 and (
divider_lines or h_divider_line_rows or v_divider_line_cols
):
self.cell_padding = 1

def _layout_cells(self):
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
for cell in self._cell_content_list:
if cell["content"] not in self:
grid_size_x = self.grid_size[0]
Expand All @@ -66,44 +113,180 @@ def _layout_cells(self):
button_size_x = cell["cell_size"][0]
button_size_y = cell["cell_size"][1]

_measured_width = (
math.ceil(button_size_x * self._width / grid_size_x)
- 2 * self.cell_padding
)

_measured_height = (
math.ceil(button_size_y * self._height / grid_size_y)
- 2 * self.cell_padding
)
if hasattr(cell["content"], "resize"):
# if it has resize function
cell["content"].resize(
(
int(button_size_x * self._width / grid_size_x)
- 2 * self.cell_padding
),
(
int(button_size_y * self._height / grid_size_y)
- 2 * self.cell_padding
),
_measured_width,
_measured_height,
)
else:
try:
# try width and height properties.
cell["content"].width = (
int(button_size_x * self._width / grid_size_x)
- 2 * self.cell_padding
)
cell["content"].height = (
int(button_size_y * self._height / grid_size_y)
- 2 * self.cell_padding
)
cell["content"].width = _measured_width
cell["content"].height = _measured_height
except AttributeError:
# This element does not allow setting width and height.
# No problem, we'll use whatever size it already is.
# _measured_width = cell["content"].width
# _measured_height = cell["content"].height

pass

cell["content"].x = (
int(grid_position_x * self._width / grid_size_x) + self.cell_padding
)
cell["content"].y = (
int(grid_position_y * self._height / grid_size_y)
+ self.cell_padding
)
if not hasattr(cell["content"], "anchor_point"):

cell["content"].x = (
int(grid_position_x * self._width / grid_size_x)
+ self.cell_padding
)
cell["content"].y = (
int(grid_position_y * self._height / grid_size_y)
+ self.cell_padding
)
else:
cell["content"].anchor_point = (0, 0)
cell["content"].anchored_position = (
int(grid_position_x * self._width / grid_size_x)
+ self.cell_padding,
int(grid_position_y * self._height / grid_size_y)
+ self.cell_padding,
)

self.append(cell["content"])

if self._divider_lines_enabled:
palette = displayio.Palette(2)
palette[0] = 0xFFFFFF
palette[1] = 0xFFFFFF

if not hasattr(cell["content"], "anchor_point"):
_bottom_line_loc_y = (
cell["content"].y + _measured_height + self.cell_padding
) - 1
_bottom_line_loc_x = cell["content"].x - self.cell_padding

_top_line_loc_y = cell["content"].y - self.cell_padding
_top_line_loc_x = cell["content"].x - self.cell_padding

_right_line_loc_y = cell["content"].y - self.cell_padding
_right_line_loc_x = (
cell["content"].x + _measured_width + self.cell_padding
) - 1
else:
_bottom_line_loc_y = (
cell["content"].anchored_position[1]
+ _measured_height
+ self.cell_padding
) - 1
_bottom_line_loc_x = (
cell["content"].anchored_position[0] - self.cell_padding
)

_top_line_loc_y = (
cell["content"].anchored_position[1] - self.cell_padding
)
_top_line_loc_x = (
cell["content"].anchored_position[0] - self.cell_padding
)

_right_line_loc_y = (
cell["content"].anchored_position[1] - self.cell_padding
)
_right_line_loc_x = (
cell["content"].anchored_position[0]
+ _measured_width
+ self.cell_padding
) - 1

_horizontal_divider_line = displayio.Shape(
_measured_width + (2 * self.cell_padding),
1,
mirror_x=False,
mirror_y=False,
)

_bottom_divider_tilegrid = displayio.TileGrid(
_horizontal_divider_line,
pixel_shader=palette,
y=_bottom_line_loc_y,
x=_bottom_line_loc_x,
)

_top_divider_tilegrid = displayio.TileGrid(
_horizontal_divider_line,
pixel_shader=palette,
y=_top_line_loc_y,
x=_top_line_loc_x,
)

_vertical_divider_line = displayio.Shape(
1,
_measured_height + (2 * self.cell_padding),
mirror_x=False,
mirror_y=False,
)

_left_divider_tilegrid = displayio.TileGrid(
_vertical_divider_line,
pixel_shader=palette,
y=_top_line_loc_y,
x=_top_line_loc_x,
)

_right_divider_tilegrid = displayio.TileGrid(
_vertical_divider_line,
pixel_shader=palette,
y=_right_line_loc_y,
x=_right_line_loc_x,
)

for line_obj in self._divider_lines:
self.remove(line_obj["tilegrid"])

if grid_position_y == grid_size_y - 1 and (
grid_position_y + 1 in self.h_divider_line_rows
):
self._divider_lines.append(
{
"shape": _horizontal_divider_line,
"tilegrid": _bottom_divider_tilegrid,
}
)
if grid_position_y in self.h_divider_line_rows:
self._divider_lines.append(
{
"shape": _horizontal_divider_line,
"tilegrid": _top_divider_tilegrid,
}
)
if grid_position_x in self.v_divider_line_cols:
self._divider_lines.append(
{
"shape": _horizontal_divider_line,
"tilegrid": _left_divider_tilegrid,
}
)
if grid_position_x == grid_size_x - 1 and (
grid_position_x + 1 in self.v_divider_line_cols
):
self._divider_lines.append(
{
"shape": _vertical_divider_line,
"tilegrid": _right_divider_tilegrid,
}
)

for line_obj in self._divider_lines:
self.append(line_obj["tilegrid"])

def add_content(self, cell_content, grid_position, cell_size):
"""Add a child to the grid.

Expand Down
9 changes: 9 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ Ensure your device works with this simple test.
:caption: examples/displayio_layout_simpletest.py
:linenos:

GridLayout divider lines example
--------------------------------

Create GridLayouts with divider lines.

.. literalinclude:: ../examples/displayio_layout_gridlayout_dividers.py
:caption: examples/displayio_layout_gridlayout_dividers.py
:linenos:

Pygame simple test
------------------

Expand Down
85 changes: 85 additions & 0 deletions examples/displayio_layout_gridlayout_dividers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# SPDX-FileCopyrightText: 2021 Tim C, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Illustrate how to use divider lines with GridLayout
"""
import board
import displayio
import terminalio
from adafruit_display_text import label
from adafruit_displayio_layout.layouts.grid_layout import GridLayout

# use built in display (PyPortal, PyGamer, PyBadge, CLUE, etc.)
# see guide for setting up external displays (TFT / OLED breakouts, RGB matrices, etc.)
# https://learn.adafruit.com/circuitpython-display-support-using-displayio/display-and-display-bus
display = board.DISPLAY

# Make the display context
main_group = displayio.Group()
display.show(main_group)

layout = GridLayout(
x=10,
y=10,
width=200,
height=100,
grid_size=(2, 2),
cell_padding=8,
divider_lines=True, # divider lines around every cell
)
_labels = []

_labels.append(
label.Label(
terminalio.FONT, scale=2, x=0, y=0, text="Hello", background_color=0x770077
)
)
layout.add_content(_labels[0], grid_position=(0, 0), cell_size=(1, 1))
_labels.append(
label.Label(
terminalio.FONT, scale=2, x=0, y=0, text="World", background_color=0x007700
)
)
layout.add_content(_labels[1], grid_position=(1, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Hello"))
layout.add_content(_labels[2], grid_position=(0, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Grid"))
layout.add_content(_labels[3], grid_position=(1, 1), cell_size=(1, 1))

main_group.append(layout)

other_layout = GridLayout(
x=10,
y=120,
width=140,
height=80,
grid_size=(2, 3),
cell_padding=4,
h_divider_line_rows=(1, 2), # Lines before rows 1 and 2
)

other_layout.add_content(
label.Label(terminalio.FONT, text="0x0"), grid_position=(0, 0), cell_size=(1, 1)
)
other_layout.add_content(
label.Label(terminalio.FONT, text="0x1"), grid_position=(0, 1), cell_size=(1, 1)
)
other_layout.add_content(
label.Label(terminalio.FONT, text="0x2"), grid_position=(0, 2), cell_size=(1, 1)
)

other_layout.add_content(
label.Label(terminalio.FONT, text="1x0"), grid_position=(1, 0), cell_size=(1, 1)
)
other_layout.add_content(
label.Label(terminalio.FONT, text="1x1"), grid_position=(1, 1), cell_size=(1, 1)
)
other_layout.add_content(
label.Label(terminalio.FONT, text="1x2"), grid_position=(1, 2), cell_size=(1, 1)
)

main_group.append(other_layout)

while True:
pass