From b417df720584c101f3799874a0c836a543a8d0a8 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 12 Oct 2008 14:55:10 +0000 Subject: user interface updates, work-in-progress --- src/client/chat.cc | 202 +++++++++++------------------- src/client/chat.h | 46 ++++--- src/client/client.cc | 28 +++-- src/client/client.h | 6 + src/client/console.cc | 2 +- src/client/input.cc | 61 ++++----- src/client/keyboard.cc | 21 ++-- src/client/keyboard.h | 8 +- src/client/video.cc | 148 ++++------------------ src/client/video.h | 25 ++-- src/client/view.cc | 170 +++++++++---------------- src/client/view.h | 9 +- src/render/camera.cc | 22 +++- src/render/camera.h | 21 +++- src/render/draw.cc | 5 - src/render/render.cc | 207 ++++++++++++++++++++++++++----- src/render/render.h | 3 + src/ui/Makefile.am | 6 +- src/ui/bitmap.cc | 11 +- src/ui/bitmap.h | 33 +++-- src/ui/button.cc | 32 +++-- src/ui/button.h | 45 ++++--- src/ui/container.h | 36 ++++++ src/ui/definitions.h | 7 +- src/ui/font.cc | 13 +- src/ui/font.h | 51 ++++---- src/ui/input.cc | 174 ++++++++++++++++++++++++++ src/ui/input.h | 55 +++++++++ src/ui/label.cc | 13 +- src/ui/label.h | 38 +++--- src/ui/menu.cc | 47 +++---- src/ui/menu.h | 22 ++-- src/ui/paint.cc | 66 +++++----- src/ui/paint.h | 12 +- src/ui/palette.cc | 16 ++- src/ui/palette.h | 51 +++++--- src/ui/ui.cc | 191 +++++++++++++++++----------- src/ui/ui.h | 99 +++++++++------ src/ui/widget.cc | 253 ++++++++++++++++++++++++------------- src/ui/widget.h | 330 +++++++++++++++++++++++++++++++------------------ src/ui/window.cc | 9 +- src/ui/window.h | 24 ++-- 42 files changed, 1607 insertions(+), 1011 deletions(-) create mode 100644 src/ui/container.h create mode 100644 src/ui/input.cc create mode 100644 src/ui/input.h diff --git a/src/client/chat.cc b/src/client/chat.cc index 6f63a6d..bdeebe2 100644 --- a/src/client/chat.cc +++ b/src/client/chat.cc @@ -5,129 +5,86 @@ */ #include "auxiliary/functions.h" -#include "core/core.h" #include "client/chat.h" +#include "client/client.h" #include "client/console.h" -#include "client/keyboard.h" -#include "client/video.h" +#include "core/core.h" #include "render/render.h" +#include "sys/sys.h" +#include "ui/ui.h" namespace client { -namespace chat { - -// input history -std::deque history; -std::deque::reverse_iterator history_pos; -size_t input_pos = 0; - -// chatbox visibility -bool chat_visible = false; - -//--- public ------------------------------------------------------ - -void init() +Chat::Chat(ui::Widget *parent) : ui::Widget(parent) { - // add engine functions + set_label("chat"); history.clear(); history.push_back(""); history_pos = history.rbegin(); - input_pos = 0; + + chat_label = new ui::Label(this, "^BSay^F:^B"); + chat_label->set_alignment(ui::AlignLeft | ui::AlignVCenter); + + chat_input = new ui::Input(this); + chat_input->set_border(true); + + chat_input->set_focus(); + + set_background(true); + set_visible(false); } -void shutdown() +Chat::~Chat() { history.clear(); - input_pos = 0; } -bool visible() -{ - return chat_visible; -} -void draw() +void Chat::show() { - using namespace render; - - if (console()->visible() || !visible()) - return; + Widget::show(); - size_t width = (size_t) (video::width / Text::fontwidth()) - 8; - float y = video::height*8/10; - Text::draw(4, y, "^BSay^F:^B "); + raise(); + set_focus(); - std::string firstpart((*history_pos).substr(0, input_pos)); - size_t draw_width = 0; - const char *c = firstpart.c_str(); - - while (*c) { - if (aux::is_color_code(c)) { - c++; - } else { - draw_width++; - } - c++; - } - - c = firstpart.c_str(); - while (*c && draw_width > width - 2) { - if (aux::is_color_code(c)) { - c++; - Text::setcolor(*c); - } else { - draw_width--; - } - c++; - } - - if (*c) { - Text::draw(4+5*Text::fontwidth(), y, c); - } - - if (input_pos < (*history_pos).size()) { - // FIXME limit to width - if (input_pos > 1 && aux::is_color_code((*history_pos).c_str() + input_pos -1)) { - Text::setcolor((*history_pos)[input_pos]); - } - c = (*history_pos).c_str() + input_pos; - Text::draw(4+Text::fontwidth()*(draw_width+5), y, c); - } + history_pos = history.rbegin(); + (*history_pos).clear(); + chat_input->set_text((*history_pos)); +} - // draw cursor - if ((core::application()->time() - ::floorf(core::application()->time())) < 0.5f) { - std::string cursor("^B"); - cursor += (char) 11; - Text::draw(4+Text::fontwidth()*(draw_width+5), y , cursor); - } +void Chat::toggle() +{ + if (visible()) + hide(); + else + show(); } -void toggle() +void Chat::resize() { - chat_visible = !chat_visible; - if (chat_visible) { - input_pos = 0; - history_pos = history.rbegin(); - (*history_pos).clear(); - } + chat_label->set_location(font()->width(), height() / 5.0f); + chat_label->set_size(width() - font()->width() * 2, height() / 5.0f); - setkeyboardmode(console()->visible() || (core::application()->connected() && chat::visible())); + chat_input->set_location(font()->width(), height() / 5.0f * 3.0f); + chat_input->set_size(width() - font()->width() * 2, height() / 5.0f); } -void keypressed(unsigned int key) +bool Chat::on_keypress(const int key, const unsigned int modifier) { - std::deque::reverse_iterator upit; + History::reverse_iterator upit; switch( key ) { case SDLK_ESCAPE: - toggle(); - break; + if (visible()) { + hide(); + return true; + } else { + return false; + } + case SDLK_RETURN: + if (chat_input->text().size()) { + (*history_pos).assign(chat_input->text()); - case SDLK_TAB: - core::CommandBuffer::complete( (*history_pos), input_pos); - break; - case SDLK_RETURN: - if ((*history_pos).size()) { // store input into history while (history.size() >= MAXHISTOLINES) { history.pop_front(); @@ -142,61 +99,46 @@ void keypressed(unsigned int key) history.push_back(""); history_pos = history.rbegin(); - input_pos = 0; + chat_input->set_text((*history_pos)); } - toggle(); + + hide(); + return true; break; + case SDLK_UP: upit = history_pos; ++upit; if (upit != history.rend()) { history_pos = upit; - input_pos = (*history_pos).size(); + chat_input->set_text((*history_pos)); } + return true; break; + case SDLK_DOWN: if (history_pos != history.rbegin()) { --history_pos; - input_pos = (*history_pos).size(); - } - break; - case SDLK_HOME: - input_pos = 0; - break; - case SDLK_END: - input_pos = (*history_pos).size(); - break; - case SDLK_LEFT: - if (input_pos > 0) - input_pos--; - break; - case SDLK_RIGHT: - if (input_pos < (*history_pos).size()) - input_pos++; - break; - case SDLK_DELETE: - if ((*history_pos).size() && input_pos < (*history_pos).size()) { - (*history_pos).erase(input_pos, 1); - } - break; - case SDLK_BACKSPACE: - if ((*history_pos).size() && input_pos) { - (*history_pos).erase(input_pos-1, 1); - input_pos--; - } - break; - default: - if ((key >= 32 ) && (key <175)) { - if (input_pos == (*history_pos).size()) - (*history_pos) += (char)key; - else - (*history_pos).insert(input_pos, 1, (char)key); - input_pos++; + chat_input->set_text((*history_pos)); } + return true; break; } -} + return false; } +void Chat::event_draw() +{ + if (!client()->connected()) { + hide(); + return; + } + + if (ui::root()->active()) + return; + else + Widget::event_draw(); } + +} // namespace client diff --git a/src/client/chat.h b/src/client/chat.h index fdf4c70..0bef2b4 100644 --- a/src/client/chat.h +++ b/src/client/chat.h @@ -7,35 +7,43 @@ #ifndef __INCLUDED_CLIENT_CHAT_H__ #define __INCLUDED_CLIENT_CHAT_H__ -#include "sys/consoleinterface.h" - #include #include -namespace client { +#include "ui/input.h" +#include "ui/label.h" +#include "ui/window.h" -/// the client chatbox -namespace chat { +namespace client { -/// initialize chatbox functions -void init(); +class Chat : public ui::Widget +{ +public: + Chat(ui::Widget *parent = 0); + ~Chat(); -/// shut down chatbox functions -void shutdown(); + virtual void show(); + void toggle(); -/// draw the chatbox -void draw(); +protected: + virtual void event_draw(); + virtual void resize(); + + virtual bool on_keypress(const int key, const unsigned int modifier); + -/// toggle the chatbox -void toggle(); +private: + ui::Label *chat_label; + ui::Input *chat_input; + + typedef std::deque History; + + History history; + History::reverse_iterator history_pos; +}; -/// handle keyboard input -void keypressed(unsigned int key); -/// true of the console is visible -bool visible(); -} +} // namespace client -} #endif // __INCLUDED_CLIENT_CHAT_H__ diff --git a/src/client/client.cc b/src/client/client.cc index 3f368a7..ac4c0e3 100644 --- a/src/client/client.cc +++ b/src/client/client.cc @@ -12,7 +12,6 @@ #include "audio/audio.h" #include "audio/sources.h" -#include "client/chat.h" #include "client/client.h" #include "client/video.h" #include "client/console.h" @@ -52,6 +51,13 @@ void func_r_restart(std::string const &args) video::restart(); } +void func_ui_chat(std::string const &args) +{ + if (core::application()->connected()) { + client()->view()->chat()->toggle(); + } +} + //--- public ------------------------------------------------------ void client_main(int count, char **arguments) @@ -108,16 +114,13 @@ void Client::init(int count, char **arguments) // initialize user interface ui::init(); - new View(ui::root()); + client_view = new View(ui::root()); // Initialize the video subsystem if (!video::init()) { quit(1); } - // initialize console - chat::init(); - // initialize input input::init(); @@ -129,9 +132,12 @@ void Client::init(int count, char **arguments) func = core::Func::add("r_restart", (core::FuncPtr) func_r_restart); func->set_info("restart render subsystem"); + + func = core::Func::add("ui_chat", func_ui_chat); + func->set_info("toggle chat window"); - func = core::Func::add("snd_restart", (core::FuncPtr) func_snd_restart); - func->set_info("restart audio subsystem"); + //func = core::Func::add("snd_restart", (core::FuncPtr) func_snd_restart); + //func->set_info("restart audio subsystem"); } void Client::run() @@ -215,10 +221,8 @@ void Client::shutdown() if (connected()) disconnect(); core::Func::remove("r_restart"); - - core::Func::remove("snd_restart"); - - chat::shutdown(); + core::Func::remove("ui_chat"); + //core::Func::remove("snd_restart"); audio::shutdown(); @@ -245,8 +249,8 @@ void Client::notify_connect() void Client::notify_disconnect() { // FIXME unload sounds - //audio::reset(); render::reset(); + input::reset(); } void Client::notify_zonechange() diff --git a/src/client/client.h b/src/client/client.h index bd7af6b..dc70e16 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -8,6 +8,7 @@ #define __INCLUDED_CLIENT_H__ #include "core/application.h" +#include "client/view.h" /// client part of the engine namespace client { @@ -46,10 +47,15 @@ public: /// disconnect notification virtual void notify_disconnect(); + /// the main client view + inline View *view() { return client_view; } + protected: /// run a client frame virtual void frame(float seconds); +private: + View *client_view; }; diff --git a/src/client/console.cc b/src/client/console.cc index 96da900..47f95f7 100644 --- a/src/client/console.cc +++ b/src/client/console.cc @@ -92,7 +92,7 @@ void Console::toggle() SDL_ShowCursor(SDL_DISABLE); } - setkeyboardmode(console()->visible() || (core::application()->connected() && chat::visible())); + //setkeyboardmode(console()->visible() || (core::application()->connected() && chat::visible())); audio::play("ui/console"); } diff --git a/src/client/input.cc b/src/client/input.cc index d2a53e1..d2c290b 100644 --- a/src/client/input.cc +++ b/src/client/input.cc @@ -108,7 +108,7 @@ float joystick_lastmoved_time() void func_screenshot(std::string const & args) { - video::screenshot(); + render::screenshot(); } void func_ui_control(std::string const &args) @@ -138,13 +138,6 @@ void func_ui_console(std::string const &args) console()->toggle(); } -void func_ui_chat(std::string const &args) -{ - if (core::application()->connected()) { - chat::toggle(); - } -} - void func_view_next(std::string const &args) { if (core::application()->connected() && core::localcontrol()) { @@ -256,8 +249,6 @@ void init() keyboard = new Keyboard(); - client::setkeyboardmode(false); - SDL_ShowCursor(SDL_DISABLE); SDL_WM_GrabInput(SDL_GRAB_ON); // SDL_EnableUNICODE(1); @@ -280,8 +271,8 @@ void init() func = core::Func::add("ui_console", func_ui_console); func->set_info("toggle console on or off"); - func = core::Func::add("ui_chat", func_ui_chat); - func->set_info("toggle chatbox on or of"); + //func = core::Func::add("ui_chat", func_ui_chat); + //func->set_info("toggle chatbox on or of"); func = core::Func::add("ui_control",func_ui_control); func->set_info("toggle mouse control"); @@ -333,8 +324,8 @@ void shutdown() core::Func::remove("screenshot"); core::Func::remove("ui_console"); - core::Func::remove("ui_control"); - core::Func::remove("ui_chat"); + //core::Func::remove("ui_control"); + //core::Func::remove("ui_chat"); core::Func::remove("ui_view"); keyboard->save_binds(); @@ -498,21 +489,37 @@ Key::Modifier convert_SDL_modifier(int const sdlmodifier) void key_pressed(Key *key) { + ui::root()->input_key(true, Keyboard::translate_keysym(key->sym(), keyboard_modifiers), keyboard_modifiers); if (key->sym() == SDLK_ESCAPE) { - if (chat::visible()) { - chat::toggle(); - } else { + + if (console()->visible()) { + console()->toggle(); local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; - + render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); - console()->toggle(); + } else { + if (ui::root()->active()) { + ui::root()->hide_window(); + local_direction = 0.0f; + local_pitch = 0.0f; + local_roll = 0.0f; + + render::Camera::set_direction(0.0f); + render::Camera::set_pitch(0.0f); + } else { + if (core::application()->connected()) { + ui::root()->show_window("game"); + } else { + ui::root()->show_window("main"); + } + } } - + } else if (key->bind(Key::None).compare("ui_console") == 0) { local_direction = 0.0f; local_pitch = 0.0f; @@ -526,16 +533,16 @@ void key_pressed(Key *key) } else if (console()->visible()) { // send key events to the console if (key->sym() < 512) - console()->keypressed(translate_keysym(key->sym(), keyboard_modifiers)); + console()->keypressed(Keyboard::translate_keysym(key->sym(), keyboard_modifiers)); } else if (ui::root()->active()) { - ui::root()->input_key(true, key->sym(), keyboard_modifiers); - - } else if (chat::visible()) { +/* ui::root()->input_key(true, key->sym(), keyboard_modifiers); +*/ +/* } else if (chat::visible()) { // send key events to the chat box if (key->sym() < 512) chat::keypressed(translate_keysym(key->sym(), keyboard_modifiers)); - +*/ } else if (core::application()->connected() && core::localcontrol()) { char c = key->bind(convert_SDL_modifier(keyboard_modifiers)).c_str()[0]; @@ -559,9 +566,7 @@ void key_pressed(Key *key) void key_released(Key *key) { - if (ui::root()->active()) { - ui::root()->input_key(false, key->sym(), keyboard_modifiers); - } + ui::root()->input_key(false, Keyboard::translate_keysym(key->sym(), keyboard_modifiers), keyboard_modifiers); if (core::application()->connected() && core::localcontrol()) { diff --git a/src/client/keyboard.cc b/src/client/keyboard.cc index b420fb8..80e0d5c 100644 --- a/src/client/keyboard.cc +++ b/src/client/keyboard.cc @@ -515,16 +515,7 @@ void Keyboard::list_binds() con_print << n << " registered binds" << std::endl; } -void setkeyboardmode(bool input) -{ - /* if(input) - SDL_EnableKeyRepeat(250, SDL_DEFAULT_REPEAT_INTERVAL); - else - SDL_EnableKeyRepeat(10, SDL_DEFAULT_REPEAT_INTERVAL); - */ -} - -unsigned int translate_keysym(int keysym, int modifier) +unsigned int Keyboard::translate_keysym(int keysym, int modifier) { bool shift = false; @@ -715,4 +706,14 @@ unsigned int translate_keysym(int keysym, int modifier) return keysym; } +/* +void setkeyboardmode(bool input) +{ + if(input) + SDL_EnableKeyRepeat(250, SDL_DEFAULT_REPEAT_INTERVAL); + else + SDL_EnableKeyRepeat(10, SDL_DEFAULT_REPEAT_INTERVAL); + +} +*/ } // namespace client diff --git a/src/client/keyboard.h b/src/client/keyboard.h index 87a67c4..f63bce1 100644 --- a/src/client/keyboard.h +++ b/src/client/keyboard.h @@ -69,6 +69,9 @@ public: inline iterator end() { return keys.end(); } + /// convert SDL_keysym to a keystroke + static unsigned int translate_keysym(int keysym, int modifier); + private: Key *add_key(const char *name, const unsigned int keysym, const char ascii=0, const char *bind=0); @@ -81,13 +84,10 @@ private: bool capslock; }; -/// convert SDL_keysym to a keystroke -unsigned int translate_keysym(int keysym, int modifier); - /// set the keyboard input mode /** @param input true for console input, false for game input */ -void setkeyboardmode(bool input); +//void setkeyboardmode(bool input); } diff --git a/src/client/video.cc b/src/client/video.cc index 123bd88..7338d70 100644 --- a/src/client/video.cc +++ b/src/client/video.cc @@ -4,10 +4,6 @@ the terms and conditions of the GNU General Public License version 2 */ -#include -#include - -#include "auxiliary/functions.h" #include "client/video.h" #include "client/input.h" #include "client/view.h" @@ -38,49 +34,15 @@ int height = 0; int width_prev = 0; int height_prev = 0; -int screenshot_number = 0; - const int width_default = 1024; const int height_default = 768; -//--- cvars ------------------------------------------------------- +/* -- engine variables --------------------------------------------- */ core::Cvar *r_width; core::Cvar *r_height; core::Cvar *r_fullscreen; -core::Cvar *screenshotformat; -core::Cvar *screenshotquality; - -void restart() -{ - shutdown(); - - if (!init()) { - client()->quit(1); - } - - input::reset(); -} - -void reset() -{ - // setup our viewport. - gl::viewport(0, 0, width, height ); - - // recalculate the video aspect - render::Camera::set_aspect(width, height); - - // resize user interface - if (ui::root()) { - ui::root()->set_size((float) width, (float) height); - ui::root()->event_resize(); - } - - // reset the view - view::reset(); -} - bool init() { con_print << "^BInitializing video..." << std::endl; @@ -95,12 +57,6 @@ bool init() r_fullscreen = core::Cvar::get("r_fullscreen", "0", core::Cvar::Archive); r_fullscreen->set_info("[bool] enable or disable fullscreen video"); - screenshotformat = core::Cvar::get("screenshotformat", "jpg", core::Cvar::Archive); - screenshotformat->set_info("[string] screenshot format: jpg png tga"); - - screenshotquality = core::Cvar::get("screenshotquality", "85", core::Cvar::Archive); - screenshotquality->set_info("[int] screenshot jpg quality"); - int bpp = 0; int flags = 0; @@ -162,7 +118,7 @@ bool init() } else return false; } - con_print << " video mode " << width << "x" << height << "x" << bpp << "bpp" << std::endl; + con_print << " video mode " << width << "x" << height << "x" << bpp << "bpp " << (fullscreen ? "fullscreen " : "window") << std::endl; #ifdef HAVE_DEBUG_MESSAGES @@ -178,24 +134,40 @@ bool init() #endif // HAVE_DEBUG_MESSAGES - render::Camera::set_aspect(width, height); + // save r_width and r_height variables (*r_width) = width; (*r_height) = height; + // set window caption std::string version(core::name()); version += ' '; version.append(core::version()); SDL_WM_SetCaption(version.c_str(), 0); - render::init(); + // resize user interface + ui::root()->set_size((float) width, (float) height); + ui::root()->event_resize(); - video::reset(); + // initialize renderer + render::Camera::resize(width, height); + render::init(); + render::Camera::resize(width, height); // yes twice, bug view::init(); return true; } +void restart() +{ + shutdown(); + if (!init()) { + client()->quit(1); + } + + input::reset(); +} + void frame(float seconds) { // detect fullscreen/windowed mode switch @@ -222,84 +194,6 @@ void shutdown() SDL_QuitSubSystem(SDL_INIT_VIDEO); } -void screenshot() -{ - bool available = false; - std::string shortname; - std::string filename; - const int TYPETGA = 0; - const int TYPEPNG = 1; - const int TYPEJPG = 2; - int filetype = TYPETGA; - - // make sure the screenshots folder exists - filename.assign(filesystem::writedir()); - filename.append("screenshots/"); - sys::mkdir(filename); - - aux::lowercase(screenshotformat->str()); - - if ((screenshotformat->str().compare("jpg") == 0) || (screenshotformat->str().compare("jpeg") == 0)) { - filetype = TYPEJPG; - if (screenshotquality->value() < 10) { - (*screenshotquality) = 10; - } else if (screenshotquality->value() > 100) { - (*screenshotquality) = 100; - } - - } else if (screenshotformat->str().compare("png") == 0) { - filetype = TYPEPNG; - - } else if (screenshotformat->str().compare("tga") == 0) { - filetype = TYPETGA; - - } else { - filetype = TYPETGA; - (*screenshotformat) = "tga"; - } - - // find the first available screenshotxxxx - do { - std::stringstream nstr; - nstr << screenshot_number; - shortname.assign(nstr.str()); - - while(shortname.size() < 4) - shortname.insert(0, 1, '0'); - - shortname.insert(0, "screenshots/osirion"); - shortname.append("."); - shortname.append(screenshotformat->str()); - - filename.assign(filesystem::writedir()); - filename.append(shortname); - - FILE *handle = fopen(filename.c_str(), "r"); - if (handle) { - fclose(handle); - } else { - available = true; - } - screenshot_number++; - } while (!available); - - render::Image image((unsigned int)video::width, (unsigned int)video::height, 3); - - glReadPixels(0, 0, (GLsizei) video::width, (GLsizei) video::height, - GL_RGB, GL_UNSIGNED_BYTE, (void *) image.data()); - - image.flip(); - - if (filetype == TYPEPNG) { - render::PNG::save(filename.c_str(), image); - } else if (filetype == TYPEJPG) { - render::JPG::save(filename.c_str(), image, (int) screenshotquality->value()); - } else if (filetype == TYPETGA) { - render::TGA::save(filename.c_str(), image); - } -} - - } // namespace video } // namespace client diff --git a/src/client/video.h b/src/client/video.h index 59939e6..daa136c 100644 --- a/src/client/video.h +++ b/src/client/video.h @@ -9,31 +9,28 @@ namespace client { -/// the client video subsystem +/// the video subsystem namespace video { - /// initialize the client video subsystem + /// initialize the video subsystem bool init(); - /// shutdown the client video subsystem + /// shutdown the video subsystem void shutdown(); - /// draw the next client video frame - void frame(float seconds); - - /// reset and clear the viewport - void reset(); - - /// restart the video subsystem + /// re-initialize the video subsystems + /** the restart functions performs a full shutdown + * and re-initializes the video subsystem + */ void restart(); - /// make a screenshot - void screenshot(); + /// draw the next client video frame + void frame(float seconds); - /// width of the window in pixels + /// width of the application window in pixels extern int width; - /// height of the window in pixels + /// height of the application window in pixels extern int height; } // namespace video diff --git a/src/client/view.cc b/src/client/view.cc index 15f70bc..463d254 100644 --- a/src/client/view.cc +++ b/src/client/view.cc @@ -38,6 +38,15 @@ core::Cvar *draw_keypress = 0; core::Cvar *ui_pointercolor = 0; core::Cvar *ui_pointerhovercolor = 0; +const float pointer_size = 48.0f; + +void time_to_stream(std::stringstream &str, float time) +{ + int minutes = (int) floorf(time / 60.0f); + int seconds = (int) floorf( time - (float) minutes* 60.0f); + str << std::setfill(' ') << std::setw(4) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds; +} + /* -- DevInfo------------------------------------------------------- */ DevInfo::DevInfo(ui::Widget *parent) : ui::Widget(parent) @@ -54,24 +63,27 @@ void DevInfo::draw() core::Entity *target = targets::current(); float d = 0; - textstream << "^Ntime: ^B" << std::fixed << std::setprecision(4) << client()->time() << '\n'; + textstream << "^Ncore ^B"; + time_to_stream(textstream, core::application()->time()); + textstream << '\n'; if (core::game()) { - textstream << "^Ngame: ^B" << core::game()->time(); + textstream << "^Ntime ^B"; + time_to_stream(textstream, core::game()->time()); } textstream << '\n'; if (core::localcontrol()) { textstream << std::fixed << std::setprecision(2) - << "^Nx:^B" << core::localcontrol()->location().x << " " - << "^Ny:^B" << core::localcontrol()->location().y << " " - << "^Nz:^B" << core::localcontrol()->location().z << '\n'; + << "^Nx ^B" << core::localcontrol()->location().x << " " + << "^Ny ^B" << core::localcontrol()->location().y << " " + << "^Nz ^B" << core::localcontrol()->location().z << '\n'; - textstream << "^Nthurst:^B " << core::localcontrol()->thrust() << " " - << "^Nspeed:^B " << core::localcontrol()->speed() << '\n'; + textstream << "^Nthurst ^B" << core::localcontrol()->thrust() << " " + << "^Nspeed ^B" << core::localcontrol()->speed() << '\n'; if (target) { d = math::distance(core::localcontrol()->location(), target->state()->location()) - target->radius() - core::localcontrol()->radius(); - textstream << "^Ndist:^B " << d << '\n'; + textstream << "^Ndist ^B" << d << '\n'; } } @@ -114,10 +126,8 @@ void Stats::draw() std::stringstream textstream; if (core::game()) { - int minutes = (int) floorf(core::game()->clientframetime() / 60.0f); - int seconds = (int) floorf( core::game()->clientframetime() - (float) minutes* 60.0f); - - textstream << "^Ntime ^B" << std::setfill(' ') << std::setw(3) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds; + textstream << "^Ntime ^B"; + time_to_stream(textstream, core::game()->clientframetime()); } textstream << std::setfill(' ') << "\n"; @@ -183,10 +193,13 @@ View::View(ui::Widget *parent) : ui::Widget(parent) draw_keypress->set_info("[bool] draw keypress key names"); // add child widgets + view_center = new ui::Bitmap(this, "pointers/center"); view_devinfo = new DevInfo(this); view_stats = new Stats(this); view_keypress = new KeyPress(this); + view_chat = new Chat(this); + // make sure the view is at the bottom of the draw stack lower(); } @@ -195,6 +208,10 @@ void View::resize() { set_size(parent()->size()); + // reposition chat widget + view_chat->set_size(font()->width()*64, font()->height()*5); + view_chat->set_location(font()->width() * 0.5f, height() *0.5f); + // reposition devinfo widget view_devinfo->set_size(font()->width()*32, font()->height()*5); view_devinfo->set_location(font()->width() * 0.5f, font()->height() * 0.5f); @@ -207,6 +224,11 @@ void View::resize() view_keypress->set_size(font()->width()*12, font()->height()*1); view_keypress->set_location(width() - view_keypress->width() - font()->width() * 0.5, height() - view_keypress->height() - font()->height() * 0.5f); + + // reposition center + view_center->set_size(pointer_size, pointer_size); + view_center->set_location((size() - view_center->size()) * 0.5f); + view_center->set_color(palette()->pointer()); } void View::draw() @@ -214,8 +236,19 @@ void View::draw() view_devinfo->set_visible(draw_devinfo->value() ? true : false); view_stats->set_visible(draw_stats->value() ? true : false); view_keypress->set_visible(draw_keypress->value() ? true : false); + + if (core::localcontrol() && (input::mouse_control || input::joystick_control) && + (render::Camera::mode() == render::Camera::Cockpit || render::Camera::mode() == render::Camera::Track)) { + view_center->set_visible(true); + } else { + view_center->set_visible(false); + } + + if (!ui::root()->active() && !has_input_focus()) { + set_focus(); + } + - //draw_world(); } /* -- namespace view ----------------------------------------------- */ @@ -599,7 +632,7 @@ void draw_cursor() return; float angle = 0; - const float pointer_size = 48.0f; + float x = (float) input::mouse_position_x() - (pointer_size / 2.0f); float y = (float) input::mouse_position_y() - (pointer_size / 2.0f); bool cursor_animated = false; @@ -614,37 +647,6 @@ void draw_cursor() render::Textures::bind("bitmaps/pointers/aim"); } else { - // draw center cursor in Cockpit and Track mode - if ((input::mouse_control || input::joystick_control) && - (render::Camera::mode() == render::Camera::Cockpit || render::Camera::mode() == render::Camera::Track)) { - - if (ui_pointercolor) { - std::stringstream colorstr(ui_pointercolor->str()); - colorstr >> color; - } - - render::Textures::bind("bitmaps/pointers/center"); - float cx = (video::width - pointer_size) /2; - float cy = (video::height - pointer_size) /2; - - render::gl::color(color); - render::gl::begin(render::gl::Quads); - - glTexCoord2f(0,0 ); - render::gl::vertex(cx,cy,0.0f); - - glTexCoord2f(1, 0); - render::gl::vertex(cx+pointer_size, cy, 0.0f); - - glTexCoord2f(1, 1); - render::gl::vertex(cx+pointer_size, cy+pointer_size, 0.0f); - - glTexCoord2f(0, 1); - render::gl::vertex(cx, cy+pointer_size, 0.0f); - - render::gl::end(); - } - if (targets::hover()) { if (ui_pointerhovercolor) { @@ -725,63 +727,7 @@ void draw_cursor() } } -void reset() -{ - using namespace render; - - // set clear color - gl::clearcolor(0.0f, 0.0f, 0.0f, 1.0f); - - // load identity matrices - gl::matrixmode(GL_MODELVIEW); - gl::loadidentity(); - - gl::matrixmode(GL_MODELVIEW); - gl::loadidentity(); - - // shading model: Gouraud (smooth, the default) - gl::shademodel(GL_SMOOTH); - //gl::shademodel(GL_FLAT); - - // lighting settings for the default light GL_LIGHT0 - GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 }; - GLfloat ambient_light[] = { 0.01f, 0.01f, 0.01f, 1.0f }; - GLfloat diffuse_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; - GLfloat specular_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; - - glLightfv(GL_LIGHT0, GL_POSITION, light_position); - glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light); - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_light); - glLightfv(GL_LIGHT0, GL_SPECULAR, specular_light); - - // GL_LIGHT0 is always enabled - gl::enable(GL_LIGHT0); - - // color tracking - glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); - - // material settings - GLfloat specular_reflectance[] = { 0.2f, 0.2f, 0.2f, 1.0f }; - glMaterialfv(GL_FRONT, GL_SPECULAR, specular_reflectance); - glMateriali(GL_FRONT, GL_SHININESS, 128); // shininess 1-128 - - // alpha blending function - gl::blendfunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - gl::disable(GL_LIGHTING); - gl::disable(GL_COLOR_MATERIAL); - - gl::cullface(GL_BACK); - gl::frontface(GL_CCW); - gl::disable(GL_CULL_FACE); - gl::disable(GL_DEPTH_TEST); - gl::disable(GL_BLEND); - - gl::disable(GL_TEXTURE_2D); - -} - -void frame(float seconds) +void frame(float elapsed) { using namespace render; @@ -793,22 +739,21 @@ void frame(float seconds) render::Stats::clear(); - if (core::application()->connected() && core::game()->serverframetime()) { + if (core::application()->connected() && core::game()->serverframetime() && core::localplayer()->zone()) { + render::Camera::frame(elapsed); + render::Camera::frustum(); - render::draw(seconds); // draw the world + render::draw(elapsed); // draw the world targets::draw(); // validate current target, render sound if (targets::current()) // draw target docks etc draw_entity_world_target(targets::current()); - } - // switch to orthographic projection to draw the GUI - gl::matrixmode(GL_PROJECTION); - gl::loadidentity(); - glOrtho(0, video::width, video::height, 0, -16.0f, 16.0f); + render::Camera::ortho(); - gl::matrixmode(GL_MODELVIEW); - gl::loadidentity(); + } else { + render::Camera::ortho(); + } // draw the user interface gl::color(1.0f, 1.0f, 1.0f, 1.0f); @@ -829,9 +774,6 @@ void frame(float seconds) // draw the hud draw_hud(); - // draw the chat box - chat::draw(); - // draw the mouse cursor draw_cursor(); } diff --git a/src/client/view.h b/src/client/view.h index e5e42bc..dbbd7be 100644 --- a/src/client/view.h +++ b/src/client/view.h @@ -7,7 +7,9 @@ #define __INCLUDED_CLIENT_VIEW_H__ #include "core/zone.h" +#include "client/chat.h" #include "ui/widget.h" +#include "ui/bitmap.h" namespace client { @@ -68,14 +70,19 @@ class View : public ui::Widget public: View(ui::Widget *parent=0); + inline Chat *chat() { return view_chat; } + protected: virtual void draw(); + virtual void resize(); private: + Chat *view_chat; DevInfo *view_devinfo; Stats *view_stats; KeyPress *view_keypress; + ui::Bitmap *view_center; }; /// functions to draw the client view @@ -88,7 +95,7 @@ namespace view void shutdown(); /// draw the next frame - void frame(float seconds); + void frame(float elapsed); /// reset OpenGL state void reset(); diff --git a/src/render/camera.cc b/src/render/camera.cc index ae7cd43..108e4db 100644 --- a/src/render/camera.cc +++ b/src/render/camera.cc @@ -23,6 +23,9 @@ const float pitch_track = -15.0f; const float pitch_overview = -5.0f; float Camera::camera_aspect = 1.0f; +int Camera::camera_width = 0; +int Camera::camera_height = 0; + float Camera::camera_frustum_size = 0.5f; float Camera::camera_frustum_front = 1.0f; math::Vector3f Camera::camera_eye; @@ -45,6 +48,8 @@ float Camera::distance; void Camera::init() { camera_aspect = 1.0f; + camera_height = 0; + camera_width = 0; camera_frustum_size = 0.5f; camera_frustum_front = 1.0f; @@ -72,8 +77,10 @@ void Camera::shutdown() { } -void Camera::set_aspect(int width, int height) +void Camera::resize(int width, int height) { + camera_width = width; + camera_height = height; camera_aspect = (float) width / (float) height; } @@ -303,7 +310,7 @@ void Camera::frame(float seconds) camera_eye = camera_target - (distance * camera_axis.forward()); } -void Camera::draw() +void Camera::frustum() { // Change to the projection matrix and set our viewing volume large enough for the skysphere gl::matrixmode(GL_PROJECTION); @@ -325,6 +332,17 @@ void Camera::draw() gl::translate(-1.0f * camera_eye); } +void Camera::ortho() +{ + // switch to orthographic projection + gl::matrixmode(GL_PROJECTION); + gl::loadidentity(); + glOrtho(0, camera_width, camera_height, 0, -16.0f, 16.0f); + + gl::matrixmode(GL_MODELVIEW); + gl::loadidentity(); +} + void Camera::set_direction(float direction) { target_direction = direction; diff --git a/src/render/camera.h b/src/render/camera.h index db2db42..b19521e 100644 --- a/src/render/camera.h +++ b/src/render/camera.h @@ -46,8 +46,15 @@ public: /// progress the camera static void frame(float elapsed); - /// draw the OpenGL camera transformation - static void draw(); + /// enable frustum projection + /** The frustum projection is used to draw the world + */ + static void frustum(); + + /// enable orthographic projection + /** The ortographic projetion is used to draw the user interface + */ + static void ortho(); /// set target direction static void set_direction(float direction); @@ -64,8 +71,8 @@ public: /// set specified camera mode static void set_mode(Mode newmode); - /// set camera aspect ratio - static void set_aspect(int width, int height); + /// resize camera + static void resize(int width, int height); /// current frustum front static float frustum_front(); @@ -73,6 +80,10 @@ public: /// current frustum size (height); static float frustum_size(); + inline static int width() { return camera_width; } + + inline static int height() { return camera_height; } + private: static math::Vector3f camera_eye; static math::Vector3f camera_target; @@ -81,6 +92,8 @@ private: static float camera_aspect; static float camera_frustum_size; static float camera_frustum_front; + static int camera_width; + static int camera_height; // current and target yaw angle in XZ plane, positive is looking left static float direction_current; diff --git a/src/render/draw.cc b/src/render/draw.cc index 573587e..17c6fac 100644 --- a/src/render/draw.cc +++ b/src/render/draw.cc @@ -1024,11 +1024,6 @@ void draw_pass_spacegrid() void draw(float seconds) { zone = core::localplayer()->zone(); - if (!zone) - return; - - Camera::frame(seconds); - Camera::draw(); // draw the current camera transformation // calculate client state pass_prepare(seconds); diff --git a/src/render/render.cc b/src/render/render.cc index 3ba7e92..fbbaba1 100644 --- a/src/render/render.cc +++ b/src/render/render.cc @@ -9,15 +9,15 @@ #include #include +#include "auxiliary/functions.h" +#include "core/core.h" +#include "filesystem/filesystem.h" +#include "model/model.h" #include "render/gl.h" #include "render/dust.h" #include "render/textures.h" #include "render/tga.h" #include "render/render.h" -#include "model/model.h" - -#include "core/core.h" -#include "filesystem/filesystem.h" #include "sys/sys.h" namespace render { @@ -28,6 +28,10 @@ core::Cvar *r_grid = 0; core::Cvar *r_radius = 0; core::Cvar *r_sky = 0; core::Cvar *r_wireframe = 0; +core::Cvar *screenshotformat = 0; +core::Cvar *screenshotquality = 0; + +int screenshot_number = 0; using model::VertexArray; VertexArray *vertexarray = 0; @@ -37,6 +41,62 @@ void func_list_textures(std::string const &args) Textures::list(); } +void reset_gl() +{ + // setup our viewport. + gl::viewport(0, 0, Camera::width(), Camera::height()); + + // set clear color + gl::clearcolor(0.0f, 0.0f, 0.0f, 1.0f); + + // load identity matrices + gl::matrixmode(GL_MODELVIEW); + gl::loadidentity(); + + gl::matrixmode(GL_MODELVIEW); + gl::loadidentity(); + + // shading model: Gouraud (smooth, the default) + gl::shademodel(GL_SMOOTH); + //gl::shademodel(GL_FLAT); + + // lighting settings for the default light GL_LIGHT0 + GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 }; + GLfloat ambient_light[] = { 0.01f, 0.01f, 0.01f, 1.0f }; + GLfloat diffuse_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; + GLfloat specular_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; + + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_light); + glLightfv(GL_LIGHT0, GL_SPECULAR, specular_light); + + // GL_LIGHT0 is always enabled + gl::enable(GL_LIGHT0); + + // color tracking + glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + + // material settings + GLfloat specular_reflectance[] = { 0.2f, 0.2f, 0.2f, 1.0f }; + glMaterialfv(GL_FRONT, GL_SPECULAR, specular_reflectance); + glMateriali(GL_FRONT, GL_SHININESS, 128); // shininess 1-128 + + // alpha blending function + gl::blendfunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + gl::disable(GL_LIGHTING); + gl::disable(GL_COLOR_MATERIAL); + + gl::cullface(GL_BACK); + gl::frontface(GL_CCW); + gl::disable(GL_CULL_FACE); + gl::disable(GL_DEPTH_TEST); + gl::disable(GL_BLEND); + + gl::disable(GL_TEXTURE_2D); +} + void init() { con_print << "^BInitializing renderer..." << std::endl; @@ -72,6 +132,14 @@ void init() r_sky = core::Cvar::get("r_sky", "1", core::Cvar::Archive); r_sky->set_info("[bool] render the sky globe"); + screenshotformat = core::Cvar::get("screenshotformat", "jpg", core::Cvar::Archive); + screenshotformat->set_info("[string] screenshot format: jpg png tga"); + + screenshotquality = core::Cvar::get("screenshotquality", "85", core::Cvar::Archive); + screenshotquality->set_info("[int] screenshot jpg quality"); + + reset_gl(); + Camera::init(); Textures::init(); @@ -84,6 +152,32 @@ void init() func->set_info("list loaded textures"); } +// unload game assets (zone change) +void unload() +{ + // clear zone sky textures + for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { + core::Zone *zone = (*it).second; + if (zone->sky_texture()) { + render::Textures::unload(zone->sky_texture()); + zone->set_sky_texture(0); + } + } + + for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) { + core:: Entity *entity = (*it).second; + + if (entity->type() == core::Entity::Globe) { + core::EntityGlobe *globe = static_cast(entity); + if (globe->render_texture) { + render::Textures::unload(globe->render_texture); + globe->render_texture = 0; + } + } + } +} + +// clear all assets void clear() { // clear zone sky textures @@ -113,30 +207,7 @@ void clear() vertexarray = 0; } -void unload() -{ - // clear zone sky textures - for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { - core::Zone *zone = (*it).second; - if (zone->sky_texture()) { - render::Textures::unload(zone->sky_texture()); - zone->set_sky_texture(0); - } - } - - for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) { - core:: Entity *entity = (*it).second; - - if (entity->type() == core::Entity::Globe) { - core::EntityGlobe *globe = static_cast(entity); - if (globe->render_texture) { - render::Textures::unload(globe->render_texture); - globe->render_texture = 0; - } - } - } -} - +// reset render subsystem void reset() { clear(); @@ -151,6 +222,8 @@ void reset() (*r_arraysize) = (float) mb; vertexarray = new VertexArray(mb); + reset_gl(); + Dust::reset(); } void shutdown() @@ -170,5 +243,83 @@ void shutdown() Dust::shutdown(); } + +void screenshot() +{ + bool available = false; + std::string shortname; + std::string filename; + const int TYPETGA = 0; + const int TYPEPNG = 1; + const int TYPEJPG = 2; + int filetype = TYPETGA; + + // make sure the screenshots folder exists + filename.assign(filesystem::writedir()); + filename.append("screenshots/"); + sys::mkdir(filename); + + aux::lowercase(screenshotformat->str()); + + if ((screenshotformat->str().compare("jpg") == 0) || (screenshotformat->str().compare("jpeg") == 0)) { + filetype = TYPEJPG; + if (screenshotquality->value() < 10) { + (*screenshotquality) = 10; + } else if (screenshotquality->value() > 100) { + (*screenshotquality) = 100; + } + + } else if (screenshotformat->str().compare("png") == 0) { + filetype = TYPEPNG; + + } else if (screenshotformat->str().compare("tga") == 0) { + filetype = TYPETGA; + + } else { + filetype = TYPETGA; + (*screenshotformat) = "tga"; + } + + // find the first available screenshotxxxx + do { + std::stringstream nstr; + nstr << screenshot_number; + shortname.assign(nstr.str()); + + while(shortname.size() < 4) + shortname.insert(0, 1, '0'); + + shortname.insert(0, "screenshots/osirion"); + shortname.append("."); + shortname.append(screenshotformat->str()); + + filename.assign(filesystem::writedir()); + filename.append(shortname); + + FILE *handle = fopen(filename.c_str(), "r"); + if (handle) { + fclose(handle); + } else { + available = true; + } + screenshot_number++; + } while (!available); + + render::Image image(Camera::width(), Camera::height(), 3); + + glReadPixels(0, 0, (GLsizei) Camera::width(), (GLsizei) Camera::height(), + GL_RGB, GL_UNSIGNED_BYTE, (void *) image.data()); + + image.flip(); + + if (filetype == TYPEPNG) { + render::PNG::save(filename.c_str(), image); + } else if (filetype == TYPEJPG) { + render::JPG::save(filename.c_str(), image, (int) screenshotquality->value()); + } else if (filetype == TYPETGA) { + render::TGA::save(filename.c_str(), image); + } +} + } diff --git a/src/render/render.h b/src/render/render.h index 3faa097..cd90953 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -35,6 +35,9 @@ namespace render { /// unload game render data void unload(); + /// make a screenshot + void screenshot(); + extern core::Cvar *r_arraysize; extern core::Cvar *r_bbox; extern core::Cvar *r_grid; diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am index 5c7815b..b0f1625 100644 --- a/src/ui/Makefile.am +++ b/src/ui/Makefile.am @@ -7,9 +7,9 @@ else noinst_LTLIBRARIES = libui.la endif -noinst_HEADERS = bitmap.h button.h definitions.h font.h label.h \ +noinst_HEADERS = bitmap.h button.h container.h definitions.h font.h input.h label.h \ menu.h paint.h palette.h ui.h widget.h window.h -libui_la_SOURCES = bitmap.cc button.cc font.cc label.cc menu.cc paint.cc \ - palette.cc ui.cc widget.cc window.cc +libui_la_SOURCES = bitmap.cc button.cc font.cc input.cc label.cc menu.cc \ + paint.cc palette.cc ui.cc widget.cc window.cc libui_la_LDFLAGS = -avoid-version -no-undefined diff --git a/src/ui/bitmap.cc b/src/ui/bitmap.cc index c14ccff..fcf141c 100644 --- a/src/ui/bitmap.cc +++ b/src/ui/bitmap.cc @@ -24,13 +24,13 @@ Bitmap::Bitmap(Widget *parent, const char *texture) : Widget(parent) Bitmap::~Bitmap() {} -void Bitmap::print(size_t indent) +void Bitmap::print(const size_t indent) const { std::string marker(""); con_print << aux::pad_left(marker, indent*2) << label() << " \"" << texture() << "\"" << std::endl; } -void Bitmap::set_texture(std::string const & texture) +void Bitmap::set_texture(const std::string & texture) { bitmap_texture.assign(texture); } @@ -43,10 +43,15 @@ void Bitmap::set_texture(const char *texture) bitmap_texture.clear(); } +void Bitmap::set_color(const math::Color & color) +{ + bitmap_color.assign(color); +} + void Bitmap::draw_background() { if (bitmap_texture.size()) { - paint::color(1.0f, 1.0f, 1.0f, 1.0f); + paint::color(bitmap_color); paint::bitmap(global_location(), size(), bitmap_texture); } } diff --git a/src/ui/bitmap.h b/src/ui/bitmap.h index 1f60ef9..9bc1776 100644 --- a/src/ui/bitmap.h +++ b/src/ui/bitmap.h @@ -9,25 +9,38 @@ #include "ui/widget.h" -namespace ui { +namespace ui +{ -class Bitmap : public Widget { +class Bitmap : public Widget +{ public: - Bitmap (Widget *parent, const char *texture=0); + Bitmap(Widget *parent, const char *texture=0); ~Bitmap(); - - inline std::string const &texture() const { return bitmap_texture; } - void set_texture(std::string const & texture); + + inline std::string const &texture() const { + return bitmap_texture; + } + + inline math::Color const &color() const { + return bitmap_color; + } + + void set_texture(const std::string & texture); void set_texture(const char *texture); - + + void set_color(const math::Color &color); + /// print bitmap description - virtual void print(size_t indent); - + virtual void print(const size_t indent) const; + protected: + /// draw the bitmap as background virtual void draw_background(); - + private: std::string bitmap_texture; + math::Color bitmap_color; }; } diff --git a/src/ui/button.cc b/src/ui/button.cc index 1ac5fd8..08fd0e9 100644 --- a/src/ui/button.cc +++ b/src/ui/button.cc @@ -11,9 +11,10 @@ #include "ui/button.h" #include "core/commandbuffer.h" -namespace ui { +namespace ui +{ -Button::Button (Widget *parent, const char *text, const char *command) : Label(parent, text) +Button::Button(Widget *parent, const char *text, const char *command) : Label(parent, text) { set_label("button"); set_command(command); @@ -24,7 +25,7 @@ Button::~Button() { } -void Button::print(size_t indent) +void Button::print(const size_t indent) const { std::string marker(""); con_print << aux::pad_left(marker, indent*2) << label() << " \"" << text() << "\" \"" << command() << "\"" << std::endl; @@ -38,7 +39,7 @@ void Button::set_command(const char *command) button_command.clear(); } -void Button::set_command(std::string const &command) +void Button::set_command(const std::string &command) { button_command.assign(command); } @@ -47,12 +48,12 @@ void Button::draw_border() { if (!border()) return; - - if (has_focus()) + + if (has_mouse_focus()) paint::color(palette()->foreground()); else paint::color(palette()->border()); - + paint::border(global_location(), size()); } @@ -60,29 +61,34 @@ void Button::draw_text() { if (!text().size()) return; - - if (has_focus()) + + if (has_mouse_focus()) paint::color(palette()->highlight()); else paint::color(palette()->foreground()); - + paint::text(global_location(), size(), font(), text(), alignment()); } -bool Button::keypress(unsigned int key, unsigned int modifier) +bool Button::on_keypress(const int key, const unsigned int modifier) { return false; } -bool Button::keyrelease(unsigned int key, unsigned int modifier) +bool Button::on_keyrelease(const int key, const unsigned int modifier) { if (key == 512 + SDL_BUTTON_LEFT) { core::cmd() << button_command << std::endl; audio::play("ui/button"); return true; } - + return false; } +void Button::on_mouseover(const math::Vector2f &cursor) +{ + audio::play("ui/select"); +} + } diff --git a/src/ui/button.h b/src/ui/button.h index c074467..b1bb522 100644 --- a/src/ui/button.h +++ b/src/ui/button.h @@ -10,32 +10,45 @@ #include #include "ui/label.h" -namespace ui { +namespace ui +{ -class Button : public Label { +class Button : public Label +{ public: - Button (Widget *parent, const char *text=0, const char *command=0); + Button(Widget *parent, const char *text=0, const char *command=0); ~Button(); - - void set_command(std::string const &command); + + /// set the command this button will execute + void set_command(const std::string &command); + + /// set the command this button will execute void set_command(const char *command); - - inline std::string const & command() const { return button_command; } - + + /// the command this button executes + inline const std::string & command() const { + return button_command; + } + /// print button description - virtual void print(size_t indent); - - /// handle keyboard events - virtual bool keypress(unsigned int key, unsigned int modifier); - virtual bool keyrelease(unsigned int key, unsigned int modifier); - + virtual void print(const size_t indent) const; + + /// called when the mouse enters the widget + virtual void on_mouseover(const math::Vector2f &cursor); + + /// called when the widget receives a key press + virtual bool on_keypress(const int key, const unsigned int modifier); + + /// called when the widget receives a key release + virtual bool on_keyrelease(const int key, const unsigned int modifier); + protected: /// draw the button border virtual void draw_border(); - + /// draw the button text virtual void draw_text(); - + private: std::string button_command; }; diff --git a/src/ui/container.h b/src/ui/container.h new file mode 100644 index 0000000..77e987e --- /dev/null +++ b/src/ui/container.h @@ -0,0 +1,36 @@ +/* + ui/container.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_CONTAINER_H__ +#define __INCLUDED_UI_CONTAINER_H__ + +#include +#include "ui/widget.h" + +namespace ui { + +/// a widget containing childs of equal size +class Container : public Widget { +public: + Container(Widget *parent); + ~Container(); + + void set_margin(const float h, const float v); + void set_child_size(const float width, const float height); + +protected: + virtual void resize(); + +private: + math::Vector2f container_child_size; +}; + +} + +#endif // __INCLUDED_UI_CONTAINER_H__ +*/ + diff --git a/src/ui/definitions.h b/src/ui/definitions.h index d5d7fe9..b9b4e3c 100644 --- a/src/ui/definitions.h +++ b/src/ui/definitions.h @@ -7,18 +7,19 @@ #ifndef __INCLUDED_UI_DEFINITIONS_H__ #define __INCLUDED_UI_DEFINITIONS_H__ -namespace ui { +namespace ui +{ enum Alignment { AlignAuto = 0x0000, AlignLeft = 0x0001, AlignHCenter = 0x0002, AlignRight = 0x0004, - + AlignTop = 0x0010, AlignVCenter = 0x0020, AlignBottom = 0x0040, - + AlignCenter = AlignHCenter | AlignVCenter }; diff --git a/src/ui/font.cc b/src/ui/font.cc index 26d4155..9d721fd 100644 --- a/src/ui/font.cc +++ b/src/ui/font.cc @@ -6,7 +6,8 @@ #include "ui/font.h" -namespace ui { +namespace ui +{ Font::Font(const char *name, const float width, const float height) { @@ -23,17 +24,17 @@ void Font::set_size(const float width, const float height) font_size.assign(width, height); } -void Font::set_size(math::Vector2f const &size) +void Font::set_size(const math::Vector2f &size) { font_size.assign(size); } -void Font::set_width(float width) +void Font::set_width(const float width) { font_size.x = width; } -void Font::set_height(float height) +void Font::set_height(const float height) { font_size.y = height; } @@ -46,12 +47,10 @@ void Font::set_name(const char *name) font_name.clear(); } -void Font::set_name(std::string const & name) +void Font::set_name(const std::string & name) { font_name.assign(name); } } - - diff --git a/src/ui/font.h b/src/ui/font.h index a68dec8..1261427 100644 --- a/src/ui/font.h +++ b/src/ui/font.h @@ -10,38 +10,47 @@ #include #include "math/vector2f.h" -namespace ui { +namespace ui +{ class Font { public: Font(const char *name, const float width, const float height); ~Font(); - - inline std::string const & name() const { return font_name; } - - inline math::Vector2f const & size() const { return font_size; } - - inline float const width() const { return font_size.x; } - - inline float const height() const { return font_size.y; } - + + inline const std::string & name() const { + return font_name; + } + + inline const math::Vector2f & size() const { + return font_size; + } + + inline float width() const { + return font_size.x; + } + + inline float height() const { + return font_size.y; + } + void set_size(const float width, const float height); - - void set_size(math::Vector2f const &size); - - void set_width(float width); - - void set_height(float height); - + + void set_size(const math::Vector2f &size); + + void set_width(const float width); + + void set_height(const float height); + void set_name(const char *name); - - void set_name(std::string const & name); - + + void set_name(const std::string & name); + private: std::string font_name; - + math::Vector2f font_size; }; diff --git a/src/ui/input.cc b/src/ui/input.cc new file mode 100644 index 0000000..0c4ea32 --- /dev/null +++ b/src/ui/input.cc @@ -0,0 +1,174 @@ +/* + ui/input.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/input.h" +#include "ui/paint.h" +#include "auxiliary/functions.h" +#include "core/core.h" +#include "render/render.h" + +namespace ui +{ + +Input::Input(Widget *parent) : Widget(parent) +{ + input_text.clear(); + input_pos = 0; + + set_label("input"); + set_background(false); + set_border(false); +} + +Input::~Input() +{ +} + +void Input::clear() +{ + input_text.clear(); + input_pos = 0; +} +void Input::set_text(std::string const &text) +{ + input_text.assign(text); + input_pos = input_text.size(); +} + +void Input::set_text(const char *text) +{ + if (text) + input_text.assign(text); + else + input_text.clear(); + input_pos = input_text.size(); +} + +void Input::draw() +{ + draw_background(); + draw_border(); + + using namespace render; + size_t text_width = (size_t) width() / font()->width(); + math::Vector2f v(global_location()); + paint::color(palette()->foreground()); + + std::string firstpart(input_text.substr(0, input_pos)); + size_t draw_width = 0; + const char *c = firstpart.c_str(); + + while (*c) { + if (aux::is_color_code(c)) { + c++; + } else { + draw_width++; + } + c++; + } + + c = firstpart.c_str(); + while (*c && draw_width > text_width - 2) { + if (aux::is_color_code(c)) { + c++; + Text::setcolor(*c); + } else { + draw_width--; + } + c++; + } + + // draw the part before the cursor + if (*c) { + paint::text(v, size(), font(), std::string(c), AlignLeft | AlignVCenter); + } + + // draw the part behind the cursor + v.x += draw_width * font()->width(); + if (input_pos < input_text.size()) { + // FIXME limit to width + if (input_pos > 1 && aux::is_color_code(input_text.c_str() + input_pos -1)) { + Text::setcolor(input_text[input_pos]); + } + c = input_text.c_str() + input_pos; + paint::text(v, size(), font(), std::string(c), AlignLeft | AlignVCenter); + } + + // draw cursor + if (has_input_focus() && (core::application()->time() - ::floorf(core::application()->time())) < 0.5f) { + std::string cursor("^B"); + cursor += (char) 11; + paint::text(v, size(), font(), cursor, AlignLeft | AlignVCenter); + } +} + +bool Input::on_keypress(const int key, const unsigned int modifier) +{ + switch (key) { + case SDLK_TAB: + core::CommandBuffer::complete(input_text, input_pos); + return true; + break; + + case SDLK_HOME: + input_pos = 0; + return true; + break; + + case SDLK_END: + input_pos = input_text.size(); + return true; + break; + + case SDLK_LEFT: + if (input_pos > 0) + input_pos--; + return true; + break; + + case SDLK_RIGHT: + if (input_pos < input_text.size()) + input_pos++; + return true; + break; + + case SDLK_DELETE: + if (input_text.size() && input_pos < input_text.size()) { + input_text.erase(input_pos, 1); + } + return true; + break; + + case SDLK_BACKSPACE: + if (input_text.size() && input_pos) { + input_text.erase(input_pos-1, 1); + input_pos--; + } + return true; + break; + + default: + if ((key >= 32) && (key <175)) { + if (input_pos == input_text.size()) + input_text += (char)key; + else + input_text.insert(input_pos, 1, (char)key); + input_pos++; + return true; + } + break; + } + + return false; +} +bool Input::on_keyrelease(const int key, const unsigned int modifier) +{ + return false; +} + +} + + diff --git a/src/ui/input.h b/src/ui/input.h new file mode 100644 index 0000000..214f63e --- /dev/null +++ b/src/ui/input.h @@ -0,0 +1,55 @@ +/* + ui/input.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_INPUT_H__ +#define __INCLUDED_UI_INPUT_H__ + +#include "ui/widget.h" + +namespace ui +{ + +/// text input widget +class Input : public Widget +{ +public: + Input(Widget *parent); + ~Input(); + + /// set the text displayed by the label + void set_text(std::string const &text); + + /// set the text displayed by the label + void set_text(const char *text); + + /// return the text displayed by the label + inline std::string const &text() const { + return input_text; + } + + /// clear the text + void clear(); + +protected: + /// draw the widget + virtual void draw(); + + /// called when the widget receives a key press + virtual bool on_keypress(const int key, const unsigned int modifier); + + /// called when the widget receives a key release + virtual bool on_keyrelease(const int key, const unsigned int modifier); + +private: + + std::string input_text; + size_t input_pos; +}; + +} + +#endif // __INCLUDED_UI_INPUT_H__ + diff --git a/src/ui/label.cc b/src/ui/label.cc index 6f496fc..80e493e 100644 --- a/src/ui/label.cc +++ b/src/ui/label.cc @@ -10,7 +10,8 @@ using math::Vector2f; -namespace ui { +namespace ui +{ Label::Label(Widget *parent, const char *text) : Widget(parent) { @@ -23,7 +24,7 @@ Label::~Label() { } -void Label::print(size_t indent) +void Label::print(const size_t indent) const { std::string marker(""); con_print << aux::pad_left(marker, indent*2) << label() << " \"" << text() << "\"" << std::endl; @@ -37,12 +38,12 @@ void Label::set_text(const char *text) label_text.clear(); } -void Label::set_text(std::string const &text) +void Label::set_text(const std::string &text) { label_text.assign(text); } -void Label::set_alignment(unsigned int alignment) +void Label::set_alignment(const unsigned int alignment) { label_alignment = alignment; } @@ -50,7 +51,7 @@ void Label::set_alignment(unsigned int alignment) void Label::draw() { Widget::draw(); - + draw_text(); } @@ -58,7 +59,7 @@ void Label::draw_text() { if (!label_text.size()) return; - + paint::color(palette()->foreground()); paint::text(global_location(), size(), font(), text(), alignment()); } diff --git a/src/ui/label.h b/src/ui/label.h index 45171b2..b7925c7 100644 --- a/src/ui/label.h +++ b/src/ui/label.h @@ -10,39 +10,45 @@ #include #include "ui/widget.h" -namespace ui { +namespace ui +{ /// a widget displaying centered text -class Label : public Widget { +class Label : public Widget +{ public: Label(Widget *parent, const char *text=0); ~Label(); - + /// set the text displayed by the label - void set_text(std::string const &text); - + void set_text(const std::string &text); + /// set the text displayed by the label void set_text(const char *text); - + /// set the text alignment - void set_alignment(unsigned int alignment); - + void set_alignment(const unsigned int alignment); + /// return the text displayed by the label - inline std::string const &text() const { return label_text; } - + inline const std::string &text() const { + return label_text; + } + /// text alignment - inline unsigned int alignment() const { return label_alignment; } - + inline unsigned int alignment() const { + return label_alignment; + } + /// print label description - virtual void print(size_t indent); - + virtual void print(const size_t indent) const; + protected: /// draw the label virtual void draw(); - + /// draw the label text virtual void draw_text(); - + private: std::string label_text; unsigned int label_alignment; diff --git a/src/ui/menu.cc b/src/ui/menu.cc index af9929c..2a1829f 100644 --- a/src/ui/menu.cc +++ b/src/ui/menu.cc @@ -10,22 +10,23 @@ #include "ui/menu.h" #include "ui/ui.h" -namespace ui { +namespace ui +{ Menu::Menu(Window *parent, const char *label) : Window(parent) { set_label(label); set_border(false); - + menu_element_width = 256.0f; menu_element_height = 48.0f; menu_element_margin = 24.0f; - + menu_background = new Bitmap(this); menu_container = new Window(this); menu_container->set_border(true); menu_container->set_background(true); - + menu_container->set_label("container"); } @@ -38,41 +39,41 @@ void Menu::load() { std::string filename("menus/"); filename.append(label()); - + filesystem::IniFile ini; - + ini.open(filename); - + if (!ini.is_open()) { con_error << "Could not open " << ini.name() << std::endl; return; } - + std::string strval; Button *button = 0; Label *label = 0; while (ini.getline()) { if (ini.got_section()) { - + //con_debug << " " << ini.name() << " [" << ini.section() << "]" << std::endl; - + if (ini.got_section("menu")) { } else if (ini.got_section("button")) { button = add_button(); - + } else if (ini.got_section("label")) { label = add_label(); - + } else if (ini.got_section()) { ini.unknown_section(); } - + } else if (ini.got_key()) { - + //con_debug << " " << ini.name() << " " << ini.key() << "=" << ini.value() << std::endl; - + if (ini.in_section("menu")) { if (ini.got_key_string("background", strval)) { set_background(strval.c_str()); @@ -88,10 +89,10 @@ void Menu::load() } else if (ini.in_section("button")) { if (ini.got_key_string("text", strval)) { button->set_text(strval); - + } else if (ini.got_key_string("command", strval)) { button->set_command(strval); - + } else if (ini.got_key_string("align", strval)) { aux::to_label(strval); if (strval.compare("left") == 0) { @@ -99,7 +100,7 @@ void Menu::load() } else if (strval.compare("center") == 0) { button->set_alignment(AlignCenter); } else if (strval.compare("right") == 0) { - button->set_alignment(AlignRight | AlignVCenter); + button->set_alignment(AlignRight | AlignVCenter); } else { ini.unknown_value(); } @@ -124,10 +125,10 @@ void Menu::load() ini.unkown_key(); } } - + } } - + con_debug << " " << ini.name() << " " << children().size() << " widgets" << std::endl; ini.close(); } @@ -153,9 +154,9 @@ Button *Menu::add_button(char const *text, char const *command) void Menu::resize() { - size().assign(parent()->size()); - menu_background->size().assign(size()); - + set_size(parent()->size()); + menu_background->set_size(size()); + float n = (float) menu_container->children().size(); menu_container->set_size(1.5f * menu_element_width, n * (menu_element_height + menu_element_margin) + menu_element_height); menu_container->set_location(menu_element_width * 0.25, (height() - menu_container->height()) / 2.0f); diff --git a/src/ui/menu.h b/src/ui/menu.h index 376e069..1b4f17e 100644 --- a/src/ui/menu.h +++ b/src/ui/menu.h @@ -12,36 +12,38 @@ #include "ui/label.h" #include "ui/window.h" -namespace ui { +namespace ui +{ /// a menu container -class Menu : public Window { +class Menu : public Window +{ public: /// create a new menu Menu(Window *parent, const char * label); ~Menu(); - + /// load a menu from ini/menus/label.ini void load(); - + void set_background(const char *texture); - + Label *add_label(char const * text=0); - + Button *add_button(char const *text=0, char const *command=0); - + protected: virtual void resize(); - + private: float menu_element_width; float menu_element_height; float menu_element_margin; - + Bitmap *menu_background; Window *menu_container; - + }; } diff --git a/src/ui/paint.cc b/src/ui/paint.cc index ce5a5c2..33ee54f 100644 --- a/src/ui/paint.cc +++ b/src/ui/paint.cc @@ -11,7 +11,8 @@ #include "render/textures.h" #include "ui/paint.h" -namespace ui { +namespace ui +{ // contains the interface between the user interface and the render library namespace paint { @@ -26,10 +27,15 @@ void color(math::Color const & color) render::gl::color(color); } +void color_code(const char *c) +{ + render::Text::setcolor(*c); +} + void border(math::Vector2f const &location, math::Vector2f const &size) { using namespace render::gl; - + begin(LineLoop); vertex(location.x +1 , location.y); vertex(location.x + size.x, location.y); @@ -41,7 +47,7 @@ void border(math::Vector2f const &location, math::Vector2f const &size) void rectangle(math::Vector2f const &location, math::Vector2f const &size) { using namespace render::gl; - + begin(Quads); vertex(location.x +1 , location.y); vertex(location.x + size.x, location.y); @@ -57,39 +63,39 @@ void bitmap(math::Vector2f const &location, math::Vector2f const &size, std::str render::Textures::bind("bitmaps/" + texture); enable(GL_TEXTURE_2D); - + begin(Quads); - + glTexCoord2f(0.0f, 0.0f); vertex(location.x +1 , location.y); - + glTexCoord2f(1.0f, 0.0f); vertex(location.x + size.x, location.y); - + glTexCoord2f(1.0f, 1.0f); vertex(location.x + size.x, location.y + size.y -1); - + glTexCoord2f(0.0f, 1.0f); vertex(location.x +1, location.y + size.y - 1); end(); - + disable(GL_TEXTURE_2D); } void text_centered(math::Vector2f const &location, math::Vector2f const &size, std::string const &text, Font const *font) { using namespace render::gl; - + render::Text::setfont(font->name().c_str(), font->width(), font->height()); enable(GL_TEXTURE_2D); math::Vector2f v(location); - + v.x += (size.x - aux::text_strip(text).size() * font->width()) /2.0f; v.y += (size.y - font->height()) / 2.0f; - + render::Text::draw(v.x, v.y, text); - + disable(GL_TEXTURE_2D); } @@ -97,45 +103,45 @@ void text(math::Vector2f const &location, Font const *font, std::stringstream & { using namespace render::gl; render::Text::setfont(font->name().c_str(), font->width(), font->height()); - + // enable OpenGL textures enable(GL_TEXTURE_2D); - + render::Text::draw(location.x, location.y, textstream); - + // disable texturing disable(GL_TEXTURE_2D); } void text(math::Vector2f const &location, math::Vector2f const &size, - Font const *font, - std::string const &text, - unsigned int align) + Font const *font, + std::string const &text, + unsigned int align) { unsigned int align_horizontal = (align & 0x000F); if (!align_horizontal) align_horizontal = AlignLeft; - + unsigned int align_vertical = (align & 0x00F0); if (!align_vertical) align_vertical = AlignTop; - + // apply text font using namespace render::gl; render::Text::setfont(font->name().c_str(), font->width(), font->height()); - + // enable OpenGL textures enable(GL_TEXTURE_2D); - + // determine the width and height of the text // FIXME support multiline text float text_height = 1.0f * font->height(); float text_width = (float) aux::text_strip(text).size() * font->width(); - + // calculate drawing position math::Vector2f v(location); - - switch(align_horizontal) { + + switch (align_horizontal) { case AlignLeft: v.x += font->width(); break; @@ -147,7 +153,7 @@ void text(math::Vector2f const &location, math::Vector2f const &size, break; } - switch(align_vertical) { + switch (align_vertical) { case AlignTop: v.y += font->height()*0.5f; break; @@ -158,12 +164,12 @@ void text(math::Vector2f const &location, math::Vector2f const &size, v.y += size.y - text_height - font->height()*0.5f; break; } - + render::Text::draw(v.x, v.y, text); - + // disable texturing disable(GL_TEXTURE_2D); - + } } diff --git a/src/ui/paint.h b/src/ui/paint.h index 3128481..367b001 100644 --- a/src/ui/paint.h +++ b/src/ui/paint.h @@ -9,7 +9,8 @@ #include "ui/widget.h" -namespace ui { +namespace ui +{ /// low-level widget paint functions namespace paint { @@ -20,6 +21,9 @@ void color(float r=0.0f, float g=0.0f, float b=0.0f, float a=1.0f); /// set paint color void color(math::Color const & color); +/// set paint color +void color_code(const char *c); + /// draw a border void border(math::Vector2f const &location, math::Vector2f const &size); @@ -30,9 +34,9 @@ void rectangle(math::Vector2f const &location, math::Vector2f const &size); void bitmap(math::Vector2f const &location, math::Vector2f const &size, std::string const &texture); /// draw text -void text(math::Vector2f const &location, math::Vector2f const &size, Font const *font, std::string const &text, - unsigned int align = AlignCenter); - +void text(math::Vector2f const &location, math::Vector2f const &size, Font const *font, + std::string const &text, unsigned int align = AlignCenter); + /// draw textstream void text(math::Vector2f const &location, Font const *font, std::stringstream & textstream); diff --git a/src/ui/palette.cc b/src/ui/palette.cc index cb6ee91..20d1e93 100644 --- a/src/ui/palette.cc +++ b/src/ui/palette.cc @@ -7,14 +7,17 @@ #include "ui/palette.h" #include "sys/sys.h" -namespace ui { +namespace ui +{ Palette::Palette() { palette_foreground.assign(0.8f, 1.0f); palette_highlight.assign(1.0f, 1.0f); palette_background.assign(0.5f, 0.5f); - palette_border.assign(0.0f, 8.0f, 0.0f); + palette_border.assign(0.0f, 0.8f, 0.0f); + palette_pointer.assign(0.0f, 0.5f, 0.0f); + palette_active.assign(0.0f, 1.0f, 0.0f); } Palette::~Palette() @@ -41,6 +44,15 @@ void Palette::set_border(math::Color const &color) palette_border.assign(color); } +void Palette::set_pointer(math::Color const &color) +{ + palette_pointer.assign(color); +} + +void Palette::set_active(math::Color const &color) +{ + palette_active.assign(color); } +} diff --git a/src/ui/palette.h b/src/ui/palette.h index edd738f..9d27f95 100644 --- a/src/ui/palette.h +++ b/src/ui/palette.h @@ -9,7 +9,8 @@ #include "math/color.h" -namespace ui { +namespace ui +{ class Palette { @@ -17,28 +18,50 @@ class Palette public: Palette(); ~Palette(); - + void set_foreground(math::Color const &color); - + void set_highlight(math::Color const &color); - + void set_background(math::Color const &color); - + void set_border(math::Color const &color); - - inline math::Color const &foreground() const { return palette_foreground; } - - inline math::Color const &highlight() const { return palette_highlight; } - - inline math::Color const &background() const { return palette_background; } - - inline math::Color const &border() const { return palette_border; } - + + void set_pointer(math::Color const &color); + + void set_active(math::Color const &color); + + inline math::Color const &foreground() const { + return palette_foreground; + } + + inline math::Color const &highlight() const { + return palette_highlight; + } + + inline math::Color const &background() const { + return palette_background; + } + + inline math::Color const &border() const { + return palette_border; + } + + inline math::Color const &pointer() const { + return palette_pointer; + } + + inline math::Color const &active() const { + return palette_active; + } + private: math::Color palette_foreground; math::Color palette_highlight; math::Color palette_background; + math::Color palette_pointer; + math::Color palette_active; math::Color palette_border; }; diff --git a/src/ui/ui.cc b/src/ui/ui.cc index 8242381..8d60543 100644 --- a/src/ui/ui.cc +++ b/src/ui/ui.cc @@ -18,7 +18,10 @@ #include "ui/widget.h" #include "ui/window.h" -namespace ui { +namespace ui +{ + +/* -- static functions --------------------------------------------- */ UI *global_ui = 0; @@ -59,7 +62,7 @@ void func_ui(std::string const &args) con_warn << "User Interface not available!" << std::endl; return; } - + if (!args.size()) { help(); return; @@ -68,7 +71,7 @@ void func_ui(std::string const &args) std::string command; argstr >> command; aux::to_label(command); - + if (command.compare("help") == 0) { help(); } else if (command.compare("list") == 0) { @@ -103,28 +106,28 @@ void func_menu(std::string const &args) con_warn << "User Interface not available!" << std::endl; return; } - + if (!args.size()) { return; } std::stringstream argstr(args); std::string command; argstr >> command; - + aux::to_label(command); - + if (command.compare("hide") == 0) { root()->hide_window(); - + } else if (command.compare("close") == 0) { root()->hide_window(); - + } else if (command.compare("back") == 0) { root()->previous_window(); - + } else if (command.compare("previous") == 0) { root()->previous_window(); - + } else if (command.compare("list") == 0) { root()->list_menus(); } else { @@ -132,6 +135,8 @@ void func_menu(std::string const &args) } } +/* -- class UI ----------------------------------------------------- */ + UI *root() { return global_ui; @@ -140,28 +145,28 @@ UI *root() void init() { con_print << "^BInitializing user interface..." << std::endl; - + if (!global_ui) { global_ui = new UI(); } else { con_warn << "User interface already initialized!" << std::endl; return; } - + global_ui->load(); - + core::Func *func = core::Func::add("list_ui", func_list_ui); func->set_info("list user interface widgets"); - + func = core::Func::add("list_menu", func_list_menu); func->set_info("list available menus"); - + func = core::Func::add("ui", func_ui); func->set_info("[command] user interface functions"); - + func = core::Func::add("ui_restart", func_ui_restart); func->set_info("[command] [options] reload user interface files"); - + func = core::Func::add("menu", func_menu); func->set_info("[command] menu functions"); } @@ -169,42 +174,51 @@ void init() void shutdown() { con_print << "^BShutting down user interface..." << std::endl; - + core::Func::remove("list_ui"); core::Func::remove("list_menu"); core::Func::remove("menu"); core::Func::remove("ui"); - + if (global_ui) { delete global_ui; global_ui = 0; } } +/* -- class UI ----------------------------------------------------- */ + UI::UI() : Window(0) { set_label("root"); set_size(1024, 768); set_border(false); - + // default palette ui_palette = new Palette(); set_palette(ui_palette); - + // default fonts ui_font_small = new Font("gui", 12, 18); ui_font_large = new Font("gui", 14, 24); set_font(ui_font_small); + + ui_mouse_focus = this; + ui_input_focus = this; + set_focus(); } UI::~UI() { delete ui_palette; - + delete ui_font_small; delete ui_font_large; } +/* + remove all existing child windows and load ini/ui.ini +*/ void UI::load() { Windows::iterator it; @@ -213,47 +227,47 @@ void UI::load() remove_child(window); } window_children.clear(); - ui_focus = this; + ui_mouse_focus = this; ui_active_window = 0; - + std::string filename("ui"); - + filesystem::IniFile ini; - + ini.open(filename); - + if (!ini.is_open()) { con_error << "Could not open " << ini.name() << std::endl; return; } - + std::string strval; math::Color color; Menu *menu = 0; while (ini.getline()) { - + if (ini.got_section()) { - + //con_debug << " " << ini.name() << " [" << ini.section() << "]" << std::endl; - + if (ini.got_section("ui")) { continue; - + } else if (ini.got_section("colors")) { continue; - - } else { + + } else { ini.unknown_section(); continue; } - + } else if (ini.got_key()) { - + //con_debug << " " << ini.name() << " " << ini.key() << "=" << ini.value() << std::endl; - + if (ini.in_section("ui")) { - + if (ini.got_key_string("menu", strval)) { aux::to_label(strval); menu = new Menu(this, strval.c_str()); @@ -262,9 +276,9 @@ void UI::load() } else { ini.unkown_key(); } - + } else if (ini.in_section("colors")) { - + if (ini.got_key_color("foreground", color)) { ui_palette->set_foreground(color); continue; @@ -277,6 +291,10 @@ void UI::load() } else if (ini.got_key_color("border", color)) { ui_palette->set_border(color); continue; + } else if (ini.got_key_color("pointer", color)) { + ui_palette->set_pointer(color); + } else if (ini.got_key_color("active", color)) { + ui_palette->set_active(color); } else { ini.unkown_key(); } @@ -286,7 +304,7 @@ void UI::load() con_debug << " " << ini.name() << " " << window_children.size() << " menus" << std::endl; ini.close(); - + // fallback main menu if (!find_window("main")) { con_warn << "menu 'main' not found, using default" << std::endl; @@ -295,7 +313,7 @@ void UI::load() menu->add_button("Connect", "connect"); menu->add_button("Quit", "quit"); } - + // fallback game menu if (!find_window("game")) { con_warn << "menu 'game' not found, using default" << std::endl; @@ -306,17 +324,16 @@ void UI::load() } } -void UI::list() +void UI::list() const { size_t n = Widget::list(0); con_print << n << " user interface widgets" << std::endl; } -void UI::list_menus() +void UI::list_menus() const { - Windows::iterator it; - for (it = window_children.begin(); it != window_children.end(); it++) { - Window *window = (*it); + for (Windows::const_iterator it = window_children.begin(); it != window_children.end(); it++) { + const Window *window = (*it); con_print << " " << window->label() << std::endl; } con_print << window_children.size() << " menus" << std::endl; @@ -332,15 +349,16 @@ void UI::remove_window(Window *window) { if (ui_active_window == window) { ui_active_window = 0; - ui_focus = this; + ui_mouse_focus = this; + ui_input_focus = this; } - + Window::remove_window(window); } -Window *UI::find_window(const char *label) +Window *UI::find_window(const char *label) const { - for (Windows::iterator it = window_children.begin(); it != window_children.end(); it++) { + for (Windows::const_iterator it = window_children.begin(); it != window_children.end(); it++) { if ((*it)->label().compare(label) == 0) { return (*it); } @@ -351,19 +369,21 @@ Window *UI::find_window(const char *label) void UI::show_window(const char *label) { Window *window = find_window(label); - + if (window) { - if (ui_active_window) { - ui_active_window->hide(); - window->set_previous(ui_active_window); - } else { - window->clear_previous(); - } - ui_active_window = window; - ui_active_window->event_resize(); - ui_active_window->raise(); - ui_active_window->show(); - ui_focus = window; + if (ui_active_window) { + ui_active_window->hide(); + window->set_previous(ui_active_window); + } else { + window->clear_previous(); + } + ui_active_window = window; + ui_active_window->event_resize(); + ui_active_window->raise(); + ui_active_window->show(); + ui_active_window->set_focus(); + ui_mouse_focus = this; + ui_input_focus = this; } else { con_warn << "Unknown window '" << label << "'" << std::endl; } @@ -390,28 +410,57 @@ void UI::previous_window() void UI::frame() { - ui_focus = event_focus(mouse_cursor); + if (ui_active_window && !ui_active_window->visible()) { + ui_active_window = 0; + } + event_draw(); } -void UI::input_mouse(float x, float y) +/* -- global event handlers ---------------------------------------- */ +/* + These functions receive input events from the client input + subsystem and distributes them to the widget hierarchy +*/ +void UI::input_mouse(const float x, const float y) { mouse_cursor.assign(x, y); + Widget *f = find_mouse_focus(mouse_cursor); + if (f) { + f->event_mouse(mouse_cursor); + } + ui_mouse_focus = f; } -void UI::input_key(bool pressed, unsigned int key, unsigned int modifier) +void UI::input_key(const bool pressed, const int key, const unsigned int modifier) { - ui_focus->event_key(pressed, key, modifier); + if (key < 512) { + // keyboard keys + Widget *f = find_input_focus(); + if (f) { + f->event_key(pressed, key, modifier); + } + ui_input_focus = f; + } else { + // mosue buttons + if (ui_mouse_focus) + ui_mouse_focus->event_key(pressed, key, modifier); + } } -bool UI::keypress(unsigned int key, unsigned int modifier) +/* -- event handlers ----------------------------------------------- */ +/* + These functions handle events for this widget only. They are + the fallback input handlers +*/ +bool UI::on_keypress(const int key, const unsigned int modifier) { - return true; + return true; } -bool UI::keyrelease(unsigned int key, unsigned int modifier) +bool UI::on_keyrelease(const int key, const unsigned int modifier) { - return true; + return true; } } diff --git a/src/ui/ui.h b/src/ui/ui.h index a555e97..eb2d4e0 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -12,73 +12,96 @@ #include "ui/widget.h" #include "ui/window.h" -namespace ui { +namespace ui +{ -class UI : public Window { +class UI : public Window +{ public: /// constructor UI(); - + /// destructor ~UI(); - + /// list widgets - void list(); - + void list() const; + /// list meus - void list_menus(); - + void list_menus() const; + /// reload menu files void load(); - + /// make a window the active window void show_window(const char *label); - + /// hide the active window void hide_window(); - + /// show previous window void previous_window(); - + /// return the active window - Window *active() { return ui_active_window; } - - /// mouse cursor input - void input_mouse(float x, float y); - - /// keyboard input - void input_key(bool pressed, unsigned int key, unsigned int modifier); - + Window *active() { + return ui_active_window; + } + + /// return the widget with global mouse focus + inline Widget *mouse_focus() const { + return ui_mouse_focus; + } + + /// return the widget with global input focus + inline Widget *input_focus() const { + return ui_input_focus; + } + + /// receive global mouse movement + void input_mouse(const float x, const float y); + + /// receive global key input + void input_key(const bool pressed, const int key, const unsigned int modifier); + /// run a user interface frame void frame(); - - /// return the widget which has the focus - inline Widget *focus() { return ui_focus; } - - /* -- Fonts ------------------------------------------------ */ + + /* -- fonts ------------------------------------------------ */ + /// default small font - inline Font *font_small() { return ui_font_small; } - + inline const Font *font_small() const { + return ui_font_small; + } + /// default medium font - inline Font *font_large() { return ui_font_large; } - + inline const Font *font_large() const { + return ui_font_large; + } + protected: - Window *find_window(const char *label); - + Window *find_window(const char *label) const; + virtual void add_window(Window *window); virtual void remove_window(Window *window); - - /// handle keyboard input - virtual bool keypress(unsigned int key, unsigned int modifier); - virtual bool keyrelease(unsigned int key, unsigned int modifier); - + + /* -- event handlers --------------------------------------- */ + + /// handle keypress events + virtual bool on_keypress(const int key, const unsigned int modifier); + + /// handle key release events + virtual bool on_keyrelease(const int key, const unsigned int modifier); + private: Palette *ui_palette; Font *ui_font_small; Font *ui_font_large; - + Window *ui_active_window; - Widget *ui_focus; + Widget *ui_mouse_focus; + Widget *ui_input_focus; + + /// TODO move to separate object to handle mouse cursor drawing math::Vector2f mouse_cursor; }; diff --git a/src/ui/widget.cc b/src/ui/widget.cc index b2c436d..e17e5ab 100644 --- a/src/ui/widget.cc +++ b/src/ui/widget.cc @@ -10,22 +10,25 @@ #include "ui/ui.h" #include "ui/widget.h" -namespace ui { +namespace ui +{ -Widget::Widget(Widget *parent) { +Widget::Widget(Widget *parent) +{ + widget_focus = false; widget_visible = true; widget_border = true; widget_background = false; widget_palette = 0; widget_font = 0; widget_label.assign("widget"); - + if (!parent) { widget_parent = root(); } else { widget_parent = parent; } - + if (widget_parent) widget_parent->add_child(this); } @@ -33,39 +36,47 @@ Widget::Widget(Widget *parent) { Widget::~Widget() { for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { - delete (*it); + delete(*it); (*it) = 0; } widget_children.clear(); } -size_t Widget::list(size_t indent) +size_t Widget::list(const size_t indent) const { print(indent); size_t n = 1; - for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { + for (Children::const_iterator it = widget_children.begin(); it != widget_children.end(); it++) { n += (*it)->list(indent+1); } return n; } -void Widget::print(size_t indent) +void Widget::print(const size_t indent) const { - std::string marker(""); - con_print << aux::pad_left(marker, indent*2) << label() << std::endl; + if (indent) { + std::string marker; + if (widget_focus) + marker.assign("^B* ^N"); + else + marker.assign(" "); + con_print << aux::pad_left(marker, indent*2) << label() << std::endl; + } } -Palette const *Widget::palette() const { +const Palette *Widget::palette() const +{ if (widget_palette) { - return widget_palette; + return widget_palette; } else { return parent()->palette(); } } -Font const *Widget::font() const { +const Font *Widget::font() const +{ if (widget_font) { - return widget_font; + return widget_font; } else { return parent()->font(); } @@ -75,7 +86,7 @@ void Widget::lower() { if (!parent()) return; - + Children::iterator it = parent()->find_child(this); if (it != parent()->children().end()) { parent()->children().erase(it); @@ -87,7 +98,7 @@ void Widget::raise() { if (!parent()) return; - + Children::iterator it = parent()->find_child(this); if (it != parent()->children().end()) { parent()->children().erase(it); @@ -95,6 +106,20 @@ void Widget::raise() } } +void Widget::set_focus() +{ + if (!parent()) { + widget_focus = true; + return; + } + + for (Children::iterator it = parent()->children().begin(); it != parent()->children().end(); it++) { + (*it)->widget_focus = false; + } + + widget_focus = true; +} + void Widget::show() { widget_visible = true; @@ -108,7 +133,10 @@ void Widget::hide() void Widget::set_visible(bool visible) { - widget_visible = visible; + if (visible) + show(); + else + hide(); } void Widget::set_border(bool border) @@ -133,20 +161,26 @@ void Widget::set_label(char const *label) aux::to_label(widget_label); } -void Widget::set_palette(Palette *palette) +void Widget::set_palette(const Palette *palette) { widget_palette = palette; } -void Widget::set_font(Font *font) +void Widget::set_font(const Font *font) { widget_font = font; } -void Widget::set_location(float const x, float const y) { +void Widget::set_location(float const x, float const y) +{ widget_location.assign(x, y); } +void Widget::set_location(const math::Vector2f &location) +{ + widget_location.assign(location); +} + void Widget::set_size(float const w, float const h) { widget_size.assign(w, h); @@ -173,7 +207,7 @@ Widget::Children::iterator Widget::find_child(Widget *child) if ((*it) == child) return it; } - + return it; } @@ -184,118 +218,167 @@ void Widget::add_child(Widget *child) widget_children.push_back(child); } } - + void Widget::remove_child(Widget *child) { Children::iterator it = find_child(child); if (it != widget_children.end()) { - delete (*it); + delete(*it); widget_children.erase(it); } } -void Widget::event_resize() +Widget *Widget::find_input_focus() { - resize(); - for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { - (*it)->event_resize(); + if (!visible() || !widget_focus) + return 0; + + for (Children::const_reverse_iterator rit = widget_children.rbegin(); rit != widget_children.rend(); ++rit) { + Widget *w = (*rit); + if (w->visible() && w->widget_focus) { + Widget *f = w->find_input_focus(); + if (f) + return f; + } } + + // no child with input focus + return this; } -void Widget::resize() +Widget *Widget::find_mouse_focus(const math::Vector2f & pos) { + // this widget is not visible + if (!visible() || !size().contains(pos)) + return 0; + + // reverse-iterate children + for (Children::const_reverse_iterator rit = widget_children.rbegin(); rit != widget_children.rend(); ++rit) { + Widget *w = (*rit); + if (w->visible()) { + Widget *f = w->find_mouse_focus(pos - w->location()); + if (f) + return f; + } + + } + + // no child with mouse focus + return this; } -void Widget::event_draw() +bool Widget::has_mouse_focus() const { - if (!widget_visible) - return; - - draw(); - for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { - if ((*it)->visible()) - (*it)->event_draw(); - } + return (root()->mouse_focus() == this); } -void Widget::draw() +bool Widget::has_input_focus() const { - draw_background(); - draw_border(); + return (root()->input_focus() == this); } -void Widget::draw_background() +/* -- event distributors ------------------------------------------- */ + +bool Widget::event_key(const bool pressed, const int key, const unsigned int modifier) { - if (!widget_background) - return; + bool handled = false; + + if (pressed) { + handled = on_keypress(key, modifier); + } else { + handled = on_keyrelease(key, modifier); + } + + if (!handled && parent()) + handled = parent()->event_key(pressed, key, modifier); + + return handled; +} - paint::color(palette()->background()); - paint::rectangle(global_location(), size()); +bool Widget::event_mouse(const math::Vector2f &cursor) +{ + math::Vector2f local_cursor = to_local_coords(cursor); + bool handled = false; + + if (root()->mouse_focus() != this) { + on_mouseover(local_cursor); + } + + on_mousemove(local_cursor); + return handled; } -void Widget::draw_border() +void Widget::event_draw() { - if (!widget_border) + if (!visible()) return; - - paint::color(palette()->border()); - paint::border(global_location(), size()); + + draw(); + + for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { + if ((*it)->visible()) + (*it)->event_draw(); + } } -Widget *Widget::event_focus(math::Vector2f const & pos) +void Widget::event_resize() { - // this widget is not visible - if (!visible()) - return 0; + resize(); + for (Children::iterator it = widget_children.begin(); it != widget_children.end(); it++) { + (*it)->event_resize(); + } +} - // pos is outside this - if ((pos.x < 0) || (pos.y < 0) || (pos.x > size().x) || (pos.y > size().y)) - return 0; +/* -- event handlers ----------------------------------------------- */ - // reverse-iterate children - for (Children::reverse_iterator rit = widget_children.rbegin(); rit != widget_children.rend(); ++rit) { - Widget *w = (*rit); - if (w->visible()) { - Widget *f = w->event_focus(pos - w->location()); - if (f) - return f; - } +void Widget::on_mouseover(const math::Vector2f &cursor) +{ + return; +} - } - - // no child has focus - return this; +void Widget::on_mousemove(const math::Vector2f &cursor) +{ + return; } -bool Widget::has_focus() const +bool Widget::on_keypress(const int key, const unsigned int modifier) { - return (root()->focus() == this); + return false; } -bool Widget::event_key(bool pressed, unsigned int key, unsigned int modifier) +bool Widget::on_keyrelease(const int key, const unsigned int modifier) { - bool handled; + return false; +} - if (pressed) { - handled = keypress(key, modifier); - } else { - handled = keyrelease(key, modifier); - } +/* -- draw functions ----------------------------------------------- */ - if (!handled && parent()) - handled = parent()->event_key(pressed, key, modifier); +void Widget::resize() +{ +} - return handled; +void Widget::draw() +{ + draw_background(); + draw_border(); } -bool Widget::keypress(unsigned int key, unsigned int modifier) +void Widget::draw_background() { - return false; + if (!widget_background) + return; + + paint::color(palette()->background()); + paint::rectangle(global_location(), size()); } -bool Widget::keyrelease(unsigned int key, unsigned int modifier) +void Widget::draw_border() { - return false; + if (!widget_border) + return; + + paint::color(palette()->border()); + paint::border(global_location(), size()); } } diff --git a/src/ui/widget.h b/src/ui/widget.h index 2078134..e069220 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -20,199 +20,283 @@ #include "ui/definitions.h" #include "sys/sys.h" -namespace ui { +namespace ui +{ -class Widget { +class Widget +{ public: /// type definition for child widgets typedef std::list Children; - + /// create a new widget Widget(Widget *parent=0); - + /// destroy a widget virtual ~Widget(); - + + /// parent widget this widget belongs to + inline Widget *parent() const { + return widget_parent; + } + /* -- inspectors -------------------------------------------- */ - inline math::Vector2f &location() { return widget_location; } - - inline math::Vector2f &size() { return widget_size; } - - inline float const left() const { return widget_location.x; } - - inline float const top() const { return widget_location.y; } - inline float const width() const { return widget_size.x; } - - inline float const height() const { return widget_size.y; } - - - inline std::string const &label() const { return widget_label; } - - inline bool border() const { return widget_border; } - - inline bool background() const { return widget_background; } - - inline Widget *parent() const { return widget_parent; } - - inline bool visible() const { return widget_visible; } - - Palette const *palette() const; - - Font const *font() const; - - bool has_focus() const; - - + /// pixel coordinates of the top-left corner of this widget within its parent + inline const math::Vector2f &location() const { + return widget_location; + } + + /// size of this widget in pixels + inline const math::Vector2f &size() const { + return widget_size; + } + + /// left coordinate of location() + /** + * @see location + */ + inline float left() const { + return widget_location.x; + } + + /// top coordinate of location() + /** + * @see location + */ + inline float top() const { + return widget_location.y; + } + + /// width of the widget in pixels + /** + * @see size() + */ + inline float width() const { + return widget_size.x; + } + + /// height of the widget in pixels + /** + * @see size() + */ + inline float height() const { + return widget_size.y; + } + + /// widget label + inline const std::string &label() const { + return widget_label; + } + + /// true if this widget will draw a background + inline bool background() const { + return widget_background; + } + + /// true if this widget will draw a border + inline bool border() const { + return widget_border; + } + + /// true if this widget is visible + inline bool visible() const { + return widget_visible; + } + + /// true if this widget is not visible + inline bool hidden() const { + return !widget_visible; + } + + /// the palette used to draw this widget + const Palette *palette() const; + + /// the font used to draw this widget + const Font *font() const; + + /// return true if the widget has input focus + bool has_input_focus() const; + + /// returns true if the widget has mouse focus + bool has_mouse_focus() const; + /* -- mutators --------------------------------------------- */ - + /// raise the widget to the top of the widget stack void raise(); - + /// lower the widget to the bottom of the widget stack void lower(); - + /// show the widget - void show(); - + virtual void show(); + /// hide the widget - void hide(); - + virtual void hide(); + /// set visibility void set_visible(bool visible = true); - - /// set location of the top-left corner + + /// set input focus + void set_focus(); + + /// set location of the top-left corner, relative to the parent void set_location(float const x, float const y); - + + /// set location of the top-left corner, relative to the parent + void set_location(const math::Vector2f &location); + /// set the widgets width and height void set_size(float const w, float const h); - + /// set the widgets width and height void set_size(math::Vector2f const &size); - + /// set the widgets width void set_width(float const w); - + /// set the widgets height void set_height(float const h); - + /// set the widgets palette - void set_palette(Palette *palette); - + void set_palette(const Palette *palette); + /// set the widgets font - void set_font(Font *font); - + void set_font(const Font *font); + /// set the widgets label void set_label(std::string const &label); /// set the widgets label - void set_label(char const *label); - - void set_border(bool border); - - void set_background(bool background); - - /// resize event + void set_label(const char *label); + + /// enable or disable widget border + void set_border(bool border = true); + + ///enable or disable widget background + void set_background(bool background = true); + + /// child widgets + inline Children &children() { + return widget_children; + } + + + /* -- event distributors ----------------------------------- */ + + /// distribute resize event virtual void event_resize(); - - /// draw event + + /// distribute draw event virtual void event_draw(); - - /// keyboard event - virtual bool event_key(bool pressed, unsigned int key, unsigned int modifier); - - /// find the child widget with focus - /** @param pos local position within the widget - */ - Widget *event_focus(math::Vector2f const & pos); - - /// child widgets - inline Children &children() { return widget_children; } - + + /// distribute keyboard events + virtual bool event_key(const bool pressed, const int key, const unsigned int modifier); + + /// distribute mouse movement events + virtual bool event_mouse(const math::Vector2f &cursor); + protected: - /// handle keyboard events - /** returns true if the event was handled by this widget - */ - virtual bool keypress(unsigned int key, unsigned int modifier); - - /// handle keyboard events - /** returns true if the event was handled by this widget - */ - virtual bool keyrelease(unsigned int key, unsigned int modifier); - - /// draw the widget - virtual void draw(); - - /// resize the widget - virtual void resize(); - - /// draw the widget background - virtual void draw_background(); - - /// draw the widget border - virtual void draw_border(); - + /// find the widget that has input focus + virtual Widget *find_input_focus(); + + /// find widget that has mosue focus + /** @param cursor mouse cursor position relative to this widget's location + */ + Widget *find_mouse_focus(const math::Vector2f & cursor); + /// list widget content - size_t list(size_t indent); - + size_t list(const size_t indent) const; + /// print widget description - virtual void print(size_t indent); - + virtual void print(const size_t indent) const; + + /* -- coordinate mapping ----------------------------------- */ + /// map local coordinates to global coordinates - inline math::Vector2f to_global_coords(math::Vector2f const &local) - { + inline math::Vector2f to_global_coords(const math::Vector2f &local) { math::Vector2f v(local); Widget *parent = widget_parent; do { v -= parent->location(); - parent = parent->widget_parent; - } while(parent); + parent = parent->parent(); + } while (parent); return v; } - + /// map global coordinates to local coordinates - inline math::Vector2f to_local_coords(math::Vector2f const &global) { + inline math::Vector2f to_local_coords(const math::Vector2f &global) { math::Vector2f v(global); Widget *parent = this; while (parent) { v += parent->location(); - parent = parent->widget_parent; + parent = parent->parent(); } return v; } - + /// map local widget location to global location - inline math::Vector2f global_location() const { + inline math::Vector2f global_location() { math::Vector2f v(widget_location); Widget *parent = widget_parent; while (parent) { v += parent->location(); - parent = parent->widget_parent; + parent = parent->parent(); } return v; } - - /// remove a child widget + + /* -- event handlers --------------------------------------- */ + + /// called when the mouse receives mouse movement + virtual void on_mousemove(const math::Vector2f &cursor); + + /// called when the mouse enters the widget + virtual void on_mouseover(const math::Vector2f &cursor); + + /// called when the widget receives a key press + virtual bool on_keypress(const int key, const unsigned int modifier); + + /// called when the widget receives a key release + virtual bool on_keyrelease(const int key, const unsigned int modifier); + + /* -- draw functions --------------------------------------- */ + + /// resize event + virtual void resize(); + + /// draw the widget + virtual void draw(); + + /// draw the widget background + virtual void draw_background(); + + /// draw the widget border + virtual void draw_border(); + + void add_child(Widget *child); void remove_child(Widget *child); - + private: - bool widget_visible; - bool widget_background; - bool widget_border; - math::Vector2f widget_location; - math::Vector2f widget_size; - std::string widget_label; + bool widget_visible; + bool widget_background; + bool widget_border; + bool widget_focus; + + math::Vector2f widget_location; + math::Vector2f widget_size; + std::string widget_label; Children widget_children; - - Palette *widget_palette; - Font *widget_font; + + const Palette *widget_palette; + const Font *widget_font; Widget *widget_parent; Children::iterator find_child(Widget *child); - - void add_child(Widget *child); + }; } diff --git a/src/ui/window.cc b/src/ui/window.cc index cc9f3b5..4a60056 100644 --- a/src/ui/window.cc +++ b/src/ui/window.cc @@ -7,7 +7,8 @@ #include "ui/paint.h" #include "ui/window.h" -namespace ui { +namespace ui +{ Window::Window(Window *parent) : Widget(static_cast(parent)) { @@ -38,7 +39,7 @@ void Window::draw_border() { if (!border()) return; - + paint::color(palette()->foreground()); paint::border(global_location(), size()); } @@ -50,7 +51,7 @@ Window::Windows::iterator Window::find_window(Window *window) if ((*it) == window) return it; } - + return it; } @@ -61,7 +62,7 @@ void Window::add_window(Window *window) window_children.push_back(window); } } - + void Window::remove_window(Window *window) { Windows::iterator it = find_window(window); diff --git a/src/ui/window.h b/src/ui/window.h index a134f04..919559f 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -9,32 +9,36 @@ #include "ui/widget.h" -namespace ui { +namespace ui +{ -class Window : public Widget { +class Window : public Widget +{ public: Window(Window *parent=0); ~Window(); - + virtual void draw_border(); - + /// set the label of the previous window void set_previous(Window *previous); /// clear the label of the previous window void clear_previous(); - - inline std::string const &previous() const { return window_previous; } - + + inline const std::string &previous() const { + return window_previous; + } + protected: typedef std::list Windows; Windows window_children; - + Windows::iterator find_window(Window *window); - + virtual void add_window(Window *window); virtual void remove_window(Window *window); - + /// label of the previous window that got activated /** This label is used to implement the 'menu previous' * command -- cgit v1.2.3