rework mouse events propagation

This commit is contained in:
Eduardo Bart 2012-03-28 08:46:15 -03:00
parent 92d535f981
commit e2ea267703
11 changed files with 66 additions and 108 deletions

View File

@ -72,7 +72,8 @@ MiniWindow < UIMiniWindow
MiniWindowContents < ScrollablePanel MiniWindowContents < ScrollablePanel
anchors.fill: parent anchors.fill: parent
padding: 25 21 3 8 margin-right: 14
padding: 25 8 3 8
vertical-scrollbar: miniwindowScrollBar vertical-scrollbar: miniwindowScrollBar
BorderlessGameWindow < UIWindow BorderlessGameWindow < UIWindow

View File

@ -1,7 +1,7 @@
Inventory = {} Inventory = {}
-- private variables -- private variables
local inventoryWindow local inventoryPanel
local inventoryButton local inventoryButton
-- public functions -- public functions
@ -13,7 +13,7 @@ function Inventory.init()
Keyboard.bindKeyDown('Ctrl+I', Inventory.toggle) 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 = TopMenu.addGameToggleButton('inventoryButton', 'Inventory (Ctrl+I)', 'inventory.png', Inventory.toggle)
inventoryButton:setOn(true) inventoryButton:setOn(true)
@ -30,15 +30,15 @@ function Inventory.terminate()
Keyboard.unbindKeyDown('Ctrl+I') Keyboard.unbindKeyDown('Ctrl+I')
inventoryWindow:destroy() inventoryPanel:destroy()
inventoryWindow = nil inventoryPanel = nil
inventoryButton:destroy() inventoryButton:destroy()
inventoryButton = nil inventoryButton = nil
end end
function Inventory.toggle() function Inventory.toggle()
local visible = not inventoryWindow:isExplicitlyVisible() local visible = not inventoryPanel:isExplicitlyVisible()
inventoryWindow:setVisible(visible) inventoryPanel:setVisible(visible)
inventoryButton:setOn(visible) inventoryButton:setOn(visible)
end end
@ -50,16 +50,16 @@ end
-- hooked events -- hooked events
function Inventory.onInventoryChange(slot, item) function Inventory.onInventoryChange(slot, item)
local itemWidget = inventoryWindow:getChildById('slot' .. slot) local itemWidget = inventoryPanel:getChildById('slot' .. slot)
itemWidget:setItem(item) itemWidget:setItem(item)
end end
function Inventory.onFreeCapacityChange(freeCapacity) function Inventory.onFreeCapacityChange(freeCapacity)
local widget = inventoryWindow:getChildById('capacity') local widget = inventoryPanel:getChildById('capacity')
widget:setText("Cap:\n" .. freeCapacity) widget:setText("Cap:\n" .. freeCapacity)
end end
function Inventory.onSoulChange(soul) function Inventory.onSoulChange(soul)
local widget = inventoryWindow:getChildById('soul') local widget = inventoryPanel:getChildById('soul')
widget:setText("Soul:\n" .. soul) widget:setText("Soul:\n" .. soul)
end end

View File

@ -1,11 +1,12 @@
MiniWindow MiniWindow
id: inventoryMiniWindow
text: Inventory text: Inventory
icon: inventory.png icon: inventory.png
width: 192 width: 192
height: 154 height: 154
MiniWindowContents MiniWindowContents
id: inventoryWindow id: inventoryPanel
Item Item
// head // head

View File

@ -56,7 +56,6 @@ Application::Application(const std::string& appName)
{ {
g_app = this; g_app = this;
m_appName = appName; m_appName = appName;
m_pollCycleDelay = POLL_CYCLE_DELAY;
m_frameSleep = 0; m_frameSleep = 0;
} }
@ -181,14 +180,11 @@ void Application::run()
g_lua.callGlobalField("g_app", "onRun"); g_lua.callGlobalField("g_app", "onRun");
while(!m_stopping) { while(!m_stopping) {
// only update the current time once per frame to gain performance
g_clock.updateTicks(); g_clock.updateTicks();
// poll events every POLL_CYCLE_DELAY // poll all events before rendering
// this delay exists to avoid massive polling thus increasing framerate
//if(g_clock.ticksElapsed(lastPollTicks) >= m_pollCycleDelay) {
poll(); poll();
// lastPollTicks = g_clock.ticks();
//}
if(m_appFlags & Fw::AppEnableGraphics && g_window.isVisible()) { if(m_appFlags & Fw::AppEnableGraphics && g_window.isVisible()) {
g_graphics.beginRender(); g_graphics.beginRender();
@ -218,15 +214,14 @@ void Application::exit()
void Application::poll() void Application::poll()
{ {
// poll input events
if(m_appFlags & Fw::AppEnableGraphics) { if(m_appFlags & Fw::AppEnableGraphics) {
// poll input events
g_window.poll(); g_window.poll();
g_particleManager.update(); //g_particleManager.update();
} }
Connection::poll(); Connection::poll();
//g_eventDispatcher.poll(true); g_eventDispatcher.poll();
g_eventDispatcher.poll(true);
} }
void Application::close() void Application::close()
@ -237,10 +232,10 @@ void Application::close()
void Application::render() void Application::render()
{ {
// everything is rendered by UI components // everything is rendered by UI components, even the game
g_ui.render(); g_ui.render();
g_particleManager.render(); //g_particleManager.render();
} }
void Application::resize(const Size& size) void Application::resize(const Size& size)

View File

@ -44,12 +44,10 @@ public:
virtual void close(); virtual void close();
void setFrameSleep(int delay) { m_frameSleep = delay; } void setFrameSleep(int delay) { m_frameSleep = delay; }
void setPollCycleDelay(int delay) { m_pollCycleDelay = delay; }
bool isRunning() { return m_running; } bool isRunning() { return m_running; }
bool isStopping() { return m_stopping; } bool isStopping() { return m_stopping; }
int getFrameSleep() { return m_frameSleep; } int getFrameSleep() { return m_frameSleep; }
int getPollCycleDelay() { return m_pollCycleDelay; }
const std::string& getName() { return m_appName; } const std::string& getName() { return m_appName; }
const std::string& getVersion() { return m_appVersion; } const std::string& getVersion() { return m_appVersion; }
@ -68,7 +66,6 @@ protected:
std::string m_appBuildDate; std::string m_appBuildDate;
int m_appFlags; int m_appFlags;
int m_frameSleep; int m_frameSleep;
int m_pollCycleDelay;
Boolean<false> m_initialized; Boolean<false> m_initialized;
Boolean<false> m_running; Boolean<false> m_running;
Boolean<false> m_stopping; Boolean<false> m_stopping;

View File

@ -35,7 +35,7 @@ void EventDispatcher::flush()
m_scheduledEventList.pop(); m_scheduledEventList.pop();
} }
void EventDispatcher::poll(bool allEvents) void EventDispatcher::poll()
{ {
while(!m_scheduledEventList.empty()) { while(!m_scheduledEventList.empty()) {
ScheduledEventPtr scheduledEvent = m_scheduledEventList.top(); ScheduledEventPtr scheduledEvent = m_scheduledEventList.top();
@ -45,7 +45,11 @@ void EventDispatcher::poll(bool allEvents)
scheduledEvent->execute(); 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(); m_pollEventsSize = m_eventList.size();
if(m_pollEventsSize == 0) if(m_pollEventsSize == 0)
break; break;
@ -54,7 +58,7 @@ void EventDispatcher::poll(bool allEvents)
m_eventList.pop_front(); m_eventList.pop_front();
event->execute(); event->execute();
} }
} while(allEvents); }
} }
ScheduledEventPtr EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay) 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 EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront)
{ {
EventPtr event(new Event(callback)); EventPtr event(new Event(callback));
// front pushing is a way to execute an event before others
if(pushFront) { if(pushFront) {
m_eventList.push_front(event); m_eventList.push_front(event);
// the poll event list only grows when pushing into front
m_pollEventsSize++; m_pollEventsSize++;
} else } else
m_eventList.push_back(event); m_eventList.push_back(event);

View File

@ -73,7 +73,7 @@ class EventDispatcher
{ {
public: public:
void flush(); void flush();
void poll(bool allEvents = false); void poll();
EventPtr addEvent(const SimpleCallback& callback, bool pushFront = false); EventPtr addEvent(const SimpleCallback& callback, bool pushFront = false);
ScheduledEventPtr scheduleEvent(const SimpleCallback& callback, int delay); ScheduledEventPtr scheduleEvent(const SimpleCallback& callback, int delay);

View File

@ -429,11 +429,9 @@ void Application::registerLuaFunctions()
g_lua.registerStaticClass("g_app"); g_lua.registerStaticClass("g_app");
g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, 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", "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", "isRunning", std::bind(&Application::isRunning, g_app));
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, 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", "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", "getName", std::bind(&Application::getName, g_app));
g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, 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)); g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));

View File

@ -85,7 +85,16 @@ void UIManager::inputEvent(const InputEvent& event)
pressedWidget = nullptr; pressedWidget = nullptr;
updatePressedWidget(pressedWidget, event.mousePos); 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; break;
case Fw::MouseReleaseInputEvent: { case Fw::MouseReleaseInputEvent: {
// release dragging widget // release dragging widget
@ -94,7 +103,7 @@ void UIManager::inputEvent(const InputEvent& event)
accepted = updateDraggingWidget(nullptr, event.mousePos); accepted = updateDraggingWidget(nullptr, event.mousePos);
if(!accepted) { 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 // mouse release is always fired first on the pressed widget
if(m_pressedWidget) { if(m_pressedWidget) {
@ -139,7 +148,7 @@ void UIManager::inputEvent(const InputEvent& event)
break; break;
} }
case Fw::MouseWheelInputEvent: case Fw::MouseWheelInputEvent:
m_mouseReceiver->propagateOnMouseWheel(event.mousePos, event.wheelDirection, widgetList); m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList);
for(const UIWidgetPtr& widget : widgetList) { for(const UIWidgetPtr& widget : widgetList) {
if(widget->onMouseWheel(event.mousePos, event.wheelDirection)) if(widget->onMouseWheel(event.mousePos, event.wheelDirection))
break; break;

View File

@ -1483,83 +1483,36 @@ bool UIWidget::propagateOnKeyUp(uchar keyCode, int keyboardModifiers)
return onKeyUp(keyCode, keyboardModifiers); return onKeyUp(keyCode, keyboardModifiers);
} }
bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton button) bool UIWidget::propagateOnMouseEvent(const Point& mousePos, UIWidgetList& widgetList)
{ {
// do a backup of children list, because it may change while looping it bool ret = false;
UIWidgetPtr clickedChild; if(containsChildPoint(mousePos)) {
for(const UIWidgetPtr& child : m_children) { for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
// events on hidden or disabled widgets are discarded const UIWidgetPtr& child = *it;
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) if(child->isExplicitlyEnabled() && child->isExplicitlyVisible() && child->containsPoint(mousePos)) {
continue; if(child->propagateOnMouseEvent(mousePos, widgetList)) {
ret = true;
// mouse press events only go to children that contains the mouse position
if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) {
clickedChild = child;
break; 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;
} }
} }
// only non phatom widgets receives mouse release events
if(!isPhantom())
widgetList.push_back(asUIWidget()); widgetList.push_back(asUIWidget());
if(!isPhantom())
ret = true;
return ret;
} }
void UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList) bool UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList)
{ {
for(const UIWidgetPtr& child : m_children) { for(auto it = m_children.begin(); it != m_children.end(); ++it) {
// events on hidden or disabled widgets are discarded const UIWidgetPtr& child = *it;
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) if(child->isExplicitlyVisible() && child->isExplicitlyEnabled())
continue;
// mouse move events go to all children
child->propagateOnMouseMove(mousePos, mouseMoved, widgetList); child->propagateOnMouseMove(mousePos, mouseMoved, widgetList);
} }
widgetList.push_back(asUIWidget());
}
void UIWidget::propagateOnMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction, 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);
}
widgetList.push_back(asUIWidget()); widgetList.push_back(asUIWidget());
return true;
} }

View File

@ -201,10 +201,8 @@ protected:
bool propagateOnKeyDown(uchar keyCode, int keyboardModifiers); bool propagateOnKeyDown(uchar keyCode, int keyboardModifiers);
bool propagateOnKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks); bool propagateOnKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks);
bool propagateOnKeyUp(uchar keyCode, int keyboardModifiers); bool propagateOnKeyUp(uchar keyCode, int keyboardModifiers);
bool propagateOnMousePress(const Point& mousePos, Fw::MouseButton button); bool propagateOnMouseEvent(const Point& mousePos, UIWidgetList& widgetList);
void propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button, UIWidgetList& widgetList); bool propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList);
void propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList);
void propagateOnMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction, UIWidgetList& widgetList);
// function shortcuts // function shortcuts