]> git.tdb.fi Git - libs/gltk.git/commitdiff
Pass coordinates relative to the receiving widget's geometry
authorMikko Rasa <tdb@tdb.fi>
Tue, 25 Dec 2007 20:41:08 +0000 (20:41 +0000)
committerMikko Rasa <tdb@tdb.fi>
Tue, 25 Dec 2007 20:41:08 +0000 (20:41 +0000)
Add List widget
Make Indicator and List loadable from datafiles
Add Logic and Connector
Refactor some common components of Sliders to the base class
Allow specifying minimum sizes for parts

20 files changed:
source/button.cpp
source/connector.cpp [new file with mode: 0644]
source/connector.h [new file with mode: 0644]
source/geometry.cpp
source/geometry.h
source/hslider.cpp
source/hslider.h
source/list.cpp [new file with mode: 0644]
source/list.h [new file with mode: 0644]
source/logic.cpp [new file with mode: 0644]
source/logic.h [new file with mode: 0644]
source/panel.cpp
source/part.cpp
source/part.h
source/slider.cpp
source/slider.h
source/vslider.cpp
source/vslider.h
source/widget.cpp
source/widget.h

index 58e69bfdad42543216da7dbb7ff6c7001835669c..bc6fd274d7362dae651c7d15f37eafdbb62f7be7 100644 (file)
@@ -23,9 +23,9 @@ void Button::set_text(const std::string &t)
        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;
 }
 
@@ -33,7 +33,7 @@ void Button::button_release(int x, int y, unsigned btn)
 {
        if(btn==1)
        {
-               if(geom.is_inside(x, y))
+               if(geom.is_inside_relative(x, y))
                {
                        state=HOVER;
                        signal_clicked.emit();
diff --git a/source/connector.cpp b/source/connector.cpp
new file mode 100644 (file)
index 0000000..837ec80
--- /dev/null
@@ -0,0 +1,47 @@
+/* $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
diff --git a/source/connector.h b/source/connector.h
new file mode 100644 (file)
index 0000000..a936648
--- /dev/null
@@ -0,0 +1,93 @@
+/* $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
index 126a0fc7aa48b731d72819dc70ec302575062d37..8202774d62913cf894c147aec7d7e287b3828bdc 100644 (file)
@@ -15,6 +15,11 @@ bool Geometry::is_inside(int x_, int y_) const
        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),
@@ -47,13 +52,16 @@ void Alignment::apply(Geometry &geom, const Geometry &parent) const
 
 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
index 6fa8e7f1287ffd1f39942979f1f046889bc97ecc..08d660eeb05a4b9507968a6f8845924e524f70e1 100644 (file)
@@ -24,6 +24,7 @@ struct Geometry
        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;
 };
 
 
index 3b68dacfa1b142eafe31851ed6f8dfc6fcd5d64e..32d0f6521b98f3ddae610557d71625d30aaed43e 100644 (file)
@@ -16,8 +16,7 @@ namespace Msp {
 namespace GLtk {
 
 HSlider::HSlider(const Resources &r):
-       Slider(r),
-       dragging(false)
+       Slider(r)
 {
        update_style();
 }
@@ -26,14 +25,11 @@ void HSlider::button_press(int x, int y, unsigned btn)
 {
        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);
                }
        }
 }
@@ -42,7 +38,7 @@ void HSlider::button_release(int, int, unsigned btn)
 {
        if(btn==1)
        {
-               dragging=false;
+               end_drag();
                state=NORMAL;
        }
 }
@@ -50,9 +46,7 @@ void HSlider::button_release(int, int, unsigned btn)
 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
@@ -60,7 +54,8 @@ 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());
@@ -72,13 +67,18 @@ void HSlider::render_special(const Part &part) const
        }
 }
 
-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
index 8f0be9f810b2343d72365f30215138d12a0c1dd8..5da211db4b1f3d6d4bcf481afaf2fbe592eba087 100644 (file)
@@ -20,9 +20,7 @@ the current value of the widget.
 class HSlider: public Slider
 {
 private:
-       bool dragging;
-       int drag_start_x;
-       double drag_start_value;
+       unsigned slider_size;
 
 public:
        HSlider(const Resources &);
@@ -32,7 +30,9 @@ public:
 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
diff --git a/source/list.cpp b/source/list.cpp
new file mode 100644 (file)
index 0000000..73f427b
--- /dev/null
@@ -0,0 +1,261 @@
+/* $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
diff --git a/source/list.h b/source/list.h
new file mode 100644 (file)
index 0000000..edcec33
--- /dev/null
@@ -0,0 +1,75 @@
+/* $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
diff --git a/source/logic.cpp b/source/logic.cpp
new file mode 100644 (file)
index 0000000..40d06b2
--- /dev/null
@@ -0,0 +1,38 @@
+/* $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
diff --git a/source/logic.h b/source/logic.h
new file mode 100644 (file)
index 0000000..689b39b
--- /dev/null
@@ -0,0 +1,59 @@
+/* $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
index 91562cea2b5d7df9a108128d8e31e2927e83ae1a..9373ff2ebd7559fecccc0e058a140a1c647ca55a 100644 (file)
@@ -9,7 +9,9 @@ Distributed under the LGPL
 #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"
@@ -53,15 +55,19 @@ void Panel::remove(Widget &wdg)
 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);
                }
        }
 }
@@ -70,28 +76,38 @@ void Panel::button_release(int x, int y, unsigned 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);
+               }
        }
 }
 
@@ -169,7 +185,7 @@ void Panel::set_input_focus(Widget *wdg)
 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(xy))
                        return *i;
 
        return 0;
@@ -184,7 +200,9 @@ Panel::Loader::Loader(Panel &p, map<string, Widget *> &m):
        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>);
 }
index ec36de9774aba7def07af052677af8d843be2568..a74c91141ca717c86262c8a634f8db75ff359226 100644 (file)
@@ -50,6 +50,7 @@ Part::Loader::Loader(Part &p, Resources &r):
        add("align",   &Loader::align);
        add("fill",    &Loader::fill);
        add("margin",  &Loader::margin);
+       add("size",    &Loader::size);
 }
 
 Part::Loader::~Loader()
@@ -89,5 +90,11 @@ void Part::Loader::margin()
        load_sub(part.margin);
 }
 
+void Part::Loader::size(unsigned w, unsigned h)
+{
+       part.geom.w=w;
+       part.geom.h=h;
+}
+
 } // namespace GLtk
 } // namespace Msp
index edc8f77a5b84230d883d277f5bf29c2e77fe6ef9..edc524c0b67b62d1807b46eb6f271983d92de090 100644 (file)
@@ -41,6 +41,7 @@ public:
                void align(float, float);
                void fill(float, float);
                void margin();
+               void size(unsigned, unsigned);
        };
 
 private:
index 0b1d8b066ee2e992644d9cc0254dc672a6ac8db5..dacd826630bc670cb364f8fa0d27c27f005cdc64 100644 (file)
@@ -15,7 +15,9 @@ Slider::Slider(const Resources &r):
        min(0),
        max(1),
        value(0),
-       step(0.1)
+       step(0.1),
+       dragging(false),
+       drag_area_size(0)
 { }
 
 void Slider::set_value(double v)
@@ -49,6 +51,24 @@ void Slider::set_step(double s)
        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)
index e6cbe0d0a37706e02948e1e33ff7e7f743d840b7..6c95526f13f2699f14c89049fe5f252510383a2e 100644 (file)
@@ -34,6 +34,11 @@ protected:
        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;
 
@@ -44,6 +49,10 @@ public:
        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
index 397713b6c7c9bda5529fa93661205f85d402aee8..d9e9e4dee5f5cdeb9201e2f518cc888ed1eee75e 100644 (file)
@@ -16,33 +16,29 @@ namespace Msp {
 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;
        }
 }
@@ -50,9 +46,7 @@ void VSlider::button_release(int, int, unsigned btn)
 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
@@ -60,7 +54,8 @@ 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());
@@ -72,13 +67,18 @@ void VSlider::render_special(const Part &part) const
        }
 }
 
-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
index 2696051b7f7d47ee28989fd527152179b558135e..38238791aca7fd2705d45f46612916df4d1c2784 100644 (file)
@@ -16,9 +16,7 @@ namespace GLtk {
 class VSlider: public Slider
 {
 private:
-       bool dragging;
-       int drag_start_y;
-       double drag_start_value;
+       unsigned slider_size;
 
 public:
        VSlider(const Resources &);
@@ -28,7 +26,9 @@ public:
 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
index 2eade3913f4d9c9e4530675657873ee080a2d265..7d608f2ce8d32980505f3fde37e8df91a3bb35de 100644 (file)
@@ -34,17 +34,20 @@ void Widget::set_position(int x, int y)
 {
        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)
@@ -96,10 +99,11 @@ void Widget::render_text(const Part &part, const string &text) const
        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();
@@ -119,6 +123,7 @@ void Widget::update_style()
                sname+=style_name;
        }
        style=res.get<Style>(sname);
+       on_style_change();
 }
 
 void Widget::set_parent(Panel *p)
@@ -126,6 +131,8 @@ 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)
index feed5b7df014a7fa646f56ee87e7865adfad2be8..a83cecc9bef8c14643cc4855faaeda1712ac603d 100644 (file)
@@ -100,7 +100,7 @@ protected:
 
        /**
        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 *);
 
@@ -108,6 +108,11 @@ protected:
        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