Browse Source

rework mouse events propagation

Eduardo Bart 9 years ago
parent
commit
e2ea267703

+ 2
- 1
modules/game/styles/miniwindow.otui View File

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

+ 9
- 9
modules/game_inventory/inventory.lua View File

@@ -1,7 +1,7 @@
1 1
 Inventory = {}
2 2
 
3 3
 -- private variables
4
-local inventoryWindow
4
+local inventoryPanel
5 5
 local inventoryButton
6 6
 
7 7
 -- public functions
@@ -13,7 +13,7 @@ function Inventory.init()
13 13
 
14 14
   Keyboard.bindKeyDown('Ctrl+I', Inventory.toggle)
15 15
 
16
-  inventoryWindow = displayUI('inventory.otui', GameInterface.getRightPanel()):getChildById('inventoryWindow')
16
+  inventoryPanel = displayUI('inventory.otui', GameInterface.getRightPanel()):getChildById('inventoryPanel')
17 17
   inventoryButton = TopMenu.addGameToggleButton('inventoryButton', 'Inventory (Ctrl+I)', 'inventory.png', Inventory.toggle)
18 18
   inventoryButton:setOn(true)
19 19
 
@@ -30,15 +30,15 @@ function Inventory.terminate()
30 30
 
31 31
   Keyboard.unbindKeyDown('Ctrl+I')
32 32
 
33
-  inventoryWindow:destroy()
34
-  inventoryWindow = nil
33
+  inventoryPanel:destroy()
34
+  inventoryPanel = nil
35 35
   inventoryButton:destroy()
36 36
   inventoryButton = nil
37 37
 end
38 38
 
39 39
 function Inventory.toggle()
40
-  local visible = not inventoryWindow:isExplicitlyVisible()
41
-  inventoryWindow:setVisible(visible)
40
+  local visible = not inventoryPanel:isExplicitlyVisible()
41
+  inventoryPanel:setVisible(visible)
42 42
   inventoryButton:setOn(visible)
43 43
 end
44 44
 
@@ -50,16 +50,16 @@ end
50 50
 
51 51
 -- hooked events
52 52
 function Inventory.onInventoryChange(slot, item)
53
-  local itemWidget = inventoryWindow:getChildById('slot' .. slot)
53
+  local itemWidget = inventoryPanel:getChildById('slot' .. slot)
54 54
   itemWidget:setItem(item)
55 55
 end
56 56
 
57 57
 function Inventory.onFreeCapacityChange(freeCapacity)
58
-  local widget = inventoryWindow:getChildById('capacity')
58
+  local widget = inventoryPanel:getChildById('capacity')
59 59
   widget:setText("Cap:\n" .. freeCapacity)
60 60
 end
61 61
 
62 62
 function Inventory.onSoulChange(soul)
63
-  local widget = inventoryWindow:getChildById('soul')
63
+  local widget = inventoryPanel:getChildById('soul')
64 64
   widget:setText("Soul:\n" .. soul)
65 65
 end

+ 2
- 1
modules/game_inventory/inventory.otui View File

@@ -1,11 +1,12 @@
1 1
 MiniWindow
2
+  id: inventoryMiniWindow
2 3
   text: Inventory
3 4
   icon: inventory.png
4 5
   width: 192
5 6
   height: 154
6 7
 
7 8
   MiniWindowContents
8
-    id: inventoryWindow
9
+    id: inventoryPanel
9 10
 
10 11
     Item
11 12
       // head

+ 8
- 13
src/framework/application.cpp View File

@@ -56,7 +56,6 @@ Application::Application(const std::string& appName)
56 56
 {
57 57
     g_app = this;
58 58
     m_appName = appName;
59
-    m_pollCycleDelay = POLL_CYCLE_DELAY;
60 59
     m_frameSleep = 0;
61 60
 }
62 61
 
@@ -181,14 +180,11 @@ void Application::run()
181 180
     g_lua.callGlobalField("g_app", "onRun");
182 181
 
183 182
     while(!m_stopping) {
183
+        // only update the current time once per frame to gain performance
184 184
         g_clock.updateTicks();
185 185
 
186
-        // poll events every POLL_CYCLE_DELAY
187
-        // this delay exists to avoid massive polling thus increasing framerate
188
-        //if(g_clock.ticksElapsed(lastPollTicks) >= m_pollCycleDelay) {
189
-            poll();
190
-        //    lastPollTicks = g_clock.ticks();
191
-        //}
186
+        // poll all events before rendering
187
+        poll();
192 188
 
193 189
         if(m_appFlags & Fw::AppEnableGraphics && g_window.isVisible()) {
194 190
             g_graphics.beginRender();
@@ -218,15 +214,14 @@ void Application::exit()
218 214
 
219 215
 void Application::poll()
220 216
 {
221
-    // poll input events
222 217
     if(m_appFlags & Fw::AppEnableGraphics) {
218
+        // poll input events
223 219
         g_window.poll();
224
-        g_particleManager.update();
220
+        //g_particleManager.update();
225 221
     }
226 222
 
227 223
     Connection::poll();
228
-    //g_eventDispatcher.poll(true);
229
-    g_eventDispatcher.poll(true);
224
+    g_eventDispatcher.poll();
230 225
 }
231 226
 
232 227
 void Application::close()
@@ -237,10 +232,10 @@ void Application::close()
237 232
 
238 233
 void Application::render()
239 234
 {
240
-    // everything is rendered by UI components
235
+    // everything is rendered by UI components, even the game
241 236
     g_ui.render();
242 237
 
243
-    g_particleManager.render();
238
+    //g_particleManager.render();
244 239
 }
245 240
 
246 241
 void Application::resize(const Size& size)

+ 0
- 3
src/framework/application.h View File

@@ -44,12 +44,10 @@ public:
44 44
     virtual void close();
45 45
 
46 46
     void setFrameSleep(int delay) { m_frameSleep = delay; }
47
-    void setPollCycleDelay(int delay) { m_pollCycleDelay = delay; }
48 47
 
49 48
     bool isRunning() { return m_running; }
50 49
     bool isStopping() { return m_stopping; }
51 50
     int getFrameSleep() { return m_frameSleep; }
52
-    int getPollCycleDelay() { return m_pollCycleDelay; }
53 51
     const std::string& getName() { return m_appName; }
54 52
     const std::string& getVersion() { return m_appVersion; }
55 53
 
@@ -68,7 +66,6 @@ protected:
68 66
     std::string m_appBuildDate;
69 67
     int m_appFlags;
70 68
     int m_frameSleep;
71
-    int m_pollCycleDelay;
72 69
     Boolean<false> m_initialized;
73 70
     Boolean<false> m_running;
74 71
     Boolean<false> m_stopping;

+ 9
- 3
src/framework/core/eventdispatcher.cpp View File

@@ -35,7 +35,7 @@ void EventDispatcher::flush()
35 35
         m_scheduledEventList.pop();
36 36
 }
37 37
 
38
-void EventDispatcher::poll(bool allEvents)
38
+void EventDispatcher::poll()
39 39
 {
40 40
     while(!m_scheduledEventList.empty()) {
41 41
         ScheduledEventPtr scheduledEvent = m_scheduledEventList.top();
@@ -45,7 +45,11 @@ void EventDispatcher::poll(bool allEvents)
45 45
         scheduledEvent->execute();
46 46
     }
47 47
 
48
-    do {
48
+    // execute events list up to 3 times, this is needed because some events can schedule new events that would
49
+    // change the UIWidgets layout, in this case we must execute these new events before we continue rendering,
50
+    // we can't loop until the event list is empty, because this could lead to infinite loops
51
+    // if someone write a module with bad code
52
+    for(int i=0;i<3;++i) {
49 53
         m_pollEventsSize = m_eventList.size();
50 54
         if(m_pollEventsSize == 0)
51 55
             break;
@@ -54,7 +58,7 @@ void EventDispatcher::poll(bool allEvents)
54 58
             m_eventList.pop_front();
55 59
             event->execute();
56 60
         }
57
-    } while(allEvents);
61
+    }
58 62
 }
59 63
 
60 64
 ScheduledEventPtr EventDispatcher::scheduleEvent(const SimpleCallback& callback, int delay)
@@ -68,8 +72,10 @@ ScheduledEventPtr EventDispatcher::scheduleEvent(const SimpleCallback& callback,
68 72
 EventPtr EventDispatcher::addEvent(const SimpleCallback& callback, bool pushFront)
69 73
 {
70 74
     EventPtr event(new Event(callback));
75
+    // front pushing is a way to execute an event before others
71 76
     if(pushFront) {
72 77
         m_eventList.push_front(event);
78
+        // the poll event list only grows when pushing into front
73 79
         m_pollEventsSize++;
74 80
     } else
75 81
         m_eventList.push_back(event);

+ 1
- 1
src/framework/core/eventdispatcher.h View File

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

+ 0
- 2
src/framework/luafunctions.cpp View File

@@ -429,11 +429,9 @@ void Application::registerLuaFunctions()
429 429
     g_lua.registerStaticClass("g_app");
430 430
     g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app));
431 431
     g_lua.bindClassStaticFunction("g_app", "setFrameSleep", std::bind(&Application::setFrameSleep, g_app, _1));
432
-    g_lua.bindClassStaticFunction("g_app", "setPollCycleDelay", std::bind(&Application::setPollCycleDelay, g_app, _1));
433 432
     g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app));
434 433
     g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
435 434
     g_lua.bindClassStaticFunction("g_app", "getFrameSleep", std::bind(&Application::getFrameSleep, g_app));
436
-    g_lua.bindClassStaticFunction("g_app", "getPollCycleDelay", std::bind(&Application::getPollCycleDelay, g_app));
437 435
     g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app));
438 436
     g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app));
439 437
     g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));

+ 12
- 3
src/framework/ui/uimanager.cpp View File

@@ -85,7 +85,16 @@ void UIManager::inputEvent(const InputEvent& event)
85 85
                     pressedWidget = nullptr;
86 86
                 updatePressedWidget(pressedWidget, event.mousePos);
87 87
             }
88
-            m_mouseReceiver->propagateOnMousePress(event.mousePos, event.mouseButton);
88
+
89
+            m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList);
90
+            for(const UIWidgetPtr& widget : widgetList) {
91
+                if(widget->isFocusable()) {
92
+                    if(UIWidgetPtr parent = widget->getParent())
93
+                        parent->focusChild(widget, Fw::MouseFocusReason);
94
+                }
95
+                widget->onMousePress(event.mousePos, event.mouseButton);
96
+            }
97
+
89 98
             break;
90 99
         case Fw::MouseReleaseInputEvent: {
91 100
             // release dragging widget
@@ -94,7 +103,7 @@ void UIManager::inputEvent(const InputEvent& event)
94 103
                 accepted = updateDraggingWidget(nullptr, event.mousePos);
95 104
 
96 105
             if(!accepted) {
97
-                m_mouseReceiver->propagateOnMouseRelease(event.mousePos, event.mouseButton, widgetList);
106
+                m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList);
98 107
 
99 108
                 // mouse release is always fired first on the pressed widget
100 109
                 if(m_pressedWidget) {
@@ -139,7 +148,7 @@ void UIManager::inputEvent(const InputEvent& event)
139 148
             break;
140 149
         }
141 150
         case Fw::MouseWheelInputEvent:
142
-            m_mouseReceiver->propagateOnMouseWheel(event.mousePos, event.wheelDirection, widgetList);
151
+            m_mouseReceiver->propagateOnMouseEvent(event.mousePos, widgetList);
143 152
             for(const UIWidgetPtr& widget : widgetList) {
144 153
                 if(widget->onMouseWheel(event.mousePos, event.wheelDirection))
145 154
                     break;

+ 21
- 68
src/framework/ui/uiwidget.cpp View File

@@ -1483,83 +1483,36 @@ bool UIWidget::propagateOnKeyUp(uchar keyCode, int keyboardModifiers)
1483 1483
     return onKeyUp(keyCode, keyboardModifiers);
1484 1484
 }
1485 1485
 
1486
-bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton button)
1487
-{
1488
-    // do a backup of children list, because it may change while looping it
1489
-    UIWidgetPtr clickedChild;
1490
-    for(const UIWidgetPtr& child : m_children) {
1491
-        // events on hidden or disabled widgets are discarded
1492
-        if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
1493
-            continue;
1494
-
1495
-        // mouse press events only go to children that contains the mouse position
1496
-        if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) {
1497
-            clickedChild = child;
1498
-            break;
1486
+bool UIWidget::propagateOnMouseEvent(const Point& mousePos, UIWidgetList& widgetList)
1487
+{
1488
+    bool ret = false;
1489
+    if(containsChildPoint(mousePos)) {
1490
+        for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
1491
+            const UIWidgetPtr& child = *it;
1492
+            if(child->isExplicitlyEnabled() && child->isExplicitlyVisible() && child->containsPoint(mousePos)) {
1493
+                if(child->propagateOnMouseEvent(mousePos, widgetList)) {
1494
+                    ret = true;
1495
+                    break;
1496
+                }
1497
+            }
1499 1498
         }
1500 1499
     }
1501 1500
 
1502
-    if(clickedChild) {
1503
-        // focusable child gains focus when clicked
1504
-        if(clickedChild->isFocusable())
1505
-            focusChild(clickedChild, Fw::MouseFocusReason);
1506
-
1507
-        // stop propagating if the child accept the event
1508
-        if(clickedChild->propagateOnMousePress(mousePos, button))
1509
-            return true;
1510
-    }
1511
-
1512
-    // only non phatom widgets receives mouse events
1513
-    if(!isPhantom())
1514
-        return onMousePress(mousePos, button);
1515
-
1516
-    return false;
1517
-}
1518
-
1519
-void UIWidget::propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button, UIWidgetList& widgetList)
1520
-{
1521
-    // do a backup of children list, because it may change while looping it
1522
-    for(const UIWidgetPtr& child : m_children) {
1523
-        // events on hidden or disabled widgets are discarded
1524
-        if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
1525
-            continue;
1526
-
1527
-        // mouse press events only go to children that contains the mouse position
1528
-        if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) {
1529
-            child->propagateOnMouseRelease(mousePos, button, widgetList);
1530
-            break;
1531
-        }
1532
-    }
1501
+    widgetList.push_back(asUIWidget());
1533 1502
 
1534
-    // only non phatom widgets receives mouse release events
1535 1503
     if(!isPhantom())
1536
-        widgetList.push_back(asUIWidget());
1537
-}
1538
-
1539
-void UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList)
1540
-{
1541
-    for(const UIWidgetPtr& child : m_children) {
1542
-        // events on hidden or disabled widgets are discarded
1543
-        if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
1544
-            continue;
1545
-
1546
-        // mouse move events go to all children
1547
-        child->propagateOnMouseMove(mousePos, mouseMoved, widgetList);
1548
-    }
1549
-    widgetList.push_back(asUIWidget());
1504
+        ret = true;
1505
+    return ret;
1550 1506
 }
1551 1507
 
1552
-void UIWidget::propagateOnMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction, UIWidgetList& widgetList)
1508
+bool UIWidget::propagateOnMouseMove(const Point& mousePos, const Point& mouseMoved, UIWidgetList& widgetList)
1553 1509
 {
1554
-    for(const UIWidgetPtr& child : m_children) {
1555
-        // events on hidden or disabled widgets are discarded
1556
-        if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
1557
-            continue;
1558
-
1559
-        // mouse wheel events only go to children that contains the mouse position
1560
-        if(child->containsPoint(mousePos) && child == getChildByPos(mousePos))
1561
-            child->propagateOnMouseWheel(mousePos, direction, widgetList);
1510
+    for(auto it = m_children.begin(); it != m_children.end(); ++it) {
1511
+        const UIWidgetPtr& child = *it;
1512
+        if(child->isExplicitlyVisible() && child->isExplicitlyEnabled())
1513
+            child->propagateOnMouseMove(mousePos, mouseMoved, widgetList);
1562 1514
     }
1563 1515
 
1564 1516
     widgetList.push_back(asUIWidget());
1517
+    return true;
1565 1518
 }

+ 2
- 4
src/framework/ui/uiwidget.h View File

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

Loading…
Cancel
Save