Various improvements in order to make the automatic layout of a window...
...and its children work.
This commit is contained in:
parent
a724be47f1
commit
7fdb20fd62
4 changed files with 228 additions and 58 deletions
|
@ -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("") ,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue