gyscos / cursive

A Text User Interface library for the Rust programming language
MIT License
4.29k stars 244 forks source link

Add a GridView #121

Open gyscos opened 7 years ago

gyscos commented 7 years ago

As a replacement for ListView (and maybe LinearLayout?).

gyscos commented 7 years ago

I've been thinking about what an API to build a grid would look like. I'm thinking about providing a few different ways to populate such a view, to fit multiple use cases:

// For complex grid layout, directly specifying the children coordinates and sizes may be easier
GridView::with_size(3, 3)
    .child((0, 0), (1, 2), TextView::new("w\n o\n  w"))
    .child((1, 0), (2, 1), TextView::new("kowl"))
    .child((0, 2), (2, 1), TextView::new("very!"))
    .child((2, 1), (1, 2), TextView::new("! !\n ?\n! !"))
    .child((1, 1), (1, 1), TextView::new("~"));

// A row-by-row description a la HTML may feel natural
GridView::empty()
    .row(Row::new()
        .child(TextView::new("...")).rspan(2)
        .child(TextView::new("...")).cspan(2))
    .row(Row::new()
        .skip()
        .child(TextView::new("~"))
        .child(TextView::new("...")).rspan(2))
    .row(Row::new()
        .child(TextView::new("...")).cspan(2)
        .skip());

// To replace the current LinearLayout, we need some easy way to add full-width rows (or full-height columns)
GridView::empty()
    .row_item(TextView::new(""))
    .row_item(TextView::new(""))
    .row_item(TextView::new(""))
    .row_item(TextView::new(""));

The alternative is to have some newtype Views that wrap the base GridView API into either row-by-row or LinearLayout-like - but I'm not sure that's a better idea.

gyscos commented 7 years ago

The layout algorithm would look a lot like the current LinearLayout one:

However, two parts are going to be a bit more involved:

The first one looks like an optimization problem with constraints, for instance:

Required sizes for each view:
┌───┬─┐
│ 2 │1│
├─┬─┴─┤
│1│ 3 │
└─┴───┘
col_1 + col_2 >= 2
col_3         >= 1
col_1         >= 1
col_2 + col_3 >= 3

A minimal result is either 1, 2, 1 or 1, 1, 2.

Looks like something that can be done with a simplex algorithm. More specifically, for each child and its associated constraint, transform the constraint into an equality:

col_1 + col_2 - extra_A = 2
col_3 - extra_B         = 1
col_1 - extra_C         = 1
col_2 + col_3 - extra_D = 3

col_1, col_2, col_3, extra_A, extra_B, extra_C, extra_D >= 0

maximize - (col_1 + col_2 + col_3)

(extra_A is the extra space we give the view A in addition to what it asks for)

We now have a set of constraints that match the requirement for the simplex algorithm.