diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/Makefile.am | 2 | ||||
-rw-r--r-- | src/ui/font.h | 6 | ||||
-rw-r--r-- | src/ui/iconbutton.cc | 7 | ||||
-rw-r--r-- | src/ui/iconbutton.h | 4 | ||||
-rw-r--r-- | src/ui/label.h | 3 | ||||
-rw-r--r-- | src/ui/toolbar.h | 2 | ||||
-rw-r--r-- | src/ui/tooltip.cc | 90 | ||||
-rw-r--r-- | src/ui/tooltip.h | 80 | ||||
-rw-r--r-- | src/ui/ui.cc | 69 | ||||
-rw-r--r-- | src/ui/ui.h | 17 | ||||
-rw-r--r-- | src/ui/widget.cc | 40 | ||||
-rw-r--r-- | src/ui/widget.h | 100 |
12 files changed, 344 insertions, 76 deletions
diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am index 1485565..875ffcd 100644 --- a/src/ui/Makefile.am +++ b/src/ui/Makefile.am @@ -29,6 +29,7 @@ noinst_HEADERS = \ scrollpane.h \ slider.h \ toolbar.h \ + tooltip.h \ ui.h \ widget.h \ window.h @@ -53,6 +54,7 @@ libui_la_SOURCES = \ scrollpane.cc \ slider.cc \ toolbar.cc \ + tooltip.cc \ ui.cc \ widget.cc \ window.cc diff --git a/src/ui/font.h b/src/ui/font.h index b91120e..be08cfd 100644 --- a/src/ui/font.h +++ b/src/ui/font.h @@ -27,10 +27,16 @@ public: return font_size; } + /** + * @brief width of a single character + * */ inline float width() const { return font_size.width(); } + /** + * @height width of a single character + * */ inline float height() const { return font_size.height(); } diff --git a/src/ui/iconbutton.cc b/src/ui/iconbutton.cc index 18bfb5a..c23ed66 100644 --- a/src/ui/iconbutton.cc +++ b/src/ui/iconbutton.cc @@ -18,14 +18,15 @@ namespace ui { -IconButton::IconButton(Widget *parent, const char *icon, const char *command) : Widget(parent) +IconButton::IconButton(Widget *parent, const char *icon, const char *tooltip, const char *command) : Widget(parent) { set_label("iconbutton"); set_background(false); set_border(false); - set_command(command); - set_icon(icon); set_highlight(false); + set_icon(icon); + set_tooltip(tooltip); + set_command(command); } IconButton::~IconButton() diff --git a/src/ui/iconbutton.h b/src/ui/iconbutton.h index 92e73ee..0382e1a 100644 --- a/src/ui/iconbutton.h +++ b/src/ui/iconbutton.h @@ -15,8 +15,8 @@ namespace ui class IconButton : public Widget { public: - IconButton(Widget *parent, const char *icon = 0, const char *command = 0); - ~IconButton(); + IconButton(Widget *parent, const char *icon = 0, const char *tooltip = 0, const char *command = 0); + virtual ~IconButton(); /// the command this button executes inline const std::string & command() const { diff --git a/src/ui/label.h b/src/ui/label.h index f187520..f187744 100644 --- a/src/ui/label.h +++ b/src/ui/label.h @@ -12,7 +12,6 @@ namespace ui { - /** * @brief a widget displaying a single line of text */ @@ -20,7 +19,7 @@ class Label : public Widget { public: Label(Widget *parent, const char *text = 0); - ~Label(); + virtual ~Label(); /// set the text displayed by the label void set_text(const std::string &text); diff --git a/src/ui/toolbar.h b/src/ui/toolbar.h index baff21b..ccb3d30 100644 --- a/src/ui/toolbar.h +++ b/src/ui/toolbar.h @@ -27,7 +27,7 @@ public: protected: /// re-arrange child widgets - void resize(); + virtual void resize(); }; } diff --git a/src/ui/tooltip.cc b/src/ui/tooltip.cc new file mode 100644 index 0000000..1777d33 --- /dev/null +++ b/src/ui/tooltip.cc @@ -0,0 +1,90 @@ +/* + ui/tooltip.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "ui/tooltip.h" +#include "ui/ui.h" +#include "ui/paint.h" + +#include <cassert> + +namespace ui +{ + +Tooltip *Tooltip::tooltip_global = nullptr; + +Tooltip::Tooltip(Widget *parent) : Label(parent) +{ + set_label("tooltip"); + set_alignment(AlignCenter); + set_background(true); + set_visible(false); +} + +Tooltip::~Tooltip() +{ +} + +void Tooltip::resize() +{ + set_size(font()->width() * text().size() + UI::padding, font()->height() + UI::padding); +} + +void Tooltip::draw_background() +{ + Paint::draw_material(global_location(), size(), "ui/window"); +} + +void Tooltip::draw() +{ + if (tooltip_global != this) + { + hide(); + } else { + Label::draw(); + } +} + +void Tooltip::show() +{ + if ((tooltip_global != nullptr) && (tooltip_global != this)) { + tooltip_global->hide(); + } + tooltip_global = this; + event_resize(); + + Label::show(); +} + +void Tooltip::hide() +{ + if (tooltip_global == this) { + tooltip_global = nullptr; + } + Label::hide(); +} + +void Tooltip::event_draw() +{ +} + +void Tooltip::event_draw_global() +{ + if (tooltip_global && tooltip_global->visible()) + { + if (tooltip_global->background()) + tooltip_global->draw_background(); + + if (tooltip_global->border()) + tooltip_global->draw_border(); + + tooltip_global->draw(); + + if (debug()) + tooltip_global->draw_debug(); + } +} + +} diff --git a/src/ui/tooltip.h b/src/ui/tooltip.h new file mode 100644 index 0000000..629863a --- /dev/null +++ b/src/ui/tooltip.h @@ -0,0 +1,80 @@ +/* + ui/tooltip.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_UI_TOOLTIP_H__ +#define __INCLUDED_UI_TOOLTIP_H__ + +#include "ui/label.h" + +namespace ui +{ + /** + * @brief a widget displaying a tooltip. + * This class makes sure only one Tooltip is globally visible. + */ +class Tooltip : public Label +{ + public: + Tooltip(Widget *parent); + virtual ~Tooltip(); + + /** + * @brief resize the tooltip + * */ + virtual void resize(); + + /** + * @brief show the tooltip + * */ + virtual void show(); + + /** + * @brief hide the tooltip + * */ + virtual void hide(); + + /** + * @brief draw event distributor + * The default draw event distributor is overwritten to do nothing. + * Tooltips are drawn separately because the need to be on top of everything else. + * @see event_draw_global + * */ + virtual void event_draw(); + + /** + * @brief global draw event distributor + * This is called by the user interface to draw the tooltip after everything else. + * */ + static void event_draw_global(); + + /** + * @brief the tooltip that is currently visible, nullptr if no tooltip is currently shown + * + * */ + static inline Tooltip *global() + { + return tooltip_global; + } + + protected: + /** + * @brief draw the tooltip + */ + virtual void draw(); + + /** + * @brief draw the tooltip background + */ + virtual void draw_background(); + + private: + + static Tooltip *tooltip_global; +}; + +} + +#endif // __INCLUDED_UI_TOOLTIP_H__ diff --git a/src/ui/ui.cc b/src/ui/ui.cc index 0877519..95b18e2 100644 --- a/src/ui/ui.cc +++ b/src/ui/ui.cc @@ -4,13 +4,11 @@ the terms of the GNU General Public License version 2 */ -#include <string> -#include <sstream> - #include "audio/audio.h" #include "auxiliary/functions.h" #include "core/core.h" #include "core/application.h" +#include "core/cvar.h" #include "filesystem/filesystem.h" #include "render/gl.h" #include "sys/sys.h" @@ -20,6 +18,10 @@ #include "ui/ui.h" #include "ui/widget.h" +#include <string> +#include <sstream> +#include <cassert> + namespace ui { @@ -32,8 +34,12 @@ math::Vector2f UI::elementsize(256, 32); float UI::padding = 24.0f; float UI::margin = 16.0f; +float UI::icon_small = 24.0f; + float UI::pointer_size = 48.0f; +core::Cvar *ui_tooltiptimeout = nullptr; + UI *global_ui = 0; UI *root() @@ -44,6 +50,9 @@ UI *root() void init() { con_print << "^BInitializing user interface..." << std::endl; + + ui_tooltiptimeout = core::Cvar::get("ui_tooltiptimeout", "250", core::Cvar::Archive); + ui_tooltiptimeout->set_info("[int] time in milliseconds before a tooltip is shown"); if (!global_ui) { global_ui = new UI(); @@ -94,6 +103,7 @@ UI::UI() : Window(0) mouse_pointer_bitmap.assign("pointer"); mouse_buttonleft_pressed = false; + ui_tooltip_timestamp = core::application()->timestamp(); } UI::~UI() @@ -266,7 +276,7 @@ void UI::frame() Widget *f = 0; if (!mouse_buttonleft_pressed) { - f = find_mouse_focus(mouse_cursor); + f = find_widget_in_location(mouse_cursor); } else { f = find_visible_child(ui_mouse_focus); } @@ -275,15 +285,38 @@ void UI::frame() } ui_mouse_focus = f; + // show tooltip if the timeout has expired + if (ui_mouse_focus && ui_mouse_focus->tooltip() && ui_mouse_focus->tooltip()->text().size() && ui_mouse_focus->tooltip()->hidden()) + { + assert(ui_tooltiptimeout != nullptr); + unsigned long timeout = (unsigned long) ui_tooltiptimeout->value(); + + if (ui_tooltip_timestamp + timeout < core::application()->timestamp()) { + // move the tooltip below the mouse cursor + math::Vector2f p( + ui_mouse_focus->tooltip()->parent() ? + ui_mouse_focus->tooltip()->parent()->to_local_coords(mouse_cursor) : + mouse_cursor); + + ui_mouse_focus->tooltip()->set_location(p.x() - ui_mouse_focus->tooltip()->width() * 0.5f, p.y() + pointer_size); + ui_mouse_focus->tooltip()->resize(); + ui_mouse_focus->tooltip()->show(); + } + } + // reset mouse pointer ui::root()->set_pointer("pointer"); // draw the widget stack event_draw(); - - // draw the mouse pointer - if (visible()) + + if (visible()) { + // draw tooltip + Tooltip::event_draw_global(); + + // draw the mouse pointer draw_pointer(); + } } /* -- global event handlers ---------------------------------------- */ @@ -293,6 +326,13 @@ void UI::frame() */ void UI::input_mouse(const float x, const float y) { + // hide tooltip if the mouse has been moved + if (Tooltip::global() && Tooltip::global()->visible()) + { + Tooltip::global()->hide(); + } + ui_tooltip_timestamp = core::application()->timestamp(); + mouse_cursor.assign(x, y); } @@ -300,13 +340,20 @@ bool UI::input_mouse_button(const bool pressed, unsigned int button) { bool handled = false; + // hide tooltip if a mouse button has been clicked + if (Tooltip::global() && Tooltip::global()->visible()) + { + Tooltip::global()->hide(); + } + ui_tooltip_timestamp = core::application()->timestamp(); + if (button == SDL_BUTTON_LEFT) { mouse_buttonleft_pressed = pressed; } // set mouse focus - Widget *f = find_mouse_focus(mouse_cursor); + Widget *f = find_widget_in_location(mouse_cursor); if (f) { f->event_mouse(mouse_cursor); @@ -326,7 +373,7 @@ bool UI::input_mouse_wheel(const math::Vector2f & direction) bool handled = false; // set mouse focus - Widget *f = find_mouse_focus(mouse_cursor); + Widget *f = find_widget_in_location(mouse_cursor); if (f) { f->event_mouse(mouse_cursor); @@ -399,8 +446,8 @@ void UI::draw_pointer() gl::push(); gl::translate(mouse_cursor.x(), mouse_cursor.y(), 0); - float angle = core::application()->time() * 0.75f - floorf(core::application()->time() * 0.75f); - angle *= 360.0f; + const float t = core::application()->time() * 0.75f; + const float angle = (t - floorf(t)) * 360.0f; gl::rotate(angle, math::Vector3f(0, 0, 1.0f)); gl::translate(-mouse_cursor.x(), -mouse_cursor.y(), 0); } diff --git a/src/ui/ui.h b/src/ui/ui.h index aaf4acc..009bed6 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -10,6 +10,7 @@ #include "ui/console.h" #include "ui/font.h" #include "ui/palette.h" +#include "ui/tooltip.h" #include "ui/widget.h" #include "ui/window.h" @@ -64,7 +65,7 @@ public: /// load settings from ui.ini void load_settings(); - /* -- fonts ------------------------------------------------ */ + /* -- font & icon sizes------------------------------------- */ /// default tiny font inline const Font *font_tiny() const { @@ -76,17 +77,18 @@ public: return ui_font_small; } - /// default medium font + /// default large font inline const Font *font_large() const { return ui_font_large; } - + + /* -- mouse pointer ---------------------------------------- */ + + /// current position of the mouse cursor in global window coordinates inline const math::Vector2f & global_mouse_coords() { return mouse_cursor; } - - /* -- mouse pointer ---------------------------------------- */ /// set mouse pointer bitmap void set_pointer(const char *pointerbitmap = 0, const Palette::Color color = Palette::Highlight, const bool animated = false); @@ -109,7 +111,8 @@ public: static float pointer_size; - + static float icon_small; + protected: /* -- event handlers --------------------------------------- */ @@ -138,6 +141,8 @@ private: Palette::Color mouse_pointer_color; bool mouse_pointer_animated; bool mouse_buttonleft_pressed; + + unsigned long ui_tooltip_timestamp; }; /// initialize the user interface diff --git a/src/ui/widget.cc b/src/ui/widget.cc index 982b792..73c454a 100644 --- a/src/ui/widget.cc +++ b/src/ui/widget.cc @@ -9,6 +9,7 @@ #include "ui/paint.h" #include "ui/ui.h" #include "ui/widget.h" +#include "ui/tooltip.h" #include <cassert> @@ -22,8 +23,9 @@ Widget::Widget(Widget *parent) widget_border = true; widget_background = false; widget_enabled = true; - widget_palette = 0; - widget_font = 0; + widget_palette = nullptr; + widget_font = nullptr; + widget_tooltip = nullptr; widget_label.assign("widget"); if (!parent) { @@ -228,17 +230,33 @@ void Widget::set_label(const std::string & label) widget_label.assign(label); aux::to_label(widget_label); } - + void Widget::set_label(const char *label) { - if (label) { + if (label == nullptr) + { + widget_label.clear(); + } else { widget_label.assign(label); aux::to_label(widget_label); - } else { - widget_label.clear(); } } + +void Widget::set_tooltip(const std::string &tooltip_text) +{ + set_tooltip(tooltip_text.c_str()); +} + +void Widget::set_tooltip(const char *tooltip_text) +{ + if (widget_tooltip == nullptr) + { + widget_tooltip = new Tooltip(this); + } + widget_tooltip->set_text(tooltip_text); +} + void Widget::set_palette(const Palette *palette) { widget_palette = palette; @@ -300,7 +318,7 @@ Widget *Widget::next_sibling() // find this widget in the parent's children Children::iterator it = parent()->children().begin(); while (it != parent()->children().end() && ((*it) != this)) { - it++; + it++; } // assert this widget is a child of its parent @@ -384,17 +402,17 @@ Widget *Widget::find_visible_child(const Widget *widget) return 0; } -Widget *Widget::find_mouse_focus(const math::Vector2f & pos) +Widget *Widget::find_widget_in_location(const math::Vector2f & location) { // this widget is not visible - if (!visible() || !size().contains(pos)) - return 0; + if (!visible() || !size().contains(location)) + return nullptr; // reverse-iterate children for (Children::reverse_iterator rit = widget_children.rbegin(); rit != widget_children.rend(); ++rit) { Widget *w = (*rit); if (w->visible() && w->enabled()) { - Widget *f = w->find_mouse_focus(pos - w->location()); + Widget *f = w->find_widget_in_location(location - w->location()); if (f) return f; } diff --git a/src/ui/widget.h b/src/ui/widget.h index 14bea7b..9c7871f 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -23,6 +23,8 @@ namespace ui { +class Tooltip; + class Widget { @@ -129,6 +131,11 @@ public: inline const std::string &label() const { return widget_label; } + + /// widget tooltip + inline Tooltip *tooltip() { + return widget_tooltip; + } /// true if this widget will draw a background inline const bool background() const { @@ -220,29 +227,35 @@ public: /// set location of the top-left corner, relative to the parent void set_location(const math::Vector2f &location); - /// set the widgets width and height + /// set the widget's width and height void set_size(const float w, const float h); - /// set the widgets width and height + /// set the widget's width and height void set_size(const math::Vector2f &size); - /// set the widgets width + /// set the widget's width void set_width(const float w); - /// set the widgets height + /// set the widget's height void set_height(const float h); - /// set the widgets palette + /// set the widget's palette void set_palette(const Palette *palette); - /// set the widgets font + /// set the widget's font void set_font(const Font *font); - /// set the widgets label + /// set the widget's label void set_label(const std::string &label); - /// set the widgets label - void set_label(const char *label); + /// set the widget's label + void set_label(const char *label = nullptr); + + /// set the wdiget's tooltip text + void set_tooltip(const std::string &tooltip_text); + + /// set the wdiget's tooltip text + void set_tooltip(const char *tooltip_text = nullptr); /// enable or disable widget border void set_border(const bool border = true); @@ -262,7 +275,7 @@ public: * @brief calls the draw event handler and sends the event to all child widgets * @see draw **/ - void event_draw(); + virtual void event_draw(); /** * @brief calls the key event handlers and sends unhandled keys to the parent widget @@ -303,37 +316,14 @@ public: inline void emit(const Event event, void *data=0) { event_emit(this, event, data); } - -protected: - - /// find the widget that has input focus - virtual Widget *find_input_focus(); - - /// find widget that has mouse focus - /** @param cursor mouse cursor position relative to this widget's location - */ - Widget *find_mouse_focus(const math::Vector2f & cursor); - /// find a visible widget - Widget *find_visible_child(const Widget *widget); - - /// list widget content - size_t list(const size_t indent, const bool visible_only = false) const; - - /// print widget description - virtual void print(const size_t indent) const; - - /// true of this sibling has local focus - inline bool focus() const { - return widget_focus; - } - + /* -- coordinate mapping ----------------------------------- */ /// map local widget location to global location - inline math::Vector2f global_location() { + inline const math::Vector2f global_location() const { math::Vector2f v(widget_location); - Widget *parent = widget_parent; + const Widget *parent = widget_parent; while (parent) { v += parent->location(); parent = parent->parent(); @@ -342,9 +332,9 @@ protected: } /// map local coordinates to global coordinates - inline math::Vector2f to_global_coords(const math::Vector2f &local) { + inline const math::Vector2f to_global_coords(const math::Vector2f &local) const { math::Vector2f v(local); - Widget *parent = widget_parent; + const Widget *parent = widget_parent; do { v += parent->location(); parent = parent->parent(); @@ -353,9 +343,9 @@ protected: } /// map global coordinates to local coordinates - inline math::Vector2f to_local_coords(const math::Vector2f &global) { + inline const math::Vector2f to_local_coords(const math::Vector2f &global) const { math::Vector2f v(global); - Widget *parent = this; + const Widget *parent = this; while (parent) { v -= parent->location(); parent = parent->parent(); @@ -363,6 +353,34 @@ protected: return v; } +protected: + + /// find the widget that has input focus + virtual Widget *find_input_focus(); + + /** + * @brief find the widget in a given location + * Searches the widget's children tree for the leaf widget visible in a given location. + * If no child contains the location, the widget itself is tested. Returns a nullptr ig + * the location is not within the widget's boundries. + * @param location search position, relative to this widget's location + **/ + Widget *find_widget_in_location(const math::Vector2f &location); + + /// find a visible widget + Widget *find_visible_child(const Widget *widget); + + /// list widget content + size_t list(const size_t indent, const bool visible_only = false) const; + + /// print widget description + virtual void print(const size_t indent) const; + + /// true of this sibling has local focus + inline bool focus() const { + return widget_focus; + } + /* -- event handlers --------------------------------------- */ /// called when the mouse receives mouse movement @@ -437,6 +455,8 @@ private: const Palette *widget_palette; const Font *widget_font; Widget *widget_parent; + + Tooltip *widget_tooltip; Children::iterator find_child(Widget *child); |