X-Git-Url: http://git.tdb.fi/?p=libs%2Fgltk.git;a=blobdiff_plain;f=source%2Flayout.cpp;h=b4cd7683c6bc60d2b615c7c46323b773e4a668cf;hp=c616ff6fc7caff9833070cf1c30e7c7710e20ad9;hb=c90e083d1b6c9ce754765a38bf2c14de89993ed5;hpb=8eeb6ee5d40c21150839e24996cc3e9ef308374d diff --git a/source/layout.cpp b/source/layout.cpp index c616ff6..b4cd768 100644 --- a/source/layout.cpp +++ b/source/layout.cpp @@ -92,8 +92,8 @@ Layout::Layout(): row_spacing(5), col_spacing(4) { - n_slack_constraints[0] = 0; - n_slack_constraints[1] = 0; + n_slack_vars[0] = 0; + n_slack_vars[1] = 0; } Layout::~Layout() @@ -211,22 +211,34 @@ void Layout::remove_widget(Widget &wdg) void Layout::update_slot_indices() { n_active_slots = 0; + unsigned n_floating = 0; for(list::iterator i=slots.begin(); i!=slots.end(); ++i) { - if((*i)->widget.is_visible()) + if((*i)->widget.is_visible() || (*i)->ghost) + { (*i)->index = n_active_slots++; + if((*i)->floating) + ++n_floating; + } else (*i)->index = -1; } - n_slack_constraints[0] = 0; - n_slack_constraints[1] = 0; + n_slack_vars[0] = n_floating*2; + n_slack_vars[1] = n_floating*2; for(list::iterator i=slots.begin(); i!=slots.end(); ++i) if((*i)->index>=0) { + if(!(*i)->floating) + { + for(unsigned j=0; j<2; ++j) + if(((*i)->*(pointers[j].packing)).gravity==0) + n_slack_vars[j] += 2; + } + for(list::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j) if(j->target.index>(*i)->index && (j->type&SLACK)) - ++n_slack_constraints[j->type&1]; + ++n_slack_vars[j->type&1]; } } @@ -247,7 +259,7 @@ Layout::ConstraintType Layout::complement(ConstraintType type) void Layout::create_constraint(Widget &src, ConstraintType type, Widget &tgt, int sp) { if(&src==&tgt) - throw invalid_argument("&src==&tgt"); + throw invalid_argument("Layout::create_constraint"); Slot &src_slot = get_slot_for_widget(src); Slot &tgt_slot = get_slot_for_widget(tgt); @@ -282,6 +294,7 @@ void Layout::set_gravity(Widget &wdg, int h, int v) slot.horiz_pack.gravity = h; slot.vert_pack.gravity = v; + update_slot_indices(); update(); } @@ -295,6 +308,29 @@ void Layout::set_expand(Widget &wdg, bool h, bool v) update(); } +void Layout::set_ghost(Widget &wdg, bool g) +{ + Slot &slot = get_slot_for_widget(wdg); + + slot.ghost = g; + + if(!wdg.is_visible()) + { + update_slot_indices(); + update(); + } +} + +void Layout::set_floating(Widget &wdg, bool f) +{ + Slot &slot = get_slot_for_widget(wdg); + + slot.floating = f; + + update_slot_indices(); + update(); +} + void Layout::update() { solve_constraints(HORIZONTAL, UPDATE); @@ -304,12 +340,13 @@ void Layout::update() (*i)->widget.set_geometry((*i)->geom); } -void Layout::autosize() +void Layout::autosize(Geometry &geom) { solve_constraints(HORIZONTAL, AUTOSIZE); solve_constraints(VERTICAL, AUTOSIZE); - container->set_size(autosize_geom.w, autosize_geom.h); + geom.w = max(geom.w, autosize_geom.w); + geom.h = max(geom.h, autosize_geom.h); } void Layout::solve_constraints(int dir, SolveMode mode) @@ -324,8 +361,9 @@ void Layout::solve_constraints(int dir, SolveMode mode) five columns for each widget, and one constant column. The first and second columns of a widget are its position and dimension, respectively. The remaining three are slack columns; see below for their purposes. */ - LinearProgram linprog(n_active_slots*5+n_slack_constraints[dir]+1); + LinearProgram linprog(n_active_slots*5+n_slack_vars[dir]+1); float weight = slots.size(); + unsigned k = n_active_slots*5; for(list::iterator i=slots.begin(); i!=slots.end(); ++i) { if((*i)->index<0) @@ -339,7 +377,8 @@ void Layout::solve_constraints(int dir, SolveMode mode) } else { - objective[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight; + if(!(*i)->floating) + objective[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight; objective[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1); } @@ -361,14 +400,27 @@ void Layout::solve_constraints(int dir, SolveMode mode) row.back() = geom.*(ptrs.dim)-margin.*(ptrs.high_margin); } - if(((*i)->*(ptrs.packing)).gravity==0) + if((*i)->floating || ((*i)->*(ptrs.packing)).gravity==0) { - /* This forces the widget's distance from the left and right edge of - the container to be equal. It's a bit of a hack, but more time and - thought is needed for a better solution. */ + /* Try to keep the widget as close to a target position as possible. + Since linear programs can't express absolute values directly, use two + opposing slack variables that are optimized for a low value. */ + float a = ((*i)->*(ptrs.packing)).gravity*0.5+0.5; LinearProgram::Row row = linprog.add_row(); - row[(*i)->index*5+2] = 1; - row[(*i)->index*5+3] = -1; + row[(*i)->index*5] = 1; + row[(*i)->index*5+1] = a; + row[k] = 1; + row[k+1] = -1; + if((*i)->floating) + { + const Geometry &cgeom = (*i)->widget.get_geometry(); + row.back() = cgeom.*(ptrs.pos)+cgeom.*(ptrs.dim)*a; + } + else + row.back() = geom.*(ptrs.dim)/2; + objective[k] = -1; + objective[k+1] = -1; + k += 2; } { @@ -382,7 +434,6 @@ void Layout::solve_constraints(int dir, SolveMode mode) /* Add rows for user-defined constraints. Constraints are always added in pairs, so it's only necessary to create a row for one half. */ - unsigned k = n_active_slots*5; for(list::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j) if(j->target.index>(*i)->index && (j->type&1)==dir) { @@ -444,27 +495,25 @@ Layout::Packing::Packing(): Layout::Slot::Slot(Layout &l, Widget &w): layout(l), index(0), - widget(w) + widget(w), + ghost(false), + floating(false) { vert_pack.gravity = 1; widget.signal_autosize_changed.connect(sigc::mem_fun(this, &Slot::autosize_changed)); widget.signal_visibility_changed.connect(sigc::mem_fun(this, &Slot::visibility_changed)); - widget.autosize(); - autosize_geom = widget.get_geometry(); + widget.autosize(autosize_geom); } void Layout::Slot::autosize_changed() { - widget.autosize(); - autosize_geom = widget.get_geometry(); + widget.autosize(autosize_geom); - if(!widget.is_visible()) + if(!widget.is_visible() && !ghost) return; - // If the widget fits in the area it had, just leave it there. - if(autosize_geom.w<=geom.w && autosize_geom.h<=geom.h) - widget.set_geometry(geom); - else + // Only trigger an update if the widget won't fit in its current area. + if(autosize_geom.w>geom.w || autosize_geom.h>geom.h) { layout.container->signal_autosize_changed.emit(); layout.update(); @@ -474,7 +523,7 @@ void Layout::Slot::autosize_changed() void Layout::Slot::visibility_changed(bool v) { layout.update_slot_indices(); - if(v) + if(v || ghost) { layout.container->signal_autosize_changed.emit(); layout.update(); @@ -530,6 +579,7 @@ Layout::WidgetLoader::WidgetLoader(Layout &l, Widget &w, const Layout::Loader::W { add("constraint", &WidgetLoader::constraint); add("expand", &WidgetLoader::expand); + add("ghost", &WidgetLoader::ghost); add("gravity", &WidgetLoader::gravity); } @@ -544,6 +594,11 @@ void Layout::WidgetLoader::expand(bool h, bool v) layout.set_expand(widget, h, v); } +void Layout::WidgetLoader::ghost(bool g) +{ + layout.set_ghost(widget, g); +} + void Layout::WidgetLoader::gravity(int h, int v) { layout.set_gravity(widget, h, v);