diff options
48 files changed, 1032 insertions, 244 deletions
@@ -1,6 +1,9 @@ # ignore build directory /build/ +# ignore libtool's m4 directory +/m4/ + # ignore autoconf/automake related files /autom4te.cache/ /aclocal.m4 diff --git a/Makefile.am b/Makefile.am index 13a6b8e..80f061e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ # not a GNU package. You can remove this line, if # have all needed files, that a GNU package needs AUTOMAKE_OPTIONS = foreign 1.4 +ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src diff --git a/Makefile.git b/Makefile.git index d160702..8092793 100644 --- a/Makefile.git +++ b/Makefile.git @@ -1,8 +1,9 @@ default: all all: - aclocal + libtoolize + aclocal -I m4 autoheader - automake + automake --add-missing autoconf diff --git a/configure.ac b/configure.ac index 52fa82d..15d994c 100644 --- a/configure.ac +++ b/configure.ac @@ -2,15 +2,16 @@ dnl ---------------------------------------------------------------- dnl append git revision to the version number dnl determine the git revision number +dnl note: the configure scripts needs to be regenerated to update the version number define([gitversion], esyscmd([sh -c "echo -n 'git'; git rev-parse --short HEAD | tr -d '\n'"])) -AC_INIT([osirion], [0.2.8-gitversion]) - +AC_INIT([osirion],[0.2.8-gitversion]) +AC_CONFIG_MACRO_DIRS([m4]) AC_CONFIG_HEADERS(src/config.h) -AC_LANG_CPLUSPLUS +AC_LANG([C++]) AC_PROG_CXX -AC_PROG_LIBTOOL +LT_INIT AM_INIT_AUTOMAKE @@ -147,7 +148,7 @@ dnl AC_MSG_CHECKING(whether to abort on compiler warnings) WARN_CFLAGS="$WARN_CLFAGS -Wall" AC_ARG_ENABLE(error, - AC_HELP_STRING([--disable-error], [do not abort on compiler warnings]), + AS_HELP_STRING([--disable-error],[do not abort on compiler warnings]), AC_MSG_RESULT(no), AC_MSG_RESULT(yes) WARN_CFLAGS="$WARN_CFLAGS -Werror" @@ -160,7 +161,7 @@ dnl AC_MSG_CHECKING(whether to include debug messages) AC_ARG_ENABLE(debug_messages, - AC_HELP_STRING([--disable-debug-messages], [build without extra debug messages]), + AS_HELP_STRING([--disable-debug-messages],[build without extra debug messages]), AC_MSG_RESULT(no), AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED(HAVE_DEBUG_MESSAGES, 1, [Define this to enable debug messages]) @@ -171,7 +172,7 @@ dnl static libc dnl AC_ARG_ENABLE(static_stdlib, - AC_HELP_STRING([--enable-static-stdlib], [staticly link with libgcc and libstdc++]), + AS_HELP_STRING([--enable-static-stdlib],[staticly link with libgcc and libstdc++]), AC_MSG_RESULT(yes) STATIC_LDADD="-static-libgcc -static-libstdc++", AC_MSG_RESULT(no) @@ -187,9 +188,7 @@ dnl HAVE_CURSES=no AC_ARG_WITH(curses, - AC_HELP_STRING([--without-curses], - [do not include curses support] - ) + AS_HELP_STRING([--without-curses],[do not include curses support]) ) if test "x${with_curses}" != "xno"; then @@ -245,15 +244,13 @@ dnl HAVE_BULLET=no AC_ARG_ENABLE(static_bullet, - AC_HELP_STRING([--enable-static-bullet], [staticly link with bullet physics libraries]), + AS_HELP_STRING([--enable-static-bullet],[staticly link with bullet physics libraries]), AC_MSG_RESULT(yes), AC_MSG_RESULT(no) ) AC_ARG_WITH(bullet, - AC_HELP_STRING([--with-bullet=PREFIX], - [link with bullet physics library installed in PREFIX] - ) + AS_HELP_STRING([--with-bullet=PREFIX],[link with bullet physics library installed in PREFIX]) ) LDFLAGS_save="$LDFLAGS" @@ -321,7 +318,7 @@ dnl BUILD_CLIENT=no AC_ARG_WITH(client, - AC_HELP_STRING([--without-client], [do not build the client application]) + AS_HELP_STRING([--without-client],[do not build the client application]) ) AC_MSG_CHECKING(whether to build client) @@ -491,9 +488,7 @@ dnl Installation paths AC_MSG_CHECKING(installation type) AC_ARG_ENABLE(single_directory, - AC_HELP_STRING( - [--enable-single-directory], [install everything in a single directory] - ), + AS_HELP_STRING([--enable-single-directory],[install everything in a single directory]), AC_MSG_RESULT(single directory) INSTALLTYPE="single directory", AC_MSG_RESULT(standard) diff --git a/package/aur/PKGBUILD.osirion b/package/aur/PKGBUILD.osirion new file mode 100644 index 0000000..c675e0c --- /dev/null +++ b/package/aur/PKGBUILD.osirion @@ -0,0 +1,48 @@ +# +# Maintainer: Stijn Buys <ingar@telenet.be> +# + +pkgname=osirion +pkgver=0.2.8 +pkgrel=1 +pkgdesc="A free space trading and combat simulation with open gameplay" +url="http://osirion.org/" +arch=('x86_64' 'armv7h') +license=('GPL') +depends=('zlib' 'bullet' 'libjpeg' 'libpng' 'libvorbis' 'libogg' 'flac' 'sdl2' 'libgl' 'glu' 'openal') +makedepends=('automake' 'autoconf') +optdepends=(i'ncurses') +provides=('osirion') +conflicts=('osirion-server' 'osirion-git' 'osirion-server-git') +backup=() +options=() +source=("http://osirion.org/files/${pkgname}-${pkgver}-src.tar.bz2" + "http://osirion.org/files/${pkgname}-${pkgver}-data.zip") +sha256sums=('2b164ad11e2907eafb2200585afd400bdfe6eacd94d4c40a29c5750e4a2ccc6d' + 'dfa2e9ed814c03bcc0f7b4b14f5e981a6762762306ac131adb57c6a190512c74') + +prepare() { + cd "${srcdir}/${pkgname}-${pkgver}-src" + +} + +build() { + cd "${srcdir}/${pkgname}-${pkgver}-src" + + ./configure \ + --prefix=/usr + + ./configure \ + --prefix=/usr \ + --disable-debug + make +} + +package() { + cd "${srcdir}/${pkgname}-${pkgver}-src" + make DESTDIR="${pkgdir}" install + + cd "${srcdir}" + install -D --mode=644 --directory "${srcdir}/data" "${pkgdir}/usr/share/osirion/data" +} + diff --git a/src/client/Makefile.am b/src/client/Makefile.am index 391892a..fbb075f 100644 --- a/src/client/Makefile.am +++ b/src/client/Makefile.am @@ -16,6 +16,7 @@ noinst_HEADERS = \ chat.h \ client.h \ clientext.h \ + closeiconbutton.h \ control.h \ controlsettingsmenu.h \ entitymenu.h \ @@ -42,6 +43,7 @@ noinst_HEADERS = \ playersettingsmenu.h \ reputationwindow.h \ savegamemenu.h \ + serverlistmenu.h \ soundext.h \ targeticonbutton.h \ targets.h \ @@ -57,6 +59,7 @@ libclient_la_SOURCES = \ chat.cc \ client.cc \ clientext.cc \ + closeiconbutton.cc \ control.cc \ controlsettingsmenu.cc \ entitymenu.cc \ @@ -83,6 +86,7 @@ libclient_la_SOURCES = \ playersettingsmenu.cc \ reputationwindow.cc \ savegamemenu.cc \ + serverlistmenu.cc \ soundext.cc \ targeticonbutton.cc \ targets.cc \ diff --git a/src/client/audiosettingsmenu.cc b/src/client/audiosettingsmenu.cc index b09586a..13e6d57 100644 --- a/src/client/audiosettingsmenu.cc +++ b/src/client/audiosettingsmenu.cc @@ -5,6 +5,7 @@ */ #include "client/audiosettingsmenu.h" +#include "client/closeiconbutton.h" #include "core/cvar.h" #include "ui/button.h" #include "ui/checkbox.h" @@ -36,7 +37,7 @@ AudioSettingsMenu::AudioSettingsMenu(ui::Widget *parent, const char *label) : ui _titlelabel->set_text("AUDIO SETTINGS"); // close button - _closebutton = new ui::IconButton(_titlelabel, "bitmaps/icons/window_close"); + _closebutton = new CloseIconButton(_titlelabel); // content frame _frame = new ui::Widget(this); diff --git a/src/client/chat.cc b/src/client/chat.cc index dd71d57..f921e79 100644 --- a/src/client/chat.cc +++ b/src/client/chat.cc @@ -7,6 +7,7 @@ #include "auxiliary/functions.h" #include "client/chat.h" #include "client/client.h" +#include "client/closeiconbutton.h" #include "core/core.h" #include "core/gameinterface.h" #include "sys/sys.h" @@ -35,7 +36,7 @@ Chat::Chat(ui::Widget *parent) : ui::Window(parent) chat_titlelabel->set_text("CHAT"); // close button - chat_closebutton = new ui::IconButton(chat_titlelabel, "bitmaps/icons/window_close"); + chat_closebutton = new CloseIconButton(chat_titlelabel); // player list chat_playerlist = new ui::ListView(this); diff --git a/src/client/closeiconbutton.cc b/src/client/closeiconbutton.cc new file mode 100644 index 0000000..82c37f0 --- /dev/null +++ b/src/client/closeiconbutton.cc @@ -0,0 +1,22 @@ +/* + client/closeiconbutton.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "client/closeiconbutton.h" + +namespace client +{ + +CloseIconButton::CloseIconButton(Widget *parent) : ui::IconButton(parent, "bitmaps/icons/window_close", "Close") +{ + set_label("close"); +} + +CloseIconButton::~CloseIconButton() +{ +} + +} + diff --git a/src/client/closeiconbutton.h b/src/client/closeiconbutton.h new file mode 100644 index 0000000..a822731 --- /dev/null +++ b/src/client/closeiconbutton.h @@ -0,0 +1,28 @@ +/* + client/closeiconbutton.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_CLIENT_CLOSEICONBUTTON_H__ +#define __INCLUDED_CLIENT_CLOSEICONBUTTON_H__ + +#include "ui/iconbutton.h" + +namespace client +{ + +class CloseIconButton : public ui::IconButton +{ + +public: + /** + * @brief Default close button + * */ + CloseIconButton(Widget *parent); + virtual ~CloseIconButton(); +}; + +} + +#endif // __INCLUDED_CLIENT_CLOSEICONBUTTON_H__ diff --git a/src/client/controlsettingsmenu.cc b/src/client/controlsettingsmenu.cc index 4417bf3..bd60fb4 100644 --- a/src/client/controlsettingsmenu.cc +++ b/src/client/controlsettingsmenu.cc @@ -5,6 +5,7 @@ */ #include "client/controlsettingsmenu.h" +#include "client/closeiconbutton.h" #include "client/input.h" #include "ui/button.h" #include "ui/iconbutton.h" @@ -278,7 +279,7 @@ ControlSettingsMenu::ControlSettingsMenu(ui::Widget *parent, const char *label) _titlelabel->set_text("CONTROLS"); // close button - _closebutton = new ui::IconButton(_titlelabel, "bitmaps/icons/window_close"); + _closebutton = new CloseIconButton(_titlelabel); // command listview _controlslistview = new ui::ListView(this); diff --git a/src/client/galaxymapwidget.cc b/src/client/galaxymapwidget.cc index 25460b8..4bcf7c5 100644 --- a/src/client/galaxymapwidget.cc +++ b/src/client/galaxymapwidget.cc @@ -19,6 +19,9 @@ GalaxyMapWidget::GalaxyMapWidget(ui::Widget *parent) : ui::Widget(parent) galaxymapwidget_zone = 0; galaxymapwidget_hover_id = 0; galaxymapwidget_zoom = 1.0f; + + // make sure the map has a tooltip + set_tooltip(); } GalaxyMapWidget::~GalaxyMapWidget() @@ -112,11 +115,13 @@ void GalaxyMapWidget::draw() // global mouse cursor location const math::Vector2f cursor(ui::root()->global_mouse_coords()); galaxymapwidget_hover_id = 0; + set_tooltip(); // map center //math::Vector2f map_center(map_location[0] + map_size / 2.0f, map_location[1] + map_size / 2.0f); math::Vector2f icon_location; - const float r = 12.0f; // radius of map icons + const float r = 12.0f; // radius of map icons + const float rt = 24.0f; // radius of target icons float scale = 1024.0f; // galaxy size (in zone location units) math::Color color(1.0f, 1.0f, 1.0f, 1.0f); const core::Zone *zone = 0; @@ -149,6 +154,7 @@ void GalaxyMapWidget::draw() if (math::distancesquared(cursor, icon_location) < (r*r)) { galaxymapwidget_hover_id = zone->id(); + set_tooltip(zone->name()); } if (zone == galaxymapwidget_zone) @@ -180,6 +186,37 @@ void GalaxyMapWidget::draw() } gl::end(); + + if (galaxymapwidget_zone) + { + icon_location.assign(map_location); + icon_location[0] += map_size / scale * galaxymapwidget_zone->location().x(); + icon_location[1] += map_size - map_size / scale * galaxymapwidget_zone->location().y(); // flip vertically + + // draw target icon + const size_t texture_target = render::Textures::load("bitmaps/icons/map_target"); + render::Textures::bind(texture_target); + + gl::begin(gl::Quads); + + math::Color color(1.0); + gl::color(color); + + glTexCoord2f(0.0f, 0.0f); + gl::vertex(icon_location.x() - rt, icon_location.y() - rt); + + glTexCoord2f(1.0f, 0.0f); + gl::vertex(icon_location.x() + rt, icon_location.y() - rt); + + glTexCoord2f(1.0f, 1.0f); + gl::vertex(icon_location.x() + rt, icon_location.y() + rt); + + glTexCoord2f(0.0f, 1.0f); + gl::vertex(icon_location.x() - rt, icon_location.y() + rt); + + gl::end(); + } + gl::disable(GL_TEXTURE_2D); // if (has_mouse_focus()) { diff --git a/src/client/gamewindow.cc b/src/client/gamewindow.cc index 0b39cea..139755f 100644 --- a/src/client/gamewindow.cc +++ b/src/client/gamewindow.cc @@ -54,20 +54,20 @@ GameWindow::GameWindow(ui::Widget *parent) : ui::Window(parent) gamewindow_chat = new Chat(this); // icon buttons - gamewindow_menubutton = new ui::IconButton(this, "bitmaps/icons/button_menu", "ui_menu"); - - gamewindow_launchbutton = new ui::IconButton(this, "bitmaps/icons/button_launch", "launch"); + gamewindow_menubutton = new ui::IconButton(this, "bitmaps/icons/button_menu", "Menu", "ui_menu"); + gamewindow_launchbutton = new ui::IconButton(this, "bitmaps/icons/button_launch", "Launch", "launch"); + gamewindow_freeflightbutton = new ui::IconButton(this, "bitmaps/icons/button_freeflight", "Cancel Autopilot", "freeflight"); - gamewindow_freeflightbutton = new ui::IconButton(this, "bitmaps/icons/button_freeflight", "freeflight"); - gamewindow_gotobutton = new TargetIconButton(this, "bitmaps/icons/button_goto", "goto"); - gamewindow_dockbutton = new TargetIconButton(this, "bitmaps/icons/button_dock", "dock", core::Entity::Dockable); - gamewindow_formationbutton = new TargetIconButton(this, "bitmaps/icons/button_formation", "formation"); + gamewindow_gotobutton = new TargetIconButton(this, "bitmaps/icons/button_goto", "Goto", "goto"); + gamewindow_dockbutton = new TargetIconButton(this, "bitmaps/icons/button_dock", "Dock", "dock", core::Entity::Dockable); + gamewindow_formationbutton = new TargetIconButton(this, "bitmaps/icons/button_formation", "Formation", "formation"); + gamewindow_formationbutton->set_tooltip("Formation"); - gamewindow_homebutton = new ui::IconButton(this, "bitmaps/icons/button_home", "view main"); - gamewindow_chatbutton = new ui::IconButton(this, "bitmaps/icons/button_chat", "ui_chat"); - gamewindow_mapbutton = new ui::IconButton(this, "bitmaps/icons/button_map", "ui_map"); - gamewindow_inventorybutton = new ui::IconButton(this, "bitmaps/icons/button_ship", "ui_inventory"); - gamewindow_reputationbutton = new ui::IconButton(this, "bitmaps/icons/button_reputation", "ui_reputation"); + gamewindow_homebutton = new ui::IconButton(this, "bitmaps/icons/button_home", "Home", "view main"); + gamewindow_chatbutton = new ui::IconButton(this, "bitmaps/icons/button_chat", "Chat", "ui_chat"); + gamewindow_mapbutton = new ui::IconButton(this, "bitmaps/icons/button_map", "Map", "ui_map"); + gamewindow_inventorybutton = new ui::IconButton(this, "bitmaps/icons/button_ship", "Inventory", "ui_inventory"); + gamewindow_reputationbutton = new ui::IconButton(this, "bitmaps/icons/button_reputation", "Reputation", "ui_reputation"); } GameWindow::~GameWindow() diff --git a/src/client/graphicssettingsmenu.cc b/src/client/graphicssettingsmenu.cc index ab05635..716570a 100644 --- a/src/client/graphicssettingsmenu.cc +++ b/src/client/graphicssettingsmenu.cc @@ -5,6 +5,7 @@ */ #include "client/graphicssettingsmenu.h" +#include "client/closeiconbutton.h" #include "core/cvar.h" #include "ui/button.h" #include "ui/checkbox.h" @@ -34,7 +35,7 @@ GraphicsSettingsMenu::GraphicsSettingsMenu(ui::Widget *parent, const char *label _titlelabel->set_text("GRAPHICS SETTINGS"); // close button - _closebutton = new ui::IconButton(_titlelabel, "bitmaps/icons/window_close"); + _closebutton = new CloseIconButton(_titlelabel); // content frame _frame = new ui::Widget(this); diff --git a/src/client/hudenginestatus.cc b/src/client/hudenginestatus.cc index ab51709..bae1f3f 100644 --- a/src/client/hudenginestatus.cc +++ b/src/client/hudenginestatus.cc @@ -6,6 +6,7 @@ #include "client/input.h" #include "client/hudenginestatus.h" +#include "client/closeiconbutton.h" #include "core/core.h" #include "core/application.h" #include "ui/ui.h" @@ -24,11 +25,11 @@ HUDEngineStatus::HUDEngineStatus(ui::Widget *parent) : ui::Widget(parent) set_background(false); _button_auto_palette = new ui::Palette(*palette()); - _button_auto = new ui::IconButton(this, "bitmaps/hud/button_auto", "freeflight"); + _button_auto = new ui::IconButton(this, "bitmaps/hud/button_auto", "Cancel autopilot", "freeflight"); _button_auto->set_palette(_button_auto_palette); _button_lock_palette = new ui::Palette(*palette()); - _button_lock = new ui::IconButton(this, "bitmaps/hud/button_lock", "ui_control"); + _button_lock = new ui::IconButton(this, "bitmaps/hud/button_lock", "Lock control", "ui_control"); _button_lock->set_palette(_button_lock_palette); } @@ -115,10 +116,12 @@ void HUDEngineStatus::draw() if (control_lock) { _button_lock_palette->set_foreground(math::Color(1.0f, 0.0f, 0.0f)); + _button_lock->set_tooltip("Unlock control"); } else { // free controls _button_lock_palette->set_foreground(palette()->foreground()); + _button_lock->set_tooltip("Lock control"); } // health bar size diff --git a/src/client/inventorywindow.cc b/src/client/inventorywindow.cc index de27d13..09bd71e 100644 --- a/src/client/inventorywindow.cc +++ b/src/client/inventorywindow.cc @@ -6,6 +6,7 @@ #include "core/application.h" #include "client/inventorywindow.h" +#include "client/closeiconbutton.h" #include "ui/label.h" #include "ui/bitmap.h" #include "ui/listitem.h" @@ -63,9 +64,8 @@ InventoryWindow::InventoryWindow(ui::Widget *parent) : ui::Window(parent) inventorywindow_inventorytext->set_font(ui::root()->font_small()); inventorywindow_inventorytext->set_alignment(ui::AlignLeft | ui::AlignTop); - inventorywindow_shipbutton = new ui::IconButton(this, "bitmaps/icons/button_ship"); - inventorywindow_ejectbutton = new ui::IconButton(this, "bitmaps/icons/button_eject"); - //inventorywindow_mountbutton = new ui::IconButton(this, "bitmaps/icons/button_mount"); + inventorywindow_shipbutton = new ui::IconButton(this, "bitmaps/icons/button_ship", "Ship info"); + inventorywindow_ejectbutton = new ui::IconButton(this, "bitmaps/icons/button_eject", "Eject cargo"); // window title inventorywindow_titlelabel = new ui::Label(this); @@ -77,7 +77,7 @@ InventoryWindow::InventoryWindow(ui::Widget *parent) : ui::Window(parent) inventorywindow_titlelabel->set_text("INVENTORY"); // close button - inventorywindow_closebutton = new ui::IconButton(inventorywindow_titlelabel, "bitmaps/icons/window_close"); + inventorywindow_closebutton = new CloseIconButton(inventorywindow_titlelabel); // eject dialog inventorywindow_ejectconfirmbutton = new ui::Button(inventorywindow_scrollpane, "Eject"); @@ -146,24 +146,26 @@ void InventoryWindow::update_inventory() listitem->set_sortkey(sortkey); if (item->has_flag(core::Item::Mounted)) { - // unmount button - ui::IconButton *ismounted_button = new ui::IconButton(listitem, "bitmaps/icons/button_mount"); + // wepaon is mounted, show the unmount button + ui::IconButton *ismounted_button = new ui::IconButton(listitem, "bitmaps/icons/button_unmount"); std::ostringstream strcmd; strcmd << "remote mount " << item->id(); ismounted_button->set_command(strcmd.str()); ismounted_button->set_background(true); ismounted_button->set_size(icon_size, icon_size); ismounted_button->set_location(ui::UI::elementsize.width() - icon_size - padding, padding); + ismounted_button->set_tooltip("Unmount weapon"); } else if (item->has_flag(core::Item::Mountable)) { - // mount button - ui::IconButton *isunmounted_button = new ui::IconButton(listitem, "bitmaps/icons/button_unmount"); + // weapon is not mounted, show mount button + ui::IconButton *isunmounted_button = new ui::IconButton(listitem, "bitmaps/icons/button_mount"); std::ostringstream strcmd; strcmd << "remote mount " << item->id(); isunmounted_button->set_command(strcmd.str()); isunmounted_button->set_background(true); isunmounted_button->set_size(icon_size, icon_size); isunmounted_button->set_location(ui::UI::elementsize.width() - icon_size - padding, padding); + isunmounted_button->set_tooltip("Mount weapon"); } // preserve previous selection during update diff --git a/src/client/keyboard.cc b/src/client/keyboard.cc index fe6496f..37c6fc6 100644 --- a/src/client/keyboard.cc +++ b/src/client/keyboard.cc @@ -153,7 +153,7 @@ Keyboard::Keyboard() add_key(SDL_SCANCODE_KP_EQUALS, "kpequal", '='); add_key(SDL_SCANCODE_KP_LESS, "kpless", '<'); - add_key(SDL_SCANCODE_LEFT, "left", 0, "+ lookleft"); + add_key(SDL_SCANCODE_LEFT, "left", 0, "+lookleft"); add_key(SDL_SCANCODE_RIGHT, "right", 0, "+lookright"); add_key(SDL_SCANCODE_UP, "up", 0, "+lookup"); add_key(SDL_SCANCODE_DOWN, "down", 0, "+lookdown"); diff --git a/src/client/mainmenu.cc b/src/client/mainmenu.cc index 823ebfb..0e61655 100644 --- a/src/client/mainmenu.cc +++ b/src/client/mainmenu.cc @@ -11,6 +11,7 @@ #include "client/mainmenu.h" #include "client/playersettingsmenu.h" #include "client/savegamemenu.h" +#include "client/serverlistmenu.h" #include "core/core.h" #include "core/gameinterface.h" #include "core/application.h" @@ -218,7 +219,7 @@ void MainMenu::load() mainmenu_optionsmenu = 0; // menu to connect to remote servers - mainmenu_connectmenu = 0; + mainmenu_connectmenu = (ui::Widget *) new ServerListMenu(this, "connect"); // load game menu mainmenu_loadmenu = (ui::Widget *) new SaveGameMenu(this, "load", SaveGameMenu::Load); diff --git a/src/client/mapwidget.cc b/src/client/mapwidget.cc index 20bb979..49100b2 100644 --- a/src/client/mapwidget.cc +++ b/src/client/mapwidget.cc @@ -24,6 +24,9 @@ MapWidget::MapWidget(ui::Widget *parent) : ui::Widget(parent) mapwidget_target = 0; mapwidget_hover_id = 0; mapwidget_zoom = 1.0f; + + // make sure the map has a tooltip + set_tooltip(); } MapWidget::~MapWidget() @@ -43,6 +46,12 @@ void MapWidget::set_target(const core::Entity *entity) void MapWidget::set_zone(core::Zone *zone) { mapwidget_zone = zone; + + // verify current target + if (mapwidget_target && (mapwidget_zone != mapwidget_target->zone())) + { + mapwidget_target = nullptr; + } } bool MapWidget::on_mousepress(const unsigned int button) @@ -124,11 +133,13 @@ void MapWidget::draw() // global mouse cursor location const math::Vector2f cursor(ui::root()->global_mouse_coords()); mapwidget_hover_id = 0; + set_tooltip(); // map center math::Vector2f map_center(map_location[0] + map_size / 2.0f, map_location[1] + map_size / 2.0f); math::Vector2f icon_location; const float r = 12.0f; // radius of map icons + const float rt = 24.0f; // radius of target icons float scale = 4096.0f; // map size in game units math::Color color; @@ -154,6 +165,7 @@ void MapWidget::draw() if (math::distancesquared(cursor, icon_location) < (r*r)) { mapwidget_hover_id = entity->id(); + set_tooltip(entity->name()); } if (entity == mapwidget_target) @@ -264,6 +276,43 @@ void MapWidget::draw() } gl::end(); + + if (mapwidget_target) + { + // draw target icon + size_t texture_target = 0; + if (mapwidget_target->has_flag(core::Entity::Dockable)) + { + texture_target = render::Textures::load("bitmaps/icons/map_dockable"); + } else { + texture_target = render::Textures::load("bitmaps/icons/map_target"); + } + + icon_location.assign(map_center); + icon_location[0] -= map_size / scale * mapwidget_target->location().y(); + icon_location[1] -= map_size / scale * mapwidget_target->location().x(); + + texture_current = render::Textures::bind(texture_target); + + gl::begin(gl::Quads); + + math::Color color(1.0); + gl::color(color); + + glTexCoord2f(0.0f, 0.0f); + gl::vertex(icon_location.x() - rt, icon_location.y() - rt); + + glTexCoord2f(1.0f, 0.0f); + gl::vertex(icon_location.x() + rt, icon_location.y() - rt); + + glTexCoord2f(1.0f, 1.0f); + gl::vertex(icon_location.x() + rt, icon_location.y() + rt); + + glTexCoord2f(0.0f, 1.0f); + gl::vertex(icon_location.x() - rt, icon_location.y() + rt); + + gl::end(); + } // draw localcontrol icon // TODO draw a ship icon diff --git a/src/client/mapwindow.cc b/src/client/mapwindow.cc index 3dbb1fb..23620f8 100644 --- a/src/client/mapwindow.cc +++ b/src/client/mapwindow.cc @@ -5,18 +5,23 @@ the terms of the GNU General Public License version 2 */ -#include "audio/audio.h" -#include "core/application.h" -#include "core/entity.h" -#include "core/entityglobe.h" #include "client/mapwindow.h" #include "client/targets.h" +#include "client/closeiconbutton.h" + #include "ui/ui.h" #include "ui/paint.h" + #include "render/gl.h" #include "render/textures.h" #include "render/text.h" +#include "audio/audio.h" + +#include "core/application.h" +#include "core/entity.h" +#include "core/entityglobe.h" + namespace client { @@ -82,8 +87,9 @@ MapWindow::MapWindow(ui::Widget *parent) : ui::Window(parent) mapwindow_maplabel->set_alignment(ui::AlignCenter); // map buttons - mapwindow_zonebutton = new ui::IconButton(mapwindow_pane_left, "bitmaps/icons/button_map"); - mapwindow_galaxybutton = new ui::IconButton(mapwindow_pane_left, "bitmaps/icons/button_galaxy"); + mapwindow_zonebutton = new ui::IconButton(mapwindow_pane_left, "bitmaps/icons/button_map", "Zone map"); + mapwindow_galaxybutton = new ui::IconButton(mapwindow_pane_left, "bitmaps/icons/button_galaxy", "Galaxy map"); + mapwindow_galaxybutton->hide(); // ---- window title mapwindow_titlelabel = new ui::Label(this); @@ -95,10 +101,8 @@ MapWindow::MapWindow(ui::Widget *parent) : ui::Window(parent) mapwindow_titlelabel->set_text("STAR CHART"); // close button - mapwindow_closebutton = new ui::IconButton(mapwindow_titlelabel, "bitmaps/icons/window_close"); + mapwindow_closebutton = new CloseIconButton(mapwindow_titlelabel); - - hide(); } @@ -118,6 +122,8 @@ void MapWindow::show() // show map widget mapwindow_mode = ShowZone; mapwindow_galaxymapwidget->hide(); + mapwindow_galaxybutton->show(); + mapwindow_zonebutton->hide(); mapwindow_mapwidget->show(); mapwindow_mapwidget->set_zone(core::localplayer()->zone()); mapwindow_maplabel->set_text(mapwindow_mapwidget->zone()->name()); @@ -174,10 +180,10 @@ void MapWindow::resize() float l = (mapwindow_mapwidget->width() - math::min(mapwindow_mapwidget->width(), mapwindow_mapwidget->height())) * 0.5f; mapwindow_zonebutton->set_size(icon_size, icon_size); - mapwindow_zonebutton->set_location(mapwindow_maplabel->left() + l, mapwindow_mapwidget->bottom() + margin_vertical); + mapwindow_zonebutton->set_location(mapwindow_maplabel->right() - l - icon_size, mapwindow_mapwidget->bottom() + margin_vertical); mapwindow_galaxybutton->set_size(icon_size, icon_size); - mapwindow_galaxybutton->set_location(mapwindow_maplabel->right() - l - icon_size, mapwindow_mapwidget->bottom() + margin_vertical); + mapwindow_galaxybutton->set_location(mapwindow_zonebutton->location()); } // resize right pane @@ -363,8 +369,10 @@ bool MapWindow::on_emit(ui::Widget *sender, const ui::Widget::Event event, void if (mapwindow_galaxymapwidget->zone()) { mapwindow_mode = ShowZone; mapwindow_galaxymapwidget->hide(); + mapwindow_galaxybutton->show(); + mapwindow_zonebutton->hide(); mapwindow_mapwidget->set_zone(mapwindow_galaxymapwidget->zone()); - mapwindow_mapwidget->show(); + mapwindow_mapwidget->show(); mapwindow_maplabel->set_text(mapwindow_mapwidget->zone()->name()); show_zone_info(mapwindow_mapwidget->zone()); } @@ -387,6 +395,8 @@ bool MapWindow::on_emit(ui::Widget *sender, const ui::Widget::Event event, void mapwindow_galaxymapwidget->hide(); mapwindow_maplabel->set_text(mapwindow_mapwidget->zone()->name()); } + mapwindow_galaxybutton->show(); + mapwindow_zonebutton->hide(); return true; } @@ -403,6 +413,8 @@ bool MapWindow::on_emit(ui::Widget *sender, const ui::Widget::Event event, void show_zone_info(mapwindow_galaxymapwidget->zone()); mapwindow_maplabel->set_text("Starsystems"); } + mapwindow_galaxybutton->hide(); + mapwindow_zonebutton->show(); return true; } } diff --git a/src/client/playersettingsmenu.cc b/src/client/playersettingsmenu.cc index b8992cb..d09b269 100644 --- a/src/client/playersettingsmenu.cc +++ b/src/client/playersettingsmenu.cc @@ -5,6 +5,7 @@ */ #include "client/playersettingsmenu.h" +#include "client/closeiconbutton.h" #include "core/cvar.h" #include "ui/button.h" #include "ui/colorpicker.h" @@ -34,7 +35,7 @@ PlayerSettingsMenu::PlayerSettingsMenu(ui::Widget *parent, const char *label) : _titlelabel->set_text("PLAYER SETTINGS"); // close button - _closebutton = new ui::IconButton(_titlelabel, "bitmaps/icons/window_close"); + _closebutton = new CloseIconButton(_titlelabel); // content frame _frame = new ui::Widget(this); diff --git a/src/client/reputationwindow.cc b/src/client/reputationwindow.cc index e60b384..cbda138 100644 --- a/src/client/reputationwindow.cc +++ b/src/client/reputationwindow.cc @@ -4,14 +4,14 @@ the terms of the GNU General Public License version 2 */ -#include <iomanip> - -#include "ui/paint.h" -#include "ui/ui.h" #include "client/client.h" #include "client/reputationwindow.h" +#include "client/closeiconbutton.h" +#include "ui/paint.h" +#include "ui/ui.h" #include "core/reputation.h" #include "core/range.h" +#include <iomanip> namespace client { @@ -110,7 +110,7 @@ ReputationWindow::ReputationWindow(ui::Widget *parent) : ui::Window(parent) reputationwindow_titlelabel->set_text("REPUTATION"); // close button - reputationwindow_closebutton = new ui::IconButton(reputationwindow_titlelabel, "bitmaps/icons/window_close"); + reputationwindow_closebutton = new CloseIconButton(reputationwindow_titlelabel); // reputation listview reputationwindow_listview = new ui::ListView(this); diff --git a/src/client/savegamemenu.cc b/src/client/savegamemenu.cc index 1b47101..aa43087 100644 --- a/src/client/savegamemenu.cc +++ b/src/client/savegamemenu.cc @@ -4,25 +4,31 @@ the terms of the GNU General Public License version 2 */ -#include <iomanip> -#include <cstdio> - -#include "core/application.h" -#include "core/gameinterface.h" -#include "core/gameserver.h" -#include "model/material.h" #include "client/savegamemenu.h" +#include "client/closeiconbutton.h" #include "client/video.h" -#include "filesystem/filesystem.h" + +#include "ui/ui.h" + +#include "model/material.h" + #include "render/screenshot.h" #include "render/camera.h" #include "render/draw.h" #include "render/textures.h" #include "render/gl.h" -#include "ui/ui.h" +#include "filesystem/filesystem.h" + +#include "core/application.h" +#include "core/gameinterface.h" +#include "core/gameserver.h" + #include "sys/sys.h" +#include <iomanip> +#include <cstdio> + namespace client { @@ -50,9 +56,8 @@ SaveGameMenu::SaveGameMenu(ui::Widget *parent, const char *label, const Mode mod } // close button - savegamemenu_closebutton = new ui::IconButton(savegamemenu_titlelabel, "bitmaps/icons/window_close"); - savegamemenu_closebutton->set_label("close"); - + savegamemenu_closebutton = new CloseIconButton(savegamemenu_titlelabel); + // file listview savegamemenu_filelistview = new ui::ListView(this); savegamemenu_filelistview->set_label("files"); @@ -76,14 +81,14 @@ SaveGameMenu::SaveGameMenu(ui::Widget *parent, const char *label, const Mode mod savegamemenu_descrlabel->set_background(true); savegamemenu_descrlabel->set_border(true); + // delete button is a child of description label + savegamemenu_deletebutton = new ui::IconButton(savegamemenu_descrlabel, "bitmaps/icons/button_delete", "Delete"); + savegamemenu_deletebutton->set_label("delete"); + // save button savegamemenu_savebutton = new ui::Button(this, "Save"); savegamemenu_savebutton->set_label("save"); - // delete button - savegamemenu_deletebutton = new ui::IconButton(this, "bitmaps/icons/button_delete"); - savegamemenu_deletebutton->set_label("delete"); - // cancel save/load/overwrite button savegamemenu_cancelbutton = new ui::Button(this, "Cancel"); savegamemenu_cancelbutton->set_label("cancel"); @@ -109,7 +114,7 @@ SaveGameMenu::~SaveGameMenu() void SaveGameMenu::resize() { const float padding = ui::UI::padding; - const float icon_size = 24.0f; // small icons + const float icon_size = ui::UI::icon_small; // resize title label savegamemenu_titlelabel->set_size(width() - padding * 2.0f, savegamemenu_titlelabel->font()->height()); @@ -122,20 +127,13 @@ void SaveGameMenu::resize() // resize files listview savegamemenu_filelistview->set_size( ui::UI::elementsize.width(), - height() - icon_size - padding * 3.0f - savegamemenu_titlelabel->bottom() + height() - 2.0f * padding - savegamemenu_titlelabel->bottom() ); savegamemenu_filelistview->set_location( padding, savegamemenu_titlelabel->bottom() + padding ); - // resize delete button - savegamemenu_deletebutton->set_size(icon_size, icon_size); - savegamemenu_deletebutton->set_location( - savegamemenu_filelistview->left(), - savegamemenu_filelistview->bottom() + padding - ); - // resize description title savegamemenu_descrtitle->set_size( width() - savegamemenu_filelistview->right() - 2.0f * padding, @@ -149,13 +147,20 @@ void SaveGameMenu::resize() // resize description label savegamemenu_descrlabel->set_size( savegamemenu_descrtitle->width(), - savegamemenu_filelistview->bottom() - savegamemenu_descrtitle->bottom() - padding + savegamemenu_filelistview->bottom() - savegamemenu_descrtitle->bottom() - 2.0f * padding - ui::UI::elementsize.height() ); savegamemenu_descrlabel->set_location( savegamemenu_filelistview->right() + padding, savegamemenu_descrtitle->bottom() + padding ); + // resize delete button + savegamemenu_deletebutton->set_size(icon_size, icon_size); + savegamemenu_deletebutton->set_location( + savegamemenu_descrtitle->width() - savegamemenu_deletebutton->width() - padding, + padding + ); + // resize screenshot const model::Material *screenshotmaterial = model::Material::find("ui/screenshot"); if (screenshotmaterial) { diff --git a/src/client/serverlistmenu.cc b/src/client/serverlistmenu.cc new file mode 100644 index 0000000..44bd302 --- /dev/null +++ b/src/client/serverlistmenu.cc @@ -0,0 +1,173 @@ +/* + client/serverlistmenu.cc + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include "client/serverlistmenu.h" +#include "client/closeiconbutton.h" + +#include "ui/ui.h" + +#include "core/commandbuffer.h" + +#include "sys/sys.h" + + +namespace client +{ + +ServerListMenu::ServerListMenu(ui::Widget *parent, const char *label) : + ui::Window(parent) +{ + set_label(label); + set_border(true); + set_background(true); + set_font(ui::root()->font_small()); + + // window title + serverlistmenu_titlelabel = new ui::Label(this); + serverlistmenu_titlelabel->set_label("connect"); + serverlistmenu_titlelabel->set_text("CONNECT"); + serverlistmenu_titlelabel->set_background(false); + serverlistmenu_titlelabel->set_border(false); + serverlistmenu_titlelabel->set_font(ui::root()->font_large()); + serverlistmenu_titlelabel->set_alignment(ui::AlignCenter); + + // close button + serverlistmenu_closebutton = new CloseIconButton(serverlistmenu_titlelabel); + + // server listview + serverlistmenu_serverlistview = new ui::ListView(this); + serverlistmenu_serverlistview->set_label("files"); + + // save button + serverlistmenu_connectbutton = new ui::Button(this, "Connect"); + serverlistmenu_connectbutton->set_label("connect"); +} + +ServerListMenu::~ServerListMenu() +{ +} + +void ServerListMenu::resize() +{ + const float padding = ui::UI::padding; + + // resize title label + serverlistmenu_titlelabel->set_size(width() - padding * 2.0f, serverlistmenu_titlelabel->font()->height()); + serverlistmenu_titlelabel->set_location(padding, padding); + + // resize close button + serverlistmenu_closebutton->set_size(serverlistmenu_titlelabel->font()->height(), serverlistmenu_titlelabel->font()->height()); + serverlistmenu_closebutton->set_location(serverlistmenu_titlelabel->width() - serverlistmenu_closebutton->width(), 0); + + // resize files listview + serverlistmenu_serverlistview->set_size( + width() - padding * 2.0f, + height() - ui::UI::elementsize.height() - padding * 3.0f - serverlistmenu_titlelabel->bottom() + ); + serverlistmenu_serverlistview->set_location( + padding, + serverlistmenu_titlelabel->bottom() + padding + ); + + // resize connect button + serverlistmenu_connectbutton->set_size(ui::UI::elementsize); + serverlistmenu_connectbutton->set_location( + (width() - padding * 2.0f - serverlistmenu_connectbutton->width()) * 0.5f, + height() - padding - serverlistmenu_connectbutton->height() + ); +} + +void ServerListMenu::refresh() +{ + const float padding = ui::UI::padding; + + serverlistmenu_serverlistview->clear(); + serverlistmenu_connectbutton->show(); + serverlistmenu_connectbutton->disable(); + + // FIXME support custom port numbers + ui::ListItem *listitem = new ui::ListItem(serverlistmenu_serverlistview, "osirion.org development server"); + listitem->set_value("osirion.org"); + listitem->set_sortkey(""); + + listitem->set_font(ui::root()->font_tiny()); + listitem->set_height(listitem->font()->height() + padding); + + serverlistmenu_serverlistview->sort(false); + serverlistmenu_serverlistview->event_resize(); + +} + +void ServerListMenu::hide() +{ + Window::hide(); +} + +void ServerListMenu::show() +{ + Window::show(); + refresh(); +} + +void ServerListMenu::show_connect_info() +{ + if (!serverlistmenu_serverlistview->selected()) { + serverlistmenu_connectbutton->disable(); + } else { + serverlistmenu_connectbutton->enable(); + } +} + +void ServerListMenu::connect() +{ + if (serverlistmenu_serverlistview->selected()) { + std::stringstream str; + str << "connect " << serverlistmenu_serverlistview->selected()->value(); + core::CommandBuffer::exec(str.str()); + } +} + +bool ServerListMenu::on_keypress(const int key, const unsigned int modifier) +{ + if (key == SDLK_ESCAPE) { + if (visible()) { + // hide the parent mainwindow + parent()->hide(); + return true; + } + } + return false; +} + +bool ServerListMenu::on_emit(ui::Widget *sender, const ui::Widget::Event event, void *data) +{ + if (sender == serverlistmenu_closebutton) { + if (event == ui::Widget::EventButtonClicked) { + // hide the parent mainwindow + parent()->hide(); + return true; + } + } else if (sender == serverlistmenu_connectbutton) { + if (event == ui::Widget::EventButtonClicked) { + // do the actual connect and close the window + parent()->hide(); + connect(); + return true; + } + } else if (sender == serverlistmenu_serverlistview) { + if (event == ui::Widget::EventListViewChanged) { + show_connect_info(); + return true; + } + } + + return Window::on_emit(sender, event, data); +} + + +} // namespace client + + diff --git a/src/client/serverlistmenu.h b/src/client/serverlistmenu.h new file mode 100644 index 0000000..6638bd4 --- /dev/null +++ b/src/client/serverlistmenu.h @@ -0,0 +1,70 @@ +/* + client/serverlistmenu.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_CLIENT_SERVERLISTMENU_H__ +#define __INCLUDED_CLIENT_SERVERLISTMENU_H__ + +#include "ui/button.h" +#include "ui/iconbutton.h" +#include "ui/inputbox.h" +#include "ui/label.h" +#include "ui/listview.h" +#include "ui/window.h" + +namespace client +{ + +class ServerListMenu : public ui::Window +{ +public: + enum Mode { Save = 0, Load = 1 }; + + ServerListMenu(ui::Widget *parent = 0, const char *label = 0); + virtual ~ServerListMenu(); + +protected: + + /// called when the widget receives a key press + virtual bool on_keypress(const int key, const unsigned int modifier); + + /// called if the widget receives an emit evet + virtual bool on_emit(ui::Widget *sender, const ui::Widget::Event event, void *data); + + virtual void resize(); + + virtual void show(); + + virtual void hide(); + + void refresh(); + + /** + * @brief connect to the currently selected server + * */ + void connect(); + + /** + * @brief show information for the currently selected server + * */ + void show_connect_info(); + +private: + + ui::Label *serverlistmenu_titlelabel; + ui::IconButton *serverlistmenu_closebutton; + ui::ListView *serverlistmenu_serverlistview; + ui::Button *serverlistmenu_connectbutton; + + +}; // class ServerListMenu + + +} // namespace client + + +#endif // __INCLUDED_CLIENT_SERVERLISTMENU_H__ + + diff --git a/src/client/targeticonbutton.cc b/src/client/targeticonbutton.cc index ebd5ec3..27dfd0e 100644 --- a/src/client/targeticonbutton.cc +++ b/src/client/targeticonbutton.cc @@ -12,12 +12,17 @@ namespace client { -TargetIconButton::TargetIconButton(Widget *parent, const char *icon, const char *command, unsigned int flags) : IconButton(parent, icon, command) +TargetIconButton::TargetIconButton(Widget *parent, const char *icon, const char *tooltip, const char *command, unsigned int flags) : IconButton(parent, icon, tooltip, command) { set_label("targeticonbutton"); entity_flags = flags; } +TargetIconButton::~TargetIconButton() +{ + entity_flags = 0; +} + bool TargetIconButton::on_mousepress(const unsigned int button) { if (button == SDL_BUTTON_LEFT) diff --git a/src/client/targeticonbutton.h b/src/client/targeticonbutton.h index cb6a147..ccc3baf 100644 --- a/src/client/targeticonbutton.h +++ b/src/client/targeticonbutton.h @@ -17,7 +17,8 @@ class TargetIconButton : public ui::IconButton public: /// special icon button that sends target '@' commands. - TargetIconButton(Widget *parent, const char *icon, const char *command, unsigned int flags = 0); + TargetIconButton(Widget *parent, const char *icon, const char *tooltip = 0, const char *command = 0, unsigned int flags = 0); + virtual ~TargetIconButton(); protected: /** diff --git a/src/client/testmodelwindow.cc b/src/client/testmodelwindow.cc index d70fbbf..9778102 100644 --- a/src/client/testmodelwindow.cc +++ b/src/client/testmodelwindow.cc @@ -4,10 +4,14 @@ the terms and conditions of the GNU General Public License version 2 */ +#include "client/testmodelwindow.h" +#include "client/closeiconbutton.h" + #include "ui/ui.h" #include "ui/paint.h" -#include "client/testmodelwindow.h" + #include "model/model.h" + #include "core/gameinterface.h" namespace client @@ -26,9 +30,9 @@ TestModelWindow::TestModelWindow(ui::Widget *parent) testmodelwindow_text->set_background(false); testmodelwindow_text->set_border(false); - testmodelwindow_wireframebutton = new ui::IconButton(this, "bitmaps/icons/button_wireframe", "toggle r_wireframe"); + testmodelwindow_wireframebutton = new ui::IconButton(this, "bitmaps/icons/button_wireframe", "Toggle wireframe", "toggle r_wireframe"); - testmodelwindow_normalsbutton = new ui::IconButton(this, "bitmaps/icons/button_normals", "toggle r_normals"); + testmodelwindow_normalsbutton = new ui::IconButton(this, "bitmaps/icons/button_normals", "Toggle normals", "toggle r_normals"); testmodelwindow_closebutton = new ui::Button(this); testmodelwindow_closebutton->set_text("Close"); diff --git a/src/client/trademenu.cc b/src/client/trademenu.cc index a16c5c4..62ad972 100644 --- a/src/client/trademenu.cc +++ b/src/client/trademenu.cc @@ -4,16 +4,19 @@ the terms of the GNU General Public License version 2 */ -#include <iomanip> +#include "client/trademenu.h" +#include "client/closeiconbutton.h" -#include "core/application.h" -#include "core/info.h" -#include "core/inventory.h" #include "ui/button.h" #include "ui/paint.h" #include "ui/ui.h" #include "ui/listitem.h" -#include "client/trademenu.h" + +#include "core/application.h" +#include "core/info.h" +#include "core/inventory.h" + +#include <iomanip> namespace client { @@ -76,7 +79,7 @@ TradeMenu::TradeMenu(ui::Widget *parent, const char * label) : ui::Window(parent menu_titlelabel->show(); // close button - menu_closebutton = new ui::IconButton(menu_titlelabel, "bitmaps/icons/window_close"); + menu_closebutton = new CloseIconButton(menu_titlelabel); menu_inventorylistview = new InventoryListView(menu_tradewindow); menu_inventorylistview->set_label("inventorylistview"); diff --git a/src/client/video.cc b/src/client/video.cc index 5f8c14d..15f1c04 100644 --- a/src/client/video.cc +++ b/src/client/video.cc @@ -260,7 +260,7 @@ void restart() shutdown(); // clear models and materials - /* resetting the rednder subsystem will force a reload of all materials + /* resetting the render subsystem will force a reload of all materials */ model::Model::clear(); diff --git a/src/core/entity.cc b/src/core/entity.cc index f7501f7..5cf1015 100644 --- a/src/core/entity.cc +++ b/src/core/entity.cc @@ -39,10 +39,9 @@ size_t Entity::entity_nextid = 0; void Entity::add(Entity *ent) { - Registry::iterator it; entity_nextid = (entity_nextid % MAX_ENTITY) + 1; // lowest entity-id is 1 unsigned int id = entity_nextid; - for (it = entity_registry.begin(); it != entity_registry.end() && id == (*it).second->id(); it++) { + for (Registry::iterator it = entity_registry.begin(); it != entity_registry.end() && id == (*it).second->id(); ++it) { id++; } ent->entity_id = id; @@ -94,7 +93,7 @@ void Entity::erase(unsigned int id) void Entity::clear() { - for (Entity::Registry::iterator it = Entity::registry().begin(); it != Entity::registry().end(); it++) { + for (Entity::Registry::iterator it = Entity::registry().begin(); it != Entity::registry().end(); ++it) { delete(*it).second; } Entity::registry().clear(); @@ -122,7 +121,7 @@ void Entity::list(const Entity *entity) void Entity::list() { list_header(); - for (Registry::iterator it = entity_registry.begin(); it != entity_registry.end(); it++) { + for (Registry::iterator it = entity_registry.begin(); it != entity_registry.end(); ++it) { std::string typeindicator; Entity *entity = (*it).second; list(entity); @@ -215,7 +214,7 @@ Entity::~Entity() } // delete entity menus - for (Menus::iterator mit = menus().begin(); mit != menus().end(); mit++) { + for (Menus::iterator mit = menus().begin(); mit != menus().end(); ++mit) { delete (*mit); (*mit) = 0; } @@ -247,7 +246,7 @@ Entity::~Entity() } // delete child collision shapes - for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) { + for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); ++sit) { delete (*sit); (*sit) = 0; } @@ -683,7 +682,7 @@ void Entity::add_menu(MenuDescription *menu) MenuDescription *Entity::find_menu(std::string const &label) { - for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { + for (Menus::iterator it = menus().begin(); it != menus().end(); ++it) { if (label.compare((*it)->label()) == 0) return (*it); } @@ -692,7 +691,7 @@ MenuDescription *Entity::find_menu(std::string const &label) void Entity::remove_menu(std::string const &label) { - for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { + for (Menus::iterator it = menus().begin(); it != menus().end(); ++it) { if (label.compare((*it)->label()) == 0) { menus().erase(it); return; @@ -767,8 +766,7 @@ void Entity::reset() // a valid collision model has been found, use it to construct a compoundshape btCompoundShape *compoundshape = new btCompoundShape(); - for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin(); - it != model()->collisionmodel()->meshes().end(); it++) { + for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin(); it != model()->collisionmodel()->meshes().end(); ++it) { model::CollisionMesh *mesh = (*it); @@ -780,7 +778,7 @@ void Entity::reset() const float meshscale = mesh->scale() * modelscale; const btVector3 meshscalevec(meshscale, meshscale, meshscale); - // TODO + // FIXME // This does not work as advertized, it is actually the underlying mesh that // gets scaled. If there are multiple entities with the same model at a different scale, // the scale is set per-model instead of per-entity @@ -1006,7 +1004,7 @@ void EntityDynamic::reset() entity_collision_shape = 0; } - for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) { + for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); ++sit) { delete (*sit); (*sit) = 0; } @@ -1042,8 +1040,8 @@ void EntityDynamic::reset() if (model()->collisionmodel() && model()->collisionmodel()->size()) { // complex collision is enabled and a valid collision model has been found btCompoundShape *compoundshape = new btCompoundShape(); - for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin(); - it != model()->collisionmodel()->meshes().end(); it++) { + + for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin(); it != model()->collisionmodel()->meshes().end(); ++it) { model::CollisionMesh *mesh = (*it); @@ -1388,7 +1386,7 @@ void EntityControlable::receive_server_create(std::istream &is) int owner_id = 0; is >> owner_id; if (owner_id && !owner()) { - for (GameInterface::Players::iterator pit = game()->players().begin();!owner() && (pit != game()->players().end()); pit++) { + for (GameInterface::Players::iterator pit = game()->players().begin(); !owner() && (pit != game()->players().end()); ++pit) { Player *player = (*pit); if (player->id() == owner_id) { player->add_asset(this); @@ -1595,7 +1593,7 @@ void EntityControlable::reset() entity_collision_shape = 0; } - for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) { + for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); ++sit) { delete (*sit); (*sit) = 0; } @@ -1631,8 +1629,7 @@ void EntityControlable::reset() if (model()->collisionmodel() && model()->collisionmodel()->size()) { // a valid collision model has been found btCompoundShape *compoundshape = new btCompoundShape(); - for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin(); - it != model()->collisionmodel()->meshes().end(); it++) { + for (model::CollisionModel::CollisionMeshes::iterator it = model()->collisionmodel()->meshes().begin(); it != model()->collisionmodel()->meshes().end(); ++it) { model::CollisionMesh *mesh = (*it); diff --git a/src/model/mapfile.cc b/src/model/mapfile.cc index 3adcbb0..99601a6 100644 --- a/src/model/mapfile.cc +++ b/src/model/mapfile.cc @@ -459,7 +459,7 @@ bool MapFile::read_patchdef() // binomial coefficient const float binom[3] = {1.0f, 2.0f, 1.0f}; - const size_t subdivide_max = 4; + const size_t subdivide_max = 8; size_t subdivide_u = 0; size_t subdivide_v = 0; diff --git a/src/model/objfile.cc b/src/model/objfile.cc index 49a691f..323986b 100644 --- a/src/model/objfile.cc +++ b/src/model/objfile.cc @@ -34,6 +34,9 @@ OBJFile::OBJFile(std::string const &name) // reset counters obj_normalcount = 0; + + obj_collisionmodel = nullptr; + obj_load_clip = false; } OBJFile::~OBJFile() @@ -132,9 +135,11 @@ bool OBJFile::read() std::string materialname; material = 0; - if (line >> materialname) { - for(MaterialList::iterator it = obj_materiallist.begin(); it != obj_materiallist.end(); ++it) { - if( (*it).second->name() == materialname) { + if (line >> materialname) + { + for(MaterialList::iterator it = obj_materiallist.begin(); it != obj_materiallist.end(); ++it) + { + if( (*it).second->name().compare(materialname) == 0) { material = (*it).second; current_m = (*it).first; break; @@ -146,12 +151,12 @@ bool OBJFile::read() obj_materiallist[obj_materiallist.size()] = material; current_m = obj_materiallist.size()-1; } - } - else + } else { con_warn << objfile_name << " invalid material definition at line " << index_line << std::endl; + } - } else if (word.compare("v") == 0) { /* new wertex */ + } else if (word.compare("v") == 0) { /* new vertex */ float x, y, z; line >> x >> y >> z; @@ -159,14 +164,14 @@ bool OBJFile::read() obj_vertexlist[obj_vertexlist.size()] = v; obj_box.expand(*v * SCALE); - } else if (word.compare("vt") == 0) { /* wertex texture coordinate */ + } else if (word.compare("vt") == 0) { /* vertex texture coordinate */ float u, v; line >> u >> v; math::Vector2f *uv = new math::Vector2f(u, v); obj_uvlist[obj_uvlist.size()] = uv; - } else if (word.compare("vn") == 0) { /* wertex wormal */ + } else if (word.compare("vn") == 0) { /* vertex normal */ float x, y, z; line >> x >> y >> z; @@ -352,34 +357,64 @@ bool OBJFile::read() //Go through tri and quad lists and create fragments for each material size_t total_m = obj_materiallist.size(); - for(size_t i = 0; i <= total_m; i++) { + for(size_t i = 0; i < total_m; i++) + { material = obj_materiallist[i]; - fragment = new Fragment(Fragment::Triangles, material); - for(TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); ++it) { - if((*it).first == i) { - fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false); - fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false); - fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false); + if (material->has_flag_clip()) // add mesh to the collision model if the material has the Clip flag set + { + CollisionMesh *collisionmesh = new CollisionMesh(); + + // add triangles to the collision mesh + for(TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); ++it) + { + if((*it).first == i) + { + collisionmesh->add_triangle((*it).second->v0() * SCALE, (*it).second->v1() * SCALE, (*it).second->v2() * SCALE); + } } - } - - if( fragment->structural_size() + fragment->detail_size() > 0 ) - obj_fragmentgroup->add_fragment(fragment); - fragment = new Fragment(Fragment::Quads, material); - - for(QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); ++it) { - if((*it).first == i) { - fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false); - fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false); - fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false); - fragment->add_vertex(((*it).second->v3() * SCALE) , (*it).second->n3(), (*it).second->t3(), false); + for(QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); ++it) + { + if((*it).first == i) + { + collisionmesh->add_triangle((*it).second->v0() * SCALE, (*it).second->v1() * SCALE, (*it).second->v2() * SCALE); + collisionmesh->add_triangle((*it).second->v2() * SCALE, (*it).second->v3() * SCALE, (*it).second->v0() * SCALE); + } } + + obj_collisionmodel->add_mesh(collisionmesh); + } + else if (!material->has_flag_ignore()) // do not load meshes if the material has the Ignore flag + { + + fragment = new Fragment(Fragment::Triangles, material); + + for(TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); ++it) { + if((*it).first == i) { + fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false); + fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false); + fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false); + } + } + + if( fragment->structural_size() + fragment->detail_size() > 0 ) + obj_fragmentgroup->add_fragment(fragment); + + fragment = new Fragment(Fragment::Quads, material); + + for(QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); ++it) { + if((*it).first == i) { + fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false); + fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false); + fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false); + fragment->add_vertex(((*it).second->v3() * SCALE) , (*it).second->n3(), (*it).second->t3(), false); + } + } + + if( fragment->structural_size() + fragment->detail_size() > 0 ) + obj_fragmentgroup->add_fragment(fragment); } - - if( fragment->structural_size() + fragment->detail_size() > 0 ) - obj_fragmentgroup->add_fragment(fragment); } return true; @@ -387,22 +422,34 @@ bool OBJFile::read() Model *OBJFile::load(const std::string &name) { + OBJFile objfile(name); - if (!objfile.is_open()) { - return 0; + return nullptr; } - if (!objfile.read()) { - return 0; - } - - if (!objfile.fragmentgroup()->size()) - return 0; - // create a new model Model *model = new Model(name); + // load clip into collision meshes + // if the model has been loaded before (e.g. on r_restart), the collision model won't be reloaded + objfile.obj_collisionmodel = CollisionModel::find(name); + if (!objfile.obj_collisionmodel) + { + objfile.obj_load_clip = true; + objfile.obj_collisionmodel = new CollisionModel(name); + CollisionModel::add(objfile.obj_collisionmodel); + } else { + objfile.obj_load_clip = false; + } + model->set_collisionmodel(objfile.obj_collisionmodel); + + if (!objfile.read() || !objfile.fragmentgroup()->size()) + { + con_warn << " " << objfile.name() << " empty model" << std::endl; + return model; + } + // center model around (0,0,0) and set the bounding box math::Vector3f obj_center((objfile.box().min() + objfile.box().max()) * 0.5f); model->model_box.assign( @@ -414,7 +461,8 @@ Model *OBJFile::load(const std::string &name) objfile.fragmentgroup()->set_location(obj_center * -1.0f); - for (FragmentGroup::Fragments::const_iterator fit = objfile.fragmentgroup()->fragments().begin(); fit != objfile.fragmentgroup()->fragments().end(); fit++) { + for (FragmentGroup::Fragments::const_iterator fit = objfile.fragmentgroup()->fragments().begin(); fit != objfile.fragmentgroup()->fragments().end(); ++fit) + { const Fragment *fragment = (*fit); if(fragment->type() == Fragment::Triangles) diff --git a/src/model/objfile.h b/src/model/objfile.h index 7db90bc..b8286dc 100644 --- a/src/model/objfile.h +++ b/src/model/objfile.h @@ -32,7 +32,7 @@ class OBJFile public: /** * @brief load a .obj file into a Model - * @param name name of the model to be loaded, without .obj extension or models/ prefix + * @param name name of the model to be loaded, without .obj extension. * If the file can not be read, load() returns the NULL-pointer */ static Model *load(std::string const &name); @@ -66,7 +66,7 @@ private: bool read(); /** - * @brief parse a line in the .obj file. + * @brief parse a line in the .obj file */ void readline(std::string const &line); @@ -112,6 +112,9 @@ private: FragmentGroup *obj_fragmentgroup; size_t obj_normalcount; + + bool obj_load_clip; + CollisionModel *obj_collisionmodel; }; } // namespace model diff --git a/src/model/vertexarray.h b/src/model/vertexarray.h index 1474c67..5f542a8 100644 --- a/src/model/vertexarray.h +++ b/src/model/vertexarray.h @@ -13,7 +13,7 @@ namespace model { // number of segments in a sphere circle, must be uneven -const int SPHERESEGMENTS = 65; +const int SPHERESEGMENTS = 127; /// global geometry vertex array /** a VertexArray acts like a stack of model vertices, it has no knowledge of what it is holding diff --git a/src/render/tgafile.cc b/src/render/tgafile.cc index 15447df..ee12f8a 100644 --- a/src/render/tgafile.cc +++ b/src/render/tgafile.cc @@ -353,7 +353,7 @@ void TGA::save(const char *filename, Image & image) // write footer (optional, but the specification recommends it) char footer[26]; memset(footer, 0, sizeof(footer)); - strncpy(&footer[8] , "TRUEVISION-XFILE", 16); + memcpy(footer + 8 , "TRUEVISION-XFILE", 16); footer[24] = '.'; footer[25] = 0; ofs.write(footer, sizeof(footer)); 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); |