X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Flayout.cpp;h=e9461b5eddf855e72fed34b8e6b4fa31bd90eefa;hb=aa9b8db38efb9e97460c76e27cecc4d1591b23e5;hp=5df7bc1f77d2a16f87a02f80e4eea6892a2d3564;hpb=729cb06f85e2888a7ac1e72375380257936106c9;p=libs%2Fgltk.git diff --git a/source/layout.cpp b/source/layout.cpp index 5df7bc1..e9461b5 100644 --- a/source/layout.cpp +++ b/source/layout.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include #include "arrangement.h" @@ -19,44 +19,44 @@ public: { private: LinearProgram &linprog; - unsigned index; + size_t index; public: - Row(LinearProgram &, unsigned); + Row(LinearProgram &, size_t); - float &operator[](unsigned); + float &operator[](size_t); float &back(); }; private: struct Column { - unsigned basic; + size_t basic; std::vector values; Column(); }; - unsigned n_columns; - unsigned n_rows; + size_t n_columns = 1; + size_t n_rows = 1; std::vector columns; - bool solved; - bool infeasible; + bool solved = false; + bool infeasible = false; public: - LinearProgram(unsigned); + LinearProgram(size_t); Row add_row(); - Row operator[](unsigned); + Row operator[](size_t); Row get_objective_row(); - float get_variable(unsigned); + float get_variable(size_t); 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); + size_t find_minimal_ratio(size_t); + void make_basic_column(size_t, size_t); bool pivot(); }; @@ -85,21 +85,10 @@ Layout::Pointers Layout::pointers[2] = } }; -Layout::Layout(): - container(0), - n_active_slots(0), - margin(8), - row_spacing(5), - col_spacing(4) -{ - n_slack_vars[0] = 0; - n_slack_vars[1] = 0; -} - Layout::~Layout() { - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - delete *i; + for(Slot *s: slots) + delete s; } void Layout::set_container(Container &c) @@ -147,14 +136,14 @@ void Layout::push_arrangement(Arrangement &arr) Arrangement *Layout::get_arrangement() const { if(arrangement_stack.empty()) - return 0; + return nullptr; else return arrangement_stack.back(); } void Layout::pop_arrangement(Arrangement &arr) { - list::iterator begin = find(arrangement_stack.begin(), arrangement_stack.end(), &arr); + auto begin = find(arrangement_stack, &arr); if(begin==arrangement_stack.end()) return; @@ -184,62 +173,70 @@ void Layout::add_widget(Widget &wdg) void Layout::remove_widget(Widget &wdg) { - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - if(&(*i)->widget==&wdg) + auto i = find_if(slots, [&wdg](Slot *s){ return &s->widget==&wdg; }); + if(i==slots.end()) + return; + + for(Slot *s: slots) + if(s!=*i) { - for(list::iterator j=slots.begin(); j!=slots.end(); ++j) - if(j!=i) - { - for(list::iterator k=(*j)->constraints.begin(); k!=(*j)->constraints.end(); ) - { - if(&k->target==*i) - (*j)->constraints.erase(k++); - else - ++k; - } - } + for(auto k=s->constraints.begin(); k!=s->constraints.end(); ) + { + if(k->target==*i) + k = s->constraints.erase(k); + else + ++k; + } + } - delete *i; - slots.erase(i); + delete *i; + slots.erase(i); - update_slot_indices(); - update(); - return; - } + update_slot_indices(); + update(); } void Layout::update_slot_indices() { n_active_slots = 0; - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) + size_t n_floating = 0; + for(Slot *s: slots) { - if((*i)->widget.is_visible() || (*i)->ghost) - (*i)->index = n_active_slots++; + if(s->widget.is_visible() || s->ghost) + { + s->index = n_active_slots++; + if(s->floating) + ++n_floating; + } else - (*i)->index = -1; + s->index = -1; } - n_slack_vars[0] = 0; - n_slack_vars[1] = 0; - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - if((*i)->index>=0) + n_slack_vars[0] = n_floating*2; + n_slack_vars[1] = n_floating*2; + for(const Slot *s: slots) + if(s->index>=0) { - 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_vars[j->type&1]; + if(!s->floating) + { + for(unsigned j=0; j<2; ++j) + if((s->*(pointers[j].packing)).gravity==0) + n_slack_vars[j] += 2; + } + + for(const Constraint &c: s->constraints) + if(c.target->index>s->index && (c.type&SLACK)) + ++n_slack_vars[c.type&1]; } } Layout::Slot &Layout::get_slot_for_widget(Widget &wdg) { - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - if(&(*i)->widget==&wdg) - return **i; + auto i = find_if(slots, [&wdg](const Slot *s){ return &s->widget==&wdg; }); + if(i==slots.end()) + throw hierarchy_error("widget not in layout"); - throw hierarchy_error("widget not in layout"); + return **i; } Layout::ConstraintType Layout::complement(ConstraintType type) @@ -250,13 +247,13 @@ 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); - for(list::iterator i=src_slot.constraints.begin(); i!=src_slot.constraints.end(); ++i) - if(i->type==type && &i->target==&tgt_slot) + for(const Constraint &c: src_slot.constraints) + if(c.type==type && c.target==&tgt_slot) return; src_slot.constraints.push_back(Constraint(type, tgt_slot)); @@ -312,13 +309,23 @@ void Layout::set_ghost(Widget &wdg, bool g) } } +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); solve_constraints(VERTICAL, UPDATE); - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - (*i)->widget.set_geometry((*i)->geom); + for(const Slot *s: slots) + s->widget.set_geometry(s->geom); } void Layout::autosize(Geometry &geom) @@ -343,30 +350,31 @@ void Layout::solve_constraints(int dir, SolveMode mode) 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_vars[dir]+1); - float weight = slots.size(); - unsigned k = n_active_slots*5; - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) + float weight = slots.size()+1; + size_t k = n_active_slots*5; + for(const Slot *s: slots) { - if((*i)->index<0) + if(s->index<0) continue; LinearProgram::Row objective = linprog.get_objective_row(); if(mode==AUTOSIZE) { - objective[(*i)->index*5] = -1; - objective[(*i)->index*5+1] = -1; + objective[s->index*5] = -1; + objective[s->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); + if(!s->floating) + objective[s->index*5] = (s->*(ptrs.packing)).gravity/weight; + objective[s->index*5+1] = ((s->*(ptrs.packing)).expand ? weight : -1); } { // Prevent the widget from going past the container's low edge. LinearProgram::Row row = linprog.add_row(); - row[(*i)->index*5] = 1; - row[(*i)->index*5+2] = -1; + row[s->index*5] = 1; + row[s->index*5+2] = -1; row.back() = margin.*(ptrs.low_margin); } @@ -374,24 +382,30 @@ void Layout::solve_constraints(int dir, SolveMode mode) { // 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[s->index*5] = 1; + row[s->index*5+1] = 1; + row[s->index*5+3] = 1; row.back() = geom.*(ptrs.dim)-margin.*(ptrs.high_margin); } - if(((*i)->*(ptrs.packing)).gravity==0) + if(s->floating || (s->*(ptrs.packing)).gravity==0) { - /* 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. */ + /* 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 = (s->*(ptrs.packing)).gravity*0.5+0.5; LinearProgram::Row row = linprog.add_row(); - row[(*i)->index*5] = 1; - row[(*i)->index*5+1] = 0.5; + row[s->index*5] = 1; + row[s->index*5+1] = a; row[k] = 1; row[k+1] = -1; - row.back() = geom.*(ptrs.dim)/2; + if(s->floating) + { + const Geometry &cgeom = s->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; @@ -401,29 +415,30 @@ void Layout::solve_constraints(int dir, SolveMode mode) /* Don't allow the widget's dimension to get below that determined by autosizing. */ LinearProgram::Row row = linprog.add_row(); - row[(*i)->index*5+1] = 1; - row[(*i)->index*5+4] = -1; - row.back() = (*i)->autosize_geom.*(ptrs.dim); + row[s->index*5+1] = 1; + row[s->index*5+4] = -1; + row.back() = s->autosize_geom.*(ptrs.dim); } /* Add rows for user-defined constraints. Constraints are always added in pairs, so it's only necessary to create a row for one half. */ - for(list::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j) - if(j->target.index>(*i)->index && (j->type&1)==dir) + for(const Constraint &c: s->constraints) + if(c.target->index>s->index && (c.type&1)==dir) { LinearProgram::Row row = linprog.add_row(); - float polarity = ((j->type&SELF_DIM) ? -1 : 1); - if(j->type&SELF_POS) - row[(*i)->index*5] = polarity; - if(j->type&SELF_DIM) - row[(*i)->index*5+1] = polarity; - if(j->type&TARGET_POS) - row[j->target.index*5] = -polarity; - if(j->type&TARGET_DIM) - row[j->target.index*5+1] = -polarity; - if(j->type&SPACING) - row.back() = (j->spacing>=0 ? j->spacing : this->*(ptrs.spacing)); - if(j->type&SLACK) + float polarity = ((c.type&SELF_DIM) ? -1 : 1); + float dim_weight = ((c.type&HALF_DIM) ? 0.5f : 1); + if(c.type&SELF_POS) + row[s->index*5] = polarity; + if(c.type&SELF_DIM) + row[s->index*5+1] = polarity*dim_weight; + if(c.type&TARGET_POS) + row[c.target->index*5] = -polarity; + if(c.type&TARGET_DIM) + row[c.target->index*5+1] = -polarity*dim_weight; + if(c.type&SPACING) + row.back() = (c.spacing>=0 ? c.spacing : this->*(ptrs.spacing)); + if(c.type&SLACK) row[k++] = -1; } } @@ -434,20 +449,20 @@ void Layout::solve_constraints(int dir, SolveMode mode) if(mode==AUTOSIZE) { autosize_geom.*(ptrs.dim) = 0; - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - if((*i)->index>=0) + for(const Slot *s: slots) + if(s->index>=0) { - int high_edge = linprog.get_variable((*i)->index*5)+linprog.get_variable((*i)->index*5+1); + int high_edge = linprog.get_variable(s->index*5)+linprog.get_variable(s->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) - if((*i)->index>=0) + for(Slot *s: slots) + if(s->index>=0) { - (*i)->geom.*(ptrs.pos) = linprog.get_variable((*i)->index*5); - (*i)->geom.*(ptrs.dim) = linprog.get_variable((*i)->index*5+1); + s->geom.*(ptrs.pos) = linprog.get_variable(s->index*5); + s->geom.*(ptrs.dim) = linprog.get_variable(s->index*5+1); } } } @@ -455,22 +470,13 @@ void Layout::solve_constraints(int dir, SolveMode mode) Layout::Constraint::Constraint(ConstraintType t, Slot &s): type(t), - target(s), - spacing(-1) -{ } - - -Layout::Packing::Packing(): - gravity(-1), - expand(false) + target(&s) { } Layout::Slot::Slot(Layout &l, Widget &w): layout(l), - index(0), - widget(w), - ghost(false) + widget(w) { vert_pack.gravity = 1; widget.signal_autosize_changed.connect(sigc::mem_fun(this, &Slot::autosize_changed)); @@ -599,10 +605,14 @@ void operator>>(const LexicalConverter &conv, Layout::ConstraintType &ctype) ctype = Layout::FAR_LEFT_OF; else if(str=="ALIGN_TOP") ctype = Layout::ALIGN_TOP; + else if(str=="ALIGN_VCENTER") + ctype = Layout::ALIGN_VCENTER; else if(str=="ALIGN_BOTTOM") ctype = Layout::ALIGN_BOTTOM; else if(str=="ALIGN_RIGHT") ctype = Layout::ALIGN_RIGHT; + else if(str=="ALIGN_HCENTER") + ctype = Layout::ALIGN_HCENTER; else if(str=="ALIGN_LEFT") ctype = Layout::ALIGN_LEFT; else if(str=="COPY_WIDTH") @@ -614,12 +624,9 @@ void operator>>(const LexicalConverter &conv, Layout::ConstraintType &ctype) } -Layout::LinearProgram::LinearProgram(unsigned s): +Layout::LinearProgram::LinearProgram(size_t s): n_columns(s), - n_rows(1), - columns(n_columns), - solved(false), - infeasible(false) + columns(n_columns) { } Layout::LinearProgram::Row Layout::LinearProgram::add_row() @@ -627,7 +634,7 @@ Layout::LinearProgram::Row Layout::LinearProgram::add_row() return Row(*this, n_rows++); } -Layout::LinearProgram::Row Layout::LinearProgram::operator[](unsigned r) +Layout::LinearProgram::Row Layout::LinearProgram::operator[](size_t r) { if(r>=n_rows) throw out_of_range("LinearProgram::operator[]"); @@ -640,14 +647,14 @@ Layout::LinearProgram::Row Layout::LinearProgram::get_objective_row() return Row(*this, 0); } -float Layout::LinearProgram::get_variable(unsigned i) +float Layout::LinearProgram::get_variable(size_t i) { if(!solved || infeasible) throw logic_error("not solved"); if(i+1>=n_columns) throw out_of_range("LinearProgram::get_variable"); - if(unsigned r = columns[i].basic) + if(size_t r = columns[i].basic) return columns.back().values[r]; else return 0; @@ -696,63 +703,61 @@ void Layout::LinearProgram::prepare_columns() vector obj_coeff(n_rows, 0.0f); vector row_coeff(n_rows, 1.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) && obj_coeff[i->values.size()-1]==0.0f) + for(Column &c: columns) + if(c.values.size()>=2 && c.values.back()!=0.0f && (constants.size()=0.0f) && obj_coeff[c.values.size()-1]==0.0f) { bool basic = true; - for(unsigned j=1; (basic && j+1values.size()); ++j) - basic = (i->values[j]==0.0f); + for(size_t j=1; (basic && j+1basic = i->values.size()-1; - row_coeff[i->basic] = 1.0f/i->values.back(); - obj_coeff[i->basic] = -i->values.front(); - i->values.clear(); + c.basic = c.values.size()-1; + row_coeff[c.basic] = 1.0f/c.values.back(); + obj_coeff[c.basic] = -c.values.front(); + c.values.clear(); } } - } // Price out the newly-created basic variables. - for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) - if(!i->values.empty()) + for(Column &c: columns) + if(!c.values.empty()) { - for(unsigned j=0; jvalues.size(); ++j) + for(size_t j=0; jvalues[j] *= row_coeff[j]; - i->values.front() += obj_coeff[j]*i->values[j]; + c.values[j] *= row_coeff[j]; + c.values.front() += obj_coeff[j]*c.values[j]; } } } void Layout::LinearProgram::add_artificial_variables() { - vector artificial_rows(n_rows-1); - for(unsigned i=0; i artificial_rows(n_rows-1); + for(size_t i=0; i::iterator i=columns.begin(); i!=columns.end(); ++i) - if(i->basic) - artificial_rows[i->basic-1] = 0; + for(const Column &c: columns) + if(c.basic) + artificial_rows[c.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) + for(Column &c: columns) + if(!c.basic) { float objective = 0.0f; - if(!i->values.empty()) + if(!c.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]; + objective = c.values.front(); + c.values.front() = 0.0f; + for(size_t r: artificial_rows) + if(rvalues.resize(n_rows+1, 0.0f); - i->values.back() = objective; + c.values.resize(n_rows+1, 0.0f); + c.values.back() = objective; } if(artificial_rows.empty()) @@ -764,7 +769,7 @@ void Layout::LinearProgram::add_artificial_variables() columns.resize(n_columns+artificial_rows.size()); columns.back() = columns[n_columns-1]; columns[n_columns-1].values.clear(); - for(unsigned i=0; i::iterator i=columns.begin(); i!=columns.end(); ++i) - if(!i->basic) + for(Column &c: columns) + if(!c.basic) { - i->values.front() = i->values.back(); - i->values.pop_back(); + c.values.front() = c.values.back(); + c.values.pop_back(); } } -unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c) +size_t Layout::LinearProgram::find_minimal_ratio(size_t c) { /* Pick the row with the minimum ratio between the constant column and the pivot column. This ensures that when the pivot column is made basic, values @@ -806,8 +811,8 @@ unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c) The use of n_rows instead of the true size of the column is intentional, since the relocated objective row must be ignored in phase 1. */ float best = numeric_limits::infinity(); - unsigned row = 0; - for(unsigned i=1; i0) { float ratio = columns.back().values[i]/columns[c].values[i]; @@ -821,11 +826,11 @@ unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c) return row; } -void Layout::LinearProgram::make_basic_column(unsigned c, unsigned r) +void Layout::LinearProgram::make_basic_column(size_t c, size_t r) { /* Perform row transfer operations to make the pivot column basic, containing a 1 on the pivot row. */ - for(unsigned i=0; i0) - if(unsigned row = find_minimal_ratio(i)) + if(size_t row = find_minimal_ratio(i)) { make_basic_column(i, row); return true; @@ -864,12 +869,12 @@ bool Layout::LinearProgram::pivot() } -Layout::LinearProgram::Row::Row(LinearProgram &lp, unsigned i): +Layout::LinearProgram::Row::Row(LinearProgram &lp, size_t i): linprog(lp), index(i) { } -float &Layout::LinearProgram::Row::operator[](unsigned c) +float &Layout::LinearProgram::Row::operator[](size_t c) { if(c>=linprog.n_columns) throw out_of_range("Row::operator[]");