canvas: more work on table layout, still incomplete
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user