diff --git a/modules/game/styles/miniwindow.otui b/modules/game/styles/miniwindow.otui index 2fe51486..cde84ea2 100644 --- a/modules/game/styles/miniwindow.otui +++ b/modules/game/styles/miniwindow.otui @@ -72,7 +72,8 @@ MiniWindow < UIMiniWindow MiniWindowContents < ScrollablePanel anchors.fill: parent - padding: 25 21 3 8 + margin-right: 14 + padding: 25 8 3 8 vertical-scrollbar: miniwindowScrollBar BorderlessGameWindow < UIWindow diff --git a/modules/game_inventory/inventory.lua b/modules/game_inventory/inventory.lua index 673b48a6..96bd2021 100644 --- a/modules/game_inventory/inventory.lua +++ b/modules/game_inventory/inventory.lua @@ -1,7 +1,7 @@ Inventory = {} -- private variables -local inventoryWindow +local inventoryPanel local inventoryButton -- public functions @@ -13,7 +13,7 @@ function Inventory.init() Keyboard.bindKeyDown('Ctrl+I', Inventory.toggle) - inventoryWindow = displayUI('inventory.otui', GameInterface.getRightPanel()):getChildById('inventoryWindow') + inventoryPanel = displayUI('inventory.otui', GameInterface.getRightPanel()):getChildById('inventoryPanel') inventoryButton = TopMenu.addGameToggleButton('inventoryButton', 'Inventory (Ctrl+I)', 'inventory.png', Inventory.toggle) inventoryButton:setOn(true) @@ -30,15 +30,15 @@ function Inventory.terminate() Keyboard.unbindKeyDown('Ctrl+I') - inventoryWindow:destroy() - inventoryWindow = nil + inventoryPanel:destroy() + inventoryPanel = nil inventoryButton:destroy() inventoryButton = nil end function Inventory.toggle() - local visible = not inventoryWindow:isExplicitlyVisible() - inventoryWindow:setVisible(visible) + local visible = not inventoryPanel:isExplicitlyVisible() + inventoryPanel:setVisible(visible) inventoryButton:setOn(visible) end @@ -50,16 +50,16 @@ end -- hooked events function Inventory.onInventoryChange(slot, item) - local itemWidget = inventoryWindow:getChildById('slot' .. slot) + local itemWidget = inventoryPanel:getChildById('slot' .. slot) itemWidget:setItem(item) end function Inventory.onFreeCapacityChange(freeCapacity) - local widget = inventoryWindow:getChildById('capacity') + local widget = inventoryPanel:getChildById('capacity') widget:setText("Cap:\n" .. freeCapacity) end function Inventory.onSoulChange(soul) - local widget = inventoryWindow:getChildById('soul') + local widget = inventoryPanel:getChildById('soul') widget:setText("Soul:\n" .. soul) end diff --git a/modules/game_inventory/inventory.otui b/modules/game_inventory/inventory.otui index dce5182c..03cbd647 100644 --- a/modules/game_inventory/inventory.otui +++ b/modules/game_inventory/inventory.otui @@ -1,11 +1,12 @@ MiniWindow + id: inventoryMiniWindow text: Inventory icon: inventory.png width: 192 height: 154 MiniWindowContents - id: inventoryWindow + id: inventoryPanel Item // head diff --git a/src/framework/application.cpp b/src/framework/application.cpp index 9980c2d6..45a61047 100644 --- a/src/framework/application.cpp +++ b/src/framework/application.cpp @@ -56,7 +56,6 @@ Application::Application(const std::string& appName) { g_app = this; m_appName = appName; - m_pollCycleDelay = POLL_CYCLE_DELAY; m_frameSleep = 0; } @@ -181,14 +180,11 @@ void Application::run() g_lua.callGlobalField("g_app", "onRun"); while(!m_stopping) { + // only update the current time once per frame to gain performance g_clock.updateTicks(); - // poll events every POLL_CYCLE_DELAY - // this delay exists to avoid massive polling thus increasing framerate - //if(g_clock.ticksElapsed(lastPollTicks) >= m_pollCycleDelay) { - poll(); - // lastPollTicks = g_clock.ticks(); - //} + // poll all events before rendering + poll(); if(m_appFlags & Fw::AppEnableGraphics && g_window.isVisible()) { g_graphics.beginRender(); @@ -218,15 +214,14 @@ void Application::exit() void Application::poll() { - // poll input events if(m_appFlags & Fw::AppEnableGraphics) { + // poll input events g_window.poll(); - g_particleManager.update(); + //g_particleManager.update(); } Connection::poll(); - //g_eventDispatcher.poll(true); - g_eventDispatcher.poll(true); + g_eventDispatcher.poll(); } void Application::close() @@ -237,10 +232,10 @@ void Application::close() void Application::render() { - // everything is rendered by UI components + // everything is rendered by UI components, even the game g_ui.render(); - g_particleManager.render(); + //g_particleManager.render(); } void Application::resize(const Size& size) diff --git a/src/framework/application.h b/src/framework/application.h index e3cf6524..e30d4d29 100644 --- a/src/framework/application.h +++ b/src/framework/application.h @@ -44,12 +44,10 @@ public: virtual void close(); void setFrameSleep(int delay) { m_frameSleep = delay; } - void setPollCycleDelay(int delay) { m_pollCycleDelay = delay; } bool isRunning() { return m_running; } bool isStopping() { return m_stopping; } int getFrameSleep() { return m_frameSleep; } - int getPollCycleDelay() { return m_pollCycleDelay; } const std::string& getName() { return m_appName; } const std::string& getVersion() { return m_appVersion; } @@ -68,7 +66,6 @@ protected: std::string m_appBuildDate; int m_appFlags; int m_frameSleep; - int m_pollCycleDelay; Boolean m_initialized; Boolean m_running; Boolean m_stopping; diff --git a/src/framework/core/eventdispatcher.cpp b/src/framework/core/eventdispatcher.cpp index c649f07c..6a855161 100644 --- a/src/framework/core/eventdispatcher.cpp +++ b/src/framework/core/eventdispatcher.cpp @@ -35,7 +35,7 @@ void EventDispatcher::flush() m_scheduledEventList.pop(); } -void EventDispatcher::poll(bool allEvents) +void EventDispatcher::poll() { while(!m_scheduledEventList.empty()) { ScheduledEventPtr scheduledEvent = m_scheduledEventList.top(); @@ -45,7 +45,11 @@ void EventDispatcher::poll(bool allEvents) scheduledEvent->execute(); } - do { + // execute events list up to 3 times, this is needed because some events can schedule new events that would + // change the UIWidgets layout, in this case we must execute these new events before we continue rendering, + // we can't loop until the event list is empty, because this could lead to infinite loops + // if someone write a module with bad code + for(int i=0;i<3;++i) { m_pollEventsSize = m_eventList.size(); if(m_pollEventsSize == 0) break; @@ -54,7 +58,7 @@ void EventDispatcher::poll(bool allEvents) m_eventList.pop_front(); event->execute(); } - } while(allEvents); + } } ScheduledEventPtr EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay) @@ -68,8 +72,10 @@ ScheduledEventPtr EventDispatcher::scheduleEvent(const SimpleCallback& callback, EventPtr EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront) { EventPtr event(new Event(callback)); + // front pushing is a way to execute an event before others if(pushFront) { m_eventList.push_front(event); + // the poll event list only grows when pushing into front m_pollEventsSize++; } else m_eventList.push_back(event); diff --git a/src/framework/core/eventdispatcher.h b/src/framework/core/eventdispatcher.h index a571fd29..28aafe6f 100644 --- a/src/framework/core/eventdispatcher.h +++ b/src/framework/core/eventdispatcher.h @@ -73,7 +73,7 @@ class EventDispatcher { public: void flush(); - void poll(bool allEvents = false); + void poll(); EventPtr addEvent(const SimpleCallback& callback, bool pushFront = false); ScheduledEventPtr scheduleEvent(const SimpleCallback& callback, int delay); diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 8aa23052..dfb8b2a2 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -429,11 +429,9 @@ void Application::registerLuaFunctions() g_lua.registerStaticClass("g_app"); g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app)); g_lua.bindClassStaticFunction("g_app", "setFrameSleep", std::bind(&Application::setFrameSleep, g_app, _1)); - g_lua.bindClassStaticFunction("g_app", "setPollCycleDelay", std::bind(&Application::setPollCycleDelay, g_app, _1)); g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app)); g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app)); g_lua.bindClassStaticFunction("g_app", "getFrameSleep", std::bind(&Application::getFrameSleep, g_app)); - g_lua.bindClassStaticFunction("g_app", "getPollCycleDelay", std::bind(&Application::getPollCycleDelay, g_app)); g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app)); g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app)); g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app)); diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index c9e22871..7a806411 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -85,7 +85,16 @@ void UIManager::inputEvent(const InputEvent& event) pressedWidget = nullptr; updatePressedWidget(pressedWidget, event.mousePos); } - m_mouseReceiver->propagateOnMousePress(event.mousePos, event.mouseButton); + + m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList); + for(const UIWidgetPtr& widget : widgetList) { + if(widget->isFocusable()) { + if(UIWidgetPtr parent = widget->getParent()) + parent->focusChild(widget, Fw::MouseFocusReason); + } + widget->onMousePress(event.mousePos, event.mouseButton); + } + break; case Fw::MouseReleaseInputEvent: { // release dragging widget @@ -94,7 +103,7 @@ void UIManager::inputEvent(const InputEvent& event) accepted = updateDraggingWidget(nullptr, event.mousePos); if(!accepted) { - m_mouseReceiver->propagateOnMouseRelease(event.mousePos, event.mouseButton, widgetList); + m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList); // mouse release is always fired first on the pressed widget if(m_pressedWidget) { @@ -139,7 +148,7 @@ void UIManager::inputEvent(const InputEvent& event) break; } case Fw::MouseWheelInputEvent: - m_mouseReceiver->propagateOnMouseWheel(event.mousePos, event.wheelDirection, widgetList); + m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList); for(const UIWidgetPtr& widget : widgetList) { if(widget->onMouseWheel(event.mousePos, event.wheelDirection)) break; diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 1f19361f..d95f935c 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -1483,83 +1483,36 @@ bool UIWidget::propagateOnKeyUp(uchar keyCode, int keyboardModifiers) return onKeyUp(keyCode, keyboardModifiers); } -bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton button) -{ - // do a backup of children list, because it may change while looping it - UIWidgetPtr clickedChild; - for(const UIWidgetPtr& child : m_children) { - // events on hidden or disabled widgets are discarded - if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) - continue; - - // mouse press events only go to children that contains the mouse position - if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) { - clickedChild = child; - break; +bool UIWidget::propagateOnMouseEvent(const Point& mousePos, UIWidgetList& widgetList) +{ + bool ret = false; + if(containsChildPoint(mousePos)) { + for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) { + const UIWidgetPtr& child = *it; + if(child->isExplicitlyEnabled() && child->isExplicitlyVisible() && child->containsPoint(mousePos)) { + if(child->propagateOnMouseEvent(mousePos, widgetList)) { + ret = true; + break; + } + } } } - if(clickedChild) { - // focusable child gains focus when clicked - if(clickedChild->isFocusable()) - focusChild(clickedChild, Fw::MouseFocusReason); - - // stop propagating if the child accept the event - if(clickedChild->propagateOnMousePress(mousePos, button)) - return true; - } - - // only non phatom widgets receives mouse events - if(!isPhantom()) - return onMousePress(mousePos, button); - - return false; -} - -void UIWidget::propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button, UIWidgetList& widgetList) -{ - // do a backup of children list, because it may change while looping it - for(const UIWidgetPtr& child : m_children) { - // events on hidden or disabled widgets are discarded - if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) - continue; - - // mouse press events only go to children that contains the mouse position - if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) { - child->propagateOnMouseRelease(mousePos, button, widgetList); - break; - } - } + widgetList.push_back(asUIWidget()); - // only non phatom widgets receives mouse release events if(!isPhantom()) - widgetList.push_back(asUIWidget()); -} - -void UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList) -{ - for(const UIWidgetPtr& child : m_children) { - // events on hidden or disabled widgets are discarded - if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) - continue; - - // mouse move events go to all children - child->propagateOnMouseMove(mousePos, mouseMoved, widgetList); - } - widgetList.push_back(asUIWidget()); + ret = true; + return ret; } -void UIWidget::propagateOnMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction, UIWidgetList& widgetList) +bool UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList) { - for(const UIWidgetPtr& child : m_children) { - // events on hidden or disabled widgets are discarded - if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) - continue; - - // mouse wheel events only go to children that contains the mouse position - if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) - child->propagateOnMouseWheel(mousePos, direction, widgetList); + for(auto it = m_children.begin(); it != m_children.end(); ++it) { + const UIWidgetPtr& child = *it; + if(child->isExplicitlyVisible() && child->isExplicitlyEnabled()) + child->propagateOnMouseMove(mousePos, mouseMoved, widgetList); } widgetList.push_back(asUIWidget()); + return true; } diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 37a614db..c297cb41 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -201,10 +201,8 @@ protected: bool propagateOnKeyDown(uchar keyCode, int keyboardModifiers); bool propagateOnKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks); bool propagateOnKeyUp(uchar keyCode, int keyboardModifiers); - bool propagateOnMousePress(const Point& mousePos, Fw::MouseButton button); - void propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button, UIWidgetList& widgetList); - void propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList); - void propagateOnMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction, UIWidgetList& widgetList); + bool propagateOnMouseEvent(const Point& mousePos, UIWidgetList& widgetList); + bool propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList); // function shortcuts