Prechádzať zdrojové kódy

LibCore+LibGUI+WindowServer: Make events bubble up through ancestors

With this patch, CEvents no longer stop at the target object, but will
bubble up the ancestor chain as long as CEvent::is_accepted() is false.

To the set accepted flag, call CEvent::accept().
To clear the accepted flag, call CEvent::ignore().

Events start out in the accepted state, so if you want them to bubble
up, you have to call ignore() on them.

Using this mechanism, we now ignore non-tabbing keydown events in
GWidget, causing them to bubble up through the widget's ancestors. :^)
Andreas Kling 5 rokov pred
rodič
commit
fcc3745b02

+ 5 - 0
Libraries/LibCore/CEvent.h

@@ -31,8 +31,13 @@ public:
 
     unsigned type() const { return m_type; }
 
+    bool is_accepted() const { return m_accepted; }
+    void accept() { m_accepted = true; }
+    void ignore() { m_accepted = false; }
+
 private:
     unsigned m_type { Type::Invalid };
+    bool m_accepted { true };
 };
 
 class CDeferredInvocationEvent : public CEvent {

+ 1 - 1
Libraries/LibCore/CEventLoop.cpp

@@ -245,7 +245,7 @@ void CEventLoop::pump(WaitMode mode)
 #endif
             static_cast<CDeferredInvocationEvent&>(event).m_invokee(*receiver);
         } else {
-            receiver->event(event);
+            receiver->dispatch_event(event);
         }
 
         if (m_exit_requested) {

+ 21 - 0
Libraries/LibCore/CObject.cpp

@@ -134,3 +134,24 @@ void CObject::save_to(JsonObject& json)
     json.set("name", name());
     json.set("parent", String::format("%p", parent()));
 }
+
+bool CObject::is_ancestor_of(const CObject& other) const
+{
+    if (&other == this)
+        return false;
+    for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) {
+        if (ancestor == this)
+            return true;
+    }
+    return false;
+}
+
+void CObject::dispatch_event(CEvent& e, CObject* stay_within)
+{
+    ASSERT(!stay_within || stay_within == this || stay_within->is_ancestor_of(*this));
+    auto* target = this;
+    do {
+        target->event(e);
+        target = target->parent();
+    } while (target && target != stay_within && !e.is_accepted());
+}

+ 4 - 0
Libraries/LibCore/CObject.h

@@ -48,6 +48,8 @@ public:
     template<typename T, typename Callback>
     void for_each_child_of_type(Callback callback);
 
+    bool is_ancestor_of(const CObject&) const;
+
     CObject* parent() { return m_parent; }
     const CObject* parent() const { return m_parent; }
 
@@ -71,6 +73,8 @@ public:
 
     static IntrusiveList<CObject, &CObject::m_all_objects_list_node>& all_objects();
 
+    void dispatch_event(CEvent&, CObject* stay_within = nullptr);
+
 protected:
     explicit CObject(CObject* parent = nullptr, bool is_widget = false);
 

+ 4 - 1
Libraries/LibGUI/GAbstractButton.cpp

@@ -134,8 +134,11 @@ void GAbstractButton::leave_event(CEvent&)
 
 void GAbstractButton::keydown_event(GKeyEvent& event)
 {
-    if (event.key() == KeyCode::Key_Return)
+    if (event.key() == KeyCode::Key_Return) {
         click();
+        event.accept();
+        return;
+    }
     GWidget::keydown_event(event);
 }
 

+ 4 - 1
Libraries/LibGUI/GWidget.cpp

@@ -122,7 +122,7 @@ void GWidget::handle_paint_event(GPaintEvent& event)
             return IterationDecision::Continue;
         if (child.relative_rect().intersects(event.rect())) {
             GPaintEvent local_event(event.rect().intersected(child.relative_rect()).translated(-child.relative_position()));
-            child.event(local_event);
+            child.dispatch_event(local_event, this);
         }
         return IterationDecision::Continue;
     });
@@ -231,7 +231,10 @@ void GWidget::keydown_event(GKeyEvent& event)
             focus_previous_widget();
         else
             focus_next_widget();
+        event.accept();
+        return;
     }
+    event.ignore();
 }
 
 void GWidget::keyup_event(GKeyEvent&)

+ 9 - 9
Libraries/LibGUI/GWindow.cpp

@@ -191,14 +191,14 @@ void GWindow::event(CEvent& event)
             auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect();
             Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() };
             auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
-            m_global_cursor_tracking_widget->event(*local_event);
+            m_global_cursor_tracking_widget->dispatch_event(*local_event, this);
             return;
         }
         if (m_automatic_cursor_tracking_widget) {
             auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect();
             Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() };
             auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
-            m_automatic_cursor_tracking_widget->event(*local_event);
+            m_automatic_cursor_tracking_widget->dispatch_event(*local_event, this);
             if (mouse_event.buttons() == 0)
                 m_automatic_cursor_tracking_widget = nullptr;
             return;
@@ -212,7 +212,7 @@ void GWindow::event(CEvent& event)
         if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget)
             m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr();
         if (result.widget != m_global_cursor_tracking_widget.ptr())
-            return result.widget->event(*local_event);
+            return result.widget->dispatch_event(*local_event, this);
         return;
     }
 
@@ -241,7 +241,7 @@ void GWindow::event(CEvent& event)
         }
 
         for (auto& rect : rects)
-            m_main_widget->event(*make<GPaintEvent>(rect));
+            m_main_widget->dispatch_event(*make<GPaintEvent>(rect), this);
 
         paint_keybinds();
 
@@ -290,9 +290,9 @@ void GWindow::event(CEvent& event)
                 if (found_widget != m_keyboard_activation_targets.end()) {
                     m_keybind_mode = false;
                     auto event = make<GMouseEvent>(GEvent::MouseDown, Point(), 0, GMouseButton::Left, 0, 0);
-                    found_widget->value->event(*event);
+                    found_widget->value->dispatch_event(*event, this);
                     event = make<GMouseEvent>(GEvent::MouseUp, Point(), 0, GMouseButton::Left, 0, 0);
-                    found_widget->value->event(*event);
+                    found_widget->value->dispatch_event(*event, this);
                 } else if (m_entered_keybind.length() >= m_max_keybind_length) {
                     m_keybind_mode = false;
                 }
@@ -300,9 +300,9 @@ void GWindow::event(CEvent& event)
             }
         } else {
             if (m_focused_widget)
-                return m_focused_widget->event(event);
+                return m_focused_widget->dispatch_event(event, this);
             if (m_main_widget)
-                return m_main_widget->event(event);
+                return m_main_widget->dispatch_event(event, this);
         }
         return;
     }
@@ -315,7 +315,7 @@ void GWindow::event(CEvent& event)
 
         m_is_active = event.type() == GEvent::WindowBecameActive;
         if (m_main_widget)
-            m_main_widget->event(event);
+            m_main_widget->dispatch_event(event, this);
         if (m_focused_widget)
             m_focused_widget->update();
         return;

+ 2 - 1
Servers/WindowServer/WSWindow.cpp

@@ -178,7 +178,8 @@ void WSWindow::event(CEvent& event)
 {
     if (!m_client) {
         ASSERT(parent());
-        return parent()->event(event);
+        event.ignore();
+        return;
     }
 
     if (is_blocked_by_modal_window())

+ 4 - 4
Servers/WindowServer/WSWindowManager.cpp

@@ -664,11 +664,11 @@ void WSWindowManager::process_event_for_doubleclick(WSWindow& window, WSMouseEve
 
 void WSWindowManager::deliver_mouse_event(WSWindow& window, WSMouseEvent& event)
 {
-    window.event(event);
+    window.dispatch_event(event);
     if (event.type() == WSEvent::MouseUp) {
         process_event_for_doubleclick(window, event);
         if (event.type() == WSEvent::MouseDoubleClick)
-            window.event(event);
+            window.dispatch_event(event);
     }
 }
 
@@ -703,7 +703,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovere
 
     // FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary?
     if (!active_window_is_modal() && menubar_rect().contains(event.position())) {
-        m_menu_manager.event(event);
+        m_menu_manager.dispatch_event(event);
         return;
     }
 
@@ -915,7 +915,7 @@ void WSWindowManager::event(CEvent& event)
             return;
         }
         if (m_active_window)
-            return m_active_window->event(event);
+            return m_active_window->dispatch_event(event);
         return;
     }