]> git.tdb.fi Git - libs/gltk.git/blobdiff - source/layout.cpp
Better method of implementing zero gravity
[libs/gltk.git] / source / layout.cpp
index c616ff6fc7caff9833070cf1c30e7c7710e20ad9..b8d8fb7d23ad6b196d68550c71f7de6db6f29205 100644 (file)
@@ -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()
@@ -213,20 +213,23 @@ void Layout::update_slot_indices()
        n_active_slots = 0;
        for(list<Slot *>::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++;
                else
                        (*i)->index = -1;
        }
 
-       n_slack_constraints[0] = 0;
-       n_slack_constraints[1] = 0;
+       n_slack_vars[0] = 0;
+       n_slack_vars[1] = 0;
        for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
                if((*i)->index>=0)
                {
+                       for(unsigned j=0; j<2; ++j)
+                               if(((*i)->*(pointers[j].packing)).gravity==0)
+                                       n_slack_vars[j] += 2;
                        for(list<Constraint>::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];
                }
 }
 
@@ -282,6 +285,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 +299,19 @@ 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::update()
 {
        solve_constraints(HORIZONTAL, UPDATE);
@@ -304,12 +321,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 +342,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<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
        {
                if((*i)->index<0)
@@ -363,12 +382,19 @@ void Layout::solve_constraints(int dir, SolveMode mode)
 
                if(((*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 the center of the container
+                       as possible.  Since linear programs can't express absolute values
+                       directly, use two opposing slack variables that are optimized for
+                       a low value. */
                        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] = 0.5;
+                       row[k] = 1;
+                       row[k+1] = -1;
+                       row.back() = geom.*(ptrs.dim)/2;
+                       objective[k] = -1;
+                       objective[k+1] = -1;
+                       k += 2;
                }
 
                {
@@ -382,7 +408,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<Constraint>::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j)
                        if(j->target.index>(*i)->index && (j->type&1)==dir)
                        {
@@ -444,7 +469,8 @@ Layout::Packing::Packing():
 Layout::Slot::Slot(Layout &l, Widget &w):
        layout(l),
        index(0),
-       widget(w)
+       widget(w),
+       ghost(false)
 {
        vert_pack.gravity = 1;
        widget.signal_autosize_changed.connect(sigc::mem_fun(this, &Slot::autosize_changed));
@@ -455,16 +481,13 @@ Layout::Slot::Slot(Layout &l, Widget &w):
 
 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 +497,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 +553,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 +568,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);