add new Box container for Canvas.
authorPaul Davis <paul@linuxaudiosystems.com>
Sun, 25 Sep 2016 19:46:07 +0000 (14:46 -0500)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 27 Sep 2016 19:59:32 +0000 (14:59 -0500)
API subject to change and improvement

libs/canvas/box.cc [new file with mode: 0644]
libs/canvas/canvas/box.h [new file with mode: 0644]
libs/canvas/wscript

diff --git a/libs/canvas/box.cc b/libs/canvas/box.cc
new file mode 100644 (file)
index 0000000..d7d0c90
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+    Copyright (C) 2011-2014 Paul Davis
+    Author: Carl Hetherington <cth@carlh.net>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+
+#include "canvas/box.h"
+#include "canvas/rectangle.h"
+
+using namespace ArdourCanvas;
+
+Box::Box (Canvas* canvas, Orientation o)
+       : Item (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)
+{
+       self = new Rectangle (this);
+       self->set_outline (false);
+       self->set_fill (false);
+}
+
+Box::Box (Item* parent, Orientation o)
+       : Item (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)
+{
+       self = new Rectangle (this);
+       self->set_outline (false);
+       self->set_fill (false);
+}
+
+
+Box::Box (Item* parent, Duple const & p, Orientation o)
+       : Item (parent, p)
+       , 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)
+{
+       self = new Rectangle (this);
+       self->set_outline (false);
+       self->set_fill (false);
+}
+
+void
+Box::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+       Item::render_children (area, context);
+}
+
+void
+Box::compute_bounding_box () const
+{
+       _bounding_box = boost::none;
+
+       if (_items.empty()) {
+               _bounding_box_dirty = false;
+               return;
+       }
+
+       add_child_bounding_boxes (!collapse_on_hide);
+
+       if (_bounding_box) {
+               Rect r = _bounding_box.get();
+
+               _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);
+       }
+
+       _bounding_box_dirty = false;
+}
+
+void
+Box::set_spacing (double s)
+{
+       spacing = s;
+}
+
+void
+Box::set_padding (double t, double r, double b, double l)
+{
+       double last = t;
+
+       top_padding = t;
+
+       if (r >= 0) {
+               last = r;
+       }
+       right_padding = last;
+       if (b >= 0) {
+               last = b;
+       }
+       bottom_padding = last;
+       if (l >= 0) {
+               last = l;
+       }
+       left_padding = last;
+}
+
+void
+Box::set_margin (double t, double r, double b, double l)
+{
+       double last = t;
+       top_margin = t;
+       if (r >= 0) {
+               last = r;
+       }
+       right_margin = last;
+       if (b >= 0) {
+               last = b;
+       }
+       bottom_margin = last;
+       if (l >= 0) {
+               last = 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.get());
+
+       /* XXX need to shrink by margin */
+
+       self->set (r);
+}
+
+void
+Box::reposition_children ()
+{
+       Duple previous_edge (0, 0);
+       Distance largest_width = 0;
+       Distance largest_height = 0;
+
+       if (homogenous) {
+
+               for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
+                       boost::optional<Rect> bb = (*i)->bounding_box();
+                       if (bb) {
+                               largest_height = std::max (largest_height, bb.get().height());
+                               largest_width = std::max (largest_height, bb.get().width());
+                       }
+               }
+       }
+
+       for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
+
+               (*i)->set_position (previous_edge);
+
+               if (orientation == Vertical) {
+
+                       Distance shift = 0;
+
+                       if (homogenous) {
+                               shift = largest_height;
+                       } else {
+                               boost::optional<Rect> bb = (*i)->bounding_box();
+
+                               if (!(*i)->visible()) {
+                                       /* invisible child */
+                                       if (!collapse_on_hide) {
+                                               /* still add in its size */
+                                               if (bb) {
+                                                       shift += bb.get().height();
+                                               }
+                                       }
+                               } else {
+                                       if (bb) {
+                                               shift += bb.get().height();
+                                       }
+                               }
+                       }
+
+                       previous_edge = previous_edge.translate (Duple (0, spacing + shift));
+               } else {
+
+                       Distance shift = 0;
+
+                       if (homogenous) {
+                               shift = largest_width;
+                       } else {
+                               boost::optional<Rect> bb = (*i)->bounding_box();
+
+                               if (!(*i)->visible()) {
+                                       if (!collapse_on_hide) {
+                                               if (bb) {
+                                                       shift += bb.get().width();
+                                               }
+                                       }
+                               } else {
+                                       if (bb) {
+                                               shift += bb.get().width();
+                                       }
+                               }
+                       }
+
+                       previous_edge = previous_edge.translate (Duple (0, spacing + shift));
+               }
+       }
+
+       _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);
+}
+
+void
+Box::child_changed ()
+{
+       /* catch visibility and size changes */
+
+       Item::child_changed ();
+       reposition_children ();
+}
+
+void
+Box::set_collapse_on_hide (bool yn)
+{
+       if (collapse_on_hide != yn) {
+               collapse_on_hide = yn;
+               reposition_children ();
+       }
+}
+
+/*----*/
+
+VBox::VBox (Canvas* c)
+       : Box (c, Vertical)
+{
+}
+VBox::VBox (Item* i)
+       : Box (i, Vertical)
+{
+}
+VBox::VBox (Item* i, Duple const & position)
+       : Box (i, position, Vertical)
+{
+}
+
+HBox::HBox (Canvas* c)
+       : Box (c, Horizontal)
+{
+}
+HBox::HBox (Item* i)
+       : Box (i, Horizontal)
+{
+}
+HBox::HBox (Item* i, Duple const & position)
+       : Box (i, position, Horizontal)
+{
+}
diff --git a/libs/canvas/canvas/box.h b/libs/canvas/canvas/box.h
new file mode 100644 (file)
index 0000000..b3b2b85
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+    Copyright (C) 2011-2014 Paul Davis
+    Original Author: Carl Hetherington <cth@carlh.net>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_BOX_H__
+#define __CANVAS_BOX_H__
+
+#include "canvas/item.h"
+
+namespace ArdourCanvas
+{
+
+class Rectangle;
+
+/** a Container is an item which has no content of its own
+ * but renders its children in some geometrical arrangement.
+ *
+ * Imagined examples of containers:
+ *
+ *   Container: renders each child at the child's self-determined position
+ *   Box: renders each child along an axis (vertical or horizontal)
+ *   Table/Grid: renders each child within a two-dimensional grid
+ *
+ *   Other?
+ */
+class LIBCANVAS_API Box : public Item
+{
+public:
+       enum Orientation {
+               Vertical,
+               Horizontal
+       };
+
+       Box (Canvas *, Orientation);
+       Box (Item *, Orientation);
+       Box (Item *, Duple const & position, Orientation);
+
+       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 (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 set_collapse_on_hide (bool);
+       void set_homogenous (bool);
+
+       void compute_bounding_box () const;
+       void render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+
+  protected:
+       Orientation orientation;
+       double spacing;
+       double top_padding, right_padding, bottom_padding, left_padding;
+       double top_margin, right_margin, bottom_margin, left_margin;
+
+       void child_changed ();
+  private:
+       Rectangle *self;
+       bool collapse_on_hide;
+       bool homogenous;
+
+       void reset_self ();
+       void reposition_children ();
+};
+
+class LIBCANVAS_API VBox : public Box
+{
+  public:
+       VBox (Canvas *);
+       VBox (Item *);
+       VBox (Item *, Duple const & position);
+};
+
+class LIBCANVAS_API HBox : public Box
+{
+  public:
+       HBox (Canvas *);
+       HBox (Item *);
+       HBox (Item *, Duple const & position);
+};
+
+}
+
+#endif
index b40f72412dbee5cbcc6779215ce62cc32314594e..0038b783c60e62f0eaf87a666edd095166041b51 100644 (file)
@@ -30,6 +30,7 @@ path_prefix = 'libs/canvas/'
 canvas_sources = [
         'arc.cc',
         'arrow.cc',
+        'box.cc',
         'canvas.cc',
         'circle.cc',
         'colors.cc',