text=t;
}
-void Button::button_press(int x, int y, unsigned btn)
+void Button::button_press(int, int, unsigned btn)
{
- if(geom.is_inside(x, y) && btn==1)
+ if(btn==1)
state=ACTIVE;
}
{
if(btn==1)
{
- if(geom.is_inside(x, y))
+ if(geom.is_inside_relative(x, y))
{
state=HOVER;
signal_clicked.emit();
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "connector.h"
+#include "logic.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+Connector::~Connector()
+{
+ for(map<string, ConnAction *>::iterator i=actions.begin(); i!=actions.end(); ++i)
+ delete i->second;
+}
+
+void Connector::connect(const Logic &logic)
+{
+ const list<Logic::WidgetAction> &logic_acts=logic.get_actions();
+
+ for(list<Logic::WidgetAction>::const_iterator i=logic_acts.begin(); i!=logic_acts.end(); ++i)
+ {
+ map<string, ConnAction *>::const_iterator j=actions.find(i->type);
+ if(j!=actions.end())
+ j->second->connect(*this, *i->wdg, i->data);
+ }
+}
+
+void Connector::add(const string &type, ConnAction *act)
+{
+ map<string, ConnAction *>::iterator i=actions.find(type);
+ if(i!=actions.end())
+ {
+ delete i->second;
+ i->second=act;
+ }
+ else
+ actions[type]=act;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_CONNECTOR_H_
+#define MSP_GLTK_CONNECTOR_H_
+
+#include <map>
+#include <string>
+
+namespace Msp {
+namespace GLtk {
+
+class Connector;
+class Logic;
+class Widget;
+
+class ConnAction
+{
+public:
+ virtual void connect(Connector &conn, Widget &wdg, const std::string &data) const =0;
+ virtual ~ConnAction() { }
+};
+
+template<typename C, typename W>
+class ConnFunc0: public ConnAction
+{
+public:
+ typedef void (C::*FuncType)(W &);
+
+ ConnFunc0(FuncType f): func(f) { }
+ virtual void connect(Connector &conn, Widget &wdg, const std::string &) const
+ {
+ (dynamic_cast<C &>(conn).*func)(dynamic_cast<W &>(wdg));
+ }
+
+private:
+ FuncType func;
+};
+
+template<typename C, typename W>
+class ConnFunc1: public ConnAction
+{
+public:
+ typedef void (C::*FuncType)(W &, const std::string &);
+
+ ConnFunc1(FuncType f): func(f) { }
+ virtual void connect(Connector &conn, Widget &wdg, const std::string &data) const
+ {
+ (dynamic_cast<C &>(conn).*func)(dynamic_cast<W &>(wdg), data);
+ }
+
+private:
+ FuncType func;
+};
+
+/**
+Provides an interface for associating the actions stored in a Logic object with
+actual code. Derive a class from this and use the add functions to specify
+handlers for each action type.
+*/
+class Connector
+{
+private:
+ std::map<std::string, ConnAction *> actions;
+
+protected:
+ Connector() { }
+public:
+ virtual ~Connector();
+
+ void connect(const Logic &);
+
+protected:
+ template<typename C, typename W>
+ void add(const std::string &type, void (C::*func)(W &))
+ { add(type, new ConnFunc0<C, W>(func)); }
+
+ template<typename C, typename W>
+ void add(const std::string &type, void (C::*func)(W &, const std::string &))
+ { add(type, new ConnFunc1<C, W>(func)); }
+
+private:
+ void add(const std::string &, ConnAction *);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
return (x_>=x && x_<x+static_cast<int>(w) && y_>=y && y_<y+static_cast<int>(h));
}
+bool Geometry::is_inside_relative(int x_, int y_) const
+{
+ return (x_>=0 && x_<static_cast<int>(w) && y_>=0 && y_<static_cast<int>(h));
+}
+
Sides::Sides():
top(0),
void Alignment::apply(Geometry &geom, const Geometry &parent, const Sides &margin) const
{
+ unsigned pw=parent.w-margin.left-margin.right;
+ unsigned ph=parent.h-margin.bottom-margin.top;
+
if(parent.w>geom.w)
- geom.w+=static_cast<unsigned>((parent.w-geom.w)*w);
+ geom.w+=static_cast<unsigned>((pw-geom.w)*w);
if(parent.h>geom.h)
- geom.h+=static_cast<unsigned>((parent.h-geom.h)*h);
+ geom.h+=static_cast<unsigned>((ph-geom.h)*h);
- geom.x+=static_cast<int>(margin.left+(parent.w-margin.left-margin.right-geom.w)*x);
- geom.y+=static_cast<int>(margin.bottom+(parent.h-margin.bottom-margin.top-geom.h)*y);
+ geom.x+=static_cast<int>(margin.left+(pw-geom.w)*x);
+ geom.y+=static_cast<int>(margin.bottom+(ph-geom.h)*y);
}
} // namespace GLtk
Geometry(): x(0), y(0), w(1), h(1) { }
Geometry(int x_, int y_, unsigned w_, unsigned h_): x(x_), y(y_), w(w_), h(h_) { }
bool is_inside(int, int) const;
+ bool is_inside_relative(int, int) const;
};
namespace GLtk {
HSlider::HSlider(const Resources &r):
- Slider(r),
- dragging(false)
+ Slider(r)
{
update_style();
}
{
if(geom.is_inside(x, y))
{
- int sw=get_slider_width();
- int sx=geom.x+static_cast<int>((geom.w-sw)*(value-min)/(max-min));
- if(btn==1 && x>=sx && x<sx+sw)
+ int sx=geom.x+static_cast<int>((geom.w-slider_size)*(value-min)/(max-min));
+ if(btn==1 && x>=sx && x<static_cast<int>(sx+slider_size))
{
- dragging=true;
state=ACTIVE;
- drag_start_x=x;
- drag_start_value=value;
+ start_drag(x);
}
}
}
{
if(btn==1)
{
- dragging=false;
+ end_drag();
state=NORMAL;
}
}
void HSlider::pointer_motion(int x, int)
{
if(dragging)
- {
- set_value(drag_start_value+(x-drag_start_x)*(max-min)/(geom.w-get_slider_width()));
- }
+ drag(x);
}
void HSlider::render_special(const Part &part) const
if(part.get_name()=="slider")
{
Alignment align=part.get_alignment();
- align.x=(value-min)/(max-min);
+ if(max>min)
+ align.x=(value-min)/(max-min);
Geometry pgeom=part.get_geometry();
align.apply(pgeom, geom, part.get_margin());
}
}
-unsigned HSlider::get_slider_width() const
+void HSlider::on_geometry_change()
+{
+ drag_area_size=geom.w-slider_size;
+}
+
+void HSlider::on_style_change()
{
for(PartSeq::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i)
if(i->get_name()=="slider")
- return i->get_geometry().w;
+ slider_size=i->get_geometry().w;
- return 0;
+ on_geometry_change();
}
} // namespace GLtk
class HSlider: public Slider
{
private:
- bool dragging;
- int drag_start_x;
- double drag_start_value;
+ unsigned slider_size;
public:
HSlider(const Resources &);
private:
virtual const char *get_class() const { return "hslider"; }
virtual void render_special(const Part &) const;
- unsigned get_slider_width() const;
+
+ virtual void on_geometry_change();
+ virtual void on_style_change();
};
} // namespace GLtk
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <msp/gl/matrix.h>
+#include <msp/gl/transform.h>
+#include "graphic.h"
+#include "list.h"
+#include "part.h"
+#include "style.h"
+#include "vslider.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+List::List(const Resources &r):
+ Widget(r),
+ sel_index(-1),
+ first(0),
+ n_visible(1),
+ items_part(0),
+ slider(new VSlider(res)),
+ slider_active(false)
+{
+ slider->set_step(1);
+ slider->signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
+
+ update_style();
+}
+
+void List::append(const string &v)
+{
+ items.push_back(v);
+ recalculate_parameters();
+}
+
+void List::insert(unsigned i, const string &v)
+{
+ if(i>items.size())
+ throw InvalidParameterValue("Index out of range");
+
+ items.insert(items.begin()+i, v);
+ recalculate_parameters();
+}
+
+void List::remove(unsigned i)
+{
+ if(i>items.size())
+ throw InvalidParameterValue("Index out of range");
+
+ 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;
+
+ recalculate_parameters();
+}
+
+void List::clear()
+{
+ items.clear();
+ sel_index=-1;
+}
+
+const string &List::get_selected() const
+{
+ if(sel_index<0)
+ throw InvalidState("No selection");
+
+ return items[sel_index];
+}
+
+void List::button_press(int x, int y, unsigned btn)
+{
+ if(slider->get_geometry().is_inside(x, y))
+ {
+ const Geometry &sgeom=slider->get_geometry();
+ slider->button_press(x-sgeom.x, y-sgeom.y, btn);
+ slider_active=true;
+ }
+ else if(btn==1)
+ {
+ const GL::Font *const font=style->get_font();
+ const unsigned row_height=static_cast<unsigned>(font->get_default_size());
+
+ if(items_part)
+ y+=items_part->get_margin().top;
+
+ unsigned i=(geom.h-1-y)/row_height;
+ if(i<n_visible)
+ {
+ sel_index=first+i;
+
+ signal_item_selected.emit(sel_index, items[sel_index]);
+ }
+ }
+}
+
+void List::button_release(int x, int y, unsigned btn)
+{
+ if(slider_active)
+ {
+ const Geometry &sgeom=slider->get_geometry();
+ slider->button_release(x-sgeom.x, y-sgeom.y, btn);
+ slider_active=false;
+ }
+}
+
+void List::pointer_motion(int x, int y)
+{
+ if(slider_active)
+ {
+ const Geometry &sgeom=slider->get_geometry();
+ slider->pointer_motion(x-sgeom.x, y-sgeom.y);
+ }
+}
+
+void List::render_special(const Part &part) const
+{
+ if(part.get_name()=="items")
+ {
+ const GL::Font *const font=style->get_font();
+ const float font_size=font->get_default_size();
+ const unsigned row_height=static_cast<unsigned>(font_size);
+ const Sides &margin=part.get_margin();
+
+ Geometry pgeom=geom;
+ pgeom.h=row_height;
+
+ for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
+ {
+ Geometry rgeom;
+ rgeom.w=static_cast<unsigned>(font->get_string_width(items[first+i])*font_size);
+ rgeom.h=static_cast<unsigned>((font->get_ascent()-font->get_descent())*font_size);
+ rgeom.y=geom.h-margin.top-(i+1)*row_height-static_cast<int>(font->get_descent()*font_size);
+ part.get_alignment().apply(rgeom, pgeom);
+
+ GL::push_matrix();
+ GL::translate(rgeom.x, rgeom.y, 0);
+ GL::scale_uniform(font_size);
+ font->draw_string(items[first+i]);
+ GL::pop_matrix();
+ }
+ }
+ else if(part.get_name()=="selection")
+ {
+ if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
+ {
+ const GL::Font *const font=style->get_font();
+ const float font_size=font->get_default_size();
+ const unsigned row_height=static_cast<unsigned>(font_size);
+ const Sides &margin=part.get_margin();
+
+ Geometry pgeom=geom;
+ pgeom.h=row_height;
+ pgeom.w-=margin.left+margin.right;
+
+ Geometry rgeom=part.get_geometry();
+ rgeom.y+=geom.h-margin.top-row_height*(sel_index-first+1);
+ rgeom.x+=margin.left;
+ part.get_alignment().apply(rgeom, pgeom);
+
+ GL::push_matrix();
+ GL::translate(rgeom.x, rgeom.y, 0);
+ part.get_graphic(state)->render(rgeom.w, rgeom.h);
+ GL::pop_matrix();
+ }
+ }
+ else if(part.get_name()=="slider")
+ slider->render();
+}
+
+void List::on_geometry_change()
+{
+ reposition_slider();
+
+ recalculate_parameters();
+}
+
+void List::on_style_change()
+{
+ reposition_slider();
+
+ items_part=0;
+ for(list<Part>::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i)
+ if(i->get_name()=="items")
+ items_part=&*i;
+
+ recalculate_parameters();
+}
+
+void List::reposition_slider()
+{
+ for(list<Part>::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i)
+ if(i->get_name()=="slider")
+ {
+ Geometry sgeom=i->get_geometry();
+ i->get_alignment().apply(sgeom, geom, i->get_margin());
+ slider->set_geometry(sgeom);
+ }
+}
+
+void List::recalculate_parameters()
+{
+ const GL::Font *font=style->get_font();
+ unsigned row_height=static_cast<unsigned>(font->get_default_size());
+
+ unsigned h=geom.h;
+ if(items_part)
+ {
+ const Sides &margin=items_part->get_margin();
+ h-=margin.top+margin.bottom;
+ }
+
+ n_visible=h/row_height;
+
+ if(first+n_visible>items.size())
+ {
+ if(items.size()>n_visible)
+ first=items.size()-n_visible;
+ else
+ first=0;
+ }
+
+ if(items.size()>n_visible)
+ {
+ slider->set_range(0, items.size()-n_visible);
+ slider->set_value(items.size()-n_visible-first);
+ }
+ else
+ {
+ slider->set_range(0, 0);
+ slider->set_value(0);
+ }
+}
+
+void List::slider_value_changed(double value)
+{
+ first=items.size()-n_visible-static_cast<unsigned>(value);
+}
+
+
+List::Loader::Loader(List &l):
+ Widget::Loader(l)
+{
+ add("item", &Loader::item);
+}
+
+void List::Loader::item(const string &v)
+{
+ static_cast<List &>(wdg).append(v);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_LIST_H_
+#define MSP_GLTK_LIST_H_
+
+#include <sigc++/signal.h>
+#include "widget.h"
+
+namespace Msp {
+namespace GLtk {
+
+class VSlider;
+
+/**
+Shows a list of items, allowing the user to select one. A slider is included
+to allow scrolling through a long list.
+*/
+class List: public Widget
+{
+public:
+ class Loader: public Widget::Loader
+ {
+ public:
+ Loader(List &);
+ private:
+ void item(const std::string &);
+ };
+
+private:
+ std::vector<std::string> items;
+ int sel_index;
+ unsigned first;
+ unsigned n_visible;
+
+ const Part *items_part;
+
+ VSlider *slider;
+ bool slider_active;
+
+public:
+ sigc::signal<void, unsigned, const std::string &> signal_item_selected;
+
+ List(const Resources &);
+ void append(const std::string &);
+ void insert(unsigned, const std::string &);
+ void remove(unsigned);
+ void clear();
+
+ const std::string &get_selected() const;
+ int get_selected_index() const { return sel_index; }
+
+ virtual void button_press(int, int, unsigned);
+ virtual void button_release(int, int, unsigned);
+ virtual void pointer_motion(int, int);
+
+private:
+ virtual const char *get_class() const { return "list"; }
+ virtual void render_special(const Part &) const;
+
+ virtual void on_geometry_change();
+ virtual void on_style_change();
+ void reposition_slider();
+ void recalculate_parameters();
+ void slider_value_changed(double);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "logic.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+Logic::Loader::Loader(Logic &l, const map<string, Widget *> &w):
+ logic(l),
+ widgets(w)
+{
+ add("action", &Loader::action);
+}
+
+void Logic::Loader::action(const string &wdg, const string &data)
+{
+ map<string, Widget *>::const_iterator i=widgets.find(wdg);
+ if(i==widgets.end())
+ throw KeyError("Unknown widget", wdg);
+
+ unsigned colon=data.find(':');
+ WidgetAction act;
+ act.wdg=i->second;
+ act.type=data.substr(0, colon);
+ if(colon!=string::npos)
+ act.data=data.substr(colon+1);
+ logic.actions.push_back(act);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgltk
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GLTK_LOGIC_H_
+#define MSP_GLTK_LOGIC_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <sigc++/slot.h>
+#include <msp/core/except.h>
+#include <msp/datafile/loader.h>
+
+namespace Msp {
+namespace GLtk {
+
+class Widget;
+
+/**
+Stores use interface logic. This is stored as actions associated to widgets.
+Each action has type and data. See also class Connector.
+*/
+class Logic
+{
+public:
+ class Loader: public DataFile::Loader
+ {
+ private:
+ Logic &logic;
+ const std::map<std::string, Widget *> &widgets;
+
+ public:
+ Loader(Logic &, const std::map<std::string, Widget *> &);
+ private:
+ void action(const std::string &, const std::string &);
+ };
+
+ struct WidgetAction
+ {
+ Widget *wdg;
+ std::string type;
+ std::string data;
+ };
+
+private:
+ std::list<WidgetAction> actions;
+
+public:
+ const std::list<WidgetAction> &get_actions() const { return actions; }
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
#include "button.h"
#include "entry.h"
#include "hslider.h"
+#include "indicator.h"
#include "label.h"
+#include "list.h"
#include "panel.h"
#include "part.h"
#include "vslider.h"
void Panel::button_press(int x, int y, unsigned btn)
{
if(pointer_grab>0)
- pointer_focus->button_press(x-geom.x, y-geom.y, btn);
- else if(geom.is_inside(x, y))
+ {
+ const Geometry &cgeom=pointer_focus->get_geometry();
+ pointer_focus->button_press(x-cgeom.x, y-cgeom.y, btn);
+ }
+ else if(geom.is_inside_relative(x, y))
{
if(Widget *wdg=get_child_at(x, y))
{
set_pointer_focus(wdg, btn);
set_input_focus(wdg);
- wdg->button_press(x-geom.x, y-geom.y, btn);
+ const Geometry &cgeom=wdg->get_geometry();
+ wdg->button_press(x-cgeom.x, y-cgeom.y, btn);
}
}
}
{
if(pointer_grab>0)
{
- pointer_focus->button_release(x-geom.x, y-geom.y, btn);
+ const Geometry &cgeom=pointer_focus->get_geometry();
+ pointer_focus->button_release(x-cgeom.x, y-cgeom.y, btn);
if(btn==pointer_grab)
set_pointer_focus(get_child_at(x, y), 0);
}
- else if(geom.is_inside(x, y))
+ else if(geom.is_inside_relative(x, y))
{
if(Widget *wdg=get_child_at(x, y))
- wdg->button_release(x-geom.x, y-geom.y, btn);
+ {
+ const Geometry &cgeom=wdg->get_geometry();
+ wdg->button_release(x-cgeom.x, y-cgeom.y, btn);
+ }
}
}
void Panel::pointer_motion(int x, int y)
{
if(pointer_grab>0)
- pointer_focus->pointer_motion(x-geom.x, y-geom.y);
- else if(geom.is_inside(x, y))
+ {
+ const Geometry &cgeom=pointer_focus->get_geometry();
+ pointer_focus->pointer_motion(x-cgeom.x, y-cgeom.y);
+ }
+ else if(geom.is_inside_relative(x, y))
{
Widget *wdg=get_child_at(x, y);
set_pointer_focus(wdg, 0);
if(wdg)
- wdg->pointer_motion(x-geom.x, y-geom.y);
+ {
+ const Geometry &cgeom=wdg->get_geometry();
+ wdg->pointer_motion(x-cgeom.x, y-cgeom.y);
+ }
}
}
Widget *Panel::get_child_at(int x, int y)
{
for(ChildSeq::reverse_iterator i=children.rbegin(); i!=children.rend(); ++i)
- if((*i)->is_visible() && (*i)->get_geometry().is_inside(x-geom.x, y-geom.y))
+ if((*i)->is_visible() && (*i)->get_geometry().is_inside(x, y))
return *i;
return 0;
add("button", &Loader::child<Button>);
add("entry", &Loader::child<Entry>);
add("hslider", &Loader::child<HSlider>);
+ add("indicator", &Loader::child<Indicator>);
add("label", &Loader::child<Label>);
+ add("list", &Loader::child<List>);
add("panel", &Loader::panel);
add("vslider", &Loader::child<VSlider>);
}
add("align", &Loader::align);
add("fill", &Loader::fill);
add("margin", &Loader::margin);
+ add("size", &Loader::size);
}
Part::Loader::~Loader()
load_sub(part.margin);
}
+void Part::Loader::size(unsigned w, unsigned h)
+{
+ part.geom.w=w;
+ part.geom.h=h;
+}
+
} // namespace GLtk
} // namespace Msp
void align(float, float);
void fill(float, float);
void margin();
+ void size(unsigned, unsigned);
};
private:
min(0),
max(1),
value(0),
- step(0.1)
+ step(0.1),
+ dragging(false),
+ drag_area_size(0)
{ }
void Slider::set_value(double v)
set_value(value);
}
+void Slider::start_drag(int p)
+{
+ dragging=true;
+ drag_start_pos=p;
+ drag_start_value=value;
+}
+
+void Slider::drag(int p)
+{
+ if(max>min)
+ set_value(drag_start_value+(p-drag_start_pos)*(max-min)/drag_area_size);
+}
+
+void Slider::end_drag()
+{
+ dragging=false;
+}
+
Slider::Loader::Loader(Slider &s):
Widget::Loader(s)
double value;
double step;
+ bool dragging;
+ double drag_start_pos;
+ double drag_start_value;
+ unsigned drag_area_size;
+
public:
sigc::signal<void, double> signal_value_changed;
void set_range(double, double);
void set_step(double);
double get_value() const { return value; }
+protected:
+ void start_drag(int);
+ void drag(int);
+ void end_drag();
};
} // namespace GLtk
namespace GLtk {
VSlider::VSlider(const Resources &r):
- Slider(r),
- dragging(false)
+ Slider(r)
{
update_style();
}
void VSlider::button_press(int x, int y, unsigned btn)
{
- if(geom.is_inside(x, y))
+ if(geom.is_inside_relative(x, y) && max>min)
{
- int sh=get_slider_height();
- int sy=geom.y+static_cast<int>((geom.h-sh)*(value-min)/(max-min));
- if(btn==1 && y>=sy && y<sy+sh)
+ int sy=static_cast<int>((geom.h-slider_size)*(value-min)/(max-min));
+ if(btn==1 && y>=sy && y<static_cast<int>(sy+slider_size))
{
- dragging=true;
state=ACTIVE;
- drag_start_y=y;
- drag_start_value=value;
+ start_drag(y);
}
}
}
void VSlider::button_release(int, int, unsigned btn)
{
- if(btn==1)
+ if(btn==1 && dragging)
{
- dragging=false;
+ end_drag();
state=NORMAL;
}
}
void VSlider::pointer_motion(int, int y)
{
if(dragging)
- {
- set_value(drag_start_value+(y-drag_start_y)*(max-min)/(geom.h-get_slider_height()));
- }
+ drag(y);
}
void VSlider::render_special(const Part &part) const
if(part.get_name()=="slider")
{
Alignment align=part.get_alignment();
- align.y=(value-min)/(max-min);
+ if(max>min)
+ align.y=(value-min)/(max-min);
Geometry pgeom=part.get_geometry();
align.apply(pgeom, geom, part.get_margin());
}
}
-unsigned VSlider::get_slider_height() const
+void VSlider::on_geometry_change()
+{
+ drag_area_size=geom.h-slider_size;
+}
+
+void VSlider::on_style_change()
{
for(PartSeq::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i)
if(i->get_name()=="slider")
- return i->get_geometry().h;
+ slider_size=i->get_geometry().h;
- return 0;
+ on_geometry_change();
}
} // namespace GLtk
class VSlider: public Slider
{
private:
- bool dragging;
- int drag_start_y;
- double drag_start_value;
+ unsigned slider_size;
public:
VSlider(const Resources &);
private:
virtual const char *get_class() const { return "vslider"; }
virtual void render_special(const Part &) const;
- unsigned get_slider_height() const;
+
+ virtual void on_geometry_change();
+ virtual void on_style_change();
};
} // namespace GLtk
{
geom.x=x;
geom.y=y;
+ on_geometry_change();
}
void Widget::set_size(unsigned w, unsigned h)
{
geom.w=w;
geom.h=h;
+ on_geometry_change();
}
void Widget::set_geometry(const Geometry &g)
{
geom=g;
+ on_geometry_change();
}
void Widget::set_style(const string &s)
Geometry rgeom;
rgeom.w=static_cast<unsigned>(font->get_string_width(text)*font_size);
rgeom.h=static_cast<unsigned>((font->get_ascent()-font->get_descent())*font_size);
+ rgeom.y=static_cast<int>(-font->get_descent()*font_size);
part.get_alignment().apply(rgeom, geom, part.get_margin());
GL::push_matrix();
- GL::translate(rgeom.x, rgeom.y-font->get_descent()*font_size, 0);
+ GL::translate(rgeom.x, rgeom.y, 0);
GL::scale_uniform(font_size);
const GL::Color &color=style->get_font_color();
sname+=style_name;
}
style=res.get<Style>(sname);
+ on_style_change();
}
void Widget::set_parent(Panel *p)
if(parent && p)
throw InvalidState("Widget is already in a Panel");
parent=p;
+
+ on_reparent();
}
void Widget::set_parent(Widget &w, Panel *p)
/**
Sets the widget's parent Panel. The widget must be unparented when calling
- this function.
+ this function with a nonzero parameter.
*/
void set_parent(Panel *);
A helper function to set the parent of another widget.
*/
void set_parent(Widget &, Panel *);
+
+ // More events
+ virtual void on_geometry_change() { }
+ virtual void on_style_change() { }
+ virtual void on_reparent() { }
};
} // namespace GLtk