namespace Msp {
namespace GLtk {
-Dropdown::Dropdown():
- dropped(false)
+Dropdown::Dropdown()
{
+ init();
+}
+
+Dropdown::Dropdown(ListData &d):
+ list(d)
+{
+ init();
+}
+
+void Dropdown::init()
+{
+ dropped = false;
+
add(list);
list.signal_item_selected.connect(sigc::mem_fun(this, &Dropdown::list_item_selected));
list.signal_autosize_changed.connect(sigc::mem_fun(this, &Dropdown::list_autosize_changed));
const Sides &margin = text_part->get_margin();
const GL::Font &font = style->get_font();
float font_size = style->get_font_size();
+
+ unsigned max_w = 0;
+ const ListData &data = list.get_data();
+ for(unsigned i=0; i<data.size(); ++i)
+ {
+ unsigned w = static_cast<unsigned>(font.get_string_width(data.get_string(i))*font_size);
+ max_w = max(max_w, w);
+ }
+ geom.w = max(geom.w, max_w+margin.left+margin.right);
+
unsigned line_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font_size);
geom.h = max(geom.h, line_height+margin.top+margin.bottom);
}
rebuild();
}
-void Dropdown::append(const string &item)
-{
- list.append(item);
-}
-
-void Dropdown::insert(unsigned i, const string &v)
-{
- list.insert(i, v);
-}
-
-void Dropdown::remove(unsigned i)
-{
- list.remove(i);
-}
-
-void Dropdown::clear()
-{
- list.clear();
-}
-
-unsigned Dropdown::get_n_items() const
-{
- return list.get_n_items();
-}
-
-void Dropdown::set_selected_index(int i)
-{
- list.set_selected_index(i);
-}
-
-const string &Dropdown::get_selected() const
-{
- return list.get_selected();
-}
-
-int Dropdown::get_selected_index() const
-{
- return list.get_selected_index();
-}
-
void Dropdown::rebuild_special(const Part &part, CachedPart &cache)
{
if(part.get_name()=="text")
{
- if(list.get_selected_index()>=0)
- Text(*style, list.get_selected()).build(part, geom, cache);
+ int sel = list.get_selected_index();
+ if(sel>=0)
+ Text(*style, list.get_data().get_string(sel)).build(part, geom, cache);
else
cache.texture = 0;
}
list.set_geometry(lgeom);
}
-void Dropdown::list_item_selected(unsigned index, const std::string &item)
+void Dropdown::list_item_selected(unsigned index)
{
if(dropped)
{
signal_ungrab_pointer.emit();
}
- signal_item_selected.emit(index, item);
+ signal_item_selected.emit(index);
rebuild();
}
add("item", &Loader::item);
}
-void Dropdown::Loader::item(const string &str)
+void Dropdown::Loader::item(const string &v)
{
- dynamic_cast<Dropdown &>(obj).append(str);
+ dynamic_cast<BasicListData<string> &>(dynamic_cast<Dropdown &>(obj).list.get_data()).append(v);
}
} // namespace GLtk
void item(const std::string &);
};
- sigc::signal<void, int, const std::string &> signal_item_selected;
+ sigc::signal<void, unsigned> signal_item_selected;
private:
List list;
public:
Dropdown();
+ Dropdown(ListData &);
+private:
+ void init();
+public:
virtual const char *get_class() const { return "dropdown"; }
virtual void autosize();
- void append(const std::string &);
- void insert(unsigned, const std::string &);
- void remove(unsigned);
- void clear();
- unsigned get_n_items() const;
+ void set_data(ListData &d) { list.set_data(d); }
+ ListData &get_data() { return list.get_data(); }
+ const ListData &get_data() const { return list.get_data(); }
- void set_selected_index(int);
- const std::string &get_selected() const;
- int get_selected_index() const;
+ void set_selected_index(int i) { list.set_selected_index(i); }
+ int get_selected_index() const { return list.get_selected_index(); }
private:
virtual void rebuild_special(const Part &, CachedPart &);
void list_autosize_changed();
void resize_list();
- void list_item_selected(unsigned, const std::string &);
+ void list_item_selected(unsigned);
};
} // namespace GLtk
namespace GLtk {
List::List():
- sel_index(-1),
- first(0),
- n_visible(1),
- row_height(1),
- items_part(0)
+ data(new BasicListData<string>),
+ own_data(true)
{
+ init();
+}
+
+List::List(ListData &d):
+ data(&d),
+ own_data(false)
+{
+ init();
+}
+
+void List::init()
+{
+ sel_index = -1;
+ first = 0;
+ n_visible = 1;
+ row_height = 1;
+ items_part = 0;
+
+ observer = new DataObserver(*this);
+
add(slider);
slider.set_step(1);
slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
}
+List::~List()
+{
+ delete observer;
+ if(own_data)
+ delete data;
+}
+
void List::autosize()
{
autosize_rows(5);
float font_size = style->get_font_size();
unsigned max_w = 0;
- for(vector<string>::iterator i=items.begin(); i!=items.end(); ++i)
+ unsigned n_items = data->size();
+ for(unsigned i=0; i<n_items; ++i)
{
- unsigned w = static_cast<unsigned>(font.get_string_width(*i)*font_size);
+ unsigned w = static_cast<unsigned>(font.get_string_width(data->get_string(i))*font_size);
max_w = max(max_w, w);
}
void List::autosize_all()
{
- autosize_rows(items.size());
+ autosize_rows(data->size());
}
-void List::append(const string &v)
+void List::set_data(ListData &d)
{
- items.push_back(v);
- items_changed();
-}
-
-void List::insert(unsigned i, const string &v)
-{
- if(i>items.size())
- throw out_of_range("List::insert");
-
- items.insert(items.begin()+i, v);
- items_changed();
-}
+ delete observer;
+ if(own_data)
+ delete data;
-void List::remove(unsigned i)
-{
- if(i>items.size())
- throw out_of_range("List::remove");
-
- items.erase(items.begin()+i);
- if(sel_index>static_cast<int>(i))
- --sel_index;
- else if(sel_index==static_cast<int>(i))
- sel_index = -1;
+ data = &d;
+ own_data = false;
+ observer = new DataObserver(*this);
items_changed();
}
-void List::clear()
-{
- items.clear();
- sel_index = -1;
- items_changed();
-}
-
void List::items_changed()
{
check_view_range();
{
if(i<0)
sel_index = -1;
- else if(i<static_cast<int>(items.size()))
+ else if(i<static_cast<int>(data->size()))
{
sel_index = i;
- signal_item_selected.emit(sel_index, items[sel_index]);
+ signal_item_selected.emit(sel_index);
rebuild();
}
else
throw out_of_range("List::set_selected_index");
}
-const string &List::get_selected() const
-{
- if(sel_index<0)
- throw logic_error("sel_index<0");
-
- return items[sel_index];
-}
-
void List::rebuild_special(const Part &part, CachedPart &cache)
{
if(part.get_name()=="items")
bld.color(style->get_font_color());
bld.matrix() *= GL::Matrix::translation(margin.left, geom.h-pgeom.h-font.get_descent()*style->get_font_size(), 0);
- for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
+ for(unsigned i=0; (i<n_visible && first+i<data->size()); ++i)
{
if(i!=0)
bld.matrix() *= GL::Matrix::translation(0, -static_cast<int>(row_height), 0);
GL::MatrixStack::Push _pushm(bld.matrix());
bld.matrix() *= GL::Matrix::scaling(style->get_font_size());
- style->get_font().build_string(items[first+i], bld);
+ style->get_font().build_string(data->get_string(first+i), bld);
}
}
else if(part.get_name()=="selection")
y += items_part->get_margin().top;
unsigned i = (geom.h-1-y)/row_height;
- if(i<n_visible && first+i<items.size())
+ if(i<n_visible && first+i<data->size())
{
sel_index = first+i;
- signal_item_selected.emit(sel_index, items[sel_index]);
+ signal_item_selected.emit(sel_index);
rebuild();
}
}
n_visible = h/row_height;
- if(first+n_visible>items.size())
+ if(first+n_visible>data->size())
{
- if(items.size()>n_visible)
- first = items.size()-n_visible;
+ if(data->size()>n_visible)
+ first = data->size()-n_visible;
else
first = 0;
}
- if(items.size()>n_visible)
+ if(data->size()>n_visible)
{
- slider.set_range(0, items.size()-n_visible);
- slider.set_value(items.size()-n_visible-first);
+ slider.set_range(0, data->size()-n_visible);
+ slider.set_value(data->size()-n_visible-first);
}
else
{
void List::slider_value_changed(double value)
{
- if(items.size()>n_visible)
+ if(data->size()>n_visible)
{
- first = items.size()-n_visible-static_cast<unsigned>(value);
+ first = data->size()-n_visible-static_cast<unsigned>(value);
rebuild();
}
}
+List::DataObserver::DataObserver(List &l):
+ list(l)
+{
+ list.data->signal_item_added.connect(sigc::mem_fun(this, &DataObserver::item_added));
+ list.data->signal_item_removed.connect(sigc::mem_fun(this, &DataObserver::item_removed));
+ list.data->signal_cleared.connect(sigc::mem_fun(this, &DataObserver::cleared));
+ list.data->signal_refresh_strings.connect(sigc::mem_fun(this, &DataObserver::refresh_strings));
+}
+
+void List::DataObserver::item_added(unsigned i)
+{
+ if(list.sel_index>=static_cast<int>(i))
+ ++list.sel_index;
+
+ list.items_changed();
+}
+
+void List::DataObserver::item_removed(unsigned i)
+{
+ if(list.sel_index>static_cast<int>(i))
+ --list.sel_index;
+ else if(list.sel_index==static_cast<int>(i))
+ list.sel_index = -1;
+
+ list.items_changed();
+}
+
+void List::DataObserver::cleared()
+{
+ list.sel_index = -1;
+ list.items_changed();
+}
+
+void List::DataObserver::refresh_strings()
+{
+ list.items_changed();
+}
+
+
List::Loader::Loader(List &l):
Widget::Loader(l)
{
void List::Loader::item(const string &v)
{
- dynamic_cast<List &>(obj).append(v);
+ dynamic_cast<BasicListData<string> &>(*dynamic_cast<List &>(obj).data).append(v);
}
} // namespace GLtk
#include <sigc++/signal.h>
#include "container.h"
+#include "listdata.h"
#include "vslider.h"
namespace Msp {
void item(const std::string &);
};
- sigc::signal<void, unsigned, const std::string &> signal_item_selected;
+private:
+ /// This exists to make disconnecting signals easier
+ class DataObserver: public sigc::trackable
+ {
+ private:
+ List &list;
+
+ public:
+ DataObserver(List &);
+
+ void item_added(unsigned);
+ void item_removed(unsigned);
+ void cleared();
+ void refresh_strings();
+ };
+
+public:
+ sigc::signal<void, unsigned> signal_item_selected;
private:
- std::vector<std::string> items;
+ ListData *data;
+ bool own_data;
+ DataObserver *observer;
int sel_index;
unsigned first;
unsigned n_visible;
public:
List();
+ List(ListData &);
+private:
+ void init();
+public:
+ virtual ~List();
virtual const char *get_class() const { return "list"; }
void autosize_rows(unsigned);
void autosize_all();
- void append(const std::string &);
- void insert(unsigned, const std::string &);
- void remove(unsigned);
- void clear();
+ void set_data(ListData &);
+ ListData &get_data() { return *data; }
+ const ListData &get_data() const { return *data; }
private:
void items_changed();
public:
- unsigned get_n_items() const { return items.size(); }
void set_selected_index(int);
- const std::string &get_selected() const;
int get_selected_index() const { return sel_index; }
private:
--- /dev/null
+#ifndef MSP_GLTK_LISTDATA_H_
+#define MSP_GLTK_LISTDATA_H_
+
+#include <string>
+#include <vector>
+#include <sigc++/signal.h>
+#include <msp/strings/lexicalcast.h>
+
+namespace Msp {
+namespace GLtk {
+
+class ListData
+{
+public:
+ sigc::signal<void, unsigned> signal_item_added;
+ sigc::signal<void, unsigned> signal_item_removed;
+ sigc::signal<void> signal_cleared;
+ sigc::signal<void> signal_refresh_strings;
+
+protected:
+ ListData() { }
+public:
+ virtual ~ListData() { }
+
+ virtual unsigned size() const = 0;
+ virtual std::string get_string(unsigned) const = 0;
+ void refresh_strings() const { signal_refresh_strings.emit(); }
+};
+
+template<typename T>
+class ListDataStore: public ListData
+{
+protected:
+ std::vector<T> items;
+
+ ListDataStore() { }
+
+public:
+ void append(const T &v) { insert(items.size(), v); }
+
+ void insert(unsigned i, const T & v)
+ {
+ if(i>items.size())
+ throw std::out_of_range("ListDataStore::insert");
+
+ items.insert(items.begin()+i, v);
+ signal_item_added.emit(i);
+ }
+
+ const T &get(unsigned i) const
+ {
+ if(i>=items.size())
+ throw std::out_of_range("ListDataStore::get");
+
+ return items[i];
+ }
+
+ void remove(unsigned i)
+ {
+ if(i>=items.size())
+ throw std::out_of_range("ListDataStore::remove");
+
+ items.erase(items.begin()+i);
+ signal_item_removed.emit(i);
+ }
+
+ void clear()
+ {
+ items.clear();
+ signal_cleared.emit();
+ }
+
+ virtual unsigned size() const { return items.size(); }
+};
+
+template<typename T>
+class BasicListData: public ListDataStore<T>
+{
+public:
+ virtual std::string get_string(unsigned i) const
+ { return lexical_cast<std::string>(this->get(i)); }
+};
+
+template<typename T>
+class FunctionListData: public ListDataStore<T>
+{
+public:
+ typedef std::string Func(const T &);
+
+private:
+ Func *func;
+
+public:
+ FunctionListData(Func f): func(f) { }
+
+ virtual std::string get_string(unsigned i) const
+ { return func(this->get(i)); }
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif