nuclear@6: /* nuclear@6: GoatKit - a themable/animated widget toolkit for games nuclear@6: Copyright (C) 2014 John Tsiombikas nuclear@6: nuclear@6: This program is free software: you can redistribute it and/or modify nuclear@6: it under the terms of the GNU Lesser General Public License as published by nuclear@6: the Free Software Foundation, either version 3 of the License, or nuclear@6: (at your option) any later version. nuclear@6: nuclear@6: This program is distributed in the hope that it will be useful, nuclear@6: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@6: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@6: GNU Lesser General Public License for more details. nuclear@6: nuclear@6: You should have received a copy of the GNU Lesser General Public License nuclear@6: along with this program. If not, see . nuclear@6: */ nuclear@6: #include nuclear@6: #include nuclear@6: #include nuclear@6: #include nuclear@6: #include nuclear@6: #include "widget.h" nuclear@6: #include "boolanm.h" nuclear@6: #include "theme.h" nuclear@6: #include "screen.h" nuclear@6: nuclear@6: namespace goatkit { nuclear@6: nuclear@6: struct WidgetImpl { nuclear@6: Screen *scr; nuclear@6: std::string name, text; nuclear@6: BBox box; nuclear@6: nuclear@6: BoolAnim visible, active, press, hover, focus; nuclear@6: nuclear@6: struct { nuclear@6: EventCallback func; nuclear@6: void *cls; nuclear@6: } cb[NUM_EVENTS]; nuclear@6: }; nuclear@6: nuclear@6: nuclear@6: Widget::Widget() nuclear@6: { nuclear@6: static int widget_count; nuclear@6: nuclear@6: widget = new WidgetImpl; nuclear@6: widget->scr = 0; nuclear@6: nuclear@6: std::stringstream sstr; nuclear@6: sstr << get_type_name() << widget_count++; nuclear@6: widget->name = sstr.str(); nuclear@6: nuclear@6: widget->box.bmin = Vec2(0, 0); nuclear@6: widget->box.bmax = Vec2(1, 1); nuclear@6: nuclear@6: widget->visible.set(true); nuclear@6: widget->active.set(true); nuclear@6: nuclear@6: widget->hover.set_transition_duration(250); nuclear@6: widget->press.set_transition_duration(50); nuclear@6: nuclear@6: memset(widget->cb, 0, sizeof widget->cb); nuclear@6: } nuclear@6: nuclear@6: Widget::~Widget() nuclear@6: { nuclear@6: delete widget; nuclear@6: } nuclear@6: nuclear@6: void Widget::set_screen(Screen *scr) nuclear@6: { nuclear@6: widget->scr = scr; nuclear@6: } nuclear@6: nuclear@6: Screen *Widget::get_screen() const nuclear@6: { nuclear@6: return widget->scr; nuclear@6: } nuclear@6: nuclear@6: const char *Widget::get_type_name() const nuclear@6: { nuclear@6: return "widget"; nuclear@6: } nuclear@6: nuclear@6: void Widget::set_name(const char *name) nuclear@6: { nuclear@6: widget->name = std::string(name); nuclear@6: } nuclear@6: nuclear@6: const char *Widget::get_name() const nuclear@6: { nuclear@6: return widget->name.c_str(); nuclear@6: } nuclear@6: nuclear@6: void Widget::set_text(const char *text) nuclear@6: { nuclear@6: widget->text = std::string(text); nuclear@6: } nuclear@6: nuclear@6: const char *Widget::get_text() const nuclear@6: { nuclear@6: return widget->text.c_str(); nuclear@6: } nuclear@6: nuclear@6: void Widget::show() nuclear@6: { nuclear@6: widget->visible.change(true); nuclear@6: } nuclear@6: nuclear@6: void Widget::hide() nuclear@6: { nuclear@6: widget->visible.change(false); nuclear@6: } nuclear@6: nuclear@6: float Widget::get_visibility() const nuclear@6: { nuclear@6: return widget->visible.get_value(); nuclear@6: } nuclear@6: nuclear@6: bool Widget::is_visible() const nuclear@6: { nuclear@6: return widget->visible.get_state(); nuclear@6: } nuclear@6: nuclear@14: void Widget::set_visibility_transition(long msec) nuclear@14: { nuclear@14: widget->visible.set_transition_duration(msec); nuclear@14: } nuclear@14: nuclear@14: long Widget::get_visibility_transition() const nuclear@14: { nuclear@14: return widget->visible.get_transition_duration(); nuclear@14: } nuclear@14: nuclear@6: void Widget::activate() nuclear@6: { nuclear@6: widget->active.change(true); nuclear@6: } nuclear@6: nuclear@6: void Widget::deactivate() nuclear@6: { nuclear@6: widget->active.change(false); nuclear@6: } nuclear@6: nuclear@6: float Widget::get_active() const nuclear@6: { nuclear@6: return widget->active.get_value(); nuclear@6: } nuclear@6: nuclear@6: bool Widget::is_active() const nuclear@6: { nuclear@6: return widget->active.get_state(); nuclear@6: } nuclear@6: nuclear@14: void Widget::set_active_transition(long msec) nuclear@14: { nuclear@14: widget->active.set_transition_duration(msec); nuclear@14: } nuclear@14: nuclear@14: long Widget::get_active_transition() const nuclear@14: { nuclear@15: return widget->active.get_transition_duration(); nuclear@14: } nuclear@14: nuclear@6: void Widget::press() nuclear@6: { nuclear@6: widget->press.change(true); nuclear@6: } nuclear@6: nuclear@6: void Widget::release() nuclear@6: { nuclear@6: widget->press.change(false); nuclear@6: } nuclear@6: nuclear@6: float Widget::get_pressed() const nuclear@6: { nuclear@6: return widget->press.get_value(); nuclear@6: } nuclear@6: nuclear@6: bool Widget::is_pressed() const nuclear@6: { nuclear@6: return widget->press.get_state(); nuclear@6: } nuclear@6: nuclear@14: void Widget::set_press_transition(long msec) nuclear@14: { nuclear@14: widget->press.set_transition_duration(msec); nuclear@14: } nuclear@14: nuclear@14: long Widget::get_press_transition() const nuclear@14: { nuclear@14: return widget->press.get_transition_duration(); nuclear@14: } nuclear@14: nuclear@6: void Widget::mousein() nuclear@6: { nuclear@6: widget->hover.change(true); nuclear@6: } nuclear@6: nuclear@6: void Widget::mouseout() nuclear@6: { nuclear@6: widget->hover.change(false); nuclear@6: if(widget->press) { nuclear@6: widget->press.change(false); nuclear@6: } nuclear@6: } nuclear@6: nuclear@6: float Widget::get_under_mouse() const nuclear@6: { nuclear@6: return widget->hover.get_value(); nuclear@6: } nuclear@6: nuclear@6: bool Widget::is_under_mouse() const nuclear@6: { nuclear@6: return widget->hover.get_state(); nuclear@6: } nuclear@6: nuclear@14: void Widget::set_hover_transition(long msec) nuclear@14: { nuclear@14: widget->hover.set_transition_duration(msec); nuclear@14: } nuclear@14: nuclear@14: long Widget::get_hover_transition() const nuclear@14: { nuclear@14: return widget->hover.get_transition_duration(); nuclear@14: } nuclear@14: nuclear@6: bool Widget::can_focus() const nuclear@6: { nuclear@6: return false; nuclear@6: } nuclear@6: nuclear@6: void Widget::focusin() nuclear@6: { nuclear@6: widget->focus.change(true); nuclear@6: } nuclear@6: nuclear@6: void Widget::focusout() nuclear@6: { nuclear@6: widget->focus.change(false); nuclear@6: } nuclear@6: nuclear@6: float Widget::get_focus() const nuclear@6: { nuclear@6: return widget->focus.get_value(); nuclear@6: } nuclear@6: nuclear@6: bool Widget::is_focused() const nuclear@6: { nuclear@6: return widget->focus.get_state(); nuclear@6: } nuclear@6: nuclear@14: void Widget::set_focus_transition(long msec) nuclear@14: { nuclear@14: widget->focus.set_transition_duration(msec); nuclear@14: } nuclear@14: nuclear@14: long Widget::get_focus_transition() const nuclear@14: { nuclear@14: return widget->focus.get_transition_duration(); nuclear@14: } nuclear@14: nuclear@6: void Widget::set_position(float x, float y) nuclear@6: { nuclear@6: set_position(Vec2(x, y)); nuclear@6: } nuclear@6: nuclear@6: void Widget::set_position(const Vec2 &pos) nuclear@6: { nuclear@6: Vec2 sz = get_size(); nuclear@6: nuclear@6: widget->box.bmin = pos; nuclear@6: widget->box.bmax.x = pos.x + sz.x; nuclear@6: widget->box.bmax.y = pos.y + sz.y; nuclear@6: } nuclear@6: nuclear@6: const Vec2 &Widget::get_position() const nuclear@6: { nuclear@6: return widget->box.bmin; nuclear@6: } nuclear@6: nuclear@6: void Widget::set_size(float x, float y) nuclear@6: { nuclear@6: set_size(Vec2(x, y)); nuclear@6: } nuclear@6: nuclear@6: void Widget::set_size(const Vec2 &sz) nuclear@6: { nuclear@6: widget->box.bmax.x = widget->box.bmin.x + sz.x; nuclear@6: widget->box.bmax.y = widget->box.bmin.y + sz.y; nuclear@6: } nuclear@6: nuclear@6: const Vec2 Widget::get_size() const nuclear@6: { nuclear@6: return Vec2(widget->box.bmax.x - widget->box.bmin.x, nuclear@6: widget->box.bmax.y - widget->box.bmin.y); nuclear@6: } nuclear@6: nuclear@6: nuclear@6: const BBox &Widget::get_box() const nuclear@6: { nuclear@6: return widget->box; nuclear@6: } nuclear@6: nuclear@6: bool Widget::hit_test(const Vec2 &pt) const nuclear@6: { nuclear@6: return pt.x >= widget->box.bmin.x && pt.x < widget->box.bmax.x && nuclear@6: pt.y >= widget->box.bmin.y && pt.y < widget->box.bmax.y; nuclear@6: } nuclear@6: nuclear@6: void Widget::draw() const nuclear@6: { nuclear@6: WidgetDrawFunc draw_func = default_draw_func; nuclear@6: nuclear@6: if(theme) { nuclear@6: draw_func = theme->get_draw_func(get_type_name()); nuclear@6: } nuclear@6: nuclear@6: draw_func(this); nuclear@6: } nuclear@6: nuclear@6: // dummy event handlers nuclear@6: void Widget::on_mouse_button(const ButtonEvent &ev) nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: void Widget::on_mouse_motion(const MotionEvent &ev) nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: void Widget::on_mouse_focus(const FocusEvent &ev) nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: void Widget::on_key(const KeyEvent &ev) nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: void Widget::on_click() nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: void Widget::on_double_click() nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: void Widget::on_change() nuclear@6: { nuclear@6: } nuclear@6: nuclear@6: nuclear@6: #define CALL_CB(w, ev) \ nuclear@6: do { \ nuclear@6: if((w)->widget->cb[ev.type].func) { \ nuclear@6: (w)->widget->cb[ev.type].func((w), ev, (w)->widget->cb[ev.type].cls); \ nuclear@6: } \ nuclear@6: } while(0) nuclear@6: nuclear@6: #define CALL_CB_TYPE(w, t) \ nuclear@6: do { \ nuclear@6: Event ev; \ nuclear@6: ev.type = (t); \ nuclear@6: CALL_CB(w, ev); \ nuclear@6: } while(0) nuclear@6: nuclear@6: /* the event dispatcher generates high-level events (click, etc) nuclear@6: * and calls the on_whatever() functions for both low and high-level nuclear@6: * events. nuclear@6: * The on_whatever functions are called *after* any other actions performed nuclear@6: * here, to give subclasses the opportunity to override them easily, by nuclear@6: * overriding the on_ functions, without having to override handle_event itself nuclear@6: */ nuclear@6: // TODO also call callbacks here I guess... nuclear@6: void Widget::handle_event(const Event &ev) nuclear@6: { nuclear@6: switch(ev.type) { nuclear@6: case EV_MOUSE_BUTTON: nuclear@6: if(ev.button.press) { nuclear@6: press(); nuclear@6: } else { nuclear@6: if(is_pressed()) { nuclear@6: CALL_CB_TYPE(this, EV_CLICK); nuclear@6: on_click(); nuclear@6: } nuclear@6: release(); nuclear@6: } nuclear@6: nuclear@6: on_mouse_button(ev.button); nuclear@6: break; nuclear@6: nuclear@6: case EV_MOUSE_MOTION: nuclear@6: on_mouse_motion(ev.motion); nuclear@6: break; nuclear@6: nuclear@6: case EV_MOUSE_FOCUS: nuclear@6: if(ev.focus.enter) { nuclear@6: mousein(); nuclear@6: } else { nuclear@6: mouseout(); nuclear@6: } nuclear@6: on_mouse_focus(ev.focus); nuclear@6: break; nuclear@6: nuclear@6: case EV_KEY: nuclear@6: on_key(ev.key); nuclear@6: break; nuclear@6: nuclear@6: case EV_CHANGE: nuclear@6: on_change(); nuclear@6: break; nuclear@6: nuclear@6: default: nuclear@6: break; nuclear@6: } nuclear@6: nuclear@6: CALL_CB(this, ev); nuclear@6: } nuclear@6: nuclear@6: nuclear@6: void Widget::set_callback(EventType evtype, EventCallback func, void *cls) nuclear@6: { nuclear@6: widget->cb[evtype].func = func; nuclear@6: widget->cb[evtype].cls = cls; nuclear@6: } nuclear@6: nuclear@6: nuclear@6: } // namespace goatkit