Browse Source

LibWeb: Allow layout nodes to receive and track mouse events

To implement form controls internally in LibWeb (necessary for multi
process forms), we'll need the ability to handle events since we can't
rely on LibGUI widgets anymore.

A LayoutNode can now override wants_mouse_events() and if it returns
true, it will now receive mousedown, mousemove and mouseup events. :^)
Andreas Kling 4 năm trước cách đây
mục cha
commit
d6889ecf35

+ 12 - 0
Libraries/LibWeb/Layout/LayoutNode.cpp

@@ -254,4 +254,16 @@ void LayoutNodeWithStyle::apply_style(const CSS::StyleProperties& specified_styl
     style.border_bottom().color = specified_style.color_or_fallback(CSS::PropertyID::BorderBottomColor, document(), Color::Transparent);
 }
 
+void LayoutNode::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
+{
+}
+
+void LayoutNode::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
+{
+}
+
+void LayoutNode::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
+{
+}
+
 }

+ 6 - 0
Libraries/LibWeb/Layout/LayoutNode.h

@@ -99,6 +99,12 @@ public:
 
     bool is_inline_block() const { return is_inline() && is_block(); }
 
+    virtual bool wants_mouse_events() const { return false; }
+
+    virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+    virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+    virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers);
+
     enum class LayoutMode {
         Default,
         AllPossibleLineBreaks,

+ 44 - 0
Libraries/LibWeb/Page/EventHandler.cpp

@@ -75,9 +75,20 @@ bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button
 {
     if (!layout_root())
         return false;
+
+    if (m_mouse_event_tracking_layout_node) {
+        m_mouse_event_tracking_layout_node->handle_mouseup({}, position, button, modifiers);
+        return true;
+    }
+
     bool handled_event = false;
 
     auto result = layout_root()->hit_test(position, HitTestType::Exact);
+
+    if (result.layout_node && result.layout_node->wants_mouse_events()) {
+        result.layout_node->handle_mouseup({}, position, button, modifiers);
+    }
+
     if (result.layout_node && result.layout_node->node()) {
         RefPtr<DOM::Node> node = result.layout_node->node();
         if (is<HTML::HTMLIFrameElement>(*node)) {
@@ -101,6 +112,12 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt
 {
     if (!layout_root())
         return false;
+
+    if (m_mouse_event_tracking_layout_node) {
+        m_mouse_event_tracking_layout_node->handle_mousedown({}, position, button, modifiers);
+        return true;
+    }
+
     NonnullRefPtr document = *m_frame.document();
     auto& page_client = m_frame.page().client();
 
@@ -108,6 +125,11 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt
     if (!result.layout_node)
         return false;
 
+    if (result.layout_node->wants_mouse_events()) {
+        result.layout_node->handle_mousedown({}, position, button, modifiers);
+        return true;
+    }
+
     RefPtr<DOM::Node> node = result.layout_node->node();
     document->set_hovered_node(node);
     if (!node)
@@ -171,6 +193,12 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt
 {
     if (!layout_root())
         return false;
+
+    if (m_mouse_event_tracking_layout_node) {
+        m_mouse_event_tracking_layout_node->handle_mousemove({}, position, buttons, modifiers);
+        return true;
+    }
+
     auto& document = *m_frame.document();
     auto& page_client = m_frame.page().client();
 
@@ -180,6 +208,14 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt
     auto result = layout_root()->hit_test(position, HitTestType::Exact);
     const HTML::HTMLAnchorElement* hovered_link_element = nullptr;
     if (result.layout_node) {
+
+        if (result.layout_node->wants_mouse_events()) {
+            result.layout_node->handle_mousemove({}, position, buttons, modifiers);
+            // FIXME: It feels a bit aggressive to always update the cursor like this.
+            page_client.page_did_request_cursor_change(Gfx::StandardCursor::None);
+            return true;
+        }
+
         RefPtr<DOM::Node> node = result.layout_node->node();
 
         if (node && is<HTML::HTMLIFrameElement>(*node)) {
@@ -313,4 +349,12 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin
     return false;
 }
 
+void EventHandler::set_mouse_event_tracking_layout_node(LayoutNode* layout_node)
+{
+    if (layout_node)
+        m_mouse_event_tracking_layout_node = layout_node->make_weak_ptr();
+    else
+        m_mouse_event_tracking_layout_node = nullptr;
+}
+
 }

+ 5 - 0
Libraries/LibWeb/Page/EventHandler.h

@@ -27,6 +27,7 @@
 #pragma once
 
 #include <AK/Forward.h>
+#include <AK/WeakPtr.h>
 #include <Kernel/API/KeyCode.h>
 #include <LibGUI/Forward.h>
 #include <LibGfx/Forward.h>
@@ -47,6 +48,8 @@ public:
 
     bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
 
+    void set_mouse_event_tracking_layout_node(LayoutNode*);
+
 private:
     bool focus_next_element();
     bool focus_previous_element();
@@ -59,6 +62,8 @@ private:
     Frame& m_frame;
 
     bool m_in_mouse_selection { false };
+
+    WeakPtr<LayoutNode> m_mouse_event_tracking_layout_node;
 };
 
 }