From 1b6689054781d4525879fec016ff4c1b4de34771 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 10 Jul 2020 18:26:23 -0600 Subject: [PATCH] Canvas: continuing move of cBox functionality into ConstraintPacker --- libs/canvas/canvas/cbox.h | 39 -- libs/canvas/canvas/constraint_packer.h | 32 +- libs/canvas/cbox.cc | 476 ------------------------- libs/canvas/constraint_packer.cc | 468 ++++++++++++++++++++++-- 4 files changed, 458 insertions(+), 557 deletions(-) diff --git a/libs/canvas/canvas/cbox.h b/libs/canvas/canvas/cbox.h index ef701224ef..e5c56bedcd 100644 --- a/libs/canvas/canvas/cbox.h +++ b/libs/canvas/canvas/cbox.h @@ -37,54 +37,15 @@ public: cBox (Canvas *, Orientation); cBox (Item *, 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 (Gtkmm2ext::Color c) { set_outline_color (c); } - - void remove (Item*); - - BoxConstrainedItem* pack_start (Item*, PackOptions primary_axis_packing = PackOptions (0), PackOptions secondary_axis_packing = PackOptions (PackExpand|PackFill)); - BoxConstrainedItem* pack_end (Item*, PackOptions primary_axis_packing = PackOptions (0), PackOptions secondary_axis_packing = PackOptions (PackExpand|PackFill)); - - void add_vertical_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimenion, double second_dimension, kiwi::Variable& alloc_var); - void add_horizontal_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimenion, double second_dimension, kiwi::Variable& alloc_var); - void set_collapse_on_hide (bool); void set_homogenous (bool); - void preferred_size(Duple& minimum, Duple& natural) const; - void size_allocate (Rect const &); - - void render (Rect const & area, Cairo::RefPtr context) const; - protected: - Orientation orientation; - - double _spacing; - double _top_padding; - double _bottom_padding; - double _left_padding; - double _right_padding; - double _top_margin; - double _bottom_margin; - double _left_margin; - double _right_margin; - void child_changed (bool bbox_changed); - void update_constraints (); private: - typedef std::list Order; - Order order; bool collapse_on_hide; bool homogenous; - kiwi::Variable expanded_item_size; - - BoxConstrainedItem* pack (Item*, PackOptions primary_axis_packing, PackOptions secondary_axis_packing); }; } diff --git a/libs/canvas/canvas/constraint_packer.h b/libs/canvas/canvas/constraint_packer.h index e96af367a7..f230d08dcf 100644 --- a/libs/canvas/canvas/constraint_packer.h +++ b/libs/canvas/canvas/constraint_packer.h @@ -38,6 +38,15 @@ public: ConstraintPacker (Canvas *, Orientation o = Horizontal); ConstraintPacker (Item *, Orientation o = Horizontal); + 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 add (Item *); void add_front (Item *); void remove (Item *); @@ -56,12 +65,27 @@ public: void preferred_size (Duple& mininum, Duple& natural) const; void size_allocate (Rect const &); + void render (Rect const & area, Cairo::RefPtr context) const; + kiwi::Variable width; kiwi::Variable height; protected: void child_changed (bool bbox_changed); + Orientation _orientation; + double _spacing; + double _top_padding; + double _bottom_padding; + double _left_padding; + double _right_padding; + double _top_margin; + double _bottom_margin; + double _left_margin; + double _right_margin; + + kiwi::Variable expanded_item_size; + typedef std::map ConstrainedItemMap; ConstrainedItemMap constrained_map; typedef std::list ConstraintList; @@ -77,13 +101,15 @@ public: void non_const_preferred_size (Duple& mininum, Duple& natural); virtual void update_constraints (); + void add_vertical_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimenion, double second_dimension, kiwi::Variable& alloc_var); + void add_horizontal_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimenion, double second_dimension, kiwi::Variable& alloc_var); + private: - Orientation _orientation; typedef std::list BoxPackedItems; - BoxPackedItems vpacked; - BoxPackedItems hpacked; + BoxPackedItems packed; BoxConstrainedItem* pack (Item*, PackOptions primary_axis_packing, PackOptions secondary_axis_packing); + void box_preferred_size (Duple& mininum, Duple& natural) const; }; } diff --git a/libs/canvas/cbox.cc b/libs/canvas/cbox.cc index 8f658d43d8..09bff1b756 100644 --- a/libs/canvas/cbox.cc +++ b/libs/canvas/cbox.cc @@ -31,322 +31,16 @@ using std::endl; cBox::cBox (Canvas* c, Orientation o) : ConstraintPacker (c) - , orientation (o) - , _spacing (0) - , _top_padding (0) - , _bottom_padding (0) - , _left_padding (0) - , _right_padding (0) - , _top_margin (0) - , _bottom_margin (0) - , _left_margin (0) - , _right_margin (0) , collapse_on_hide (false) , homogenous (true) { - _solver.addEditVariable (expanded_item_size, kiwi::strength::strong); } cBox::cBox (Item* i, Orientation o) : ConstraintPacker (i) - , orientation (o) - , _spacing (0) - , _top_padding (0) - , _bottom_padding (0) - , _left_padding (0) - , _right_padding (0) - , _top_margin (0) - , _bottom_margin (0) - , _left_margin (0) - , _right_margin (0) , collapse_on_hide (false) , homogenous (true) { - _solver.addEditVariable (expanded_item_size, kiwi::strength::strong); -} - -void -cBox::set_spacing (double s) -{ - _spacing = s; -} - -void -cBox::set_padding (double top, double right, double bottom, double left) -{ - double last = top; - - _top_padding = last; - - if (right >= 0) { - last = right; - } - _right_padding = last; - - if (bottom >= 0) { - last = bottom; - } - _bottom_padding = last; - - if (left >= 0) { - last = left; - } - _left_padding = last; -} - -void -cBox::set_margin (double top, double right, double bottom, double left) -{ - double last = top; - - _top_margin = last; - - if (right >= 0) { - last = right; - } - _right_margin = last; - - if (bottom >= 0) { - last = bottom; - } - _bottom_margin = last; - - if (left >= 0) { - last = left; - } - _left_margin = last; -} - -void -cBox::remove (Item* item) -{ - for (Order::iterator t = order.begin(); t != order.end(); ++t) { - if (&(*t)->item() == item) { - order.erase (t); - break; - } - } - - ConstraintPacker::remove (item); -} - -BoxConstrainedItem* -cBox::pack_start (Item* item, PackOptions primary_axis_opts, PackOptions secondary_axis_opts) -{ - return pack (item, PackOptions (primary_axis_opts|PackFromStart), secondary_axis_opts); -} - -BoxConstrainedItem* -cBox::pack_end (Item* item, PackOptions primary_axis_opts, PackOptions secondary_axis_opts) -{ - return pack (item, PackOptions (primary_axis_opts|PackFromEnd), secondary_axis_opts); -} - -BoxConstrainedItem* -cBox::pack (Item* item, PackOptions primary_axis_opts, PackOptions secondary_axis_opts) -{ - BoxConstrainedItem* ci = new BoxConstrainedItem (*item, primary_axis_opts, secondary_axis_opts); - - add_constrained_internal (item, ci); - order.push_back (ci); - - return ci; -} - -void -cBox::preferred_size (Duple& min, Duple& natural) const -{ - Order::size_type n_expanding = 0; - Order::size_type n_nonexpanding = 0; - Order::size_type total = 0; - Distance non_expanding_used = 0; - Distance largest = 0; - Distance largest_opposite = 0; - Duple i_min, i_natural; - - for (Order::const_iterator o = order.begin(); o != order.end(); ++o) { - - (*o)->item().preferred_size (i_min, i_natural); - - // cerr << '\t' << (*o)->item().whoami() << " min " << i_min << " nat " << i_natural << endl; - - if ((*o)->primary_axis_pack_options() & PackExpand) { - n_expanding++; - - if (orientation == Vertical) { - if (i_natural.height() > largest) { - largest = i_natural.height(); - } - } else { - if (i_natural.width() > largest) { - largest = i_natural.width(); - } - if (i_natural.height() > largest) { - largest_opposite = i_natural.height(); - } - } - - } else { - n_nonexpanding++; - - if (orientation == Vertical) { - non_expanding_used += i_natural.height(); - } else { - non_expanding_used += i_natural.width(); - } - } - - /* determine the maximum size for the opposite axis. All items - * will be this size or less on this axis - */ - - if (orientation == Vertical) { - if (i_natural.width() > largest_opposite) { - largest_opposite = i_natural.width(); - } - } else { - if (i_natural.height() > largest_opposite) { - largest_opposite = i_natural.height(); - } - } - - total++; - } - - Duple r; - - if (orientation == Vertical) { - // cerr << "+++ vertical box, neu = " << non_expanding_used << " neuo " << non_expanding_used_opposite << " largest = " << largest << " opp " << largest_opposite << " total " << total << endl; - min.y = non_expanding_used + (n_expanding * largest) + _top_margin + _bottom_margin + ((total - 1) * _spacing); - min.x = largest_opposite + _left_margin + _right_margin; - } else { - // cerr << "+++ horiz box, neu = " << non_expanding_used << " neuo " << non_expanding_used_opposite << " largest = " << largest << " opp " << largest_opposite << " total " << total << endl; - min.x = non_expanding_used + (n_expanding * largest) + _left_margin + _right_margin + ((total - 1) * _spacing); - min.y = largest_opposite + _top_margin + _bottom_margin; - - } - - // cerr << whoami() << " preferred-size = " << min << endl; - - natural = min; -} - -void -cBox::size_allocate (Rect const & r) -{ - PBD::Unwinder uw (in_alloc, true); - - Item::size_allocate (r); - - double expanded_size; - Order::size_type n_expanding = 0; - Order::size_type n_nonexpanding = 0; - Order::size_type total = 0; - Distance non_expanding_used = 0; - - for (Order::iterator o = order.begin(); o != order.end(); ++o) { - if ((*o)->primary_axis_pack_options() & PackExpand) { - n_expanding++; - } else { - n_nonexpanding++; - - Duple min, natural; - - (*o)->item().preferred_size (min, natural); - - if (orientation == Vertical) { - non_expanding_used += natural.height(); - } else { - non_expanding_used += natural.width(); - } - - } - total++; - } - - if (orientation == Vertical) { - expanded_size = (r.height() - _top_margin - _bottom_margin - ((total - 1) * _spacing) - non_expanding_used) / n_expanding; - } else { - expanded_size = (r.width() - _left_margin - _right_margin - ((total - 1) * _spacing) - non_expanding_used) / n_expanding; - } - - // cerr << "\n\n\n" << whoami() << " SIZE-ALLOC " << r << " NCU ? " << _need_constraint_update << " expanded items (" << n_expanding << ")will be " << expanded_size << " neu " << non_expanding_used << " t = " << total << " s " << _spacing - // << " t " << _top_margin << " b " << _bottom_margin << " l " << _left_margin << " r " << _right_margin - // << endl; - - - if (_need_constraint_update) { - update_constraints (); - } - - _solver.suggestValue (width, r.width()); - _solver.suggestValue (height, r.height()); - _solver.suggestValue (expanded_item_size, expanded_size); - - _solver.updateVariables (); - // _solver.dump (cerr); - - for (ConstrainedItemMap::const_iterator o = constrained_map.begin(); o != constrained_map.end(); ++o) { - //o->second->dump (cerr); - } - - apply (&_solver); - - _bounding_box_dirty = true; - -} - -void -cBox::update_constraints () -{ - /* must totally override ConstraintPacker::update_constraints() */ - - _solver.reset (); - _solver.addEditVariable (width, kiwi::strength::strong); - _solver.addEditVariable (height, kiwi::strength::strong); - _solver.addEditVariable (expanded_item_size, kiwi::strength::strong); - - try { - - Order::iterator prev = order.end(); - - for (Order::iterator o = order.begin(); o != order.end(); ++o) { - - Duple min, natural; - - (*o)->item().preferred_size (min, natural); - - if (orientation == Vertical) { - add_vertical_box_constraints (_solver, *o, prev == order.end() ? 0 : *prev, natural.height(), natural.width(), width); - } else { - add_horizontal_box_constraints (_solver, *o, prev == order.end() ? 0 : *prev, natural.width(), natural.height(), height); - } - - prev = o; - } - - /* There maybe items that were not pack_start()'ed or - * pack_end()'ed into this box, but just added with - * constraints. Find all items in the box, and add any - * constraints that come with them. - */ - - for (ConstrainedItemMap::const_iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) { - - std::vector const & constraints (x->second->constraints()); - - for (std::vector::const_iterator c = constraints.begin(); c != constraints.end(); ++c) { - _solver.addConstraint (*c); - } - } - - - } catch (std::exception& e) { - cerr << "Setting up sovler failed: " << e.what() << endl; - return; - } - - _need_constraint_update = false; } void @@ -354,174 +48,4 @@ cBox::child_changed (bool bbox_changed) { } -/* It would be nice to do this with templates or even by passing ptr-to-method, - * but both of them interfere with the similarly meta-programming-ish nature of - * the way that kiwi builds Constraint objects from expressions. So a macro it - * is ... - */ -#define add_box_constraints(\ - solver, \ - bci, \ - prev, \ - natural_main_dimension, \ - natural_second_dimension, \ - alloc_var, \ - m_main_dimension, \ - m_second_dimension, \ - m_trailing, \ - m_leading, \ - m_trailing_padding, \ - m_leading_padding, \ - m_second_trailing, \ - m_second_leading, \ - m_second_trailing_padding, \ - m_second_leading_padding, \ - m_trailing_margin, \ - m_leading_margin, \ - m_second_trailing_margin, \ - m_second_leading_margin) \ - \ - /* Add constraints that will size the item within this box */ \ - \ - /* set up constraints for expand/fill options, done by \ - * adjusting height and margins of each item \ - */ \ - \ - if (bci->primary_axis_pack_options() & PackExpand) { \ - \ - /* item will take up more than it's natural \ - * size, if space is available \ - */ \ - \ - if (bci->primary_axis_pack_options() & PackFill) { \ - \ - /* item is expanding to fill all \ - * available space and wants that space \ - * for itself. \ - */ \ - \ - solver.addConstraint ({(bci->m_main_dimension() == expanded_item_size) | kiwi::strength::strong}); \ - solver.addConstraint ({(bci->m_trailing_padding() == 0. ) | kiwi::strength::strong}); \ - solver.addConstraint ({(bci->m_leading_padding() == 0. ) | kiwi::strength::strong}); \ - \ - } else { \ - \ - /* item is expanding to fill all \ - * available space and wants that space \ - * as padding \ - */ \ - \ - solver.addConstraint ({bci->m_main_dimension() == natural_main_dimension}); \ - solver.addConstraint ({(bci->m_trailing_padding() + bci->m_leading_padding() + bci->m_main_dimension() == expanded_item_size) | kiwi::strength::strong}); \ - solver.addConstraint ({(bci->m_leading_padding() == bci->m_trailing_padding()) | kiwi::strength::strong}); \ - } \ - \ - } else { \ - \ - /* item is not going to expand to fill \ - * available space. just give it's preferred \ - * height. \ - */ \ - \ - /* cerr << bci->item().whoami() << " will usenatural height of " << natural.height() << endl; */ \ - \ - solver.addConstraint ({bci->m_main_dimension() == natural_main_dimension}); \ - solver.addConstraint ({bci->m_trailing_padding() == 0.}); \ - solver.addConstraint ({bci->m_leading_padding() == 0.}); \ - } \ - \ - /* now set upper upper edge of the item */ \ - \ - if (prev == 0) { \ - \ - /* first item */ \ - \ - solver.addConstraint ({(bci->m_trailing() == m_trailing_margin + bci->m_trailing_padding()) | kiwi::strength::strong}); \ - \ - } else { \ - /* subsequent items */ \ - \ - solver.addConstraint ({(bci->m_trailing() == prev->m_leading() + prev->m_leading_padding() + bci->m_trailing_padding() + _spacing) | kiwi::strength::strong}); \ - } \ - \ - solver.addConstraint ({bci->m_leading() == bci->m_trailing() + bci->m_main_dimension()}); \ - \ - /* set the side-effect variables and/or constants */ \ - \ - solver.addConstraint ({(bci->m_second_trailing_padding() == 0) | kiwi::strength::weak}); \ - solver.addConstraint ({(bci->m_second_leading_padding() == 0) | kiwi::strength::weak}); \ - \ - solver.addConstraint ({bci->m_second_trailing() + bci->m_second_dimension() == bci->m_second_leading()}); \ - solver.addConstraint ({(bci->m_second_trailing() == m_second_trailing_margin + bci->m_second_trailing_padding()) | kiwi::strength::strong}); \ - \ - if (!(bci->secondary_axis_pack_options() & PackExpand) && natural_second_dimension > 0) { \ - solver.addConstraint ({bci->m_second_dimension() == natural_second_dimension}); \ - } else { \ - solver.addConstraint ({(bci->m_second_dimension() == alloc_var - (m_second_trailing_margin + m_second_leading_margin + bci->m_second_leading_padding())) | kiwi::strength::strong}); \ - } - - -void -cBox::add_vertical_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimension, double second_dimension, kiwi::Variable & alloc_var) -{ - add_box_constraints (solver, ci, prev, main_dimension, second_dimension, alloc_var, - height, width, - top, bottom, top_padding, bottom_padding, - left, right, left_padding, right_padding, - _top_margin, _bottom_margin, _left_margin, _right_margin); - -} - -void -cBox::add_horizontal_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimension, double second_dimension, kiwi::Variable& alloc_var) -{ - add_box_constraints (solver, ci, prev, main_dimension, second_dimension, alloc_var, - width, height, - left, right, left_padding, right_padding, - top, bottom, top_padding, bottom_padding, - _left_margin, _right_margin, _top_margin, _bottom_margin); -} - -void -cBox::render (Rect const & area, Cairo::RefPtr context) const -{ - if ((fill() || outline()) && _allocation) { - - Rect contents = _allocation; - - /* allocation will have been left with (x0,y0) as given by the - * parent, but _position is set to the same value and will - * be taken into account by item_to_window() - */ - - double width = contents.width() - (_left_margin + _top_margin); - double height = contents.height() - (_top_margin + _bottom_margin); - - contents.x0 = _left_margin; - contents.y0 = _top_margin; - - contents.x1 = contents.x0 + width; - contents.y1 = contents.y0 + height; - - Rect self (item_to_window (contents, false)); - const Rect draw = self.intersection (area); - - if (fill()) { - - setup_fill_context (context); - context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); - context->fill_preserve (); - } - - if (outline()) { - if (!fill()) { - context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); - } - setup_outline_context (context); - context->stroke (); - } - } - - Item::render_children (area, context); -} diff --git a/libs/canvas/constraint_packer.cc b/libs/canvas/constraint_packer.cc index 4daf930eb9..e8e193dcef 100644 --- a/libs/canvas/constraint_packer.cc +++ b/libs/canvas/constraint_packer.cc @@ -41,9 +41,18 @@ ConstraintPacker::ConstraintPacker (Canvas* canvas, Orientation o) : Container (canvas) , width (X_("packer width")) , height (X_("packer height")) + , _orientation (o) + , _spacing (0) + , _top_padding (0) + , _bottom_padding (0) + , _left_padding (0) + , _right_padding (0) + , _top_margin (0) + , _bottom_margin (0) + , _left_margin (0) + , _right_margin (0) , in_alloc (false) , _need_constraint_update (false) - , _orientation (o) { set_fill (false); set_outline (false); @@ -57,9 +66,18 @@ ConstraintPacker::ConstraintPacker (Item* parent, Orientation o) : Container (parent) , width (X_("packer width")) , height (X_("packer height")) + , _orientation (o) + , _spacing (0) + , _top_padding (0) + , _bottom_padding (0) + , _left_padding (0) + , _right_padding (0) + , _top_margin (0) + , _bottom_margin (0) + , _left_margin (0) + , _right_margin (0) , in_alloc (false) , _need_constraint_update (false) - , _orientation (o) { set_fill (false); set_outline (false); @@ -142,6 +160,85 @@ ConstraintPacker::preferred_size (Duple& minimum, Duple& natural) const const_cast(this)->non_const_preferred_size (minimum, natural); } + +void +ConstraintPacker::box_preferred_size (Duple& min, Duple& natural) const +{ + BoxPackedItems::size_type n_expanding = 0; + BoxPackedItems::size_type n_nonexpanding = 0; + BoxPackedItems::size_type total = 0; + Distance non_expanding_used = 0; + Distance largest = 0; + Distance largest_opposite = 0; + Duple i_min, i_natural; + + for (BoxPackedItems::const_iterator o = packed.begin(); o != packed.end(); ++o) { + + (*o)->item().preferred_size (i_min, i_natural); + + // cerr << '\t' << (*o)->item().whoami() << " min " << i_min << " nat " << i_natural << endl; + + if ((*o)->primary_axis_pack_options() & PackExpand) { + n_expanding++; + + if (_orientation == Vertical) { + if (i_natural.height() > largest) { + largest = i_natural.height(); + } + } else { + if (i_natural.width() > largest) { + largest = i_natural.width(); + } + if (i_natural.height() > largest) { + largest_opposite = i_natural.height(); + } + } + + } else { + n_nonexpanding++; + + if (_orientation == Vertical) { + non_expanding_used += i_natural.height(); + } else { + non_expanding_used += i_natural.width(); + } + } + + /* determine the maximum size for the opposite axis. All items + * will be this size or less on this axis + */ + + if (_orientation == Vertical) { + if (i_natural.width() > largest_opposite) { + largest_opposite = i_natural.width(); + } + } else { + if (i_natural.height() > largest_opposite) { + largest_opposite = i_natural.height(); + } + } + + total++; + } + + Duple r; + + if (_orientation == Vertical) { + // cerr << "+++ vertical box, neu = " << non_expanding_used << " neuo " << non_expanding_used_opposite << " largest = " << largest << " opp " << largest_opposite << " total " << total << endl; + min.y = non_expanding_used + (n_expanding * largest) + _top_margin + _bottom_margin + ((total - 1) * _spacing); + min.x = largest_opposite + _left_margin + _right_margin; + } else { + // cerr << "+++ horiz box, neu = " << non_expanding_used << " neuo " << non_expanding_used_opposite << " largest = " << largest << " opp " << largest_opposite << " total " << total << endl; + min.x = non_expanding_used + (n_expanding * largest) + _left_margin + _right_margin + ((total - 1) * _spacing); + min.y = largest_opposite + _top_margin + _bottom_margin; + + } + + // cerr << whoami() << " preferred-size = " << min << endl; + + natural = min; +} + void ConstraintPacker::non_const_preferred_size (Duple& minimum, Duple& natural) { @@ -160,7 +257,15 @@ ConstraintPacker::non_const_preferred_size (Duple& minimum, Duple& natural) */ if (_intrinsic_width == 0 && _intrinsic_height == 0) { - natural = Duple (100,100); + + Duple m, n; + + /* we can use the size of things packed using the box + interface + */ + + box_preferred_size (m, n); + natural = Duple (std::min (100.0, n.x), std::min (100.0, n.y)); minimum = natural; return; } @@ -200,15 +305,55 @@ void ConstraintPacker::size_allocate (Rect const & r) { PBD::Unwinder uw (in_alloc, true); + double expanded_size; Item::size_allocate (r); + if (!packed.empty()) { + + BoxPackedItems::size_type n_expanding = 0; + BoxPackedItems::size_type n_nonexpanding = 0; + BoxPackedItems::size_type total = 0; + Distance non_expanding_used = 0; + + for (BoxPackedItems::iterator o = packed.begin(); o != packed.end(); ++o) { + if ((*o)->primary_axis_pack_options() & PackExpand) { + n_expanding++; + } else { + n_nonexpanding++; + + Duple min, natural; + + (*o)->item().preferred_size (min, natural); + + if (_orientation == Vertical) { + non_expanding_used += natural.height(); + } else { + non_expanding_used += natural.width(); + } + + } + total++; + } + + if (_orientation == Vertical) { + expanded_size = (r.height() - _top_margin - _bottom_margin - ((total - 1) * _spacing) - non_expanding_used) / n_expanding; + } else { + expanded_size = (r.width() - _left_margin - _right_margin - ((total - 1) * _spacing) - non_expanding_used) / n_expanding; + } + } + if (_need_constraint_update) { update_constraints (); } _solver.suggestValue (width, r.width()); _solver.suggestValue (height, r.height()); + + if (!packed.empty()) { + _solver.suggestValue (expanded_item_size, expanded_size); + } + _solver.updateVariables (); #if 0 @@ -294,26 +439,13 @@ ConstraintPacker::remove (Item* item) } - bool found_packed = false; - - for (BoxPackedItems::iterator t = hpacked.begin(); t != hpacked.end(); ++t) { + for (BoxPackedItems::iterator t = packed.begin(); t != packed.end(); ++t) { if (&(*t)->item() == item) { - hpacked.erase (t); - found_packed = true; + packed.erase (t); break; } } - if (!found_packed) { - for (BoxPackedItems::iterator t = vpacked.begin(); t != vpacked.end(); ++t) { - if (&(*t)->item() == item) { - vpacked.erase (t); - found_packed = true; - break; - } - } - } - _need_constraint_update = true; } @@ -332,23 +464,62 @@ ConstraintPacker::update_constraints () _solver.addEditVariable (width, kiwi::strength::strong); _solver.addEditVariable (height, kiwi::strength::strong); - for (ConstrainedItemMap::iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) { - - Duple min, natural; - ConstrainedItem* ci = x->second; - - x->first->preferred_size (min, natural); - - _solver.addConstraint ((ci->width() >= min.width()) | kiwi::strength::required); - _solver.addConstraint ((ci->height() >= min.height()) | kiwi::strength::required); - _solver.addConstraint ((ci->width() == natural.width()) | kiwi::strength::medium); - _solver.addConstraint ((ci->height() == natural.width()) | kiwi::strength::medium); - - add_constraints (_solver, ci); + if (!packed.empty()) { + _solver.addEditVariable (expanded_item_size, kiwi::strength::strong); } - for (ConstraintList::const_iterator c = constraint_list.begin(); c != constraint_list.end(); ++c) { - _solver.addConstraint (*c); + try { + + /* First handle box-packed items */ + + BoxPackedItems::iterator prev = packed.end(); + + for (BoxPackedItems::iterator o = packed.begin(); o != packed.end(); ++o) { + + Duple min, natural; + + (*o)->item().preferred_size (min, natural); + + if (_orientation == Vertical) { + add_vertical_box_constraints (_solver, *o, prev == packed.end() ? 0 : *prev, natural.height(), natural.width(), width); + } else { + add_horizontal_box_constraints (_solver, *o, prev == packed.end() ? 0 : *prev, natural.width(), natural.height(), height); + } + + prev = o; + } + + /* Now handle all other items (exclude those already dealt with */ + + for (ConstrainedItemMap::iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) { + + if (std::find (packed.begin(), packed.end(), x->second) != packed.end()) { + continue; + } + + Duple min, natural; + ConstrainedItem* ci = x->second; + + x->first->preferred_size (min, natural); + + _solver.addConstraint ((ci->width() >= min.width()) | kiwi::strength::required); + _solver.addConstraint ((ci->height() >= min.height()) | kiwi::strength::required); + _solver.addConstraint ((ci->width() == natural.width()) | kiwi::strength::medium); + _solver.addConstraint ((ci->height() == natural.width()) | kiwi::strength::medium); + + add_constraints (_solver, ci); + } + + /* Now add packer-level constraints */ + + for (ConstraintList::const_iterator c = constraint_list.begin(); c != constraint_list.end(); ++c) { + _solver.addConstraint (*c); + } + + _need_constraint_update = false; + + } catch (std::exception& e) { + cerr << "Setting up sovler failed: " << e.what() << endl; } } @@ -370,13 +541,232 @@ ConstraintPacker::pack (Item* item, PackOptions primary_axis_opts, PackOptions s BoxConstrainedItem* ci = new BoxConstrainedItem (*item, primary_axis_opts, secondary_axis_opts); add_constrained_internal (item, ci); - - if (_orientation == Horizontal) { - hpacked.push_back (ci); - } else { - vpacked.push_back (ci); - } + packed.push_back (ci); return ci; } + +/* It would be nice to do this with templates or even by passing ptr-to-method, + * but both of them interfere with the similarly meta-programming-ish nature of + * the way that kiwi builds Constraint objects from expressions. So a macro it + * is ... + */ + +#define add_box_constraints(\ + solver, \ + bci, \ + prev, \ + natural_main_dimension, \ + natural_second_dimension, \ + alloc_var, \ + m_main_dimension, \ + m_second_dimension, \ + m_trailing, \ + m_leading, \ + m_trailing_padding, \ + m_leading_padding, \ + m_second_trailing, \ + m_second_leading, \ + m_second_trailing_padding, \ + m_second_leading_padding, \ + m_trailing_margin, \ + m_leading_margin, \ + m_second_trailing_margin, \ + m_second_leading_margin) \ + \ + /* Add constraints that will size the item within this box */ \ + \ + /* set up constraints for expand/fill options, done by \ + * adjusting height and margins of each item \ + */ \ + \ + if (bci->primary_axis_pack_options() & PackExpand) { \ + \ + /* item will take up more than it's natural \ + * size, if space is available \ + */ \ + \ + if (bci->primary_axis_pack_options() & PackFill) { \ + \ + /* item is expanding to fill all \ + * available space and wants that space \ + * for itself. \ + */ \ + \ + solver.addConstraint ({(bci->m_main_dimension() == expanded_item_size) | kiwi::strength::strong}); \ + solver.addConstraint ({(bci->m_trailing_padding() == 0. ) | kiwi::strength::strong}); \ + solver.addConstraint ({(bci->m_leading_padding() == 0. ) | kiwi::strength::strong}); \ + \ + } else { \ + \ + /* item is expanding to fill all \ + * available space and wants that space \ + * as padding \ + */ \ + \ + solver.addConstraint ({bci->m_main_dimension() == natural_main_dimension}); \ + solver.addConstraint ({(bci->m_trailing_padding() + bci->m_leading_padding() + bci->m_main_dimension() == expanded_item_size) | kiwi::strength::strong}); \ + solver.addConstraint ({(bci->m_leading_padding() == bci->m_trailing_padding()) | kiwi::strength::strong}); \ + } \ + \ + } else { \ + \ + /* item is not going to expand to fill \ + * available space. just give it's preferred \ + * height. \ + */ \ + \ + /* cerr << bci->item().whoami() << " will usenatural height of " << natural.height() << endl; */ \ + \ + solver.addConstraint ({bci->m_main_dimension() == natural_main_dimension}); \ + solver.addConstraint ({bci->m_trailing_padding() == 0.}); \ + solver.addConstraint ({bci->m_leading_padding() == 0.}); \ + } \ + \ + /* now set upper upper edge of the item */ \ + \ + if (prev == 0) { \ + \ + /* first item */ \ + \ + solver.addConstraint ({(bci->m_trailing() == m_trailing_margin + bci->m_trailing_padding()) | kiwi::strength::strong}); \ + \ + } else { \ + /* subsequent items */ \ + \ + solver.addConstraint ({(bci->m_trailing() == prev->m_leading() + prev->m_leading_padding() + bci->m_trailing_padding() + _spacing) | kiwi::strength::strong}); \ + } \ + \ + solver.addConstraint ({bci->m_leading() == bci->m_trailing() + bci->m_main_dimension()}); \ + \ + /* set the side-effect variables and/or constants */ \ + \ + solver.addConstraint ({(bci->m_second_trailing_padding() == 0) | kiwi::strength::weak}); \ + solver.addConstraint ({(bci->m_second_leading_padding() == 0) | kiwi::strength::weak}); \ + \ + solver.addConstraint ({bci->m_second_trailing() + bci->m_second_dimension() == bci->m_second_leading()}); \ + solver.addConstraint ({(bci->m_second_trailing() == m_second_trailing_margin + bci->m_second_trailing_padding()) | kiwi::strength::strong}); \ + \ + if (!(bci->secondary_axis_pack_options() & PackExpand) && natural_second_dimension > 0) { \ + solver.addConstraint ({bci->m_second_dimension() == natural_second_dimension}); \ + } else { \ + solver.addConstraint ({(bci->m_second_dimension() == alloc_var - (m_second_trailing_margin + m_second_leading_margin + bci->m_second_leading_padding())) | kiwi::strength::strong}); \ + } + + +void +ConstraintPacker::add_vertical_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimension, double second_dimension, kiwi::Variable & alloc_var) +{ + add_box_constraints (solver, ci, prev, main_dimension, second_dimension, alloc_var, + height, width, + top, bottom, top_padding, bottom_padding, + left, right, left_padding, right_padding, + _top_margin, _bottom_margin, _left_margin, _right_margin); + +} + +void +ConstraintPacker::add_horizontal_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimension, double second_dimension, kiwi::Variable& alloc_var) +{ + add_box_constraints (solver, ci, prev, main_dimension, second_dimension, alloc_var, + width, height, + left, right, left_padding, right_padding, + top, bottom, top_padding, bottom_padding, + _left_margin, _right_margin, _top_margin, _bottom_margin); +} + +void +ConstraintPacker::set_spacing (double s) +{ + _spacing = s; +} + +void +ConstraintPacker::set_padding (double top, double right, double bottom, double left) +{ + double last = top; + + _top_padding = last; + + if (right >= 0) { + last = right; + } + _right_padding = last; + + if (bottom >= 0) { + last = bottom; + } + _bottom_padding = last; + + if (left >= 0) { + last = left; + } + _left_padding = last; +} + +void +ConstraintPacker::set_margin (double top, double right, double bottom, double left) +{ + double last = top; + + _top_margin = last; + + if (right >= 0) { + last = right; + } + _right_margin = last; + + if (bottom >= 0) { + last = bottom; + } + _bottom_margin = last; + + if (left >= 0) { + last = left; + } + _left_margin = last; +} + +void +ConstraintPacker::render (Rect const & area, Cairo::RefPtr context) const +{ + if ((fill() || outline()) && _allocation) { + + Rect contents = _allocation; + + /* allocation will have been left with (x0,y0) as given by the + * parent, but _position is set to the same value and will + * be taken into account by item_to_window() + */ + + double width = contents.width() - (_left_margin + _top_margin); + double height = contents.height() - (_top_margin + _bottom_margin); + + contents.x0 = _left_margin; + contents.y0 = _top_margin; + + contents.x1 = contents.x0 + width; + contents.y1 = contents.y0 + height; + + Rect self (item_to_window (contents, false)); + const Rect draw = self.intersection (area); + + if (fill()) { + + setup_fill_context (context); + context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); + context->fill_preserve (); + } + + if (outline()) { + if (!fill()) { + context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); + } + setup_outline_context (context); + context->stroke (); + } + } + + Item::render_children (area, context); +}