--- /dev/null
+#include "column.h"
+
+namespace Msp {
+namespace GLtk {
+
+Column::Column(bool u):
+ uniform(u)
+{ }
+
+Layout::Slot *Column::create_slot(Widget &w)
+{
+ Slot *slot = new Slot(*this, w);
+
+ if(!slots.empty())
+ {
+ Slot &prev = *slots.back();
+ slot->constraints.push_back(Constraint(BELOW, prev));
+ slot->constraints.push_back(Constraint(ALIGN_LEFT, prev));
+ slot->constraints.push_back(Constraint(ALIGN_RIGHT, prev));
+ if(uniform)
+ slot->constraints.push_back(Constraint(COPY_HEIGHT, prev));
+ slot->horiz_pack.expand = true;
+ }
+
+ return slot;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_COLUMN_H_
+#define MSP_GLTK_COLUMN_H_
+
+#include "layout.h"
+
+namespace Msp {
+namespace GLtk {
+
+/**
+Arranges widgets in a single column.
+*/
+class Column: public Layout
+{
+private:
+ bool uniform;
+
+public:
+ Column(bool = false);
+
+private:
+ virtual Slot *create_slot(Widget &);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "grid.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+Grid::Grid(bool u):
+ uniform(u),
+ cur_column(0),
+ first(true),
+ skipped(false)
+{ }
+
+void Grid::start_row()
+{
+ cur_column = 0;
+ first = true;
+}
+
+void Grid::skip_cell()
+{
+ ++cur_column;
+ skipped = true;
+ if(cur_column>=columns.size())
+ columns.push_back(0);
+}
+
+Layout::Slot *Grid::create_slot(Widget &wdg)
+{
+ Slot *slot = new Slot(*this, wdg);
+
+ slot->vert_pack.gravity = 1;
+
+ if(!slots.empty())
+ {
+ Slot &prev = *slots.back();
+ if(first)
+ slot->constraints.push_back(Constraint(BELOW, prev));
+ else
+ {
+ slot->constraints.push_back(Constraint(ALIGN_TOP, prev));
+ slot->constraints.push_back(Constraint(ALIGN_BOTTOM, prev));
+ if(!skipped)
+ slot->constraints.push_back(Constraint(RIGHT_OF, prev));
+ }
+
+ if(uniform)
+ {
+ slot->constraints.push_back(Constraint(COPY_WIDTH, prev));
+ slot->constraints.push_back(Constraint(COPY_HEIGHT, prev));
+ }
+
+ if(cur_column<columns.size() && columns[cur_column])
+ {
+ Slot &col = *columns[cur_column];
+ slot->constraints.push_back(Constraint(ALIGN_LEFT, col));
+ slot->constraints.push_back(Constraint(ALIGN_RIGHT, col));
+ }
+ }
+
+ if(cur_column>=columns.size())
+ columns.push_back(slot);
+ else if(!columns[cur_column])
+ columns[cur_column] = slot;
+
+ first = false;
+ skipped = false;
+ ++cur_column;
+
+ return slot;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_GRID_H_
+#define MSP_GLTK_GRID_H_
+
+#include "layout.h"
+
+namespace Msp {
+namespace GLtk {
+
+/**
+Arranges widgets in a grid.
+
+This layout class places widgets in a grid, lining up their edges in rows and
+columns. Widgets are placed on rows left-to-right and rows are placed top-to-
+bottom.
+
+The start of each row must be indicated manually. Individual cells can be
+skipped to leave them empty.
+*/
+class Grid: public Layout
+{
+private:
+ bool uniform;
+ std::vector<Slot *> columns;
+ unsigned cur_column;
+ bool first;
+ bool skipped;
+
+public:
+ Grid(bool = false);
+
+ void start_row();
+ void skip_cell();
+
+private:
+ Slot *create_slot(Widget &);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
#include <limits>
#include "container.h"
#include "layout.h"
void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt)
{
+ if(&src==&tgt)
+ throw InvalidParameterValue("Can't add a self-referencing constraint");
+
Slot &src_slot = get_slot_for_widget(src);
Slot &tgt_slot = get_slot_for_widget(tgt);
Pointers &ptrs = pointers[dir&VERTICAL];
LinearProgram linprog(slots.size()*5+1);
+ float weight = slots.size();
for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
{
- linprog.get_object_row()[(*i)->index*5] = 0.1*((*i)->*(ptrs.packing)).gravity;
- linprog.get_object_row()[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? 5 : -1);
+ linprog.get_object_row()[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight;
+ linprog.get_object_row()[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1);
{
LinearProgram::Row row = linprog.add_row();
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
#ifndef MSP_GLTK_LAYOUT_H_
#define MSP_GLTK_LAYOUT_H_
class Container;
class Widget;
+/**
+Positions Widgets inside a Container.
+
+A layout operates on constraints, which are used to form a linear program that
+is then solved to obtain positions and dimensions that fulfill the constraints.
+There are three kinds of constraints available: ordering, alignment and
+dimension matching.
+
+Ordering constraints specify that the widgets should be placed next to other
+along X or Y axis. These operate on one axis at a time, so a widget could be
+"right of" another even if they are separated by hundreds of pixels vertically.
+The widgets will be separated by a spacing value, which is settable on a per-
+layout basis.
+
+Alignment constraints make the corresponding edges of two widgets be on the
+same line. These are incompatible with ordering constraints, so only one or
+the other should be used between any pair of widgets for the same axis.
+
+Dimension matching constraints force the two widgets to have the same dimension
+along the relevant axis.
+
+In addition to constraints, there are some other properties that can be set on
+widgets to determine how they are laid out. Gravity affects which edge of the
+container the widget should be placed at. This is a relatively weak hint and
+will be overridden by many other things. Margins can also be specified to
+prevent widgets from getting too close to the container's edges.
+
+Usually widgets are made as small as their content allows. Setting the expand
+flag for a widget causes it to use as much space as possible. If multiple co-
+dependent widgets have the expand flag set, the results are currently
+undefined.
+
+Since specifiyng constraints manually can be quite tedious, there are some
+derived Layout classes that implement common positioning patterns. See
+MixedRows and Grid.
+*/
class Layout
{
private:
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "mixedrows.h"
+
+namespace Msp {
+namespace GLtk {
+
+MixedRows::MixedRows(bool u):
+ uniform_rows(u),
+ uniform_cols(false),
+ hgravity(-1),
+ hsplit(false),
+ vgravity(1),
+ vsplit(false),
+ first(true)
+{ }
+
+void MixedRows::start_row(bool u)
+{
+ uniform_cols = u;
+ first = true;
+ hgravity = -1;
+ hsplit = false;
+}
+
+void MixedRows::split_rows()
+{
+ vgravity = -1;
+ vsplit = true;
+}
+
+void MixedRows::split_columns()
+{
+ hgravity = 1;
+ hsplit = true;
+}
+
+Layout::Slot *MixedRows::create_slot(Widget &wdg)
+{
+ Slot *slot = new Slot(*this, wdg);
+
+ slot->horiz_pack.gravity = hgravity;
+ slot->vert_pack.gravity = vgravity;
+
+ if(!first)
+ {
+ Slot *prev = slots.back();
+ if(!hsplit)
+ slot->constraints.push_back(Constraint(RIGHT_OF, *prev));
+ slot->constraints.push_back(Constraint(ALIGN_TOP, *prev));
+ slot->constraints.push_back(Constraint(ALIGN_BOTTOM, *prev));
+ if(uniform_cols)
+ slot->constraints.push_back(Constraint(COPY_WIDTH, *prev));
+ }
+ else if(!slots.empty())
+ {
+ Slot *prev = slots.back();
+ if(!vsplit)
+ slot->constraints.push_back(Constraint(BELOW, *prev));
+ if(uniform_rows)
+ slot->constraints.push_back(Constraint(COPY_HEIGHT, *prev));
+ }
+
+ first = false;
+ hsplit = false;
+ vsplit = false;
+
+ return slot;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_MIXEDROWS_H_
+#define MSP_GLTK_MIXEDROWS_H_
+
+#include "layout.h"
+
+namespace Msp {
+namespace GLtk {
+
+/**
+Places widgets on multiple heterogeneous rows.
+
+This layout class creates rows of widgets, with no horizontal constraints
+between rows. Widgets are placed on rows left-to-right and rows are placed
+top-to-bottom.
+
+The start of each new row must be indicated manually. The widgets on a row
+can be specified to have a uniform width, and similarly all rows can be set to
+have a uniform height.
+
+By default widgets have a top-left gravity. It's possible to specify a "split"
+to align remaining widgets on a row to the right, or remaining rows to the
+bottom. This does not change the placement order of widgets.
+*/
+class MixedRows: public Layout
+{
+private:
+ bool uniform_rows;
+ bool uniform_cols;
+ int hgravity;
+ bool hsplit;
+ int vgravity;
+ bool vsplit;
+ bool first;
+
+public:
+ MixedRows(bool = false);
+
+ void start_row(bool = false);
+ void split_rows();
+ void split_columns();
+
+private:
+ virtual Slot *create_slot(Widget &);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "row.h"
+
+namespace Msp {
+namespace GLtk {
+
+Row::Row(bool u):
+ uniform(u)
+{ }
+
+Layout::Slot *Row::create_slot(Widget &w)
+{
+ Slot *slot = new Slot(*this, w);
+
+ if(!slots.empty())
+ {
+ Slot &prev = *slots.back();
+ slot->constraints.push_back(Constraint(RIGHT_OF, prev));
+ slot->constraints.push_back(Constraint(ALIGN_TOP, prev));
+ slot->constraints.push_back(Constraint(ALIGN_BOTTOM, prev));
+ if(uniform)
+ slot->constraints.push_back(Constraint(COPY_WIDTH, prev));
+ slot->vert_pack.expand = true;
+ }
+
+ return slot;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_ROW_H_
+#define MSP_GLTK_ROW_H_
+
+#include "layout.h"
+
+namespace Msp {
+namespace GLtk {
+
+/**
+Arranges widgets in a single row.
+*/
+class Row: public Layout
+{
+private:
+ bool uniform;
+
+public:
+ Row(bool = false);
+
+private:
+ virtual Slot *create_slot(Widget &);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif