diff options
Diffstat (limited to 'src/gui/gui.cpp')
-rw-r--r-- | src/gui/gui.cpp | 561 |
1 files changed, 559 insertions, 2 deletions
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5914e43d9..31722797f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -20,6 +20,49 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/* _______ __ __ __ ______ __ __ _______ __ __ + * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\ + * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / / + * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / / + * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / / + * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ / + * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/ + * + * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson + * + * + * Per Larsson a.k.a finalman + * Olof Naessén a.k.a jansem/yakslem + * + * Visit: http://guichan.sourceforge.net + * + * License: (BSD) + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Guichan nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include "gui/gui.h" #include "gui/focushandler.h" @@ -41,6 +84,8 @@ #include "events/keyevent.h" #include "listeners/focuslistener.h" +#include "listeners/keylistener.h" +#include "listeners/mouselistener.h" #include "input/keydata.h" #include "input/keyinput.h" @@ -91,7 +136,23 @@ class GuiConfigListener final : public ConfigListener }; Gui::Gui() : - gcn::Gui(), + mTop(nullptr), + mGraphics(nullptr), + mInput(nullptr), + mFocusHandler(new FocusHandler), + mTabbing(true), + mKeyListeners(), + mShiftPressed(false), + mMetaPressed(false), + mControlPressed(false), + mAltPressed(false), + mLastMousePressButton(0), + mLastMousePressTimeStamp(0), + mLastMouseX(0), + mLastMouseY(0), + mClickCount(1), + mLastMouseDragButton(0), + mWidgetWithMouseQueue(), mConfigListener(new GuiConfigListener(this)), mGuiFont(), mInfoParticleFont(), @@ -262,6 +323,12 @@ Gui::~Gui() guiInput = nullptr; Theme::deleteInstance(); + + if (Widget::widgetExists(mTop)) + setTop(nullptr); + + delete mFocusHandler; + mFocusHandler = nullptr; } void Gui::logic() @@ -527,7 +594,171 @@ void Gui::setUseCustomCursor(const bool customCursor) void Gui::handleMouseMoved(const MouseInput &mouseInput) { - gcn::Gui::handleMouseMoved(mouseInput); + // Check if the mouse leaves the application window. + if (!mWidgetWithMouseQueue.empty() && (mouseInput.getX() < 0 + || mouseInput.getY() < 0 || !mTop->getDimension().isPointInRect( + mouseInput.getX(), mouseInput.getY()))) + { + // Distribute an event to all widgets in the + // "widget with mouse" queue. + while (!mWidgetWithMouseQueue.empty()) + { + Widget *const widget = mWidgetWithMouseQueue.front(); + + if (Widget::widgetExists(widget)) + { + distributeMouseEvent(widget, + MouseEvent::EXITED, + mouseInput.getButton(), + mouseInput.getX(), + mouseInput.getY(), + true, + true); + } + + mWidgetWithMouseQueue.pop_front(); + } + + mMouseInactivityTimer = 0; + return; + } + + // Check if there is a need to send mouse exited events by + // traversing the "widget with mouse" queue. + bool widgetWithMouseQueueCheckDone = mWidgetWithMouseQueue.empty(); + while (!widgetWithMouseQueueCheckDone) + { + unsigned int iterations = 0; + for (std::deque<Widget*>::iterator + iter = mWidgetWithMouseQueue.begin(); + iter != mWidgetWithMouseQueue.end(); + ++ iter) + { + Widget *const widget = *iter; + + // If a widget in the "widget with mouse queue" doesn't + // exists anymore it should be removed from the queue. + if (!Widget::widgetExists(widget)) + { + mWidgetWithMouseQueue.erase(iter); + break; + } + else + { + int x, y; + widget->getAbsolutePosition(x, y); + + if (x > mouseInput.getX() + || y > mouseInput.getY() + || x + widget->getWidth() <= mouseInput.getX() + || y + widget->getHeight() <= mouseInput.getY() + || !widget->isVisible()) + { + distributeMouseEvent(widget, + MouseEvent::EXITED, + mouseInput.getButton(), + mouseInput.getX(), + mouseInput.getY(), + true, + true); + mClickCount = 1; + mLastMousePressTimeStamp = 0; + mWidgetWithMouseQueue.erase(iter); + break; + } + } + + iterations++; + } + + widgetWithMouseQueueCheckDone = + (iterations == mWidgetWithMouseQueue.size()); + } + + // Check all widgets below the mouse to see if they are + // present in the "widget with mouse" queue. If a widget + // is not then it should be added and an entered event should + // be sent to it. + Widget* parent = getMouseEventSource( + mouseInput.getX(), mouseInput.getY()); + Widget* widget = parent; + + // If a widget has modal mouse input focus then it will + // always be returned from getMouseEventSource, but we only wan't to + // send mouse entered events if the mouse has actually entered the + // widget with modal mouse input focus, hence we need to check if + // that's the case. If it's not we should simply ignore to send any + // mouse entered events. + if (mFocusHandler->getModalMouseInputFocused() + && widget == mFocusHandler->getModalMouseInputFocused() + && Widget::widgetExists(widget)) + { + int x, y; + widget->getAbsolutePosition(x, y); + + if (x > mouseInput.getX() || y > mouseInput.getY() + || x + widget->getWidth() <= mouseInput.getX() + || y + widget->getHeight() <= mouseInput.getY()) + { + parent = nullptr; + } + } + + while (parent) + { + parent = widget->getParent(); + + // Check if the widget is present in the "widget with mouse" queue. + bool widgetIsPresentInQueue = false; + FOR_EACH (std::deque<Widget*>::const_iterator, + iter, mWidgetWithMouseQueue) + { + if (*iter == widget) + { + widgetIsPresentInQueue = true; + break; + } + } + + // Widget is not present, send an entered event and add + // it to the "widget with mouse" queue. + if (!widgetIsPresentInQueue + && Widget::widgetExists(widget)) + { + distributeMouseEvent(widget, + MouseEvent::ENTERED, + mouseInput.getButton(), + mouseInput.getX(), + mouseInput.getY(), + true, + true); + mWidgetWithMouseQueue.push_front(widget); + } + + const Widget *const swap = widget; + widget = parent; + parent = swap->getParent(); + } + + if (mFocusHandler->getDraggedWidget()) + { + distributeMouseEvent(mFocusHandler->getDraggedWidget(), + MouseEvent::DRAGGED, + mLastMouseDragButton, + mouseInput.getX(), + mouseInput.getY()); + } + else + { + Widget *const sourceWidget = getMouseEventSource( + mouseInput.getX(), mouseInput.getY()); + + distributeMouseEvent(sourceWidget, + MouseEvent::MOVED, + mouseInput.getButton(), + mouseInput.getX(), + mouseInput.getY()); + } mMouseInactivityTimer = 0; } @@ -893,3 +1124,329 @@ uint32_t Gui::getMouseState(int *const x, int *const y) (*y) /= scale; return res; } + +void Gui::setTop(Widget *const top) +{ + if (mTop) + mTop->_setFocusHandler(nullptr); + if (top) + top->_setFocusHandler(mFocusHandler); + + mTop = top; +} + +void Gui::setGraphics(Graphics *const graphics) +{ + mGraphics = graphics; +} + +Graphics* Gui::getGraphics() const +{ + return mGraphics; +} + +void Gui::setInput(SDLInput *const input) +{ + mInput = input; +} + +SDLInput* Gui::getInput() const +{ + return mInput; +} + +void Gui::addGlobalKeyListener(KeyListener *const keyListener) +{ + mKeyListeners.push_back(keyListener); +} + +void Gui::removeGlobalKeyListener(KeyListener *const keyListener) +{ + mKeyListeners.remove(keyListener); +} + +void Gui::handleMouseWheelMovedDown(const MouseInput& mouseInput) +{ + Widget* sourceWidget = getMouseEventSource( + mouseInput.getX(), mouseInput.getY()); + + if (mFocusHandler->getDraggedWidget()) + sourceWidget = mFocusHandler->getDraggedWidget(); + + int sourceWidgetX = 0; + int sourceWidgetY = 0; + sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); + + distributeMouseEvent(sourceWidget, + MouseEvent::WHEEL_MOVED_DOWN, + mouseInput.getButton(), + mouseInput.getX(), + mouseInput.getY()); +} + +void Gui::handleMouseWheelMovedUp(const MouseInput& mouseInput) +{ + Widget* sourceWidget = getMouseEventSource( + mouseInput.getX(), mouseInput.getY()); + + if (mFocusHandler->getDraggedWidget()) + sourceWidget = mFocusHandler->getDraggedWidget(); + + int sourceWidgetX, sourceWidgetY; + sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); + + distributeMouseEvent(sourceWidget, + MouseEvent::WHEEL_MOVED_UP, + mouseInput.getButton(), + mouseInput.getX(), + mouseInput.getY()); +} + +Widget* Gui::getWidgetAt(int x, int y) +{ + // If the widget's parent has no child then we have found the widget.. + Widget* parent = mTop; + Widget* child = mTop; + + while (child) + { + Widget *const swap = child; + int parentX, parentY; + parent->getAbsolutePosition(parentX, parentY); + child = parent->getWidgetAt(x - parentX, y - parentY); + parent = swap; + } + + return parent; +} + +Widget* Gui::getMouseEventSource(int x, int y) +{ + Widget *const widget = getWidgetAt(x, y); + if (!widget) + return nullptr; + + if (mFocusHandler && mFocusHandler->getModalMouseInputFocused() + && !widget->isModalMouseInputFocused()) + { + return mFocusHandler->getModalMouseInputFocused(); + } + + return widget; +} + +Widget* Gui::getKeyEventSource() +{ + Widget* widget = mFocusHandler->getFocused(); + + while (widget && widget->_getInternalFocusHandler() + && widget->_getInternalFocusHandler()->getFocused()) + { + widget = widget->_getInternalFocusHandler()->getFocused(); + } + + return widget; +} + +void Gui::distributeKeyEvent(KeyEvent& keyEvent) +{ + Widget* parent = keyEvent.getSource(); + Widget* widget = keyEvent.getSource(); + + if (mFocusHandler->getModalFocused() + && !widget->isModalFocused()) + { + return; + } + + if (mFocusHandler->getModalMouseInputFocused() + && !widget->isModalMouseInputFocused()) + { + return; + } + + while (parent) + { + // If the widget has been removed due to input + // cancel the distribution. + if (!Widget::widgetExists(widget)) + break; + + parent = widget->getParent(); + + if (widget->isEnabled()) + { + std::list<KeyListener*> keyListeners + = widget->_getKeyListeners(); + + // Send the event to all key listeners of the source widget. + for (std::list<KeyListener*>::const_iterator + it = keyListeners.begin(); + it != keyListeners.end(); + ++ it) + { + switch (keyEvent.getType()) + { + case KeyEvent::PRESSED: + (*it)->keyPressed(keyEvent); + break; + case KeyEvent::RELEASED: + (*it)->keyReleased(keyEvent); + break; + default: + break; + } + } + } + + const Widget *const swap = widget; + widget = parent; + parent = swap->getParent(); + + // If a non modal focused widget has been reach + // and we have modal focus cancel the distribution. + if (mFocusHandler->getModalFocused() + && !widget->isModalFocused()) + { + break; + } + } +} + +void Gui::distributeKeyEventToGlobalKeyListeners(KeyEvent& keyEvent) +{ + for (KeyListenerListIterator it = mKeyListeners.begin(); + it != mKeyListeners.end(); ++ it) + { + switch (keyEvent.getType()) + { + case KeyEvent::PRESSED: + (*it)->keyPressed(keyEvent); + break; + case KeyEvent::RELEASED: + (*it)->keyReleased(keyEvent); + break; + default: + break; + } + + if (keyEvent.isConsumed()) + break; + } +} + +void Gui::handleModalMouseInputFocus() +{ + BLOCK_START("Gui::handleModalMouseInputFocus") + // Check if modal mouse input focus has been gained by a widget. + if ((mFocusHandler->getLastWidgetWithModalMouseInputFocus() + != mFocusHandler->getModalMouseInputFocused()) + && (!mFocusHandler->getLastWidgetWithModalMouseInputFocus())) + { + handleModalFocusGained(); + mFocusHandler->setLastWidgetWithModalMouseInputFocus( + mFocusHandler->getModalMouseInputFocused()); + } + // Check if modal mouse input focus has been released. + else if ((mFocusHandler->getLastWidgetWithModalMouseInputFocus() + != mFocusHandler->getModalMouseInputFocused()) + && (mFocusHandler->getLastWidgetWithModalMouseInputFocus())) + { + handleModalFocusReleased(); + mFocusHandler->setLastWidgetWithModalMouseInputFocus(nullptr); + } + BLOCK_END("Gui::handleModalMouseInputFocus") +} + +void Gui::handleModalFocus() +{ + BLOCK_START("Gui::handleModalFocus") + // Check if modal focus has been gained by a widget. + if ((mFocusHandler->getLastWidgetWithModalFocus() + != mFocusHandler->getModalFocused()) + && (!mFocusHandler->getLastWidgetWithModalFocus())) + { + handleModalFocusGained(); + mFocusHandler->setLastWidgetWithModalFocus( + mFocusHandler->getModalFocused()); + } + // Check if modal focus has been released. + else if ((mFocusHandler->getLastWidgetWithModalFocus() + != mFocusHandler->getModalFocused()) + && (mFocusHandler->getLastWidgetWithModalFocus())) + { + handleModalFocusReleased(); + mFocusHandler->setLastWidgetWithModalFocus(nullptr); + } + BLOCK_END("Gui::handleModalFocus") +} + +void Gui::handleModalFocusGained() +{ + // Distribute an event to all widgets in the "widget with mouse" queue. + while (!mWidgetWithMouseQueue.empty()) + { + Widget *const widget = mWidgetWithMouseQueue.front(); + + if (Widget::widgetExists(widget)) + { + distributeMouseEvent(widget, + MouseEvent::EXITED, + mLastMousePressButton, + mLastMouseX, + mLastMouseY, + true, + true); + } + + mWidgetWithMouseQueue.pop_front(); + } + + mFocusHandler->setLastWidgetWithModalMouseInputFocus( + mFocusHandler->getModalMouseInputFocused()); +} + +void Gui::handleModalFocusReleased() +{ + // Check all widgets below the mouse to see if they are + // present in the "widget with mouse" queue. If a widget + // is not then it should be added and an entered event should + // be sent to it. + Widget* widget = getMouseEventSource(mLastMouseX, mLastMouseY); + Widget* parent = widget; + + while (parent) + { + parent = widget->getParent(); + + // Check if the widget is present in the "widget with mouse" queue. + bool widgetIsPresentInQueue = false; + FOR_EACH (std::deque<Widget*>::const_iterator, + iter, mWidgetWithMouseQueue) + { + if (*iter == widget) + { + widgetIsPresentInQueue = true; + break; + } + } + + // Widget is not present, send an entered event and add + // it to the "widget with mouse" queue. + if (!widgetIsPresentInQueue && Widget::widgetExists(widget)) + { + distributeMouseEvent(widget, + MouseEvent::ENTERED, + mLastMousePressButton, + mLastMouseX, + mLastMouseY, + false, + true); + mWidgetWithMouseQueue.push_front(widget); + } + + const Widget *const swap = widget; + widget = parent; + parent = swap->getParent(); + } +} |