From: Mikko Rasa Date: Fri, 4 Mar 2011 20:25:55 +0000 (+0000) Subject: Add some classes for automatically creating basic layouts X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=a84c813859b6908c511b12ab7d82d6643f4f1a42;p=libs%2Fgltk.git Add some classes for automatically creating basic layouts Some enhancements to the base Layout class --- diff --git a/source/column.cpp b/source/column.cpp new file mode 100644 index 0000000..5b86660 --- /dev/null +++ b/source/column.cpp @@ -0,0 +1,29 @@ +#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 diff --git a/source/column.h b/source/column.h new file mode 100644 index 0000000..92919be --- /dev/null +++ b/source/column.h @@ -0,0 +1,34 @@ +/* $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 diff --git a/source/grid.cpp b/source/grid.cpp new file mode 100644 index 0000000..4bf0167 --- /dev/null +++ b/source/grid.cpp @@ -0,0 +1,82 @@ +/* $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_columnconstraints.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 diff --git a/source/grid.h b/source/grid.h new file mode 100644 index 0000000..789672a --- /dev/null +++ b/source/grid.h @@ -0,0 +1,48 @@ +/* $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 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 diff --git a/source/layout.cpp b/source/layout.cpp index 3501948..468d45f 100644 --- a/source/layout.cpp +++ b/source/layout.cpp @@ -1,3 +1,10 @@ +/* $Id$ + +This file is part of libmspgltk +Copyright © 2011 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + #include #include "container.h" #include "layout.h" @@ -179,6 +186,9 @@ Layout::ConstraintType Layout::complement(ConstraintType type) 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); @@ -233,10 +243,11 @@ void Layout::solve_constraints(int dir) Pointers &ptrs = pointers[dir&VERTICAL]; LinearProgram linprog(slots.size()*5+1); + float weight = slots.size(); for(list::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(); diff --git a/source/layout.h b/source/layout.h index 540c5ee..7e8cdea 100644 --- a/source/layout.h +++ b/source/layout.h @@ -1,3 +1,10 @@ +/* $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_ @@ -12,6 +19,42 @@ namespace GLtk { 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: diff --git a/source/mixedrows.cpp b/source/mixedrows.cpp new file mode 100644 index 0000000..275629d --- /dev/null +++ b/source/mixedrows.cpp @@ -0,0 +1,77 @@ +/* $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 diff --git a/source/mixedrows.h b/source/mixedrows.h new file mode 100644 index 0000000..721b877 --- /dev/null +++ b/source/mixedrows.h @@ -0,0 +1,56 @@ +/* $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 diff --git a/source/row.cpp b/source/row.cpp new file mode 100644 index 0000000..30e1811 --- /dev/null +++ b/source/row.cpp @@ -0,0 +1,29 @@ +#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 diff --git a/source/row.h b/source/row.h new file mode 100644 index 0000000..f8ebe6c --- /dev/null +++ b/source/row.h @@ -0,0 +1,34 @@ +/* $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