diff options
-rw-r--r-- | src/gui/gui.cpp | 231 | ||||
-rw-r--r-- | src/gui/gui.h | 31 | ||||
-rw-r--r-- | src/resources/resourcemanager.cpp | 15 | ||||
-rw-r--r-- | src/resources/resourcemanager.h | 34 | ||||
-rw-r--r-- | src/resources/theme.cpp | 4 |
5 files changed, 194 insertions, 121 deletions
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ae74cab2..dd637170 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -32,18 +32,17 @@ #include "client.h" #include "configuration.h" -#include "eventlistener.h" #include "graphics.h" #include "log.h" -#include "resources/image.h" -#include "resources/imageset.h" #include "resources/resourcemanager.h" #include "resources/theme.h" #include <guichan/exception.hpp> #include <guichan/image.hpp> +#include <SDL_image.h> + // Guichan stuff Gui *gui = nullptr; SDLInput *guiInput = nullptr; @@ -54,30 +53,8 @@ gcn::Font *boldFont = nullptr; // Mono font gcn::Font *monoFont = nullptr; -class GuiConfigListener : public EventListener -{ - public: - GuiConfigListener(Gui *g): - mGui(g) - {} - - void event(Event::Channel channel, const Event &event) override - { - if (channel == Event::ConfigChannel) - { - if (event.getType() == Event::ConfigOptionChanged && - event.getString("option") == "customcursor") - { - bool bCustomCursor = config.getBoolValue("customcursor"); - mGui->setUseCustomCursor(bCustomCursor); - } - } - } - private: - Gui *mGui; -}; - Gui::Gui(Graphics *graphics) + : mCustomCursorScale(graphics->getScale()) { logger->log("Initializing GUI..."); // Set graphics @@ -146,20 +123,24 @@ Gui::Gui(Graphics *graphics) std::string("': ") + e.getMessage()); } + loadCustomCursors(); + loadSystemCursors(); + gcn::Widget::setGlobalFont(mGuiFont); // Initialize mouse cursor and listen for changes to the option setUseCustomCursor(config.getBoolValue("customcursor")); - mConfigListener = new GuiConfigListener(this); - mConfigListener->listen(Event::ConfigChannel); + + listen(Event::ConfigChannel); } Gui::~Gui() { - delete mConfigListener; + for (auto cursor : mSystemMouseCursors) + SDL_FreeCursor(cursor); - if (mMouseCursors) - mMouseCursors->decRef(); + for (auto cursor : mCustomMouseCursors) + SDL_FreeCursor(cursor); delete mGuiFont; delete boldFont; @@ -174,14 +155,9 @@ Gui::~Gui() void Gui::logic() { - // Fade out mouse cursor after extended inactivity - if (mMouseInactivityTimer < 100 * 15) - { - ++mMouseInactivityTimer; - mMouseCursorAlpha = std::min(1.0f, mMouseCursorAlpha + 0.05f); - } - else - mMouseCursorAlpha = std::max(0.0f, mMouseCursorAlpha - 0.005f); + // Hide mouse cursor after extended inactivity + if (get_elapsed_time(mLastMouseActivityTime) > 15000) + SDL_ShowCursor(SDL_DISABLE); Palette::advanceGradients(); @@ -194,40 +170,30 @@ void Gui::logic() } } -void Gui::draw() +void Gui::event(Event::Channel channel, const Event &event) { - mGraphics->_beginDraw(); - - mGraphics->pushClipArea(mTop->getDimension()); - mTop->draw(mGraphics); - mGraphics->popClipArea(); - - int mouseX; - int mouseY; - Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); - float logicalX; - float logicalY; - graphics->windowToLogical(mouseX, mouseY, logicalX, logicalY); - - if ((Client::hasMouseFocus() || button & SDL_BUTTON(1)) - && mCustomCursor - && mMouseCursorAlpha > 0.0f) + if (channel == Event::ConfigChannel) { - Image *mouseCursor = mMouseCursors->get(static_cast<size_t>(mCursorType)); - mouseCursor->setAlpha(mMouseCursorAlpha); - - static_cast<Graphics*>(mGraphics)->drawImageF( - mouseCursor, - logicalX - 15, - logicalY - 17); + if (event.getType() == Event::ConfigOptionChanged && + event.getString("option") == "customcursor") + { + setUseCustomCursor(config.getBoolValue("customcursor")); + } } - - mGraphics->_endDraw(); } bool Gui::videoResized(int width, int height) { - TrueTypeFont::updateFontScale(static_cast<Graphics*>(mGraphics)->getScale()); + const float scale = static_cast<Graphics*>(mGraphics)->getScale(); + + TrueTypeFont::updateFontScale(scale); + + if (mCustomCursorScale != scale) + { + mCustomCursorScale = scale; + loadCustomCursors(); + updateCursor(); + } auto *top = static_cast<WindowContainer*>(getTop()); @@ -247,36 +213,33 @@ void Gui::setUseCustomCursor(bool customCursor) return; mCustomCursor = customCursor; + updateCursor(); +} - if (mCustomCursor) - { - // Hide the SDL mouse cursor - SDL_ShowCursor(SDL_DISABLE); +void Gui::setCursorType(Cursor cursor) +{ + if (mCursorType == cursor) + return; - // Load the mouse cursor - mMouseCursors = Theme::getImageSetFromTheme("mouse.png", 40, 40); + mCursorType = cursor; + updateCursor(); +} - if (!mMouseCursors) - logger->error("Unable to load mouse cursors."); - } +void Gui::updateCursor() +{ + if (mCustomCursor && !mCustomMouseCursors.empty()) + SDL_SetCursor(mCustomMouseCursors[static_cast<int>(mCursorType)]); else - { - // Show the SDL mouse cursor - SDL_ShowCursor(SDL_ENABLE); - - // Unload the mouse cursor - if (mMouseCursors) - { - mMouseCursors->decRef(); - mMouseCursors = nullptr; - } - } + SDL_SetCursor(mSystemMouseCursors[static_cast<int>(mCursorType)]); } void Gui::handleMouseMoved(const gcn::MouseInput &mouseInput) { gcn::Gui::handleMouseMoved(mouseInput); - mMouseInactivityTimer = 0; + mLastMouseActivityTime = tick_time; + + // Make sure the cursor is visible + SDL_ShowCursor(SDL_ENABLE); } void Gui::handleTextInput(const TextInput &textInput) @@ -289,3 +252,97 @@ void Gui::handleTextInput(const TextInput &textInput) } } } + +static SDL_Surface *loadSurface(const std::string &path) +{ + if (SDL_RWops *file = ResourceManager::getInstance()->open(path)) + return IMG_Load_RW(file, 1); + return nullptr; +} + +void Gui::loadCustomCursors() +{ + for (auto cursor : mCustomMouseCursors) + SDL_FreeCursor(cursor); + + mCustomMouseCursors.clear(); + + const std::string cursorPath = Theme::resolveThemePath("mouse.png"); + SDL_Surface *mouseSurface = loadSurface(cursorPath); + if (!mouseSurface) + { + logger->log("Warning: Unable to load mouse cursor file (%s): %s", + cursorPath.c_str(), SDL_GetError()); + return; + } + + SDL_SetSurfaceBlendMode(mouseSurface, SDL_BLENDMODE_NONE); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const Uint32 rmask = 0xff000000; + const Uint32 gmask = 0x00ff0000; + const Uint32 bmask = 0x0000ff00; + const Uint32 amask = 0x000000ff; +#else + const Uint32 rmask = 0x000000ff; + const Uint32 gmask = 0x0000ff00; + const Uint32 bmask = 0x00ff0000; + const Uint32 amask = 0xff000000; +#endif + + constexpr int cursorSize = 40; + const int targetCursorSize = cursorSize * mCustomCursorScale; + const int columns = mouseSurface->w / cursorSize; + + SDL_Surface *cursorSurface = SDL_CreateRGBSurface( + 0, targetCursorSize, targetCursorSize, 32, + rmask, gmask, bmask, amask); + + for (int i = 0; i <= static_cast<int>(Cursor::DOWN); ++i) + { + int x = i % columns * cursorSize; + int y = i / columns * cursorSize; + + SDL_Rect srcrect = { x, y, cursorSize, cursorSize }; + SDL_Rect dstrect = { 0, 0, targetCursorSize, targetCursorSize }; + SDL_BlitScaled(mouseSurface, &srcrect, cursorSurface, &dstrect); + + SDL_Cursor *cursor = SDL_CreateColorCursor(cursorSurface, + 15 * mCustomCursorScale, + 17 * mCustomCursorScale); + if (!cursor) + { + logger->log("Warning: Unable to create cursor: %s", SDL_GetError()); + } + + mCustomMouseCursors.push_back(cursor); + } + + SDL_FreeSurface(cursorSurface); + SDL_FreeSurface(mouseSurface); +} + +void Gui::loadSystemCursors() +{ + constexpr struct { + Cursor cursor; + SDL_SystemCursor systemCursor; + } cursors[] = { + { Cursor::POINTER, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::RESIZE_ACROSS, SDL_SYSTEM_CURSOR_SIZEWE }, + { Cursor::RESIZE_DOWN, SDL_SYSTEM_CURSOR_SIZENS }, + { Cursor::RESIZE_DOWN_LEFT, SDL_SYSTEM_CURSOR_SIZENESW }, + { Cursor::RESIZE_DOWN_RIGHT, SDL_SYSTEM_CURSOR_SIZENWSE }, + { Cursor::FIGHT, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::PICKUP, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::TALK, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::ACTION, SDL_SYSTEM_CURSOR_HAND }, + { Cursor::LEFT, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::UP, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::RIGHT, SDL_SYSTEM_CURSOR_ARROW }, + { Cursor::DOWN, SDL_SYSTEM_CURSOR_ARROW } + }; + + for (auto cursor : cursors) + mSystemMouseCursors.push_back(SDL_CreateSystemCursor(cursor.systemCursor)); +} diff --git a/src/gui/gui.h b/src/gui/gui.h index 29dcdef2..e5f5149a 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -22,14 +22,17 @@ #ifndef GUI_H #define GUI_H +#include "eventlistener.h" #include "guichanfwd.h" #include <guichan/gui.hpp> +#include <SDL.h> + +#include <vector> + class TextInput; class Graphics; -class GuiConfigListener; -class ImageSet; class SDLInput; /** @@ -65,7 +68,7 @@ enum class Cursor { * * \ingroup GUI */ -class Gui : public gcn::Gui +class Gui : public gcn::Gui, public EventListener { public: Gui(Graphics *screen); @@ -78,11 +81,7 @@ class Gui : public gcn::Gui */ void logic() override; - /** - * Draws the whole Gui by calling draw functions down in the - * Gui hierarchy. It also draws the mouse pointer. - */ - void draw() override; + void event(Event::Channel channel, const Event &event) override; /** * Called when the application window has been resized. @@ -115,21 +114,25 @@ class Gui : public gcn::Gui /** * Sets which cursor should be used. */ - void setCursorType(Cursor index) - { mCursorType = index; } + void setCursorType(Cursor cursor); protected: void handleMouseMoved(const gcn::MouseInput &mouseInput) override; void handleTextInput(const TextInput &textInput); private: - GuiConfigListener *mConfigListener; + void updateCursor(); + + void loadCustomCursors(); + void loadSystemCursors(); + gcn::Font *mGuiFont; /**< The global GUI font */ gcn::Font *mInfoParticleFont; /**< Font for Info Particles*/ bool mCustomCursor = false; /**< Show custom cursor */ - ImageSet *mMouseCursors = nullptr; /**< Mouse cursor images */ - float mMouseCursorAlpha = 1.0f; - int mMouseInactivityTimer = 0; + float mCustomCursorScale = 1.0f; + std::vector<SDL_Cursor *> mSystemMouseCursors; + std::vector<SDL_Cursor *> mCustomMouseCursors; + int mLastMouseActivityTime = 0; Cursor mCursorType = Cursor::POINTER; }; diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index e2979f06..f43aea41 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -230,6 +230,11 @@ std::string ResourceManager::getPath(const std::string &file) return path; } +SDL_RWops *ResourceManager::open(const std::string &path) +{ + return PHYSFSRWOPS_openRead(path.c_str()); +} + Resource *ResourceManager::get(const std::string &idPath, const std::function<Resource *()> &generator) { @@ -265,10 +270,10 @@ Resource *ResourceManager::get(const std::string &idPath, return resource; } -Resource *ResourceManager::load(const std::string &path, loader fun) +Resource *ResourceManager::get(const std::string &path, loader fun) { return get(path, [&] () -> Resource * { - if (SDL_RWops *rw = PHYSFSRWOPS_openRead(path.c_str())) + if (SDL_RWops *rw = open(path)) return fun(rw); return nullptr; }); @@ -276,12 +281,12 @@ Resource *ResourceManager::load(const std::string &path, loader fun) Music *ResourceManager::getMusic(const std::string &idPath) { - return static_cast<Music*>(load(idPath, Music::load)); + return static_cast<Music*>(get(idPath, Music::load)); } SoundEffect *ResourceManager::getSoundEffect(const std::string &idPath) { - return static_cast<SoundEffect*>(load(idPath, SoundEffect::load)); + return static_cast<SoundEffect*>(get(idPath, SoundEffect::load)); } Image *ResourceManager::getImage(const std::string &idPath) @@ -295,7 +300,7 @@ Image *ResourceManager::getImage(const std::string &idPath) d = std::make_unique<Dye>(path.substr(p + 1)); path = path.substr(0, p); } - SDL_RWops *rw = PHYSFSRWOPS_openRead(path.c_str()); + SDL_RWops *rw = open(path); if (!rw) return nullptr; diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 7a92818f..6694321c 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -105,6 +105,16 @@ class ResourceManager std::string getPath(const std::string &file); /** + * Opens a file for reading. The caller is responsible for closing the + * file. + * + * @param path The file name. + * @return A valid SDL_RWops pointer or <code>NULL</code> if the file + * could not be opened. + */ + SDL_RWops *open(const std::string &path); + + /** * Creates a resource and adds it to the resource map. * * @param idPath The resource identifier path. @@ -124,18 +134,7 @@ class ResourceManager * @return A valid resource or <code>NULL</code> if the resource could * not be loaded. */ - Resource *load(const std::string &path, loader fun); - - /** - * Copies a file from one place to another (useful for extracting - * raw files from a zip archive, for example) - * - * @param src Source file name - * @param dst Destination file name - * @return true on success, false on failure. An error message should be - * in the log file. - */ - bool copyFile(const std::string &src, const std::string &dst); + Resource *get(const std::string &path, loader fun); /** * Convenience wrapper around ResourceManager::get for loading @@ -183,6 +182,17 @@ class ResourceManager bool inflate = true); /** + * Copies a file from one place to another (useful for extracting + * raw files from a zip archive, for example) + * + * @param src Source file name + * @param dst Destination file name + * @return true on success, false on failure. An error message should be + * in the log file. + */ + bool copyFile(const std::string &src, const std::string &dst); + + /** * Retrieves the contents of a text file. */ std::vector<std::string> loadTextFile(const std::string &fileName); diff --git a/src/resources/theme.cpp b/src/resources/theme.cpp index 1db92feb..67cd6650 100644 --- a/src/resources/theme.cpp +++ b/src/resources/theme.cpp @@ -49,9 +49,7 @@ static void initDefaultThemePath() ResourceManager *resman = ResourceManager::getInstance(); defaultThemePath = branding.getStringValue("guiThemePath"); - if (!defaultThemePath.empty() && resman->isDirectory(defaultThemePath)) - return; - else + if (defaultThemePath.empty() || !resman->isDirectory(defaultThemePath)) defaultThemePath = "graphics/gui/"; } |