diff --git a/libs/canvas/box.cc b/libs/canvas/box.cc index eaf39f931e..50bac700a6 100644 --- a/libs/canvas/box.cc +++ b/libs/canvas/box.cc @@ -19,54 +19,55 @@ #include +#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 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::iterator i = _items.begin(); ++i != _items.end(); ++i) { + for (std::list::iterator i = _items.begin(); i != _items.end(); ++i) { Rect bb = (*i)->bounding_box(); if (bb) { largest_height = std::max (largest_height, bb.height()); @@ -180,88 +166,99 @@ Box::reposition_children () uniform_size = Rect (0, 0, largest_width, largest_height); } - for (std::list::iterator i = _items.begin(); ++i != _items.end(); ++i) { + { - (*i)->set_position (previous_edge); + PBD::Unwinder uw (ignore_child_changes, true); - if (homogenous) { - (*i)->size_allocate (uniform_size); - } + for (std::list::iterator i = _items.begin(); i != _items.end(); ++i) { - if (orientation == Vertical) { + (*i)->set_position (previous_edge); - 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(); - } - } - } else { - if (bb) { - shift += bb.height(); - } + if (homogenous) { + (*i)->size_allocate (uniform_size); } - previous_edge = previous_edge.translate (Duple (0, spacing + shift)); + double width; + double height; - } else { + (*i)->size_request (width, height); - Distance shift = 0; - Rect bb = (*i)->bounding_box(); + if (orientation == Vertical) { - if (!(*i)->visible()) { - if (!collapse_on_hide) { - if (bb) { - shift += bb.width(); + Distance shift = 0; + + if (!(*i)->visible()) { + /* invisible child */ + if (!collapse_on_hide) { + /* still add in its size */ + shift += height; } + } else { + shift += height; } - } else { - if (bb) { - shift += bb.width(); - } - } - previous_edge = previous_edge.translate (Duple (spacing + shift, 0)); + previous_edge = previous_edge.translate (Duple (0, spacing + shift)); + + } else { + + Distance shift = 0; + + if (!(*i)->visible()) { + if (!collapse_on_hide) { + shift += width; + } + } else { + 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 (); } diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index 60ed54809f..7a92260b31 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -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. diff --git a/libs/canvas/canvas/box.h b/libs/canvas/canvas/box.h index fb2f01c3ec..2e1ca25002 100644 --- a/libs/canvas/canvas/box.h +++ b/libs/canvas/canvas/box.h @@ -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); diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h index 90b6db4192..5a5cdddd85 100644 --- a/libs/canvas/canvas/canvas.h +++ b/libs/canvas/canvas/canvas.h @@ -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 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 _canvas_image; diff --git a/libs/canvas/canvas/item.h b/libs/canvas/canvas/item.h index b32f052204..7d3f957350 100644 --- a/libs/canvas/canvas/item.h +++ b/libs/canvas/canvas/item.h @@ -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 (); diff --git a/libs/canvas/item.cc b/libs/canvas/item.cc index e72b38d0bd..c444953960 100644 --- a/libs/canvas/item.cc +++ b/libs/canvas/item.cc @@ -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::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) {