diff --git a/data/gui/default/window/addon.cfg b/data/gui/default/window/addon.cfg index 43cfa74b14d..0f4d9a3e3ec 100644 --- a/data/gui/default/window/addon.cfg +++ b/data/gui/default/window/addon.cfg @@ -19,8 +19,14 @@ [grid] [row] - + scale = 1 + [column] + scale = 0 + + border = "all" + border_size = 5 + horizontal_alignment = "left" [label] label_definition = "default" @@ -31,6 +37,11 @@ [/column] [column] + scale = 1 + + border = "all" + border_size = 5 + horizontal_grow = "true" [text_box] id = "host_name" @@ -45,6 +56,7 @@ [/row] [row] + scale = 0 [column] @@ -58,6 +70,9 @@ [/column] [column] + border = "all" + border_size = 5 + horizontal_alignment = "right" [button] # just show how the default looks. @@ -77,8 +92,12 @@ [/row] [row] + scale = 0 [column] + border = "all" + border_size = 5 + horizontal_alignment = "right" [button] id = "ok" @@ -91,6 +110,9 @@ [/column] [column] + border = "all" + border_size = 5 + horizontal_alignment = "right" [button] id = "cancel" diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp index b5a6960fda7..f9668d555ff 100644 --- a/src/gui/widgets/button.cpp +++ b/src/gui/widgets/button.cpp @@ -97,6 +97,15 @@ void tbutton::mouse_left_button_double_click(tevent_handler&) DBG_G_E << "Button: left mouse button double click.\n"; } +tpoint tbutton::get_minimum_size() const +{ + if(definition_ == std::vector::const_iterator()) { + return tpoint(get_button(definition())->min_width, get_button(definition())->min_height); + } else { + return tpoint(definition_->min_width, definition_->min_height); + } +} + tpoint tbutton::get_best_size() const { if(definition_ == std::vector::const_iterator()) { @@ -106,6 +115,15 @@ tpoint tbutton::get_best_size() const } } +tpoint tbutton::get_maximum_size() const +{ + if(definition_ == std::vector::const_iterator()) { + return tpoint(get_button(definition())->max_width, get_button(definition())->max_height); + } else { + return tpoint(definition_->max_width, definition_->max_height); + } +} + void tbutton::set_best_size(const tpoint& origin) { resolve_definition(); diff --git a/src/gui/widgets/button.hpp b/src/gui/widgets/button.hpp index 7ed5058354b..d40cab01928 100644 --- a/src/gui/widgets/button.hpp +++ b/src/gui/widgets/button.hpp @@ -43,7 +43,10 @@ public: void mouse_left_button_double_click(tevent_handler&); //FIXME remove // note we should check whether the label fits in the button + // Inherited from twidget. + tpoint get_minimum_size() const; tpoint get_best_size() const; + tpoint get_maximum_size() const; void set_best_size(const tpoint& origin); diff --git a/src/gui/widgets/control.cpp b/src/gui/widgets/control.cpp index b9bb70ae22c..dbc778be63f 100644 --- a/src/gui/widgets/control.cpp +++ b/src/gui/widgets/control.cpp @@ -70,6 +70,18 @@ void tcontrol::set_height(const unsigned height) twidget::set_height(height); } +void tcontrol::set_size(const SDL_Rect& rect) +{ + // resize canvasses + foreach(tcanvas& canvas, canvas_) { + canvas.set_width(rect.w); + canvas.set_height(rect.h); + } + + // inherited + twidget::set_size(rect); +} + void tcontrol::set_label(const std::string& label) { if(label == label_) { diff --git a/src/gui/widgets/control.hpp b/src/gui/widgets/control.hpp index 7db677c1584..7bae55e0b54 100644 --- a/src/gui/widgets/control.hpp +++ b/src/gui/widgets/control.hpp @@ -37,6 +37,9 @@ public: //! Inherited from twidget. void set_height(const unsigned height); + //! Inherited from twidget. + void set_size(const SDL_Rect& rect); + void set_visible(const bool visible) { if(visible_ != visible) { visible_ = visible; set_dirty();} } bool get_visible() const { return visible_; } diff --git a/src/gui/widgets/grid.cpp b/src/gui/widgets/grid.cpp index fc8df55faf6..9bc5b6f250e 100644 --- a/src/gui/widgets/grid.cpp +++ b/src/gui/widgets/grid.cpp @@ -14,6 +14,7 @@ #include "gui/widgets/grid.hpp" +#include "foreach.hpp" #include "log.hpp" #include @@ -48,6 +49,14 @@ tgrid::tgrid(const unsigned rows, const unsigned cols, cols_(cols), default_flags_(default_flags), default_border_size_(default_border_size), + best_row_height_(), + best_col_width_(), + minimum_row_height_(), + minimum_col_width_(), + row_height_(), + col_width_(), + row_scaling_(), + col_scaling_(), children_(rows * cols) { } @@ -89,6 +98,8 @@ void tgrid::add_child(twidget* widget, const unsigned row, } else { cell.set_id(""); } + + clear_cache(); } void tgrid::set_rows(const unsigned rows) @@ -122,7 +133,10 @@ void tgrid::set_rows_cols(const unsigned rows, const unsigned cols) rows_ = rows; cols_ = cols; + row_scaling_.resize(rows); + col_scaling_.resize(cols); children_.resize(rows_ * cols_); + clear_cache(); } void tgrid::remove_child(const unsigned row, const unsigned col) @@ -133,6 +147,7 @@ void tgrid::remove_child(const unsigned row, const unsigned col) cell.set_id(""); cell.set_widget(0); + clear_cache(); } void tgrid::remove_child(const std::string& id, const bool find_all) @@ -143,6 +158,7 @@ void tgrid::remove_child(const std::string& id, const bool find_all) if(itor->id() == id) { itor->set_id(""); itor->set_widget(0); + clear_cache(); if(!find_all) { break; @@ -151,90 +167,155 @@ void tgrid::remove_child(const std::string& id, const bool find_all) } } -tpoint tgrid::get_best_size() +tpoint tgrid::get_best_size() const { - std::vector best_col_width(cols_, 0); - std::vector best_row_height(rows_, 0); - - // First get the best sizes for all items. - for(unsigned row = 0; row < rows_; ++row) { - for(unsigned col = 0; col < cols_; ++col) { - const tpoint size = child(row, col).get_best_size(); + if(best_row_height_.empty() || best_col_width_.empty()) { + + DBG_G << "Grid: calculate best size.\n"; + + best_row_height_.resize(rows_, 0); + best_col_width_.resize(cols_, 0); + + // First get the best sizes for all items. + for(unsigned row = 0; row < rows_; ++row) { + for(unsigned col = 0; col < cols_; ++col) { + + tpoint size = child(row, col).get_best_size(); + + if(size.x > best_col_width_[col]) { + best_col_width_[col] = size.x; + } + + if(size.y > best_row_height_[row]) { + best_row_height_[row] = size.y; + } - if(size.x > best_col_width[col]) { - best_col_width[col] = size.x; } - - if(size.y > best_row_height[row]) { - best_row_height[row] = size.y; - } - } + } else { + DBG_G << "Grid: used cached best size.\n"; } for(unsigned row = 0; row < rows_; ++row) { DBG_G << "Grid: the best height for row " << row - << " will be " << best_row_height[row] << ".\n"; + << " will be " << best_row_height_[row] << ".\n"; } for(unsigned col = 0; col < cols_; ++col) { DBG_G << "Grid: the best width for col " << col - << " will be " << best_col_width[col] << ".\n"; + << " will be " << best_col_width_[col] << ".\n"; } return tpoint( - std::accumulate(best_col_width.begin(), best_col_width.end(), 0), - std::accumulate(best_row_height.begin(), best_row_height.end(), 0)); - + std::accumulate(best_col_width_.begin(), best_col_width_.end(), 0), + std::accumulate(best_row_height_.begin(), best_row_height_.end(), 0)); } void tgrid::set_best_size(const tpoint& origin) { - std::vector best_col_width(cols_, 0); - std::vector best_row_height(rows_, 0); - - // First get the best sizes for all items. (FIXME copy and paste of get best size) - for(unsigned row = 0; row < rows_; ++row) { - for(unsigned col = 0; col < cols_; ++col) { + // Update the sizes. + get_best_size(); + assert(best_col_width_.size() == cols_); + assert(best_row_height_.size() == rows_); - const tpoint size = child(row, col).get_best_size(); + row_height_ = best_row_height_; + col_width_ = best_col_width_; - if(size.x > best_col_width[col]) { - best_col_width[col] = size.x; - } + layout(origin); + return; - if(size.y > best_row_height[row]) { - best_row_height[row] = size.y; - } +} - } +void tgrid::set_size(const SDL_Rect& rect) +{ + twidget::set_size(rect); + + const tpoint orig(rect.x, rect.y); + const tpoint size(rect.w, rect.h); + + const tpoint best_size = get_best_size(); + row_height_ = best_row_height_; + col_width_ = best_col_width_; + + assert(row_height_.size() == rows_); + assert(col_width_.size() == cols_); + assert(row_scaling_.size() == rows_); + assert(col_scaling_.size() == cols_); + if(best_size == size) { + row_height_ = best_row_height_; + col_width_ = best_col_width_; + + layout(orig); + return; } - // Set the sizes - tpoint orig = origin; - for(unsigned row = 0; row < rows_; ++row) { - for(unsigned col = 0; col < cols_; ++col) { + if(best_size < size) { + row_height_ = best_row_height_; + col_width_ = best_col_width_; - DBG_G << "Grid: set widget at " << row - << ',' << col << " at origin " << orig << ".\n"; + // expand it. + const unsigned w = size.x - best_size.x; + unsigned w_size = std::accumulate(col_scaling_.begin(), col_scaling_.end(), 0); + DBG_G << "Grid: extra width " << w << " will be divided amount " << w_size << " units in " << cols_ << " columns.\n"; - if(child(row, col).widget()) { - child(row, col).widget()->set_best_size(orig); + if(w_size == 0) { + // If all sizes are 0 reset them to 1 + foreach(unsigned& val, col_scaling_) { + val = 1; } - - orig.x += best_col_width[col]; + w_size = cols_; } - orig.y += best_row_height[row]; - orig.x = origin.x; + // We might have a bit 'extra' if the division doesn't fix exactly + // but we ignore that part for now. + const unsigned w_normal = w / w_size; + for(unsigned i = 0; i < cols_; ++i) { + col_width_[i] += w_normal * col_scaling_[i]; + DBG_G << "Grid: column " << i << " with scale factor " + << col_scaling_[i] << " set width to " << col_width_[i] << ".\n"; + } + + + + const unsigned h = size.y - best_size.y; + unsigned h_size = std::accumulate(row_scaling_.begin(), row_scaling_.end(), 0); + DBG_G << "Grid: extra height " << h << " will be divided amount " << h_size << " units in " << rows_ << " rows.\n"; + + if(h_size == 0) { + // If all sizes are 0 reset them to 1 + foreach(unsigned& val, row_scaling_) { + val = 1; + } + h_size = rows_; + } + // We might have a bit 'extra' if the division doesn't fix exactly + // but we ignore that part for now. + const unsigned h_normal = h / h_size; + for(unsigned i = 0; i < rows_; ++i) { + row_height_[i] += h_normal * row_scaling_[i]; + DBG_G << "Grid: row " << i << " with scale factor " + << row_scaling_[i] << " set height to " << row_height_[i] << ".\n"; + } + + + layout(orig); + return; + + } + + + // FIXME make other cases work as well + assert(false); +/* + const tpoint minimum_size = get_minimum_size(); + if(minimum_size == size) { +*/ } twidget* tgrid::get_widget(const tpoint& coordinate) { - - //! FIXME we need to store the sizes, since this is quite - //! pathatic. + //! FIXME use iterator. for(unsigned row = 0; row < rows_; ++row) { for(unsigned col = 0; col < cols_; ++col) { @@ -245,6 +326,7 @@ twidget* tgrid::get_widget(const tpoint& coordinate) widget = widget->get_widget(coordinate); if(widget) { + clear_cache(); return widget; } } @@ -257,9 +339,7 @@ twidget* tgrid::get_widget(const tpoint& coordinate) //! Override base class. twidget* tgrid::get_widget_by_id(const std::string& id) { - - //! FIXME we need to store the sizes, since this is quite - //! pathatic. + //! FIXME use iterator. for(unsigned row = 0; row < rows_; ++row) { for(unsigned col = 0; col < cols_; ++col) { @@ -270,6 +350,7 @@ twidget* tgrid::get_widget_by_id(const std::string& id) widget = widget->get_widget_by_id(id); if(widget) { + clear_cache(); return widget; } } @@ -278,24 +359,170 @@ twidget* tgrid::get_widget_by_id(const std::string& id) return twidget::get_widget_by_id(id); } -tpoint tgrid::tchild::get_best_size() +void tgrid::clear_cache() { - if(!dirty_ && (!widget_ || !widget_->dirty())) { - return best_size_; - } - - best_size_ = widget_ ? widget_->get_best_size() : tpoint(0, 0); - - //FIXME take care of the border configuration. - best_size_.x += 2 * border_size_; - best_size_.y += 2 * border_size_; - - dirty_ = false; - - return best_size_; + best_row_height_.clear(); + best_col_width_.clear(); + minimum_row_height_.clear(); + minimum_col_width_.clear(); } +void tgrid::layout(const tpoint& origin) +{ + tpoint orig = origin; + for(unsigned row = 0; row < rows_; ++row) { + for(unsigned col = 0; col < cols_; ++col) { + + const tpoint size(col_width_[col], row_height_[row]); + DBG_G << "Grid: set widget at " << row << ',' << col + << " at origin " << orig << " with size " << size << ".\n"; + + if(child(row, col).widget()) { + child(row, col).set_size(orig, size); + } + + orig.x += col_width_[col]; + } + orig.y += row_height_[row]; + orig.x = origin.x; + } +} + +tpoint tgrid::tchild::get_best_size() const +{ + if(!widget_) { + return tpoint(0, 0); + } + + if(widget_->dirty() || best_size_ == tpoint(0, 0)) { + + best_size_ = widget_->get_best_size(); + + if(border_size_) { + + if(flags_ & BORDER_TOP) best_size_.y += border_size_; + if(flags_ & BORDER_BOTTOM) best_size_.y += border_size_; + + if(flags_ & BORDER_LEFT) best_size_.x += border_size_; + if(flags_ & BORDER_RIGHT) best_size_.x += border_size_; + } + } + + return best_size_; +} + +void tgrid::tchild::set_size(tpoint orig, tpoint size) +{ + assert(widget()); + widget()->set_best_size(orig); // needed for calling resolve_resolution() + + if(border_size_) { + if(flags_ & BORDER_TOP) { + orig.y += border_size_; + size.y -= border_size_; + } + if(flags_ & BORDER_BOTTOM) { + size.y -= border_size_; + } + + if(flags_ & BORDER_LEFT) { + orig.x += border_size_; + size.x -= border_size_; + } + if(flags_ & BORDER_RIGHT) { + size.x -= border_size_; + } + } + + // If size smaller or equal to best size set that size. + // No need to check > min size since this is what we got. + const tpoint best_size = widget()->get_best_size(); + if(size <= best_size) { + DBG_G << "Grid cell: in best size range setting widget to " + << orig << " x " << size << ".\n"; + + widget()->set_size(create_rect(orig, size)); + return; + } + + const tpoint maximum_size = widget()->get_maximum_size(); + if(flags_ & (HORIZONTAL_GROW_SEND_TO_CLIENT | HORIZONTAL_GROW_SEND_TO_CLIENT)) { + if(maximum_size == tpoint(0,0) || size <= maximum_size) { + + DBG_G << "Grid cell: in maximum size range setting widget to " + << orig << " x " << size << ".\n"; + + widget()->set_size(create_rect(orig, size)); + return; + + } + } + + tpoint widget_size = best_size; + tpoint widget_orig = orig; + + if(flags_ & HORIZONTAL_GROW_SEND_TO_CLIENT) { + if(maximum_size.x) { + widget_size.x = std::min(size.x, maximum_size.x); + } else { + widget_size.x = size.x; + } + DBG_G << "Grid cell: horizontal growing from " << best_size.x << " to " << widget_size.x << ".\n"; + } + + if(flags_ & VERTICAL_GROW_SEND_TO_CLIENT) { + if(maximum_size.y) { + widget_size.y = std::min(size.y, maximum_size.y); + } else { + widget_size.y = size.y; + } + DBG_G << "Grid cell: vertical growing from " << best_size.y << " to " << widget_size.y << ".\n"; + } + + if((flags_ & VERTICAL_ALIGN_TOP) == VERTICAL_ALIGN_TOP) { + // Do nothing. + + DBG_G << "Grid cell: vertically aligned at the top.\n"; + + } else if((flags_ & VERTICAL_ALIGN_CENTER) == VERTICAL_ALIGN_CENTER) { + + widget_orig.y += (size.y - widget_size.y) / 2; + DBG_G << "Grid cell: vertically centred.\n"; + + } else if((flags_ & VERTICAL_ALIGN_BOTTOM) == VERTICAL_ALIGN_BOTTOM) { + + widget_orig.y += (size.y - widget_size.y); + DBG_G << "Grid cell: vertically aligned at the bottom.\n"; + + } else { + assert(false); + } + + if((flags_ & HORIZONTAL_ALIGN_LEFT) == HORIZONTAL_ALIGN_LEFT) { + // Do nothing. + DBG_G << "Grid cell: horizontally aligned at the left.\n"; + + } else if((flags_ & HORIZONTAL_ALIGN_CENTER) == HORIZONTAL_ALIGN_CENTER) { + + widget_orig.x += (size.x - widget_size.x) / 2; + DBG_G << "Grid cell: horizontally centred.\n"; + + } else if((flags_ & HORIZONTAL_ALIGN_RIGHT) == HORIZONTAL_ALIGN_RIGHT) { + + widget_orig.x += (size.x - widget_size.x); + DBG_G << "Grid cell: horizontally aligned at the right.\n"; + + } else { + assert(false); + } + + DBG_G << "Grid cell: resize widget to " + << widget_orig << " x " << widget_size << ".\n"; + + + widget()->set_size(create_rect(widget_orig, widget_size)); +} } // namespace gui2 diff --git a/src/gui/widgets/grid.hpp b/src/gui/widgets/grid.hpp index 677943391d2..90d73546fce 100644 --- a/src/gui/widgets/grid.hpp +++ b/src/gui/widgets/grid.hpp @@ -26,22 +26,22 @@ class tgrid : public virtual twidget public: // ***** ***** FLAGS ***** ***** - static const unsigned VERTICAL_RESIZE_GROW = 1 << 1; +// static const unsigned VERTICAL_RESIZE_GROW = 1 << 1; static const unsigned VERTICAL_GROW_SEND_TO_CLIENT = 1 << 2; - static const unsigned VERTICAL_ALIGN_TOP = 1 << 4; - static const unsigned VERTICAL_ALIGN_CENTER = 1 << 5; - static const unsigned VERTICAL_ALIGN_BOTTOM = 1 << 6; - static const unsigned VERTICAL_ALIGN_LANGUAGE = 1 << 7; + static const unsigned VERTICAL_ALIGN_TOP = 3 << 4; + static const unsigned VERTICAL_ALIGN_CENTER = 2 << 4; + static const unsigned VERTICAL_ALIGN_BOTTOM = 1 << 4; +// static const unsigned VERTICAL_ALIGN_LANGUAGE = 0 << 4; - static const unsigned HORIZONTAL_RESIZE_GROW = 1 << 16; +// static const unsigned HORIZONTAL_RESIZE_GROW = 1 << 16; static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT = 1 << 17; - static const unsigned HORIZONTAL_ALIGN_TOP = 1 << 18; - static const unsigned HORIZONTAL_ALIGN_CENTER = 1 << 19; - static const unsigned HORIZONTAL_ALIGN_BOTTOM = 1 << 20; - static const unsigned HORIZONTAL_ALIGN_LANGUAGE = 1 << 21; + static const unsigned HORIZONTAL_ALIGN_LEFT = 3 << 18; + static const unsigned HORIZONTAL_ALIGN_CENTER = 2 << 18; + static const unsigned HORIZONTAL_ALIGN_RIGHT = 1 << 18; +// static const unsigned HORIZONTAL_ALIGN_LANGUAGE = 0 << 18; static const unsigned BORDER_TOP = 1 << 24; @@ -55,15 +55,16 @@ public: virtual ~tgrid(); - void add_child(twidget* widget, const unsigned row, - const unsigned col, const unsigned flags, const unsigned border_size); - - void add_child(twidget* widget, const unsigned row, const unsigned col) - { add_child(widget, row, col, default_flags_, default_border_size_); } - +#if 0 + // FIXME if these are really not needed remove them. void add_child(twidget* widget, const unsigned row, const unsigned col, const unsigned flags) { add_child(widget, row, col, flags, default_border_size_); } + void add_child(twidget* widget, const unsigned row, const unsigned col) + { add_child(widget, row, col, default_flags_, default_border_size_); } +#endif + void add_child(twidget* widget, const unsigned row, + const unsigned col, const unsigned flags, const unsigned border_size); void set_rows(const unsigned rows); unsigned int get_rows() const { return rows_; } @@ -73,15 +74,28 @@ public: void set_rows_cols(const unsigned rows, const unsigned cols); + void set_row_scaling(const unsigned row, const unsigned scale) + { row_scaling_[row] = scale; set_dirty(); } //FIXME add assert. + + void set_col_scaling(const unsigned col, const unsigned scale) + { col_scaling_[col] = scale; set_dirty(); } //FIXME add assert. + void remove_child(const unsigned row, const unsigned col); void remove_child(const std::string& id, const bool find_all = false); - //! Inherited - tpoint get_best_size(); + //FIXME add the option to set the proportional growth for each row and column - //! Inherited + //! Inherited from twidget. + tpoint get_minimum_size() const { /*FIXME IMPLEMENT*/ return tpoint(0,0); } + tpoint get_best_size() const; + tpoint get_maximum_size() const { /*FIXME IMPLEMENT*/ return tpoint(0,0); } + + //! Inherited from twidget. void set_best_size(const tpoint& origin); + //! Inherited from twidget. + void set_size(const SDL_Rect& rect); + //! Gets the widget at the wanted coordinates. //! Override base class. twidget* get_widget(const tpoint& coordinate); @@ -90,6 +104,9 @@ public: //! Override base class. twidget* get_widget_by_id(const std::string& id); + //! Inherited from twidget. + void draw(surface& surface) { /* FIXME IMPLEMENT */ } + private: class tchild { @@ -100,7 +117,7 @@ private: border_size_(0), widget_(0), best_size_(0, 0), - dirty_(true), + minimum_size_(0, 0), clip_() // Fixme make a class wo we can store some properties in the cache @@ -111,18 +128,20 @@ private: void set_id(const std::string& id) { id_ = id; } unsigned get_flags() const { return flags_; } - void set_flags(const unsigned flags) { flags_ = flags; dirty_ = true; } + void set_flags(const unsigned flags) { flags_ = flags; set_dirty(); } unsigned get_border_size() const { return border_size_; } void set_border_size(const unsigned border_size) - { border_size_ = border_size; dirty_ = true; } + { border_size_ = border_size; set_dirty(); } twidget* widget() { return widget_; } - void set_widget(twidget* widget) { widget_ = widget; dirty_ = true; } + void set_widget(twidget* widget) { widget_ = widget; set_dirty(); } //! Gets the best size for the cell, not const since we modify the internal //! state, might use mutable later (if really needed). - tpoint get_best_size(); + tpoint get_best_size() const; + + void set_size(tpoint orig, tpoint size); private: //! The id of the widget if it has a widget. @@ -140,15 +159,23 @@ private: //! The best size for this cell, determined by the best size //! of the widget and the border_size_ and flags_. - tpoint best_size_; + mutable tpoint best_size_; - //! Tracks the dirty state of the cell regarding best_size_. - bool dirty_; + //! The minimum size for this cell, like best_size_. + mutable tpoint minimum_size_; //! The clipping area for the widget. This is also the size of //! the container. SDL_Rect clip_; + //! Sets the calculations to be dirty, this means all caches + //! are simply cleared. + void set_dirty() // FIXME rename to clear cache?? + { + best_size_ = tpoint(0, 0); + minimum_size_ = tpoint(0, 0); + } + }; // class tchild public: @@ -178,33 +205,101 @@ public: iterator end() { return iterator(children_.end()); } private: + //! The number of rows / columns. unsigned rows_; unsigned cols_; + //! Default flags for a grid cell. const unsigned default_flags_; + //! Default border size for a grid cell. const unsigned default_border_size_; - + //! The optimal row heights / col widths. + mutable std::vector best_row_height_; //FIXME implement + mutable std::vector best_col_width_; //FIXME implement + //! The minimal row heights / col widths. + mutable std::vector minimum_row_height_; //FIXME implement + mutable std::vector minimum_col_width_; //FIXME implement + //! The row heights / col widths currently used. + std::vector row_height_; + std::vector col_width_; + + //! The resize factors for rows / cols. + std::vector row_scaling_; + std::vector col_scaling_; + + //! Contains all cells. std::vector children_; - tchild& child(const unsigned row, const unsigned col) + const tchild& child(const unsigned row, const unsigned col) const { return children_[rows_ * col + row]; } + tchild& child(const unsigned row, const unsigned col) + { clear_cache(); return children_[rows_ * col + row]; } + void clear_cache(); + + void layout(const tpoint& origin); }; //! Visible container to hold children. -class tpanel : public tgrid, public tcontrol +class tpanel : public tcontrol { public: tpanel() : - tgrid(0, 0, 0, 0), - tcontrol(0) - {} + tcontrol(0), + grid_(0, 0, 0, 0) + { + grid_.set_parent(this); + } + + + // Inherited from twidget. + twidget* get_widget(const tpoint& coordinate) { return grid_.get_widget(coordinate); } + + // Inherited from twidget. + twidget* get_widget_by_id(const std::string& id) { return grid_.get_widget_by_id(id); } + + // Inherited from twidget. + bool dirty() const { return twidget::dirty() || grid_.dirty(); } + + + //***** **** wrappers to the grid **** **** + tgrid::iterator begin() { return grid_.begin(); } + tgrid::iterator end() { return grid_.end(); } + + void set_client_size(const SDL_Rect& rect) { grid_.set_size(rect); } + + void set_rows(const unsigned rows) { grid_.set_rows(rows); } + unsigned int get_rows() const { return grid_.get_rows(); } + + void set_cols(const unsigned cols) { grid_.set_cols(cols); } + unsigned int get_cols() const { return grid_.get_cols(); } + + void set_rows_cols(const unsigned rows, const unsigned cols) + { grid_.set_rows_cols(rows, cols); } +#if 0 + // FIXME if these are really not needed remove them. + void add_child(twidget* widget, const unsigned row, const unsigned col) + { grid_.add_child(widget, row, col); } + + void add_child(twidget* widget, const unsigned row, const unsigned col, const unsigned flags) + { grid_.add_child(widget, row, col, flags); } +#endif + void add_child(twidget* widget, const unsigned row, + const unsigned col, const unsigned flags, const unsigned border_size) + { grid_.add_child(widget, row, col, flags, border_size); } + + void set_row_scaling(const unsigned row, const unsigned scale) + { grid_.set_row_scaling(row, scale); } + + void set_col_scaling(const unsigned col, const unsigned scale) + { grid_.set_col_scaling(col, scale); } private: + tgrid grid_; }; diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp index 4e045bd4f9b..2745577e00a 100644 --- a/src/gui/widgets/label.cpp +++ b/src/gui/widgets/label.cpp @@ -39,6 +39,15 @@ namespace gui2 { +tpoint tlabel::get_minimum_size() const +{ + if(definition_ == std::vector::const_iterator()) { + return tpoint(get_label(definition())->min_width, get_label(definition())->min_height); + } else { + return tpoint(definition_->min_width, definition_->min_height); + } +} + tpoint tlabel::get_best_size() const { if(definition_ == std::vector::const_iterator()) { @@ -48,6 +57,15 @@ tpoint tlabel::get_best_size() const } } +tpoint tlabel::get_maximum_size() const +{ + if(definition_ == std::vector::const_iterator()) { + return tpoint(get_label(definition())->max_width, get_label(definition())->max_height); + } else { + return tpoint(definition_->max_width, definition_->max_height); + } +} + void tlabel::mouse_hover(tevent_handler&) { DBG_G_E << "Text_box: mouse hover.\n"; diff --git a/src/gui/widgets/label.hpp b/src/gui/widgets/label.hpp index b434e181cf7..d5a19373889 100644 --- a/src/gui/widgets/label.hpp +++ b/src/gui/widgets/label.hpp @@ -39,7 +39,10 @@ public: void draw(surface& surface); // note we should check whether the label fits in the label + // Inherited from twidget. + tpoint get_minimum_size() const; tpoint get_best_size() const; + tpoint get_maximum_size() const; void set_best_size(const tpoint& origin); private: diff --git a/src/gui/widgets/text_box.cpp b/src/gui/widgets/text_box.cpp index 21f21731afe..361b5b3c445 100644 --- a/src/gui/widgets/text_box.cpp +++ b/src/gui/widgets/text_box.cpp @@ -420,6 +420,15 @@ void ttext_box::set_canvas_text() } } +tpoint ttext_box::get_minimum_size() const +{ + if(definition_ == std::vector::const_iterator()) { + return tpoint(get_text_box(definition())->min_width, get_text_box(definition())->min_height); + } else { + return tpoint(definition_->min_width, definition_->min_height); + } +} + tpoint ttext_box::get_best_size() const { if(definition_ == std::vector::const_iterator()) { @@ -429,6 +438,15 @@ tpoint ttext_box::get_best_size() const } } +tpoint ttext_box::get_maximum_size() const +{ + if(definition_ == std::vector::const_iterator()) { + return tpoint(get_text_box(definition())->max_width, get_text_box(definition())->max_height); + } else { + return tpoint(definition_->max_width, definition_->max_height); + } +} + void ttext_box::set_best_size(const tpoint& origin) { resolve_definition(); diff --git a/src/gui/widgets/text_box.hpp b/src/gui/widgets/text_box.hpp index 470d52b1015..2be918e7ec9 100644 --- a/src/gui/widgets/text_box.hpp +++ b/src/gui/widgets/text_box.hpp @@ -198,7 +198,11 @@ protected: void goto_end_of_line(const bool select = false) { goto_end_of_data(select); } void goto_start_of_line(const bool select = false) { goto_start_of_data(select); } + // note we should check whether the label fits in the button + // Inherited from twidget. + tpoint get_minimum_size() const; tpoint get_best_size() const; + tpoint get_maximum_size() const; void set_best_size(const tpoint& origin); private: diff --git a/src/gui/widgets/widget.hpp b/src/gui/widgets/widget.hpp index 6e9b69e448d..0594da3d955 100644 --- a/src/gui/widgets/widget.hpp +++ b/src/gui/widgets/widget.hpp @@ -37,8 +37,17 @@ struct tpoint int x; int y; + + bool operator==(const tpoint& point) const { return x == point.x && y == point.y; } + bool operator<(const tpoint& point) const + { return x < point.x || (x == point.x && y < point.y); } + + bool operator<=(const tpoint& point) const + { return x < point.x || (x == point.x && y <= point.y); } + }; + std::ostream &operator<<(std::ostream &stream, const tpoint& point); SDL_Rect create_rect(const tpoint& origin, const tpoint& size); @@ -108,14 +117,6 @@ public: virtual void help(); // send when F1 is pressed on widget to get more help #endif - //! determines the best size for a widget. - // x = best width - // y = best height - virtual tpoint get_best_size() const { return tpoint(0, 0); } - - //! determines the minimal size for a widget, needed? - - // layout sets up the children, this is only used by container // containers should know their own size and optimize their // children. @@ -204,7 +205,8 @@ public: virtual void set_height(const unsigned height) { h_ = height; set_dirty(); } unsigned get_height() const { return h_; } - bool dirty() const { return dirty_; } + //! Is the widget dirty? + virtual bool dirty() const { return dirty_; } //! Sets the best size for the object. virtual void set_best_size(const tpoint& origin) @@ -214,6 +216,15 @@ public: //! Sets the minumum size for the object. // virtual void set_minimum_size(); + //! Gets the minimum size for the object should != 0,0. + virtual tpoint get_minimum_size() const = 0; + + //! Gets the best size for the object should != 0,0. + virtual tpoint get_best_size() const = 0; + + //! Gets the best size for an object, 0,0 means no limits. + virtual tpoint get_maximum_size() const = 0; + //! Sets a predefined size for the object. virtual void set_size(const SDL_Rect& rect) { @@ -225,14 +236,14 @@ public: } //! Gets the widget at the wanted coordinates. - twidget* get_widget(const tpoint& coordinate) + virtual twidget* get_widget(const tpoint& coordinate) { return coordinate.x >= x_ && coordinate.x < (x_ + w_) && coordinate.y >= y_ && coordinate.y < (y_ + h_) ? this : 0; } //! Gets a widget with the wanted id. - twidget* get_widget_by_id(const std::string& id) + virtual twidget* get_widget_by_id(const std::string& id) { return id_ == id ? this : 0; } //! The toplevel item should always be a window if not null is returned diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index afe1f442078..3220f458476 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -109,9 +109,8 @@ int twindow::show(const bool restore, void* /*flip_function*/) SDL_Rect temp_rect = {0, 0, screen->w, screen->h}; fill_rect_alpha(temp_rect, 0, 1, screen); #endif - + // FIXME call grid().draw() and it do it's drawing. for(tgrid::iterator itor = begin(); itor != end(); ++itor) { - if(! *itor || !itor->dirty()) { continue; } @@ -152,21 +151,11 @@ void twindow::layout(const SDL_Rect position) { need_layout_ = false; - tpoint best_size = get_best_size(); + DBG_G << "Window: layout area " << position.x + << ',' << position.y << " x " << position.w + << ',' << position.h << ".\n"; - if(best_size.x < position.w && best_size.y < position.h) { - set_best_size(tpoint(position.x, position.y)); - return; - } - - DBG_G << "Window: layout can't be set to best size, try minimum.\n"; - - // Implement the code. - assert(false); - - // Failed at best size try minumum. - - // Failed at minimum log error and try to do the best possible thing. + set_client_size(position); } void twindow::set_width(const unsigned width) diff --git a/src/gui/widgets/window.hpp b/src/gui/widgets/window.hpp index 961a8d29bdb..59b77495eed 100644 --- a/src/gui/widgets/window.hpp +++ b/src/gui/widgets/window.hpp @@ -74,7 +74,8 @@ public: twindow& get_window() { return *this; } - twidget* get_widget(const tpoint& coordinate) { return tgrid::get_widget(coordinate); } + //! Inherited from tevent_handler + twidget* get_widget(const tpoint& coordinate) { return tpanel::get_widget(coordinate); } tpoint client_position(const tpoint& screen_position) const { return tpoint(screen_position.x - get_x(), screen_position.y - get_y()); } @@ -88,6 +89,11 @@ public: unsigned get_state() const { return 0; } bool full_redraw() const { return false; /* FIXME IMPLEMENT */ } + // Inherited from twidget. + tpoint get_minimum_size() const { /*FIXME IMPLEMENT*/ return tpoint(0,0); } + tpoint get_best_size() const { /*FIXME IMPLEMENT*/ return tpoint(0,0); } + tpoint get_maximum_size() const { /*FIXME IMPLEMENT*/ return tpoint(0,0); } + protected: private: diff --git a/src/gui/widgets/window_builder.cpp b/src/gui/widgets/window_builder.cpp index cb91238e8ae..794ccb9605d 100644 --- a/src/gui/widgets/window_builder.cpp +++ b/src/gui/widgets/window_builder.cpp @@ -104,7 +104,8 @@ twindow build(CVideo& video, const std::string& type) definition = get_window_builder(type); - twindow window(video, 100, 100, definition->width, definition->height); + twindow window(video, 100, 100, definition->width, definition->height); // FIXME use proper origin +// twindow window(video, 0, 0, definition->width, definition->height); // FIXME use proper origin const unsigned rows = definition->grid.rows; const unsigned cols = definition->grid.cols; @@ -112,10 +113,15 @@ twindow build(CVideo& video, const std::string& type) window.set_rows_cols(rows, cols); for(unsigned x = 0; x < rows; ++x) { + window.set_row_scaling(x, definition->grid.row_scale[x]); for(unsigned y = 0; y < cols; ++y) { + if(x == 0) { + window.set_col_scaling(y, definition->grid.col_scale[y]); + } + twidget* widget = definition->grid.widgets[x * cols + y]->build(); - window.add_child(widget, x, y); + window.add_child(widget, x, y, definition->grid.flags[x * cols + y], definition->grid.border_size[x * cols + y]); } } @@ -196,6 +202,59 @@ twindow_builder::tresolution::tresolution(const config& cfg) : } +static unsigned read_flags(const config& cfg) +{ + unsigned flags = 0; + + // Read the flags. FIXME document. + std::string v_align = cfg["vertical_alignment"]; + if(v_align == "top") { + flags |= tgrid::VERTICAL_ALIGN_TOP; + } else if(v_align == "bottom") { + flags |= tgrid::VERTICAL_ALIGN_BOTTOM; + } else { + flags |= tgrid::VERTICAL_ALIGN_CENTER; + } + + std::string h_align = cfg["horizontal_alignment"]; + if(h_align == "left") { + flags |= tgrid::HORIZONTAL_ALIGN_LEFT; + } else if(h_align == "right") { + flags |= tgrid::HORIZONTAL_ALIGN_RIGHT; + } else { + flags |= tgrid::HORIZONTAL_ALIGN_CENTER; + } + + std::vector border = utils::split(cfg["border"]); + if(std::find(border.begin(), border.end(), "all") != border.end()) { + flags |= tgrid::BORDER_TOP + | tgrid::BORDER_BOTTOM | tgrid::BORDER_LEFT | tgrid::BORDER_RIGHT; + } else { + if(std::find(border.begin(), border.end(), "top") != border.end()) { + flags |= tgrid::BORDER_TOP; + } + if(std::find(border.begin(), border.end(), "bottom") != border.end()) { + flags |= tgrid::BORDER_BOTTOM; + } + if(std::find(border.begin(), border.end(), "left") != border.end()) { + flags |= tgrid::BORDER_LEFT; + } + if(std::find(border.begin(), border.end(), "right") != border.end()) { + flags |= tgrid::BORDER_RIGHT; + } + } + + if(utils::string_bool(cfg["vertical_grow"])) { + flags |= tgrid::VERTICAL_GROW_SEND_TO_CLIENT; + } + + if(utils::string_bool(cfg["horizontal_grow"])) { + flags |= tgrid::HORIZONTAL_GROW_SEND_TO_CLIENT; + } + + return flags; +} + twindow_builder::tresolution::tgrid::tgrid(const config* cfg) : rows(0), cols(0), @@ -209,10 +268,18 @@ twindow_builder::tresolution::tgrid::tgrid(const config* cfg) : unsigned col = 0; + row_scale.push_back(lexical_cast_default((**row_itor)["scale"])); + const config::child_list& col_cfgs = (**row_itor).get_children("column"); for(std::vector::const_iterator col_itor = col_cfgs.begin(); col_itor != col_cfgs.end(); ++col_itor) { + flags.push_back(read_flags(**col_itor)); + border_size.push_back(lexical_cast_default((**col_itor)["border_size"])); + if(rows == 0) { + col_scale.push_back(lexical_cast_default((**col_itor)["scale"])); + } + if((**col_itor).child("button")) { widgets.push_back(new tbuilder_button(*((**col_itor).child("button")))); } else if((**col_itor).child("label")) { @@ -250,6 +317,7 @@ tbuilder_widget::tbuilder_widget(const config& cfg) : definition = "default"; } + DBG_G_P << "Window builder: found widget with id '" << id << "' and definition '" << definition << "'.\n"; } diff --git a/src/gui/widgets/window_builder.hpp b/src/gui/widgets/window_builder.hpp index efe7bbe8ce8..08f3a29f960 100644 --- a/src/gui/widgets/window_builder.hpp +++ b/src/gui/widgets/window_builder.hpp @@ -41,6 +41,7 @@ private: public: tbuilder_widget(const config& cfg); + //! Parameters for the widget. std::string id; std::string definition; t_string label; @@ -73,6 +74,9 @@ public: // note x, y hardcoded. + // FIXME add min max and default size + // the we can use best size to get the best. + std::string definition; struct tgrid @@ -86,6 +90,17 @@ public: unsigned rows; unsigned cols; + //! The scale factor for the rows / columns. + std::vector row_scale; + std::vector col_scale; + + //! The flags per grid cell. + std::vector flags; + + //! The border size per grid cell. + std::vector border_size; + + //! The widgets per grid cell. std::vector widgets; };