add a very (very) basic resize/layout design to the canvas

Items call ::queue_resize(), which sets a flag in the canvas; at next idle, we call
Canvas::layout() which walks the item tree and recursively calls layout (depth first)
on all items needing a resize.

Only Container types implement layout, and so far only Box
This commit is contained in:
Paul Davis 2018-11-19 11:31:09 -05:00
parent 03e32a22d0
commit 12b4807bc9
6 changed files with 197 additions and 99 deletions

View file

@ -19,54 +19,55 @@
#include <algorithm>
#include "pbd/unwind.h"
#include "canvas/box.h"
#include "canvas/rectangle.h"
using namespace ArdourCanvas;
Box::Box (Canvas* canvas, Orientation o)
: Item (canvas)
: Rectangle (canvas)
, orientation (o)
, spacing (0)
, top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
, top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
, homogenous (false)
, ignore_child_changes (false)
{
self = new Rectangle (this);
self->set_outline (false);
self->set_fill (false);
}
Box::Box (Item* parent, Orientation o)
: Item (parent)
: Rectangle (parent)
, orientation (o)
, spacing (0)
, top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
, top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
, homogenous (false)
, ignore_child_changes (false)
{
self = new Rectangle (this);
self->set_outline (false);
self->set_fill (false);
}
Box::Box (Item* parent, Duple const & p, Orientation o)
: Item (parent, p)
: Rectangle (parent)
, orientation (o)
, spacing (0)
, top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
, top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
, homogenous (false)
, ignore_child_changes (false)
{
self = new Rectangle (this);
self->set_outline (false);
self->set_fill (false);
set_position (p);
}
void
Box::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
if (_fill || _outline) {
Rectangle::render (area, context);
}
Item::render_children (area, context);
}
@ -85,10 +86,14 @@ Box::compute_bounding_box () const
if (_bounding_box) {
Rect r = _bounding_box;
_bounding_box = r.expand (top_padding + outline_width() + top_margin,
/* left and top margin and padding is already built into the
* position of children
*/
_bounding_box = r.expand (0.0,
right_padding + outline_width() + right_margin,
bottom_padding + outline_width() + bottom_margin,
left_padding + outline_width() + left_margin);
0.0);
}
_bounding_box_dirty = false;
@ -140,36 +145,17 @@ Box::set_margin (double t, double r, double b, double l)
left_margin = last;
}
void
Box::reset_self ()
{
if (_bounding_box_dirty) {
compute_bounding_box ();
}
if (!_bounding_box) {
self->hide ();
return;
}
Rect r (_bounding_box);
/* XXX need to shrink by margin */
self->set (r);
}
void
Box::reposition_children ()
{
Duple previous_edge (0, 0);
Duple previous_edge = Duple (left_margin+left_padding, top_margin+top_padding);
Distance largest_width = 0;
Distance largest_height = 0;
Rect uniform_size;
if (homogenous) {
for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
for (std::list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
Rect bb = (*i)->bounding_box();
if (bb) {
largest_height = std::max (largest_height, bb.height());
@ -180,7 +166,11 @@ Box::reposition_children ()
uniform_size = Rect (0, 0, largest_width, largest_height);
}
for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
{
PBD::Unwinder<bool> uw (ignore_child_changes, true);
for (std::list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
(*i)->set_position (previous_edge);
@ -188,24 +178,23 @@ Box::reposition_children ()
(*i)->size_allocate (uniform_size);
}
double width;
double height;
(*i)->size_request (width, height);
if (orientation == Vertical) {
Distance shift = 0;
Rect bb = (*i)->bounding_box();
if (!(*i)->visible()) {
/* invisible child */
if (!collapse_on_hide) {
/* still add in its size */
if (bb) {
shift += bb.height();
}
shift += height;
}
} else {
if (bb) {
shift += bb.height();
}
shift += height;
}
previous_edge = previous_edge.translate (Duple (0, spacing + shift));
@ -213,55 +202,63 @@ Box::reposition_children ()
} else {
Distance shift = 0;
Rect bb = (*i)->bounding_box();
if (!(*i)->visible()) {
if (!collapse_on_hide) {
if (bb) {
shift += bb.width();
}
shift += width;
}
} else {
if (bb) {
shift += bb.width();
}
shift += width;
}
previous_edge = previous_edge.translate (Duple (spacing + shift, 0));
}
}
}
_bounding_box_dirty = true;
reset_self ();
}
void
Box::pack_end (Item* i, double extra_padding)
{
if (!i) {
return;
}
/* prepend new child */
Item::add_front (i);
reposition_children ();
}
void
Box::pack_start (Item* i, double extra_padding)
{
if (!i) {
return;
}
/* append new child */
Item::add (i);
reposition_children ();
}
void
Box::add (Item* i)
{
pack_start (i);
if (!i) {
return;
}
Item::add (i);
queue_resize ();
}
void
Box::add_front (Item* i)
{
if (!i) {
return;
}
Item::add_front (i);
queue_resize ();
}
void
Box::layout ()
{
bool yes_do_it = _resize_queued;
Item::layout ();
if (yes_do_it) {
reposition_children ();
compute_bounding_box ();
const double w = std::max (requested_width, _bounding_box.width());
const double h = std::max (requested_height, _bounding_box.height());
set (Rect (get().x0, get().y0, get().x0 + w, get().y0 + h));
std::cerr << name << " box layed out, reset to " << get() << std::endl;
}
}
void
@ -269,7 +266,12 @@ Box::child_changed (bool bbox_changed)
{
/* catch visibility and size changes */
if (ignore_child_changes) {
return;
}
Item::child_changed (bbox_changed);
reposition_children ();
}

View file

@ -454,6 +454,7 @@ GtkCanvas::GtkCanvas ()
, current_tooltip_item (0)
, tooltip_window (0)
, _in_dtor (false)
, resize_queued (false)
, _nsglview (0)
{
#ifdef USE_CAIRO_IMAGE_SURFACE /* usually Windows builds */
@ -1457,6 +1458,23 @@ GtkCanvas::get_pango_context ()
return Glib::wrap (gdk_pango_context_get());
}
void
GtkCanvas::queue_resize ()
{
if (!resize_queued) {
Glib::signal_idle().connect (sigc::mem_fun (*this, &GtkCanvas::resize_handler));
resize_queued = true;
}
}
bool
GtkCanvas::resize_handler ()
{
resize_queued = false;
_root.layout ();
return false;
}
/** Create a GtkCanvaSViewport.
* @param hadj Adjustment to use for horizontal scrolling.
* @param vadj Adjustment to use for vertica scrolling.

View file

@ -60,8 +60,8 @@ public:
void set_border_color (Gtkmm2ext::Color c) { set_outline_color (c); }
void add (Item*);
void pack_start (Item*, double extra_padding = 0);
void pack_end (Item*, double extra_padding = 0);
void add_front (Item*);
void layout ();
void set_collapse_on_hide (bool);
void set_homogenous (bool);

View file

@ -83,6 +83,8 @@ public:
virtual void grab (Item *) = 0;
/** called to ask the canvas' host to `ungrab' any grabbed item */
virtual void ungrab () = 0;
/** called to ask for a resize/relayout of all or part of the canvas */
virtual void queue_resize () = 0;
/** called to ask the canvas' host to keyboard focus on an item */
virtual void focus (Item *) = 0;
@ -187,6 +189,7 @@ protected:
static uint32_t tooltip_timeout_msecs;
void queue_draw_item_area (Item *, Rect);
virtual void pick_current_item (int state) = 0;
virtual void pick_current_item (Duple const &, int state) = 0;
@ -227,6 +230,7 @@ public:
void queue_draw ();
void queue_draw_area (int x, int y, int width, int height);
void queue_resize ();
Glib::RefPtr<Pango::Context> get_pango_context();
@ -288,8 +292,10 @@ private:
bool show_tooltip ();
void hide_tooltip ();
bool really_start_tooltip_timeout ();
bool resize_handler ();
bool _in_dtor;
bool resize_queued;
void* _nsglview;
Cairo::RefPtr<Cairo::Surface> _canvas_image;

View file

@ -272,7 +272,20 @@ public:
virtual void dump (std::ostream&) const;
std::string whatami() const;
protected:
bool resize_queued() const { return _resize_queued; }
void queue_resize();
/* only derived containers need to implement this, but this
is where they compute the sizes and position and their
children. A fixed-layout container (i.e. one where every child
has just had its position fixed via ::set_position()) does not
need to do anything here. Only box/table/grid style containers,
where the position of one child depends on the position and size of
other children, need to provide an implementation.
*/
virtual void layout();
protected:
friend class Fill;
friend class Outline;
@ -335,6 +348,10 @@ protected:
public:
Duple position_offset() const;
bool _resize_queued;
double requested_width;
double requested_height;
private:
void init ();

View file

@ -46,6 +46,9 @@ Item::Item (Canvas* canvas)
, _intrinsic_width (-1.)
, _intrinsic_height(-1.)
, _lut (0)
, _resize_queued (false)
, requested_width (-1)
, requested_height (-1)
, _ignore_events (false)
{
DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
@ -63,6 +66,9 @@ Item::Item (Item* parent)
, _intrinsic_width (-1.)
, _intrinsic_height(-1.)
, _lut (0)
, _resize_queued (false)
, requested_width (-1)
, requested_height (-1)
, _ignore_events (false)
{
DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
@ -86,6 +92,7 @@ Item::Item (Item* parent, Duple const& p)
, _intrinsic_width (-1.)
, _intrinsic_height(-1.)
, _lut (0)
, _resize_queued (false)
, _ignore_events (false)
{
DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
@ -292,6 +299,18 @@ Item::set_position (Duple p)
}
}
void
Item::layout()
{
for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
if ((*i)->resize_queued()) {
(*i)->layout ();
}
}
_resize_queued = false;
}
void
Item::set_x_position (Coord x)
{
@ -590,6 +609,28 @@ Item::size_allocate (Rect const & r)
}
}
void
Item::size_request (double& w, double& h) const
{
Rect r (bounding_box());
w = std::max (requested_width, r.width());
h = std::max (requested_height, r.height());
}
void
Item::set_size_request (double w, double h)
{
/* allow reset to zero or require that both are positive */
begin_change ();
requested_width = w;
requested_height = h;
_bounding_box_dirty = true;
end_change ();
}
/** @return Bounding box in this item's coordinates */
ArdourCanvas::Rect
Item::bounding_box () const
@ -916,6 +957,20 @@ Rect self;
}
}
void
Item::queue_resize()
{
_resize_queued = true;
if (_parent) {
_parent->queue_resize ();
}
if (this == _canvas->root()) {
_canvas->queue_resize ();
}
}
void
Item::add (Item* i)
{