X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Flayout.cpp;h=d3d015f7305c7ddb70a024c4884ee88cbf3711f2;hb=eb6eee5fabf05abca4a0434c2a8f8f62c900afec;hp=74b33f0bce1933de385720eed1eabe6e821956b2;hpb=c8f5fd14a1fbdaaa9e1216dd5163d1f5c1b5ff27;p=libs%2Fgltk.git diff --git a/source/layout.cpp b/source/layout.cpp index 74b33f0..d3d015f 100644 --- a/source/layout.cpp +++ b/source/layout.cpp @@ -1,3 +1,4 @@ +#include #include #include "container.h" #include "layout.h" @@ -45,9 +46,12 @@ public: Row add_row(); Row operator[](unsigned); Row get_objective_row(); - bool solve(); float get_variable(unsigned); + bool solve(); private: + void prepare_columns(); + void add_artificial_variables(); + void remove_artificial_variables(); unsigned find_minimal_ratio(unsigned); void make_basic_column(unsigned, unsigned); bool pivot(); @@ -106,6 +110,28 @@ void Layout::set_margin(const Sides &m) update(); } +void Layout::set_spacing(unsigned s) +{ + row_spacing = s; + col_spacing = s; + if(container) + update(); +} + +void Layout::set_row_spacing(unsigned s) +{ + row_spacing = s; + if(container) + update(); +} + +void Layout::set_column_spacing(unsigned s) +{ + col_spacing = s; + if(container) + update(); +} + void Layout::add_widget(Widget &wdg) { if(!container) @@ -177,7 +203,7 @@ Layout::ConstraintType Layout::complement(ConstraintType type) return type; } -void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt) +void Layout::create_constraint(Widget &src, ConstraintType type, Widget &tgt, int sp) { if(&src==&tgt) throw invalid_argument("&src==&tgt"); @@ -190,11 +216,23 @@ void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt) return; src_slot.constraints.push_back(Constraint(type, tgt_slot)); + src_slot.constraints.back().spacing = sp; tgt_slot.constraints.push_back(Constraint(complement(type), src_slot)); + tgt_slot.constraints.back().spacing = sp; update(); } +void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt) +{ + create_constraint(src, type, tgt, -1); +} + +void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt, unsigned spacing) +{ + create_constraint(src, type, tgt, spacing); +} + void Layout::set_gravity(Widget &wdg, int h, int v) { Slot &slot = get_slot_for_widget(wdg); @@ -217,17 +255,29 @@ void Layout::set_expand(Widget &wdg, bool h, bool v) void Layout::update() { - solve_constraints(HORIZONTAL); - solve_constraints(VERTICAL); + solve_constraints(HORIZONTAL, UPDATE); + solve_constraints(VERTICAL, UPDATE); for(list::iterator i=slots.begin(); i!=slots.end(); ++i) (*i)->widget.set_geometry((*i)->geom); } -void Layout::solve_constraints(int dir) +void Layout::autosize() +{ + solve_constraints(HORIZONTAL, AUTOSIZE); + solve_constraints(VERTICAL, AUTOSIZE); + + container->set_size(autosize_geom.w, autosize_geom.h); +} + +void Layout::solve_constraints(int dir, SolveMode mode) { Pointers &ptrs = pointers[dir&VERTICAL]; + const Geometry &geom = container->get_geometry(); + if(mode==UPDATE && geom.*(ptrs.dim)::iterator i=slots.begin(); i!=slots.end(); ++i) { - linprog.get_objective_row()[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight; - linprog.get_objective_row()[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1); + LinearProgram::Row objective = linprog.get_objective_row(); + if(mode==AUTOSIZE) + { + objective[(*i)->index*5] = -1; + objective[(*i)->index*5+1] = -1; + } + else + { + objective[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight; + objective[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1); + } { // Prevent the widget from going past the container's low edge. @@ -247,13 +306,24 @@ void Layout::solve_constraints(int dir) row.back() = margin.*(ptrs.low_margin); } + if(mode==UPDATE) { // Prevent the widget from going past the container's high edge. LinearProgram::Row row = linprog.add_row(); row[(*i)->index*5] = 1; row[(*i)->index*5+1] = 1; row[(*i)->index*5+3] = 1; - row.back() = container->get_geometry().*(ptrs.dim)-margin.*(ptrs.high_margin); + row.back() = geom.*(ptrs.dim)-margin.*(ptrs.high_margin); + } + + 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. */ + LinearProgram::Row row = linprog.add_row(); + row[(*i)->index*5+2] = 1; + row[(*i)->index*5+3] = -1; } { @@ -281,24 +351,37 @@ void Layout::solve_constraints(int dir) if(j->type&TARGET_DIM) row[j->target.index*5+1] = -1; if(j->type&SPACING) - row.back() = this->*(ptrs.spacing); + row.back() = (j->spacing>=0 ? j->spacing : this->*(ptrs.spacing)); } } if(!linprog.solve()) return; - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) + if(mode==AUTOSIZE) { - (*i)->geom.*(ptrs.pos) = linprog.get_variable((*i)->index*5); - (*i)->geom.*(ptrs.dim) = linprog.get_variable((*i)->index*5+1); + autosize_geom.*(ptrs.dim) = 0; + for(list::iterator i=slots.begin(); i!=slots.end(); ++i) + { + int high_edge = linprog.get_variable((*i)->index*5)+linprog.get_variable((*i)->index*5+1); + autosize_geom.*(ptrs.dim) = max(autosize_geom.*(ptrs.dim), high_edge+margin.*(ptrs.high_margin)); + } + } + else + { + for(list::iterator i=slots.begin(); i!=slots.end(); ++i) + { + (*i)->geom.*(ptrs.pos) = linprog.get_variable((*i)->index*5); + (*i)->geom.*(ptrs.dim) = linprog.get_variable((*i)->index*5+1); + } } } Layout::Constraint::Constraint(ConstraintType t, Slot &s): type(t), - target(s) + target(s), + spacing(-1) { } @@ -328,7 +411,10 @@ void Layout::Slot::autosize_changed() if(autosize_geom.w<=geom.w && autosize_geom.h<=geom.h) widget.set_geometry(geom); else + { + layout.container->signal_autosize_changed.emit(); layout.update(); + } } @@ -365,8 +451,10 @@ float Layout::LinearProgram::get_variable(unsigned i) if(i+1>=n_columns) throw out_of_range("LinearProgram::get_variable"); - unsigned r = columns[i].basic; - return columns.back().values[r]; + if(unsigned r = columns[i].basic) + return columns.back().values[r]; + else + return 0; } bool Layout::LinearProgram::solve() @@ -378,34 +466,9 @@ bool Layout::LinearProgram::solve() objective variable is kept implicit, as it would never change during the execution of the algorithm. */ - /* Force all columns fully into existence and relocate objective row to - bottom in preparation of phase 1. A new objective row is calculated by - pricing out the constraint rows. */ - for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) - { - float objective = 0.0f; - if(!i->values.empty()) - { - objective = i->values.front(); - i->values.front() = 0.0f; - for(vector::iterator j=i->values.begin(); j!=i->values.end(); ++j) - i->values.front() += *j; - } - i->values.resize(n_rows+1, 0.0f); - i->values.back() = objective; - } + prepare_columns(); - /* Create artificial variables for phase 1. This ensures that each row has - a basic variable associated with it. The original objective row already - contains the implicit objective variable, which is basic. */ - columns.resize(n_columns+n_rows-1); - columns.back() = columns[n_columns-1]; - columns[n_columns-1].values.clear(); - for(unsigned i=1; i basic_coeff(n_rows, 0.0f); + const vector &constants = columns.back().values; + for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) + { + if(i->values.size()>=2 && i->values.back()!=0.0f && (constants.size()values.size() || i->values.back()*constants[i->values.size()-1]>=0.0f) && basic_coeff[i->values.size()-1]==0.0f) + { + bool basic = true; + for(unsigned j=1; (basic && j+1values.size()); ++j) + basic = (i->values[j]==0.0f); + if(basic) + { + i->basic = i->values.size()-1; + basic_coeff[i->basic] = -i->values.front()/i->values.back(); + i->values.clear(); + } + } + } + + // Price out the newly-created basic variables. + for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) + if(!i->values.empty()) + { + for(unsigned j=0; jvalues.size(); ++j) + i->values.front() += basic_coeff[j]*i->values[j]; + } +} + +void Layout::LinearProgram::add_artificial_variables() +{ + vector artificial_rows(n_rows-1); + for(unsigned i=0; i::iterator i=columns.begin(); i!=columns.end(); ++i) + if(i->basic) + artificial_rows[i->basic-1] = 0; + artificial_rows.erase(std::remove(artificial_rows.begin(), artificial_rows.end(), 0), artificial_rows.end()); + + /* Force all non-basic columns fully into existence and relocate objective + row to bottom in preparation of phase 1. A new objective row is calculated + by pricing out the constraint rows. */ + for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) + if(!i->basic) + { + float objective = 0.0f; + if(!i->values.empty()) + { + objective = i->values.front(); + i->values.front() = 0.0f; + for(vector::iterator j=artificial_rows.begin(); j!=artificial_rows.end(); ++j) + if(*jvalues.size()) + i->values.front() += i->values[*j]; + } + i->values.resize(n_rows+1, 0.0f); + i->values.back() = objective; + } + + if(artificial_rows.empty()) + return; + + /* Create artificial variables for phase 1. This ensures that each row has + a basic variable associated with it. The original objective row already + contains the implicit objective variable, which is basic. */ + columns.resize(n_columns+artificial_rows.size()); + columns.back() = columns[n_columns-1]; + columns[n_columns-1].values.clear(); + for(unsigned i=0; ivalues.front() = i->values.back(); i->values.pop_back(); } - - // Solve the phase 2 problem. We already know it to be feasible. - while(pivot()) ; - - solved = true; - - return true; } unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c)