tuikit/layouts/grid.py
author Radek Brich <radek.brich@devl.cz>
Sun, 15 Feb 2015 12:52:46 +0100
changeset 115 b4ff7392003a
parent 114 26c02bd94bd9
permissions -rw-r--r--
GridLayout: basic implementation.

from .layout import Layout
from ..core.coords import Size


class GridCell:

    """Grid cell,

    This class contains attributes of the row/column
    and references to all widgets placed here.

    """

    def __init__(self, widget=None):
        self.widget = widget

    @property
    def sizemin(self):
        return self.widget.sizemin if self.widget else Size(0, 0)

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.widget)


class GridRow:
    pass


class GridColumn:
    pass


class GridLayout(Layout):

    """Lay out widgets in a grid.

    Grid size is determined by each widget's specified column and row.

    """

    def __init__(self):
        Layout.__init__(self)
        #: map grid coordinates (x,y) to Widget
        self._grid = {}
        #: size of grid, expanded to accommodate given grid coordinates
        self._grid_size = Size()
        #: GridRow object for each row contains row parameters
        self._rows = []
        #: GridColumn object for each column contains column parameters
        self._columns = []

    @property
    def row_count(self):
        return self._grid_size.h

    @property
    def column_count(self):
        return self._grid_size.w

    def add(self, widget, row, column):
        Layout.add(self, widget)
        self._grid_size.update(w=max(row + 1, self._grid_size.h),
                               h=max(column + 1, self._grid_size.w))
        if (row, column) in self._grid:
            raise Exception(
                'GridLayout.add: Cell (%s,%s) is occupied, cannot add widget.'
                % (row, column))
        self._grid[(row, column)] = GridCell(widget)

    def get_widget_at(self, row, column):
        cell = self._grid.get((row, column))
        return cell.widget if cell else None

    def update(self, w, h):
        row_height = []
        column_width = []

        # compute min. height of each row
        for row in range(self.row_count):
            minh = 0
            for col in range(self.column_count):
                widget = self.get_widget_at(row, col)
                if widget:
                    minh = max(minh, widget.sizemin.h)
            row_height.append(minh)

        # compute min. width of each column
        for col in range(self.column_count):
            minw = 0
            for row in range(self.row_count):
                widget = self.get_widget_at(row, col)
                if widget:
                    minw = max(minw, widget.sizemin.w)
            column_width.append(minw)

        # distribute rest of space to rows
        resth = h - sum(row_height)
        if resth > 0:
            addh = resth // self.row_count
            resth = resth % self.row_count
            for row in range(self.row_count):
                row_height[row] += addh
                if resth:
                    row_height[row] += 1
                    resth -= 1

        # distribute rest of space to columns
        restw = w - sum(column_width)
        if restw > 0:
            addw = restw // self.column_count
            restw = restw % self.column_count
            for col in range(self.column_count):
                column_width[col] += addw
                if restw:
                    column_width[col] += 1
                    restw -= 1

        # place widgets
        for row in range(self.row_count):
            for col in range(self.column_count):
                widget = self.get_widget_at(row, col)
                if widget:
                    widget.resize(column_width[col], row_height[row])
                    widget.move(sum(column_width[:col]), sum(row_height[:row]))