Various improvements in order to make the automatic layout of a window...

...and its children work.
This commit is contained in:
Mark de Wever 2008-03-09 12:34:16 +00:00
parent a724be47f1
commit 7fdb20fd62
4 changed files with 228 additions and 58 deletions

View file

@ -21,6 +21,7 @@
#include "serialization/preprocessor.hpp"
#include <cassert>
#include <numeric>
#define DBG_GUI LOG_STREAM(debug, widget)
#define LOG_GUI LOG_STREAM(info, widget)
@ -47,6 +48,17 @@ bool init() {
return initialized_;
}
SDL_Rect create_rect(const tpoint& origin, const tpoint& size)
{
return ::create_rect(origin.x, origin.y, size.x, size.y);
}
std::ostream &operator<<(std::ostream &stream, const tpoint& point)
{
stream << point.x << ',' << point.y;
return stream;
}
#if 0
tcontainer::~tcontainer()
{
@ -102,8 +114,8 @@ tsizer::~tsizer()
for(std::vector<tchild>::iterator itor = children_.begin();
itor != children_.end(); ++itor) {
if(itor->widget) {
delete itor->widget;
if(itor->widget()) {
delete itor->widget();
}
}
}
@ -116,23 +128,23 @@ void tsizer::add_child(twidget* widget, const unsigned row,
tchild& cell = child(row, col);
// clear old child if any
if(cell.widget) {
if(cell.widget()) {
// free a child when overwriting it
LOG_GUI << "Child '" << cell.id << "' at cell '"
LOG_GUI << "Child '" << cell.id() << "' at cell '"
<< row << "," << col << "' will be overwritten and is disposed\n";
delete cell.widget;
delete cell.widget();
}
// copy data
cell.flags = flags;
cell.border_size = border_size;
cell.widget = widget;
if(cell.widget) {
cell.set_flags(flags);
cell.set_border_size(border_size);
cell.set_widget(widget);
if(cell.widget()) {
// make sure the new child is valid before deferring
cell.id = cell.widget->id();
cell.set_id(cell.widget()->id());
// cell.widget->parent() = this; FIXME enable
} else {
cell.id = "";
cell.set_id("");
}
}
@ -170,8 +182,8 @@ void tsizer::remove_child(const unsigned row, const unsigned col)
tchild& cell = child(row, col);
cell.id = "";
cell.widget = 0;
cell.set_id("");
cell.set_widget(0);
}
void tsizer::removed_child(const std::string& id, const bool find_all)
@ -180,9 +192,9 @@ void tsizer::removed_child(const std::string& id, const bool find_all)
for(std::vector<tchild>::iterator itor = children_.begin();
itor != children_.end(); ++itor) {
if(itor->id == id) {
itor->id = "";
itor->widget = 0;
if(itor->id() == id) {
itor->set_id("");
itor->set_widget(0);
if(!find_all) {
break;
@ -191,43 +203,103 @@ void tsizer::removed_child(const std::string& id, const bool find_all)
}
}
void tsizer::layout()
tpoint tsizer::get_best_size()
{
DBG_GUI << __FUNCTION__ << '\n';
std::vector<unsigned> best_col_width;
std::vector<unsigned> best_row_height;
std::vector<unsigned> best_col_width(cols_, 0);
std::vector<unsigned> 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(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;
}
}
}
// Does the best fit if so set the best, else
// we need to determine the minimum and optimize.
for(unsigned row = 0; row < rows_; ++row) {
DBG_GUI << "Row " << row << ": " << best_row_height[row] << '\n';
}
// FIXME improve
tpoint start(10, 10);
for(std::vector<tchild>::iterator itor = children_.begin();
itor != children_.end(); ++itor) {
for(unsigned col = 0; col < cols_; ++col) {
DBG_GUI << "Col " << col << ": " << best_col_width[col] << '\n';
}
if(itor->widget) {
tpoint size = itor->widget->get_best_size();
return tpoint(
std::accumulate(best_col_width.begin(), best_col_width.end(), 0),
std::accumulate(best_row_height.begin(), best_row_height.end(), 0));
itor->widget->set_x(start.x);
itor->widget->set_y(start.y);
}
itor->widget->set_width(size.x);
itor->widget->set_height(size.y);
void tsizer::set_best_size(const tpoint& origin)
{
DBG_GUI << __FUNCTION__ << '\n';
std::vector<unsigned> best_col_width(cols_, 0);
std::vector<unsigned> 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) {
const 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;
}
start.y += 30;
}
}
// Set the sizes
tpoint orig = origin;
for(unsigned row = 0; row < rows_; ++row) {
for(unsigned col = 0; col < cols_; ++col) {
DBG_GUI << "Row : " << row << " col : " << col << " put at origin " << orig << '\n';
if(child(row, col).widget()) {
child(row, col).widget()->set_best_size(orig);
}
orig.x += best_col_width[col];
}
orig.y += best_row_height[row];
orig.x = origin.x;
}
}
tpoint tsizer::tchild::get_best_size()
{
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_;
}
tcontrol::tcontrol(/*const int x, const int y, const int w, const int h*/) :
twidget("") ,

View file

@ -48,6 +48,10 @@ struct tpoint
int y;
};
std::ostream &operator<<(std::ostream &stream, const tpoint& point);
SDL_Rect create_rect(const tpoint& origin, const tpoint& size);
struct terror
{
terror(const std::string& msg) : message(msg) {}
@ -153,13 +157,31 @@ public:
virtual void set_height(const int height) { h_ = height; set_dirty(); }
int get_height() const { return h_; }
bool dirty() const { return dirty_; }
//! Sets the best size for the object.
virtual void set_best_size(const tpoint& origin)
{ set_size(create_rect(origin, get_best_size())); }
//! Sets the minumum size for the object.
// virtual void set_minimum_size();
//! Sets a predefined size for the object.
virtual void set_size(const SDL_Rect rect)
{
x_ = rect.x;
y_ = rect.y;
w_ = rect.w;
h_ = rect.h;
dirty_ = true;
}
protected:
virtual void set_dirty(const bool dirty = true) { dirty_ = dirty; }
SDL_Rect get_rect() const
{ return create_rect( x_, y_, w_, h_ ); }
{ return ::create_rect( x_, y_, w_, h_ ); }
private:
const std::string id_;
@ -233,7 +255,6 @@ public:
virtual ~tsizer();
void add_child(twidget* widget, const unsigned row,
const unsigned col, const unsigned flags, const unsigned border_size);
@ -253,20 +274,69 @@ public:
void remove_child(const unsigned row, const unsigned col);
void removed_child(const std::string& id, const bool find_all = false);
void layout();
private:
struct tchild {
tchild() :
flags(0),
border_size(0),
widget(0)
{}
//! Inherited
tpoint get_best_size();
std::string id;
unsigned flags;
unsigned border_size;
twidget* widget;
//! Inherited
void set_best_size(const tpoint& origin);
private:
class tchild
{
public:
tchild() :
id_(),
flags_(0),
border_size_(0),
widget_(0),
best_size_(0, 0),
dirty_(true),
clip_()
// Fixme make a class wo we can store some properties in the cache
// regarding size etc.
{}
const std::string& id() const { return id_; }
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; }
unsigned get_border_size() const { return border_size_; }
void set_border_size(const unsigned border_size)
{ border_size_ = border_size; dirty_ = true; }
twidget* widget() { return widget_; }
void set_widget(twidget* widget) { widget_ = widget; dirty_ = true; }
//! 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();
private:
//! The id of the widget if it has a widget.
std::string id_;
//! The flags for the border and cell setup.
unsigned flags_;
//! The size of the border, the actual configuration of the border
//! is determined by the flags.
unsigned border_size_;
//! Pointer to the widget. FIXME who owns the widget....
twidget* widget_;
//! The best size for this cell, determined by the best size
//! of the widget and the border_size_ and flags_.
tpoint best_size_;
//! Tracks the dirty state of the cell regarding best_size_.
bool dirty_;
//! The clipping area for the widget.
SDL_Rect clip_;
};
public:
class iterator
@ -280,8 +350,8 @@ public:
iterator operator++() { return iterator(++itor_); }
iterator operator--() { return iterator(--itor_); }
twidget* operator->() { return itor_->widget; }
twidget* operator*() { return itor_->widget; }
twidget* operator->() { return itor_->widget(); }
twidget* operator*() { return itor_->widget(); }
bool operator!=(const iterator& i) const
{ return i.itor_ != this->itor_; }
@ -439,6 +509,15 @@ public:
// note we should check whether the label fits in the button
tpoint get_best_size() const { return tpoint(default_width_, default_height_); }
void set_best_size(const tpoint& origin)
{
set_x(origin.x);
set_y(origin.y);
set_width(default_width_);
set_height(default_height_);
}
protected:
private:
@ -453,7 +532,6 @@ private:
static config default_enabled_draw_;
};
/**
* A widget has a mouse over which can either popup directly or after a fixed delay (this is a flag)
* A widget has a help after pressing F1 it shows a larger tooltip with more info

View file

@ -22,6 +22,8 @@
#include "serialization/parser.hpp"
#include "variable.hpp"
#include <cassert>
#define DBG_GUI LOG_STREAM(debug, widget)
#define LOG_GUI LOG_STREAM(info, widget)
#define WRN_GUI LOG_STREAM(warn, widget)
@ -77,18 +79,14 @@ void twindow::show(const bool restore, void* /*flip_function*/)
// for(std::multimap<std::string, twidget *>::iterator itor =
// children_().begin(); itor != children_().end(); ++itor) {
layout();
layout(Xrect);
for(tsizer::iterator itor = begin(); itor != end(); ++itor) {
log_scope2(widget, "Draw child");
twidget* widget = *itor;
if(widget) {
// need to set the clip_rect here or let the widget do it itself?
widget->draw(screen);
}
itor->draw(screen);
}
rect = get_rect();
SDL_BlitSurface(screen, 0, video_.getSurface(), &rect);
update_rect(get_rect());
@ -126,6 +124,25 @@ void twindow::show(const bool restore, void* /*flip_function*/)
}
}
void twindow::layout(const SDL_Rect position)
{
tpoint best_size = get_best_size();
if(best_size.x < position.w && best_size.y < position.h) {
set_best_size(tpoint(0, 0));
return;
}
DBG_GUI << "Failed for 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.
}
void twindow::flip()
{
// fixme we need to add the option to either call

View file

@ -61,6 +61,9 @@ public:
// The flip function is the disp_.flip() if ommitted the video_flip() is used
void show(const bool restore = true, void* flip_function = 0);
// layout the window
void layout(const SDL_Rect position);
enum tstatus{ NEW, SHOWING, REQUEST_CLOSE, CLOSED };
protected: