# HG changeset patch # User John Tsiombikas # Date 1395365836 -7200 # Node ID f1014234dece536c15a614c0f73ca749bda0371d # Parent e5b1525084f73ab8cb4cf1d77b99f2ab4eb08c6b transitions in gui elements are awesome :) diff -r e5b1525084f7 -r f1014234dece .clang_complete --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.clang_complete Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,2 @@ +-Isrc +-Iinclude diff -r e5b1525084f7 -r f1014234dece .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,6 @@ +\.o$ +\.d$ +\.swp$ +\.a$ +\.so$ +^test$ diff -r e5b1525084f7 -r f1014234dece Makefile --- a/Makefile Thu Mar 20 07:03:58 2014 +0200 +++ b/Makefile Fri Mar 21 03:37:16 2014 +0200 @@ -3,9 +3,9 @@ bin = test -CFLAGS = -pedantic -Wall -g -Iinclude +CFLAGS = -pedantic -Wall -g -Iinclude -Isrc CXXFLAGS = $(CFLAGS) -LDFLAGS = -lGL -lGLU -lglut +LDFLAGS = -lGL -lGLU -lglut -lpthread $(bin): $(obj) $(CXX) -o $@ $(obj) $(LDFLAGS) diff -r e5b1525084f7 -r f1014234dece include/button.h --- a/include/button.h Thu Mar 20 07:03:58 2014 +0200 +++ b/include/button.h Fri Mar 21 03:37:16 2014 +0200 @@ -5,11 +5,11 @@ namespace gameui { -class ButtonImpl; +struct ButtonImpl; -class Button { +class Button : public Widget { private: - ButtonImpl *impl; + ButtonImpl *button; public: Button(); diff -r e5b1525084f7 -r f1014234dece include/widget.h --- a/include/widget.h Thu Mar 20 07:03:58 2014 +0200 +++ b/include/widget.h Fri Mar 21 03:37:16 2014 +0200 @@ -1,18 +1,12 @@ #ifndef GAMEUI_WIDGET_H_ #define GAMEUI_WIDGET_H_ +#include "vec.h" +#include "event.h" + namespace gameui { -class Vec2 { -public: - float x, y; - - Vec2() : x(0), y(0) {} - Vec2(float xx, float yy) : x(xx), y(yy) {} -}; - -class BBox { -public: +struct BBox { Vec2 bmin, bmax; }; @@ -20,44 +14,65 @@ class Widget { private: - WidgetImpl *impl; + WidgetImpl *widget; + + void set_type_string(const char *type_str); public: - enum VisState { - VST_HIDDEN, - VST_EASEIN, - VST_VISIBLE, - VST_EASEOUT - }; - enum ActiveState { - AST_INACTIVE, - AST_EASEIN, - AST_ACTIVE, - AST_EASEOUT - }; - Widget(); virtual ~Widget(); virtual void show(); virtual void hide(); virtual float get_visibility() const; + virtual bool is_visible() const; virtual void activate(); virtual void deactivate(); virtual float get_active() const; + virtual bool is_active() const; + + virtual void press(); + virtual void release(); + virtual float get_pressed() const; + virtual bool is_pressed() const; + + virtual void mousein(); + virtual void mouseout(); + virtual float get_under_mouse() const; + virtual bool is_under_mouse() const; + + virtual void set_position(float x, float y); + virtual void set_position(const Vec2 &pos); + virtual const Vec2 &get_position() const; + + virtual void set_size(float x, float y); + virtual void set_size(const Vec2 &size); + virtual const Vec2 get_size() const; virtual const BBox &get_box() const; - virtual const Vec2 &get_position() const; - virtual const Vec2 &get_size() const; virtual bool hit_test(const Vec2 &pt) const; - virtual void draw() const = 0; + virtual void draw() const; + + // low level events + virtual void on_mouse_button(const ButtonEvent &ev); + virtual void on_mouse_motion(const MotionEvent &ev); + virtual void on_mouse_focus(const FocusEvent &ev); + virtual void on_key(const KeyEvent &ev); + + // high level events + virtual void on_click(); + virtual void on_double_click(); + virtual void on_change(); + //virtual void on_drag_move(int bn, const Vec2 &pt); + //virtual void on_drag_release(int bn, const Vec2 &pt); + + // event dispatcher + virtual void handle_event(const Event &ev); }; -long get_cur_time(); - } #endif // GAMEUI_WIDGET_H_ diff -r e5b1525084f7 -r f1014234dece src/boolanm.cc --- a/src/boolanm.cc Thu Mar 20 07:03:58 2014 +0200 +++ b/src/boolanm.cc Fri Mar 21 03:37:16 2014 +0200 @@ -4,10 +4,9 @@ BoolAnim::BoolAnim(bool st) { - value = st ? 1.0 : 0.0; - trans_dir = 0.0; + set(st); trans_start = 0; - trans_dur = 1000; + trans_dur = 500; get_msec = default_get_msec; } @@ -43,6 +42,12 @@ get_msec = time_func; } +void BoolAnim::set(bool st) +{ + value = st ? 1.0 : 0.0; + trans_dir = 0.0; +} + void BoolAnim::change(bool st) { change(st, get_msec()); diff -r e5b1525084f7 -r f1014234dece src/boolanm.h --- a/src/boolanm.h Thu Mar 20 07:03:58 2014 +0200 +++ b/src/boolanm.h Fri Mar 21 03:37:16 2014 +0200 @@ -18,6 +18,8 @@ void set_transition_duration(long dur); void set_time_callback(long (*time_func)()); + void set(bool st); + void change(bool st); void change(bool st, long trans_start); diff -r e5b1525084f7 -r f1014234dece src/button.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/button.cc Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,18 @@ +#include "button.h" + +namespace gameui { + +struct ButtonImpl { +}; + +Button::Button() +{ + button = new ButtonImpl; +} + +Button::~Button() +{ + delete button; +} + +} // namespace gameui diff -r e5b1525084f7 -r f1014234dece src/event.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/event.h Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,45 @@ +#ifndef EVENT_H_ +#define EVENT_H_ + +#include "vec.h" + +namespace gameui { + +enum EventType { + EV_MOUSE_BUTTON, + EV_MOUSE_MOTION, + EV_MOUSE_FOCUS, + EV_KEY +}; + +struct ButtonEvent { + Vec2 pos; + int button; + bool press; +}; + +struct MotionEvent { + Vec2 pos; +}; + +struct FocusEvent { + bool enter; +}; + +struct KeyEvent { + int key; + bool press; +}; + +struct Event { + EventType type; + + ButtonEvent button; + MotionEvent motion; + FocusEvent focus; + KeyEvent key; +}; + +} // namespace gameui + +#endif // EVENT_H_ diff -r e5b1525084f7 -r f1014234dece src/theme.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/theme.cc Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,100 @@ +#include +#include "theme.h" +#include "widget.h" + +#ifdef WIN32 +#include +#endif +#ifdef __APPLE__ +#include +#else +#include +#endif + + +namespace gameui { + +Theme *theme; + +Theme::Theme() +{ + so = 0; +} + +bool Theme::load(const char *name) +{ + fprintf(stderr, "theme loading not implemented yet!\n"); + return false; +} + +widget_draw_func Theme::get_draw_func(const char *type) const +{ + std::map::const_iterator it = draw_func.find(type); + if(it == draw_func.end()) { + return default_draw_func; + } + return it->second; +} + +#define LERP(a, b, t) ((a) + ((b) - (a)) * t) +#define DEF_TEX_SZ 32 +void default_draw_func(const Widget *w) +{ + static unsigned int tex; + + if(!tex) { + unsigned char *pixels = new unsigned char[DEF_TEX_SZ * DEF_TEX_SZ * 3]; + unsigned char *ptr = pixels; + for(int i=0; iget_position(); + Vec2 sz = w->get_size(); + float aspect = sz.x / sz.y; + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tex); + + float offs = w->get_pressed() * 0.1 * sz.y; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(offs, -offs, 0); + + float active = w->get_active(); + float hover = w->get_under_mouse(); + + float rg = LERP(0.4, 1.0, hover); + float b = LERP(rg, 0, active); + glColor3f(rg, rg, b); + + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex2f(pos.x, pos.y); + glTexCoord2f(aspect, 1); + glVertex2f(pos.x + sz.x, pos.y); + glTexCoord2f(aspect, 0); + glVertex2f(pos.x + sz.x, pos.y + sz.y); + glTexCoord2f(0, 0); + glVertex2f(pos.x, pos.y + sz.y); + glEnd(); + + glPopMatrix(); + + glPopAttrib(); +} + +} // namespace gameui diff -r e5b1525084f7 -r f1014234dece src/theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/theme.h Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,33 @@ +#ifndef THEME_H_ +#define THEME_H_ + +#include +#include + +namespace gameui { + +class Widget; + +typedef void (*widget_draw_func)(const Widget*); + +void default_draw_func(const Widget *w); + +class Theme { +private: + void *so; + std::map draw_func; + +public: + Theme(); + ~Theme(); + + bool load(const char *name); + + widget_draw_func get_draw_func(const char *type) const; +}; + +extern Theme *theme; // the current theme + +} // namespace gameui + +#endif // THEME_H_ diff -r e5b1525084f7 -r f1014234dece src/vec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vec.h Fri Mar 21 03:37:16 2014 +0200 @@ -0,0 +1,16 @@ +#ifndef VEC_H_ +#define VEC_H_ + +namespace gameui { + +class Vec2 { +public: + float x, y; + + Vec2() : x(0), y(0) {} + Vec2(float xx, float yy) : x(xx), y(yy) {} +}; + +} // namespace gameui + +#endif // VEC_H_ diff -r e5b1525084f7 -r f1014234dece src/widget.cc --- a/src/widget.cc Thu Mar 20 07:03:58 2014 +0200 +++ b/src/widget.cc Fri Mar 21 03:37:16 2014 +0200 @@ -1,12 +1,15 @@ +#include +#include #include #include #include "widget.h" #include "boolanm.h" +#include "theme.h" -using namespace gameui; +namespace gameui { struct WidgetImpl { - std::string name_prefix; + std::string type_str; std::string name; BBox box; @@ -15,96 +18,249 @@ Widget::Widget() - : name_prefix("widget") { static int widget_count; + widget = new WidgetImpl; + set_type_string("widget"); + std::stringstream sstr; - sstr << name_prefix << widget_count++; - name = sstr.str(); + sstr << widget->type_str << widget_count++; + widget->name = sstr.str(); - box.bmin = Vec2(0, 0); - box.bmax = Vec2(1, 1); + widget->box.bmin = Vec2(0, 0); + widget->box.bmax = Vec2(1, 1); - vis_st = VST_VISIBLE; - act_st = AST_ACTIVE; + widget->visible.set(true); + widget->active.set(true); - vis = act = 1.0; + widget->hover.set_transition_duration(250); + widget->press.set_transition_duration(50); } Widget::~Widget() { + delete widget; } void Widget::show() { - if(vis_st == VST_EASEVISIBLE || vis_st == VST_EASEIN) { - return; - } - - vis_st = VST_EASEIN; - vis_start_time = get_cur_time(); + widget->visible.change(true); } void Widget::hide() { - if(vis_st == VST_EASEHIDDEN || vis_st == VST_EASEOUT) { - return; - } - - vis_st = VST_EASEOUT; - vis_start_time = get_cur_time(); + widget->visible.change(false); } float Widget::get_visibility() const { - switch(vis_st) { - case VST_EASEIN: - vis = (get_cur_time() - vis_start_time) / gameui::ease_time; - if(vis < 0.0) vis = 0.0; - if(vis > 1.0) vis = 1.0; - break; + return widget->visible.get_value(); +} - case VST_EASEOUT: - vis = 1.0 - (get_cur_time() - vis_start_time) / gameui::ease_time; - if(vis < 0.0) vis = 0.0; - if(vis > 1.0) vis = 1.0; - break; - - case VST_HIDDEN: - vis = 0.0; - break; - - case VST_VISIBLE: - vis = 1.0; - break; - } - - return vis; +bool Widget::is_visible() const +{ + return widget->visible.get_state(); } void Widget::activate() { + widget->active.change(true); } -#ifdef WIN32 -long gameui::get_cur_time() +void Widget::deactivate() { - return GetTickCount(); + widget->active.change(false); } -#endif -#if defined(__unix__) || defined(__APPLE__) -long gameui::get_cur_time() +float Widget::get_active() const { - struct timeval tv; - static struct timeval tv0; + return widget->active.get_value(); +} - gettimeofday(&tv, 0); - if(tv0.tv_sec == 0 && tv0.tv_msec == 0) { - tv0 = tv; - return 0; +bool Widget::is_active() const +{ + return widget->active.get_state(); +} + +void Widget::press() +{ + widget->press.change(true); +} + +void Widget::release() +{ + widget->press.change(false); +} + +float Widget::get_pressed() const +{ + return widget->press.get_value(); +} + +bool Widget::is_pressed() const +{ + return widget->press.get_state(); +} + +void Widget::mousein() +{ + widget->hover.change(true); +} + +void Widget::mouseout() +{ + widget->hover.change(false); +} + +float Widget::get_under_mouse() const +{ + return widget->hover.get_value(); +} + +bool Widget::is_under_mouse() const +{ + return widget->hover.get_state(); +} + +void Widget::set_position(float x, float y) +{ + set_position(Vec2(x, y)); +} + +void Widget::set_position(const Vec2 &pos) +{ + Vec2 sz = get_size(); + + widget->box.bmin = pos; + widget->box.bmax.x = pos.x + sz.x; + widget->box.bmax.y = pos.y + sz.y; +} + +const Vec2 &Widget::get_position() const +{ + return widget->box.bmin; +} + +void Widget::set_size(float x, float y) +{ + set_size(Vec2(x, y)); +} + +void Widget::set_size(const Vec2 &sz) +{ + widget->box.bmax.x = widget->box.bmin.x + sz.x; + widget->box.bmax.y = widget->box.bmin.y + sz.y; +} + +const Vec2 Widget::get_size() const +{ + return Vec2(widget->box.bmax.x - widget->box.bmin.x, + widget->box.bmax.y - widget->box.bmin.y); +} + + +const BBox &Widget::get_box() const +{ + return widget->box; +} + +bool Widget::hit_test(const Vec2 &pt) const +{ + return pt.x >= widget->box.bmin.x && pt.x < widget->box.bmax.x && + pt.y >= widget->box.bmin.y && pt.y < widget->box.bmax.y; +} + +void Widget::draw() const +{ + widget_draw_func draw_func = default_draw_func; + + if(theme) { + draw_func = theme->get_draw_func(widget->type_str.c_str()); } - return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000; + + draw_func(this); } -#endif + +// dummy event handlers +void Widget::on_mouse_button(const ButtonEvent &ev) +{ +} + +void Widget::on_mouse_motion(const MotionEvent &ev) +{ +} + +void Widget::on_mouse_focus(const FocusEvent &ev) +{ +} + +void Widget::on_key(const KeyEvent &ev) +{ +} + +void Widget::on_click() +{ +} + +void Widget::on_double_click() +{ +} + +void Widget::on_change() +{ +} + + +void Widget::set_type_string(const char *type_str) +{ + widget->type_str = type_str; +} + +/* the event dispatcher generates high-level events (click, etc) + * and calls the on_whatever() functions for both low and high-level + * events. + * The on_whatever functions are called *after* any other actions performed + * here, to give subclasses the opportunity to override them easily, by + * overriding the on_ functions, without having to override handle_event itself + */ +// TODO also call callbacks here I guess... +void Widget::handle_event(const Event &ev) +{ + switch(ev.type) { + case EV_MOUSE_BUTTON: + if(ev.button.press) { + press(); + } else { + if(is_pressed()) { + on_click(); + } + release(); + } + on_mouse_button(ev.button); + break; + + case EV_MOUSE_MOTION: + on_mouse_motion(ev.motion); + break; + + case EV_MOUSE_FOCUS: + if(ev.focus.enter) { + mousein(); + } else { + mouseout(); + } + on_mouse_focus(ev.focus); + break; + + case EV_KEY: + on_key(ev.key); + break; + + default: + fprintf(stderr, "%s: unknown event id: %d\n", __func__, ev.type); + } +} + + +} // namespace gameui diff -r e5b1525084f7 -r f1014234dece test.cc --- a/test.cc Thu Mar 20 07:03:58 2014 +0200 +++ b/test.cc Fri Mar 21 03:37:16 2014 +0200 @@ -1,11 +1,14 @@ #include #include #include +#include #include +#include "gameui.h" static bool init(); static void cleanup(); static void disp(); +static void idle(); static void reshape(int x, int y); static void keypress(unsigned char key, int x, int y); static void keyrelease(unsigned char key, int x, int y); @@ -14,6 +17,8 @@ static void mouse(int bn, int st, int x, int y); static void motion(int x, int y); +static std::vector widgets; + int main(int argc, char **argv) { glutInitWindowSize(800, 600); @@ -22,6 +27,7 @@ glutCreateWindow("gameui test"); glutDisplayFunc(disp); + glutIdleFunc(idle); glutReshapeFunc(reshape); glutKeyboardFunc(keypress); glutKeyboardUpFunc(keyrelease); @@ -43,27 +49,45 @@ static bool init() { + gameui::Button *button = new gameui::Button; + button->set_position(350, 280); + button->set_size(100, 40); + widgets.push_back(button); + return true; } static void cleanup() { + for(size_t i=0; idraw(); + } + glutSwapBuffers(); assert(glGetError() == GL_NO_ERROR); } +static void idle() +{ + glutPostRedisplay(); +} + static void reshape(int x, int y) { glViewport(0, 0, x, y); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(0, x, y, 0, -1, 1); + glOrtho(0, x, 0, y, -1, 1); } static void keypress(unsigned char key, int x, int y) @@ -87,8 +111,46 @@ static void mouse(int bn, int st, int x, int y) { + int bidx = bn - GLUT_LEFT_BUTTON; + bool down = st == GLUT_DOWN; + + for(size_t i=0; ihit_test(gameui::Vec2(x, y))) { + gameui::Event ev; + ev.type = gameui::EV_MOUSE_BUTTON; + ev.button.button = bidx; + ev.button.press = down; + ev.button.pos = gameui::Vec2(x, y); + w->handle_event(ev); + } + } } static void motion(int x, int y) { + static gameui::Widget *active; + + if(active && !active->hit_test(gameui::Vec2(x, y))) { + gameui::Event ev; + ev.type = gameui::EV_MOUSE_FOCUS; + ev.focus.enter = false; + active->handle_event(ev); + active = 0; + } + + for(size_t i=0; ihit_test(gameui::Vec2(x, y))) { + if(active != w) { + gameui::Event ev; + ev.type = gameui::EV_MOUSE_FOCUS; + ev.focus.enter = true; + w->handle_event(ev); + active = w; + } + } + } }