--- /dev/null
+package "mspgltk"
+{
+ require "mspgl";
+
+ library "mspgltk"
+ {
+ source "source";
+ install true;
+ install_headers "msp/gltk";
+ };
+};
--- /dev/null
+#include <msp/gl/transform.h>
+#include "alignment.h"
+#include "geometry.h"
+
+namespace Msp {
+namespace GLtk {
+
+void Alignment::apply(const Geometry &geom, unsigned wd, unsigned ht) const
+{
+ GL::translate((geom.w-wd)*(x+1)/2, (geom.h-ht)*(y+1)/2, 0);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_ALIGNMENT_H_
+#define MSP_GLTK_ALIGNMENT_H_
+
+namespace Msp {
+namespace GLtk {
+
+class Geometry;
+
+/**
+Handles alignment of widget parts. Both x and y components can have values -1
+(left / bottom), 0 (center) or 1 (right / top).
+*/
+struct Alignment
+{
+ int x, y;
+
+ Alignment(): x(0), y(0) { }
+ Alignment(int x_, int y_): x(x_), y(y_) { }
+ void apply(const Geometry &, unsigned, unsigned) const;
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "button.h"
+#include "part.h"
+
+namespace Msp {
+namespace GLtk {
+
+Button::Button(const Resources &r, const std::string &t):
+ Widget(r)
+{
+ set_text(t);
+ update_style();
+}
+
+void Button::set_text(const std::string &t)
+{
+ text=t;
+}
+
+void Button::render_part(const Part &part) const
+{
+ if(part.get_name()=="text")
+ {
+ render_text(part, text);
+ /*const GL::Font *const font=style->get_font();
+
+ const float font_size=font->get_default_size();
+ unsigned text_w=static_cast<unsigned>(font->get_string_width(text)*font_size);
+
+ part.get_alignment().apply(geom, text_w, static_cast<unsigned>(font_size));
+
+ GL::push_matrix();
+ GL::scale_uniform(font_size);
+
+ const Color &color=style->get_font_color();
+ glColor3f(color.r, color.g, color.b);
+ font->draw_string(text);
+ glColor3f(1, 1, 1);
+
+ GL::pop_matrix();*/
+ }
+ else
+ Widget::render_part(part);
+}
+
+void Button::on_button_press(int, int, unsigned btn)
+{
+ if(btn==1)
+ state=ACTIVE;
+}
+
+void Button::on_button_release(int, int, unsigned btn)
+{
+ if(btn==1/* && x>=0 && y>=0 && x<static_cast<int>(geom.w) && y<static_cast<int>(geom.h)*/)
+ {
+ state=NORMAL;
+ signal_clicked.emit();
+ }
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_BUTTON_H_
+#define MSP_GLTK_BUTTON_H_
+
+#include <sigc++/sigc++.h>
+#include "widget.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Button: public Widget
+{
+public:
+ sigc::signal<void> signal_clicked;
+
+ Button(const Resources &, const std::string & =std::string());
+ void set_text(const std::string &);
+private:
+ std::string text;
+ bool pressed;
+
+ const char *get_class() const { return "button"; }
+ void render_part(const Part &) const;
+ void on_button_press(int, int, unsigned);
+ void on_button_release(int, int, unsigned);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_GLTK_COLOR_H_
+#define MSP_GLTK_COLOR_H_
+
+namespace Msp {
+namespace GLtk {
+
+struct Color
+{
+ float r, g, b;
+
+ Color(): r(1), g(1), b(1) { }
+ Color(float r_, float g_, float b_): r(r_), g(g_), b(b_) { }
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_GLTK_GEOMETRY_H_
+#define MSP_GLTK_GEOMETRY_H_
+
+namespace Msp {
+namespace GLtk {
+
+/**
+Specifies the position and size of a widget or graphic.
+*/
+struct Geometry
+{
+ int x, y;
+ unsigned w, h;
+
+ 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_) { }
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "graphic.h"
+#include "resources.h"
+
+using namespace std;
+
+#include <iostream>
+
+namespace Msp {
+namespace GLtk {
+
+Graphic::Graphic(const Resources &r, const string &n):
+ res(r),
+ name(n),
+ texture(0)
+{ }
+
+void Graphic::render(unsigned wd, unsigned ht) const
+{
+ float x[4];
+ float y[4];
+ float u[4];
+ float v[4];
+
+ x[0]=0.0f-shadow.left;
+ x[1]=x[0]+border.left;
+ x[3]=wd+shadow.right;
+ x[2]=x[3]-border.right;
+
+ y[0]=0.0f-shadow.bottom;
+ y[1]=y[0]+border.bottom;
+ y[3]=ht+shadow.top;
+ y[2]=y[3]-border.top;
+
+ const unsigned twidth=texture->get_width();
+ u[0]=static_cast<float>(slice.x)/twidth;
+ u[1]=static_cast<float>(slice.x+border.left)/twidth;
+ u[2]=static_cast<float>(slice.x+slice.w-border.right)/twidth;
+ u[3]=static_cast<float>(slice.x+slice.w)/twidth;
+
+ const unsigned theight=texture->get_height();
+ v[0]=static_cast<float>(slice.y)/theight;
+ v[1]=static_cast<float>(slice.y+border.bottom)/theight;
+ v[2]=static_cast<float>(slice.y+slice.h-border.top)/theight;
+ v[3]=static_cast<float>(slice.y+slice.h)/theight;
+
+ texture->bind();
+ unsigned xmin=border.left ? 0 : 1;
+ unsigned xmax=border.right ? 3 : 2;
+ unsigned ymin=border.bottom ? 0 : 1;
+ unsigned ymax=border.top ? 3 : 2;
+
+ for(unsigned i=ymin; i<ymax; ++i)
+ {
+ glBegin(GL_QUAD_STRIP);
+ for(unsigned j=xmin; j<=xmax; ++j)
+ {
+ glTexCoord2f(u[j], v[i]);
+ glVertex2f(x[j], y[i]);
+ glTexCoord2f(u[j], v[i+1]);
+ glVertex2f(x[j], y[i+1]);
+ }
+ glEnd();
+ }
+}
+
+
+Graphic::Sides::Sides():
+ top(0),
+ right(0),
+ bottom(0),
+ left(0)
+{ }
+
+
+Graphic::Loader::Loader(Graphic &g):
+ graph(g)
+{
+ add("texture", &Loader::texture);
+ add("slice", &Loader::slice);
+ add("border", &Loader::border);
+ add("shadow", &Loader::shadow);
+}
+
+void Graphic::Loader::texture(const string &n)
+{
+ graph.texture=&graph.res.get_texture(n);
+ graph.slice=Geometry(0, 0, graph.texture->get_width(), graph.texture->get_height());
+}
+
+void Graphic::Loader::slice(unsigned x, unsigned y, unsigned w, unsigned h)
+{
+ graph.slice=Geometry(x, y, w, h);
+}
+
+void Graphic::Loader::border()
+{
+ load_sub(graph.border);
+}
+
+void Graphic::Loader::shadow()
+{
+ load_sub(graph.shadow);
+}
+
+
+Graphic::Sides::Loader::Loader(Sides &s):
+ sides(s)
+{
+ add("top", &Sides::top);
+ add("right", &Sides::right);
+ add("bottom", &Sides::bottom);
+ add("left", &Sides::left);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_GRAPHIC_H_
+#define MSP_GLTK_GRAPHIC_H_
+
+#include <msp/gl/texture2d.h>
+#include <msp/parser/loader.h>
+#include "geometry.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Resources;
+
+class Graphic
+{
+public:
+ class Loader: public Parser::Loader
+ {
+ public:
+ Loader(Graphic &);
+ private:
+ Graphic &graph;
+
+ void texture(const std::string &);
+ void slice(unsigned, unsigned, unsigned, unsigned);
+ void border();
+ void shadow();
+ };
+
+ struct Sides
+ {
+ class Loader: public Parser::Loader
+ {
+ public:
+ Loader(Sides &);
+ Sides &get_object() { return sides; }
+ private:
+ Sides &sides;
+ };
+
+ unsigned top;
+ unsigned right;
+ unsigned bottom;
+ unsigned left;
+
+ Sides();
+ };
+
+ Graphic(const Resources &, const std::string &);
+ const Sides &get_border() const { return border; }
+ const Sides &get_shadow() const { return shadow; }
+ const GL::Texture2D *get_texture() const { return texture; }
+ unsigned get_width() const { return slice.w; }
+ unsigned get_height() const { return slice.h; }
+ void render(unsigned, unsigned) const;
+private:
+ const Resources &res;
+ std::string name;
+ Sides border;
+ Sides shadow;
+ const GL::Texture2D *texture;
+ Geometry slice;
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "hslider.h"
+
+namespace Msp {
+namespace GLtk {
+
+HSlider::HSlider(const Resources &r):
+ Slider(r)
+{
+}
+
+void HSlider::render_part(const Part &part)
+{
+ if(part.get_name()=="slider")
+ {
+ const Graphic *graphic=part.get_graphic(state);
+ const Graphic::Sides &shadow=graphic->get_shadow();
+ unsigned gw=graphic->get_slice().w-shadow.left-shadow.right;
+ unsigned gh=(part.get_fill_x() ? geom.h, graphic->get_slice().h)-shadow.bottom-shadow.top;
+ GL::translate((geom.w-gw)*(value-min)/(max-min), part.get_alignment().y
+ }
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_HSLIDER_H_
+#define MSP_GLTK_HSLIDER_H_
+
+#include "slider.h"
+
+namespace Msp {
+namespace GLtk {
+
+class HSlider: public Slider
+{
+public:
+ HSlider(const Resources &);
+private:
+ void render_part(const Part &) const;
+ void on_button_press(int, int, unsigned);
+ void on_button_release(int, int, unsigned);
+ void pointer_motion(int, int);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "indicator.h"
+
+namespace Msp {
+namespace GLtk {
+
+Indicator::Indicator(const Resources &r):
+ Widget(r)
+{
+ update_style();
+}
+
+void Indicator::set_active(bool a)
+{
+ state=(a ? ACTIVE : NORMAL);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_INDICATOR_H_
+#define MSP_GLTK_INDICATOR_H_
+
+#include "widget.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Indicator: public Widget
+{
+public:
+ Indicator(const Resources &);
+ void set_active(bool);
+private:
+ const char *get_class() const { return "indicator"; }
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "label.h"
+#include "part.h"
+
+namespace Msp {
+namespace GLtk {
+
+Label::Label(const Resources &r, const std::string &t):
+ Widget(r)
+{
+ set_text(t);
+ update_style();
+}
+
+void Label::set_text(const std::string &t)
+{
+ text=t;
+}
+
+void Label::render_part(const Part &part) const
+{
+ if(part.get_name()=="text")
+ render_text(part, text);
+ else
+ Widget::render_part(part);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef LABEL_H_
+#define LABEL_H_
+
+#include "widget.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Label: public Widget
+{
+public:
+ Label(const Resources &, const std::string & =std::string());
+ void set_text(const std::string &);
+private:
+ std::string text;
+
+ const char *get_class() const { return "label"; }
+ void render_part(const Part &) const;
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "panel.h"
+#include "part.h"
+
+namespace Msp {
+namespace GLtk {
+
+Panel::Panel(const Resources &r):
+ Widget(r)
+{
+ update_style();
+}
+
+Panel::~Panel()
+{
+ for(ChildSeq::iterator i=children.begin(); i!=children.end(); ++i)
+ delete *i;
+}
+
+void Panel::add(Widget &wdg)
+{
+ children.push_back(&wdg);
+}
+
+void Panel::render_part(const Part &part) const
+{
+ if(part.get_name()=="children")
+ {
+ for(ChildSeq::const_iterator i=children.begin(); i!=children.end(); ++i)
+ (*i)->render();
+ }
+ else
+ Widget::render_part(part);
+}
+
+void Panel::on_button_press(int x, int y, unsigned btn)
+{
+ for(ChildSeq::iterator i=children.begin(); i!=children.end(); ++i)
+ (*i)->button_press(x-geom.x, y-geom.y, btn);
+}
+
+void Panel::on_button_release(int x, int y, unsigned btn)
+{
+ for(ChildSeq::iterator i=children.begin(); i!=children.end(); ++i)
+ (*i)->button_release(x-geom.x, y-geom.y, btn);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_PANEL_H_
+#define MSP_GLTK_PANEL_H_
+
+#include "widget.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Panel: public Widget
+{
+public:
+ Panel(const Resources &);
+ ~Panel();
+ void add(Widget &);
+private:
+ typedef std::list<Widget *> ChildSeq;
+
+ ChildSeq children;
+
+ Panel(const Panel &);
+ Panel &operator=(const Panel &);
+ const char *get_class() const { return "panel"; }
+ void render_part(const Part &) const;
+ void on_button_press(int, int, unsigned);
+ void on_button_release(int, int, unsigned);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "geometry.h"
+#include "part.h"
+#include "resources.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+Part::Part(const Resources &r, const string &n):
+ res(r),
+ name(n),
+ fill_x(true),
+ fill_y(true)
+{
+ for(unsigned i=0; i<N_STATES_; ++i)
+ graphic[i]=0;
+}
+
+const Graphic *Part::get_graphic(State state) const
+{
+ if(state>N_STATES_)
+ throw InvalidParameterValue("Invalid state");
+
+ return graphic[state];
+}
+
+void Part::render(const Geometry &geom, State state) const
+{
+ const Graphic::Sides &shadow=graphic[state]->get_shadow();
+ unsigned gw=(fill_x ? geom.w : graphic[state]->get_width())-shadow.left-shadow.right;
+ unsigned gh=(fill_y ? geom.h : graphic[state]->get_height())-shadow.top-shadow.bottom;
+ align.apply(geom, gw, gh);
+ graphic[state]->render(gw, gh);
+}
+
+
+Part::Loader::Loader(Part &p):
+ part(p)
+{
+ add("graphic", &Loader::graphic);
+ add("align", &Loader::align);
+ add("fill", &Loader::fill);
+}
+
+void Part::Loader::graphic(State s, const string &n)
+{
+ part.graphic[s]=&part.res.get_graphic(n);
+ if(s==NORMAL)
+ {
+ for(unsigned i=0; i<N_STATES_; ++i)
+ if(!part.graphic[i])
+ part.graphic[i]=part.graphic[s];
+ }
+}
+
+void Part::Loader::align(int x, int y)
+{
+ part.align.x=x;
+ part.align.y=y;
+}
+
+void Part::Loader::fill(bool x, bool y)
+{
+ part.fill_x=x;
+ part.fill_y=y;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_PART_H_
+#define MSP_GLTK_PART_H_
+
+#include <map>
+#include <string>
+#include <msp/parser/loader.h>
+#include "alignment.h"
+#include "state.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Graphic;
+class Resources;
+
+class Part
+{
+public:
+ class Loader: public Parser::Loader
+ {
+ public:
+ Loader(Part &);
+ private:
+ Part ∂
+
+ void graphic(State, const std::string &);
+ void align(int, int);
+ void fill(bool, bool);
+ };
+
+ Part(const Resources &, const std::string &);
+ const std::string &get_name() const { return name; }
+ const Graphic *get_graphic(State) const;
+ const Alignment &get_alignment() const { return align; }
+ bool get_fill_x() const { return fill_x; }
+ bool get_fill_y() const { return fill_y; }
+ void render(const Geometry &, State) const;
+private:
+ const Resources &res;
+ std::string name;
+ const Graphic *graphic[N_STATES_];
+ Alignment align;
+ bool fill_x;
+ bool fill_y;
+};
+typedef std::list<Part> PartSeq;
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/core/error.h>
+#include "resources.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+Resources::Resources():
+ default_font(0)
+{ }
+
+Resources::~Resources()
+{
+ for(FontMap::iterator i=fonts.begin(); i!=fonts.end(); ++i)
+ delete i->second;
+ for(TextureMap::iterator i=textures.begin(); i!=textures.end(); ++i)
+ delete i->second;
+}
+
+const GL::Font &Resources::get_font(const string &name) const
+{
+ FontMap::const_iterator i=fonts.find(name);
+ if(i==fonts.end())
+ throw KeyError("Unknown font "+name);
+
+ return *i->second;
+}
+
+const GL::Font &Resources::get_default_font() const
+{
+ if(!default_font)
+ throw InvalidState("No default font");
+
+ return *default_font;
+}
+
+const GL::Texture2D &Resources::get_texture(const string &name) const
+{
+ TextureMap::const_iterator i=textures.find(name);
+ if(i==textures.end())
+ throw KeyError("Unknown texture "+name);
+
+ return *i->second;
+}
+
+const Graphic &Resources::get_graphic(const string &name) const
+{
+ GraphicMap::const_iterator i=graphics.find(name);
+ if(i==graphics.end())
+ throw KeyError("Unknown graphic "+name);
+
+ return i->second;
+}
+
+const Style &Resources::get_style(const string &wdg, const string &name) const
+{
+ StyleMap::const_iterator i=styles.find(StyleId(wdg, name));
+ if(i==styles.end())
+ throw KeyError("Unknown style "+name+" for widget "+wdg);
+
+ return i->second;
+}
+
+
+Resources::Loader::Loader(Resources &r):
+ res(r)
+{
+ add("font", &Loader::font);
+ add("texture", &Loader::texture);
+ add("graphic", &Loader::graphic);
+ add("style", &Loader::style);
+}
+
+void Resources::Loader::font(const string &fn)
+{
+ RefPtr<GL::Font> fnt=new GL::Font;
+ Parser::load(*fnt, fn);
+
+ res.fonts.insert(FontMap::value_type(fn.substr(0, fn.rfind('.')), fnt.get()));
+ if(!res.default_font)
+ res.default_font=fnt.get();
+ fnt.release();
+}
+
+void Resources::Loader::texture(const string &fn)
+{
+ RefPtr<GL::Texture2D> tex=new GL::Texture2D;
+ tex->image(fn);
+ tex->set_min_filter(GL::LINEAR);
+
+ res.textures.insert(TextureMap::value_type(fn.substr(0, fn.rfind('.')), tex.release()));
+}
+
+void Resources::Loader::graphic(const std::string &n)
+{
+ Graphic graph(res, n);
+ load_sub(graph);
+ if(!graph.get_texture())
+ throw Exception("Graphic without texture");
+
+ res.graphics.insert(GraphicMap::value_type(n, graph));
+}
+
+void Resources::Loader::style(const string &w, const string &n)
+{
+ Style stl(res, w, n);
+ load_sub(stl);
+
+ res.styles.insert(StyleMap::value_type(StyleId(w, n), stl));
+}
+
+
+bool Resources::StyleId::operator<(const StyleId &other) const
+{
+ if(widget<other.widget)
+ return true;
+ return widget==other.widget && name<other.name;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_RESOURCES_H_
+#define MSP_GLTK_RESOURCES_H_
+
+#include <msp/gl/font.h>
+#include <msp/gl/texture.h>
+#include <msp/parser/loader.h>
+#include "graphic.h"
+#include "style.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Resources
+{
+public:
+
+ class Loader: public Msp::Parser::Loader
+ {
+ public:
+ Loader(Resources &);
+ private:
+ Resources &res;
+
+ void font(const std::string &);
+ void texture(const std::string &);
+ void graphic(const std::string &);
+ void style(const std::string &, const std::string &);
+ };
+
+ Resources();
+ ~Resources();
+ const GL::Font &get_font(const std::string &) const;
+ const GL::Font &get_default_font() const;
+ const GL::Texture2D &get_texture(const std::string &) const;
+ const Graphic &get_graphic(const std::string &) const;
+ const Style &get_style(const std::string &, const std::string &) const;
+private:
+ struct StyleId
+ {
+ std::string widget;
+ std::string name;
+
+ StyleId(const std::string &w, const std::string &n): widget(w), name(n) { }
+ bool operator<(const StyleId &) const;
+ };
+
+ typedef std::map<std::string, GL::Font *> FontMap;
+ typedef std::map<std::string, GL::Texture2D *> TextureMap;
+ typedef std::map<std::string, Graphic> GraphicMap;
+ typedef std::map<StyleId, Style> StyleMap;
+
+ FontMap fonts;
+ GL::Font *default_font;
+ TextureMap textures;
+ GraphicMap graphics;
+ StyleMap styles;
+
+ Resources(const Resources &);
+ Resources &operator=(const Resources &);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "slider.h"
+
+namespace Msp {
+namespace GLtk {
+
+Slider::Slider(const Resources &r):
+ Widget(r),
+ min(0),
+ max(1),
+ value(0),
+ step(0.1)
+{
+}
+
+void Slider::set_value(double v)
+{
+ if(v<min)
+ value=min;
+ else if(v>max)
+ value=max;
+ else
+ {
+ unsigned steps=static_cast<unsigned>((v-min)/step+0.5);
+ value=min+steps*step;
+ }
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_SLIDER_H_
+#define MSP_GLTK_SLIDER_H_
+
+#include "widget.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Slider: public Widget
+{
+public:
+ void set_value(double);
+ void set_range(double, double);
+ void set_step(double);
+ double get_value() const { return value; }
+protected:
+ double min, max;
+ double value;
+ double step;
+
+ Slider(const Resourcres &);
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "state.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+istream &operator>>(istream &is, State &state)
+{
+ string str;
+ is>>str;
+ if(str=="normal")
+ state=NORMAL;
+ else if(str=="hover")
+ state=HOVER;
+ else if(str=="active")
+ state=ACTIVE;
+ else if(str=="disabled")
+ state=DISABLED;
+ else
+ is.setstate(ios_base::failbit);
+
+ return is;
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_STATE_H_
+#define MSP_GLTK_STATE_H_
+
+#include <istream>
+#include <msp/parser/value.h>
+
+namespace Msp {
+namespace GLtk {
+
+enum State
+{
+ NORMAL, //< Default state
+ HOVER, //< Pointer over the widget
+ ACTIVE, //< Widget is active (e.g. pressed button)
+ DISABLED, //< Widget is unresponsive
+ N_STATES_ //< Sentry value
+};
+
+extern std::istream &operator>>(std::istream &, State &);
+
+} // namespace GLtk
+
+namespace Parser {
+
+template<>
+struct TypeResolver<GLtk::State> { static const Value::Type type=Value::ENUM; };
+
+} // namespace Parser
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "resources.h"
+#include "style.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+Style::Style(const Resources &r, const string &w, const string &n):
+ res(r),
+ widget(w),
+ name(n),
+ font(&res.get_default_font())
+{ }
+
+
+Style::Loader::Loader(Style &s):
+ style(s)
+{
+ add("font", &Loader::font);
+ add("font_color", &Loader::font_color);
+ add("part", &Loader::part);
+}
+
+void Style::Loader::font(const string &f)
+{
+ style.font=&style.res.get_font(f);
+}
+
+void Style::Loader::font_color(float r, float g, float b)
+{
+ style.font_color=Color(r, g, b);
+}
+
+void Style::Loader::part(const string &n)
+{
+ Part p(style.res, n);
+ load_sub(p);
+ style.parts.push_back(p);
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_STYLE_H_
+#define MSP_GLTK_STYLE_H_
+
+#include <msp/gl/font.h>
+#include <msp/parser/loader.h>
+#include "color.h"
+#include "part.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Resources;
+
+class Style
+{
+public:
+ class Loader: public Parser::Loader
+ {
+ public:
+ Loader(Style &);
+ private:
+ Style &style;
+
+ void font(const std::string &);
+ void font_color(float, float, float);
+ void part(const std::string &);
+ };
+
+ Style(const Resources &, const std::string &, const std::string &);
+ const GL::Font *get_font() const { return font; }
+ const Color &get_font_color() const { return font_color; }
+ const PartSeq &get_parts() const { return parts; }
+private:
+ const Resources &res;
+ std::string widget;
+ std::string name;
+ const GL::Font *font;
+ Color font_color;
+ PartSeq parts;
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/gl/matrix.h>
+#include <msp/gl/transform.h>
+#include "resources.h"
+#include "widget.h"
+
+#include <iostream>
+using namespace std;
+
+namespace Msp {
+namespace GLtk {
+
+void Widget::set_position(int x, int y)
+{
+ geom.x=x;
+ geom.y=y;
+}
+
+void Widget::set_size(unsigned w, unsigned h)
+{
+ geom.w=w;
+ geom.h=h;
+}
+
+void Widget::set_geometry(const Geometry &g)
+{
+ geom=g;
+}
+
+void Widget::set_style(const string &s)
+{
+ style_name=s;
+ update_style();
+}
+
+void Widget::render() const
+{
+ if(!style)
+ throw InvalidState("Attempt to render a widget without a style");
+
+ GL::push_matrix();
+ GL::translate(geom.x, geom.y, 0);
+ for(PartSeq::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i)
+ render_part(*i);
+ GL::pop_matrix();
+}
+
+bool Widget::button_press(int x, int y, unsigned btn)
+{
+ if(x>=geom.x && y>=geom.y && x<geom.x+static_cast<int>(geom.w) && y<geom.y+static_cast<int>(geom.h))
+ {
+ on_button_press(x, y, btn);
+ return true;
+ }
+
+ return false;
+}
+
+bool Widget::button_release(int x, int y, unsigned btn)
+{
+ if(x>=geom.x && y>=geom.y && x<geom.x+static_cast<int>(geom.w) && y<geom.y+static_cast<int>(geom.h))
+ {
+ on_button_release(x, y, btn);
+ return true;
+ }
+
+ return false;
+}
+
+Widget::Widget(const Resources &r):
+ res(r),
+ style(0),
+ state(NORMAL)
+{ }
+
+void Widget::update_style()
+{
+ style=&res.get_style(get_class(), style_name);
+}
+
+void Widget::render_part(const Part &part) const
+{
+ render_graphic(part);
+}
+
+void Widget::render_graphic(const Part &part) const
+{
+ GL::push_matrix();
+ part.render(geom, state);
+ GL::pop_matrix();
+}
+
+void Widget::render_text(const Part &part, const string &text) const
+{
+ const GL::Font *const font=style->get_font();
+
+ const float font_size=font->get_default_size();
+ unsigned text_w=static_cast<unsigned>(font->get_string_width(text)*font_size);
+
+ GL::push_matrix();
+
+ part.get_alignment().apply(geom, text_w, static_cast<unsigned>(font_size));
+ GL::scale_uniform(font_size);
+
+ const Color &color=style->get_font_color();
+ glColor3f(color.r, color.g, color.b);
+ font->draw_string(text);
+ glColor3f(1, 1, 1);
+
+ GL::pop_matrix();
+}
+
+} // namespace GLtk
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GLTK_WIDGET_H_
+#define MSP_GLTK_WIDGET_H_
+
+#include <string>
+#include "geometry.h"
+#include "state.h"
+
+namespace Msp {
+namespace GLtk {
+
+class Part;
+class Resources;
+class Style;
+
+class Widget
+{
+public:
+ virtual ~Widget() { }
+ void set_position(int, int);
+ void set_size(unsigned, unsigned);
+ void set_geometry(const Geometry &);
+ void set_style(const std::string &);
+ const Geometry &get_geometry() const { return geom; }
+ void render() const;
+ bool button_press(int, int, unsigned);
+ bool button_release(int, int, unsigned);
+ bool pointer_motion(int, int);
+protected:
+ const Resources &res;
+ Geometry geom;
+ std::string style_name;
+ const Style *style;
+ State state;
+
+ Widget(const Resources &);
+ virtual const char *get_class() const { return "widget"; }
+ void update_style();
+ virtual void render_part(const Part &) const;
+ void render_graphic(const Part &) const;
+ void render_text(const Part &, const std::string &) const;
+ virtual void on_button_press(int, int, unsigned) { }
+ virtual void on_button_release(int, int, unsigned) { }
+ virtual void on_pointer_motion(int, int, unsigned) { }
+ virtual void on_pointer_enter() { }
+ virtual void on_pointer_leave() { }
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif
--- /dev/null
+#ifndef MSP_GLTK_WINDOW_H_
+#define MSP_GLTK_WINDOW_H_
+
+#include <X11/X.h>
+
+namespace Msp {
+namespace GLtk {
+
+class Window
+{
+public:
+ Window();
+private:
+ Display *display;
+ Window window;
+};
+
+} // namespace GLtk
+} // namespace Msp
+
+#endif