mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-10 23:46:20 +01:00
canvas: initial (probably incorrect) implementation of Canvas::Table
This commit is contained in:
parent
1288262ca7
commit
44b11802bf
3 changed files with 464 additions and 0 deletions
135
libs/canvas/canvas/table.h
Normal file
135
libs/canvas/canvas/table.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Paul Davis <paul@linuxaudiosystems.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __CANVAS_TABLE_H__
|
||||
#define __CANVAS_TABLE_H__
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "canvas/rectangle.h"
|
||||
|
||||
namespace ArdourCanvas
|
||||
{
|
||||
|
||||
class Rectangle;
|
||||
|
||||
class LIBCANVAS_API Table : public Rectangle
|
||||
{
|
||||
public:
|
||||
Table (Canvas *);
|
||||
Table (Item *);
|
||||
|
||||
#if 0
|
||||
void set_spacing (double s);
|
||||
void set_padding (double top, double right = -1.0, double bottom = -1.0, double left = -1.0);
|
||||
void set_margin (double top, double right = -1.0, double bottom = -1.0, double left = -1.0);
|
||||
|
||||
/* aliases so that CSS box model terms work */
|
||||
void set_border_width (double w) { set_outline_width (w); }
|
||||
void set_border_color (Gtkmm2ext::Color c) { set_outline_color (c); }
|
||||
|
||||
void set_collapse_on_hide (bool);
|
||||
void set_homogenous (bool);
|
||||
#endif
|
||||
|
||||
void compute_bounding_box () const;
|
||||
void size_request (double& w, double& h) const;
|
||||
void size_allocate_children (Rect const & r);
|
||||
void _size_allocate (Rect const & r);
|
||||
|
||||
struct Index {
|
||||
Index (uint32_t xv, uint32_t yv) : x (xv), y (yv) {}
|
||||
|
||||
bool operator== (Index const & other) const {
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
};
|
||||
|
||||
void attach (Index upper_left, Index lower_right, Item*, PackOptions row_options = PackOptions (0), PackOptions col_options = PackOptions (0));
|
||||
|
||||
protected:
|
||||
void child_changed (bool bbox_changed);
|
||||
|
||||
private:
|
||||
Distance top_margin;
|
||||
Distance right_margin;
|
||||
Distance bottom_margin;
|
||||
Distance left_margin;
|
||||
Distance row_spacing;
|
||||
Distance col_spacing;
|
||||
|
||||
bool collapse_on_hide;
|
||||
bool homogenous;
|
||||
bool draw_hgrid;
|
||||
bool draw_vgrid;
|
||||
|
||||
mutable bool ignore_child_changes;
|
||||
|
||||
struct CellInfo {
|
||||
Item* content;
|
||||
PackOptions row_options;
|
||||
PackOptions col_options;
|
||||
Rect cell;
|
||||
Duple natural_size;
|
||||
Duple allocate_size;
|
||||
Duple full_size;
|
||||
|
||||
CellInfo (Item* i, PackOptions ro, PackOptions co, Rect c)
|
||||
: content (i)
|
||||
, row_options (ro)
|
||||
, col_options (co)
|
||||
, cell (c)
|
||||
{}
|
||||
};
|
||||
|
||||
struct index_hash {
|
||||
std::size_t operator() (Table::Index const & i) const {
|
||||
/* use upper left coordinate for hash */
|
||||
return ((uint64_t) i.x << 32) | i.y;
|
||||
}
|
||||
};
|
||||
|
||||
/* cell lookup, indexed by upper left corner only
|
||||
*/
|
||||
|
||||
typedef std::unordered_map<Index, CellInfo, index_hash> Cells;
|
||||
Cells cells;
|
||||
|
||||
struct AxisInfo {
|
||||
uint32_t expanders;
|
||||
uint32_t shrinkers;
|
||||
Distance natural_size;
|
||||
Distance expand;
|
||||
Distance shrink;
|
||||
|
||||
AxisInfo() : expanders (0), shrinkers (0), natural_size (0) {}
|
||||
};
|
||||
|
||||
typedef std::vector<AxisInfo> AxisInfos;
|
||||
AxisInfos row_info;
|
||||
AxisInfos col_info;
|
||||
|
||||
void layout ();
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* __CANVAS_TABLE_H__ */
|
||||
328
libs/canvas/table.cc
Normal file
328
libs/canvas/table.cc
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Paul Davis <paul@linuxaudiosystems.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "canvas/table.h"
|
||||
|
||||
using namespace ArdourCanvas;
|
||||
using namespace PBD;
|
||||
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
|
||||
Table::Table (Canvas* canvas)
|
||||
: Rectangle (canvas)
|
||||
{
|
||||
}
|
||||
|
||||
Table::Table (Item* item)
|
||||
: Rectangle (item)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Table::attach (Table::Index upper_left, Table::Index lower_right, Item* item, PackOptions row_options, PackOptions col_options)
|
||||
{
|
||||
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)) });
|
||||
}
|
||||
|
||||
void
|
||||
Table::child_changed (bool bbox_changed)
|
||||
{
|
||||
if (ignore_child_changes) {
|
||||
return;
|
||||
}
|
||||
|
||||
Item::child_changed (bbox_changed);
|
||||
size_allocate_children (_allocation);
|
||||
}
|
||||
|
||||
void
|
||||
Table::compute_bounding_box() const
|
||||
{
|
||||
_bounding_box = Rect();
|
||||
if (cells.empty()) {
|
||||
bb_clean ();
|
||||
return;
|
||||
}
|
||||
|
||||
add_child_bounding_boxes (!collapse_on_hide);
|
||||
|
||||
if (_bounding_box) {
|
||||
#if 0
|
||||
Rect r = _bounding_box;
|
||||
|
||||
_bounding_box = r.expand (top_padding + outline_width() + top_margin,
|
||||
right_padding + outline_width() + right_margin,
|
||||
bottom_padding + outline_width() + bottom_margin,
|
||||
left_padding + outline_width() + left_margin);
|
||||
#endif
|
||||
}
|
||||
|
||||
bb_clean ();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Table::size_request (Distance& w, Distance& h) const
|
||||
{
|
||||
int rowmax = 0;
|
||||
int colmax = 0;
|
||||
|
||||
for (auto& ci : cells) {
|
||||
CellInfo const & c (ci.second);
|
||||
|
||||
if (c.cell.x1 > rowmax) {
|
||||
rowmax = c.cell.x1;
|
||||
}
|
||||
|
||||
if (c.cell.y1 > colmax) {
|
||||
colmax = c.cell.y1;
|
||||
}
|
||||
}
|
||||
|
||||
AxisInfos rinfo;
|
||||
AxisInfos cinfo;
|
||||
|
||||
rinfo.resize (rowmax);
|
||||
cinfo.resize (colmax);
|
||||
|
||||
for (auto& ci : cells) {
|
||||
|
||||
Distance cw;
|
||||
Distance ch;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
w = 0;
|
||||
h = 0;
|
||||
|
||||
for (auto& ai : rinfo) {
|
||||
w = std::max (w, ai.natural_size);
|
||||
}
|
||||
|
||||
for (auto& ai : cinfo) {
|
||||
h = std::max (h, ai.natural_size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Table::_size_allocate (Rect const & r)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
void
|
||||
Table::layout ()
|
||||
{
|
||||
size_allocate_children (_allocation);
|
||||
}
|
||||
|
||||
void
|
||||
Table::size_allocate_children (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;
|
||||
|
||||
for (auto& ci : cells) {
|
||||
CellInfo const & c (ci.second);
|
||||
if (c.cell.x1 > rowmax) {
|
||||
rowmax = c.cell.x1;
|
||||
}
|
||||
if (c.cell.y1 > colmax) {
|
||||
colmax = c.cell.y1;
|
||||
}
|
||||
}
|
||||
|
||||
row_info.clear ();
|
||||
col_info.clear ();
|
||||
|
||||
row_info.resize (rowmax);
|
||||
col_info.resize (colmax);
|
||||
|
||||
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;
|
||||
|
||||
if (c.row_options & PackExpand) {
|
||||
row_info[c.cell.x0].expanders++;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
if (c.row_options & PackShrink) {
|
||||
row_info[c.cell.x1].shrinkers++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if (c.col_options & PackExpand) {
|
||||
col_info[c.cell.y1].expanders++;
|
||||
}
|
||||
if (c.row_options & PackShrink) {
|
||||
col_info[c.cell.y1].shrinkers++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* rows with nothing in them are still counted as existing. This is a
|
||||
* design decision, not a logic inevitability.
|
||||
*/
|
||||
|
||||
const uint32_t rows = rowmax;
|
||||
const uint32_t cols = colmax;
|
||||
|
||||
/* Find the tallest column and widest row */
|
||||
|
||||
Distance natural_row_width = 0.;
|
||||
Distance natural_col_height = 0.;
|
||||
|
||||
for (AxisInfos::iterator ai = row_info.begin(); ai != row_info.end(); ++ai) {
|
||||
natural_row_width = std::max (natural_row_width, ai->natural_size);
|
||||
}
|
||||
for (AxisInfos::iterator ai = col_info.begin(); ai != col_info.end(); ++ai) {
|
||||
natural_col_height = std::max (natural_col_height, ai->natural_size);
|
||||
}
|
||||
|
||||
/* 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
|
||||
* difference, divide it by the number of expanding items per
|
||||
* (row|col). Divide the total size by the number of (rows|cols), then
|
||||
* iterate. Allocate expanders the per-cell size plus the extra for
|
||||
* expansion. Allocate shrinkers/default just the per-cell size.
|
||||
*
|
||||
* If the natural size if greated than the allocated size, find the
|
||||
* difference, divide it by the number of shrinking items per
|
||||
* (row|col). Divide the total size by the number of (rows|cols), then
|
||||
* iterate. Allocate shrinkers the per-cell size minus the excess for
|
||||
* shrinking. Allocate expanders/default just the per-cell size.
|
||||
*
|
||||
*/
|
||||
|
||||
Distance per_cell_width = within.width() / cols;
|
||||
|
||||
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_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 */
|
||||
|
||||
Distance hdistance = 0.;
|
||||
Distance vdistance = 0.;
|
||||
|
||||
for (uint32_t r = 0; r < rows; ++r) {
|
||||
|
||||
for (uint32_t c = 0; c < cols; ++c) {
|
||||
|
||||
Index idx (r, c);
|
||||
Cells::iterator ci = cells.find (idx);
|
||||
|
||||
if (ci != cells.end()) {
|
||||
|
||||
Rect rect = Rect (hdistance, vdistance, hdistance + ci->second.allocate_size.x, vdistance + ci->second.allocate_size.y);
|
||||
ci->second.content->size_allocate (rect);
|
||||
} else {
|
||||
/* this cell (r, c) has no item starting within it */
|
||||
}
|
||||
}
|
||||
|
||||
hdistance = 0.;
|
||||
}
|
||||
}
|
||||
|
|
@ -60,6 +60,7 @@ canvas_sources = [
|
|||
'scroll_group.cc',
|
||||
'stateful_image.cc',
|
||||
'step_button.cc',
|
||||
'table.cc',
|
||||
'text.cc',
|
||||
'tracking_text.cc',
|
||||
'types.cc',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue