canvas: more work on table layout, still incomplete

This commit is contained in:
Paul Davis
2021-09-14 16:08:33 -06:00
parent 3195fb720a
commit c5fcdc1119
2 changed files with 276 additions and 102 deletions

View File

@@ -63,7 +63,15 @@ public:
uint32_t y;
};
void attach (Index upper_left, Index lower_right, Item*, PackOptions row_options = PackOptions (0), PackOptions col_options = PackOptions (0));
void add (Item *);
void add_front (Item *);
void remove (Item *);
void attach (Item*, Index const & upper_left, Index const & lower_right, PackOptions row_options = PackOptions (0), PackOptions col_options = PackOptions (0), FourDimensions padding = FourDimensions (0.));
void dettach (Item*);
void set_row_size (uint32_t row, Distance);
void set_col_size (uint32_t row, Distance);
protected:
void child_changed (bool bbox_changed);
@@ -87,16 +95,20 @@ public:
Item* content;
PackOptions row_options;
PackOptions col_options;
Rect cell;
Index upper_left;
Index lower_right;
Duple natural_size;
Duple allocate_size;
Duple full_size;
FourDimensions padding;
CellInfo (Item* i, PackOptions ro, PackOptions co, Rect c)
CellInfo (Item* i, PackOptions ro, PackOptions co, Index const & ul, Index const & lr, FourDimensions const & p)
: content (i)
, row_options (ro)
, col_options (co)
, cell (c)
, upper_left (ul)
, lower_right (lr)
, padding (p)
{}
};
@@ -119,15 +131,34 @@ public:
Distance natural_size;
Distance expand;
Distance shrink;
Distance user_size;
bool occupied;
Distance spacing;
AxisInfo() : expanders (0), shrinkers (0), natural_size (0) {}
AxisInfo() : expanders (0), shrinkers (0), natural_size (0), expand (0), shrink (0), user_size (0), occupied (false), spacing (0) {}
void reset () {
expanders = 0;
shrinkers = 0;
natural_size = 0;
expand = 0;
shrink = 0;
/* leave user size alone */
occupied = false;
spacing = 0;
}
};
typedef std::vector<AxisInfo> AxisInfos;
AxisInfos row_info;
AxisInfos col_info;
void _add (Item *);
void _add_front (Item *);
void _remove (Item *);
void layout ();
Duple compute (Rect const &);
};
} /* namespace */

View File

@@ -16,6 +16,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pbd/error.h"
#include "pbd/i18n.h"
#include "canvas/table.h"
using namespace ArdourCanvas;
@@ -27,18 +30,34 @@ using std::endl;
Table::Table (Canvas* canvas)
: Rectangle (canvas)
, collapse_on_hide (false)
, homogenous (true)
, draw_hgrid (false)
, draw_vgrid (false)
{
set_layout_sensitive (true);
}
Table::Table (Item* item)
: Rectangle (item)
, collapse_on_hide (false)
, homogenous (true)
, draw_hgrid (false)
, draw_vgrid (false)
{
set_layout_sensitive (true);
}
void
Table::attach (Table::Index upper_left, Table::Index lower_right, Item* item, PackOptions row_options, PackOptions col_options)
Table::attach (Item* item, Table::Index const & upper_left, Table::Index const & lower_right, PackOptions row_options, PackOptions col_options, FourDimensions pad)
{
cells.insert ({ Index (upper_left.x, upper_left.y), CellInfo (item, row_options, col_options, Rect (upper_left.x, upper_left.y, lower_right.x, lower_right.y)) });
/* XXX maybe use z-axis to stack elements if the insert fails? Would
* involve making Index 3D and using an actual hash function
*/
if (cells.insert ({ Index (upper_left.x, upper_left.y), CellInfo (item, row_options, col_options, upper_left, lower_right, pad) }).second) {
_add (item);
}
}
void
@@ -78,29 +97,47 @@ Table::compute_bounding_box() const
}
void
Table::set_row_size (uint32_t row, Distance size)
{
if (row_info.size() <= row) {
row_info.resize (row+1);
}
row_info[row].user_size = size;
}
void
Table::set_col_size (uint32_t col, Distance size)
{
if (col_info.size() <= col) {
col_info.resize (col+1);
}
col_info[col].user_size = size;
}
void
Table::size_request (Distance& w, Distance& h) const
{
int rowmax = 0;
int colmax = 0;
uint32_t rowmax = 0;
uint32_t colmax = 0;
for (auto& ci : cells) {
CellInfo const & c (ci.second);
if (c.cell.x1 > rowmax) {
rowmax = c.cell.x1;
if (c.lower_right.x > rowmax) {
rowmax = c.lower_right.x;
}
if (c.cell.y1 > colmax) {
colmax = c.cell.y1;
if (c.lower_right.y > colmax) {
colmax = c.lower_right.y;
}
}
AxisInfos rinfo;
AxisInfos cinfo;
rinfo.resize (rowmax);
cinfo.resize (colmax);
rinfo.resize (rowmax+1);
cinfo.resize (colmax+1);
for (auto& ci : cells) {
@@ -110,8 +147,8 @@ Table::size_request (Distance& w, Distance& h) const
CellInfo const & c (ci.second);
c.content->size_request (cw, ch);
rinfo[c.cell.x0].natural_size += cw;
cinfo[c.cell.y0].natural_size += ch;
rinfo[c.upper_left.x].natural_size += cw;
cinfo[c.upper_left.y].natural_size += ch;
}
w = 0;
@@ -140,22 +177,28 @@ Table::layout ()
void
Table::size_allocate_children (Rect const & within)
{
(void) compute (within);
}
Duple
Table::compute (Rect const & within)
{
/* step 1: traverse all current cells and determine how many rows and
* columns we need. While doing that, get the current natural size of
* each cell.
*/
int rowmax = 0;
int colmax = 0;
uint32_t rowmax = 0;
uint32_t colmax = 0;
for (auto& ci : cells) {
CellInfo const & c (ci.second);
if (c.cell.x1 > rowmax) {
rowmax = c.cell.x1;
if (c.lower_right.x > rowmax) {
rowmax = c.lower_right.x;
}
if (c.cell.y1 > colmax) {
colmax = c.cell.y1;
if (c.lower_right.y > colmax) {
colmax = c.lower_right.y;
}
}
@@ -165,52 +208,55 @@ Table::size_allocate_children (Rect const & within)
row_info.resize (rowmax);
col_info.resize (colmax);
for (auto & ai : row_info) {
ai.reset ();
}
for (auto & ai : col_info) {
ai.reset ();
}
for (auto& ci : cells) {
CellInfo & c (ci.second);
c.content->size_request (c.natural_size.x, c.natural_size.y);
row_info[c.cell.x0].natural_size += c.natural_size.x;
const float hspan = c.lower_right.x - c.upper_left.x;
const float vspan = c.lower_right.y - c.upper_left.y;
if (c.row_options & PackExpand) {
row_info[c.cell.x0].expanders++;
}
for (uint32_t row = c.upper_left.x; row != c.lower_right.x; ++row) {
if (c.row_options & PackShrink) {
col_info[c.cell.x0].shrinkers++;
}
if (c.cell.x1 != c.cell.x0) {
row_info[c.cell.x1].natural_size += c.natural_size.x;
if (c.row_options & PackExpand) {
row_info[c.cell.x1].expanders++;
row_info[row].expanders++;
}
if (c.row_options & PackShrink) {
row_info[c.cell.x1].shrinkers++;
row_info[row].shrinkers++;
}
row_info[row].natural_size += c.natural_size.x / hspan;
col_info[row].natural_size += c.padding.left + c.padding.right;
col_info[row].natural_size += col_info[row].spacing;
row_info[row].occupied = true;
}
col_info[c.cell.y0].natural_size += c.natural_size.y;
if (c.row_options & PackExpand) {
col_info[c.cell.y0].expanders++;
}
if (c.row_options & PackShrink) {
col_info[c.cell.y0].shrinkers++;
}
if (c.cell.y1 != c.cell.y0) {
col_info[c.cell.y1].natural_size += c.natural_size.y;
for (uint32_t col = c.upper_left.y; col != c.lower_right.y; ++col) {
if (c.col_options & PackExpand) {
col_info[c.cell.y1].expanders++;
col_info[col].expanders++;
}
if (c.row_options & PackShrink) {
col_info[c.cell.y1].shrinkers++;
if (c.col_options & PackShrink) {
col_info[col].shrinkers++;
}
col_info[col].natural_size += c.natural_size.y / vspan;
col_info[col].natural_size += c.padding.up + c.padding.down;
col_info[col].natural_size += col_info[c.lower_right.y].spacing;
col_info[col].occupied = true;
}
}
@@ -222,7 +268,9 @@ Table::size_allocate_children (Rect const & within)
const uint32_t rows = rowmax;
const uint32_t cols = colmax;
/* Find the tallest column and widest row */
/* Find the tallest column and widest row. This will give us our
* "natural size"
*/
Distance natural_row_width = 0.;
Distance natural_col_height = 0.;
@@ -234,6 +282,11 @@ Table::size_allocate_children (Rect const & within)
natural_col_height = std::max (natural_col_height, ai->natural_size);
}
if (!within) {
/* within is empty, so this is just for a size request */
return Duple (natural_row_width, natural_col_height);
}
/* step two: compare the natural size to the size we've been given
*
* If the natural size is less than the allocated size, then find the
@@ -250,65 +303,91 @@ Table::size_allocate_children (Rect const & within)
*
*/
Distance per_cell_width = within.width() / cols;
if (homogenous) {
for (AxisInfos::iterator ai = row_info.begin(); ai != row_info.end(); ++ai) {
if (natural_row_width < within.width() && ai->expanders) {
Distance delta = within.width() - natural_row_width;
ai->expand = delta / ai->expanders;
} else if (natural_row_width > within.width() && ai->shrinkers) {
Distance delta = within.width() - natural_row_width;
ai->shrink = delta / ai->shrinkers;
Distance per_cell_width = within.width() / cols;
Distance per_cell_height = within.height() / rows;
/* compute total expansion or contraction that will be
* distributed across all rows & cols marked for expand/shrink
*/
for (auto & ai : row_info) {
if (natural_row_width < within.width() && ai.expanders) {
Distance delta = within.width() - natural_row_width;
ai.expand = delta / ai.expanders;
} else if (natural_row_width > within.width() && ai.shrinkers) {
Distance delta = within.width() - natural_row_width;
ai.shrink = delta / ai.shrinkers;
}
}
for (auto & ai : col_info) {
if (natural_col_height < within.height() && ai.expanders) {
Distance delta = within.height() - natural_col_height;
ai.expand = delta / ai.expanders;
} else if (natural_col_height > within.height() && ai.shrinkers) {
Distance delta = within.height() - natural_col_height;
ai.shrink = delta / ai.shrinkers;
}
}
for (auto& ci : cells) {
CellInfo & c (ci.second);
const float hspan = c.lower_right.x - c.upper_left.x;
const float vspan = c.lower_right.y - c.upper_left.y;
Distance w;
Distance h;
AxisInfo& col (col_info[c.upper_left.y]);
AxisInfo& row (col_info[c.upper_left.x]);
if (c.row_options & PackExpand) {
w = hspan * (per_cell_width + row.expand);
} else if (c.row_options & PackShrink) {
w = hspan * (per_cell_width + row.shrink); /* note: row_shrink is negative */
} else {
w = hspan * per_cell_width;
}
if (c.col_options & PackExpand) {
h = vspan * (per_cell_height + col.expand);
} else if (c.col_options & PackShrink) {
h = vspan * (per_cell_height + col.shrink); /* note: col_shrink is negative */
} else {
h = vspan * per_cell_height;
}
w -= c.padding.left + c.padding.right;
w -= col.spacing;
h -= c.padding.up + c.padding.down;
h -= row.spacing;
c.allocate_size = Duple (w, h);
}
} else {
/* not homogenous */
}
Distance per_cell_height = within.height() / rows;
for (AxisInfos::iterator ai = col_info.begin(); ai != col_info.end(); ++ai) {
if (natural_col_height < within.height() && ai->expanders) {
Distance delta = within.height() - natural_col_height;
ai->expand = delta / ai->expanders;
} else if (natural_col_height > within.height() && ai->shrinkers) {
Distance delta = within.height() - natural_col_height;
ai->shrink = delta / ai->shrinkers;
}
}
for (auto& ci : cells) {
CellInfo & c (ci.second);
Distance w;
Distance h;
AxisInfo& col (col_info[c.cell.y0]);
AxisInfo& row (col_info[c.cell.x0]);
if (c.row_options & PackExpand) {
w = per_cell_width + row.expand;
} else if (c.row_options & PackShrink) {
w = per_cell_width + row.shrink; /* note: row_shrink is negative */
} else {
w = per_cell_width;
}
if (c.col_options & PackExpand) {
h = per_cell_height + col.expand;
} else if (c.col_options & PackShrink) {
h = per_cell_height + col.shrink; /* note: col_shrink is negative */
} else {
h = per_cell_height;
}
c.allocate_size = Duple (w, h);
}
/* final pass: actually allocate position for each cell */
/* final pass: actually allocate position for each cell. Do this in a
* row,col order so that we can set up position based on all cells
* above and left of whichever one we are working on.
*/
Distance hdistance = 0.;
Distance vdistance = 0.;
for (uint32_t r = 0; r < rows; ++r) {
Distance vshift = 0;
for (uint32_t c = 0; c < cols; ++c) {
Index idx (r, c);
@@ -316,13 +395,77 @@ Table::size_allocate_children (Rect const & within)
if (ci != cells.end()) {
Rect rect = Rect (hdistance, vdistance, hdistance + ci->second.allocate_size.x, vdistance + ci->second.allocate_size.y);
hdistance += ci->second.padding.left;
Rect rect = Rect (hdistance, vdistance + ci->second.padding.up, hdistance + ci->second.allocate_size.x, vdistance + ci->second.allocate_size.y);
ci->second.content->size_allocate (rect);
hdistance += rect.width() + ci->second.padding.right;
hdistance += col_info[c].spacing;
const Distance total_cell_height = rect.height() + ci->second.padding.up + ci->second.padding.down;
vshift = std::max (vshift, total_cell_height);
} else {
/* this cell (r, c) has no item starting within it */
}
}
hdistance = 0.;
vshift += row_info[r].spacing;
vdistance += vshift;
}
return Duple (hdistance, vdistance);
}
void
Table::add (Item*)
{
fatal << _("programming error: add() cannot be used with Canvas::Table; use attach() instead") << endmsg;
}
void
Table::add_front (Item*)
{
fatal << _("programming error: add_front() cannot be used with Canvas::Table; use attach() instead") << endmsg;
}
void
Table::remove (Item*)
{
fatal << _("programming error: remove() cannot be used with Canvas::Table; use detach() instead") << endmsg;
}
void
Table::_add (Item* i)
{
if (!i) {
return;
}
Item::add (i);
queue_resize ();
}
void
Table::_add_front (Item* i)
{
if (!i) {
return;
}
Item::add_front (i);
queue_resize ();
}
void
Table::_remove (Item* i)
{
if (!i) {
return;
}
Item::remove (i);
queue_resize ();
}