Browse Source

WindowServer: Re-evaluate the mouse cursor when alpha hit-testing

A window repaint may change the alpha value, resulting in a different
hit test outcome. In those cases, re-evaluate the cursor hit testing
after a window was painted, and update the cursor if needed.
Tom 4 years ago
parent
commit
2d29bfc89e

+ 4 - 1
Userland/Services/WindowServer/ClientConnection.cpp

@@ -603,6 +603,8 @@ void ClientConnection::handle(const Messages::WindowServer::DidFinishPainting& m
     auto& window = *(*it).value;
     auto& window = *(*it).value;
     for (auto& rect : message.rects())
     for (auto& rect : message.rects())
         window.invalidate(rect);
         window.invalidate(rect);
+    if (window.has_alpha_channel() && window.alpha_hit_threshold() > 0.0)
+        WindowManager::the().reevaluate_hovered_window(&window);
 
 
     WindowSwitcher::the().refresh_if_needed();
     WindowSwitcher::the().refresh_if_needed();
 }
 }
@@ -661,7 +663,8 @@ OwnPtr<Messages::WindowServer::SetWindowCursorResponse> ClientConnection::handle
         return {};
         return {};
     }
     }
     window.set_cursor(Cursor::create((Gfx::StandardCursor)message.cursor_type()));
     window.set_cursor(Cursor::create((Gfx::StandardCursor)message.cursor_type()));
-    Compositor::the().invalidate_cursor();
+    if (&window == WindowManager::the().hovered_window())
+        Compositor::the().invalidate_cursor();
     return make<Messages::WindowServer::SetWindowCursorResponse>();
     return make<Messages::WindowServer::SetWindowCursorResponse>();
 }
 }
 
 

+ 3 - 1
Userland/Services/WindowServer/Compositor.cpp

@@ -127,8 +127,10 @@ void Compositor::compose()
 
 
     {
     {
         auto& current_cursor = wm.active_cursor();
         auto& current_cursor = wm.active_cursor();
-        if (m_current_cursor != &current_cursor)
+        if (m_current_cursor != &current_cursor) {
             change_cursor(&current_cursor);
             change_cursor(&current_cursor);
+            m_invalidated_cursor = m_invalidated_any = true;
+        }
     }
     }
 
 
     if (!m_invalidated_any) {
     if (!m_invalidated_any) {

+ 49 - 2
Userland/Services/WindowServer/WindowManager.cpp

@@ -1043,6 +1043,52 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
         clear_resize_candidate();
         clear_resize_candidate();
 }
 }
 
 
+void WindowManager::reevaluate_hovered_window(Window* updated_window)
+{
+    if (m_dnd_client || m_resize_window || m_move_window || m_cursor_tracking_button || MenuManager::the().has_open_menu())
+        return;
+
+    auto cursor_location = Screen::the().cursor_location();
+    auto* currently_hovered = hovered_window();
+    if (updated_window) {
+        if (!(updated_window == currently_hovered || updated_window->frame().rect().contains(cursor_location) || (currently_hovered && currently_hovered->frame().rect().contains(cursor_location))))
+            return;
+    }
+
+    Window* hovered_window = nullptr;
+    if (auto* fullscreen_window = active_fullscreen_window()) {
+        if (fullscreen_window->hit_test(cursor_location))
+            hovered_window = fullscreen_window;
+    } else {
+        for_each_visible_window_from_front_to_back([&](Window& window) {
+            if (!window.hit_test(cursor_location))
+                return IterationDecision::Continue;
+            hovered_window = &window;
+            return IterationDecision::Break;
+        });
+    }
+
+    if (set_hovered_window(hovered_window)) {
+        if (currently_hovered && m_resize_candidate == currently_hovered)
+            clear_resize_candidate();
+
+        if (hovered_window) {
+            // Send a fake MouseMove event. This allows the new hovering window
+            // to determine which widget we're hovering, and also update the cursor
+            // accordingly. We do this because this re-evaluation of the currently
+            // hovered window wasn't triggered by a mouse move event, but rather
+            // e.g. a hit-test result change due to a transparent window repaint.
+            if (hovered_window->hit_test(cursor_location, false)) {
+                MouseEvent event(Event::MouseMove, cursor_location.translated(-hovered_window->rect().location()), 0, MouseButton::None, 0);
+                hovered_window->event(event);
+            } else if (!hovered_window->is_frameless()) {
+                MouseEvent event(Event::MouseMove, cursor_location.translated(-hovered_window->frame().rect().location()), 0, MouseButton::None, 0);
+                hovered_window->frame().on_mouse_event(event);
+            }
+        }
+    }
+}
+
 void WindowManager::clear_resize_candidate()
 void WindowManager::clear_resize_candidate()
 {
 {
     if (m_resize_candidate)
     if (m_resize_candidate)
@@ -1315,10 +1361,10 @@ void WindowManager::set_active_window(Window* window, bool make_input)
     Compositor::the().invalidate_occlusions();
     Compositor::the().invalidate_occlusions();
 }
 }
 
 
-void WindowManager::set_hovered_window(Window* window)
+bool WindowManager::set_hovered_window(Window* window)
 {
 {
     if (m_hovered_window == window)
     if (m_hovered_window == window)
-        return;
+        return false;
 
 
     if (m_hovered_window)
     if (m_hovered_window)
         Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowLeft));
         Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowLeft));
@@ -1327,6 +1373,7 @@ void WindowManager::set_hovered_window(Window* window)
 
 
     if (m_hovered_window)
     if (m_hovered_window)
         Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowEntered));
         Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowEntered));
+    return true;
 }
 }
 
 
 const ClientConnection* WindowManager::active_client() const
 const ClientConnection* WindowManager::active_client() const

+ 4 - 1
Userland/Services/WindowServer/WindowManager.h

@@ -189,7 +189,7 @@ public:
 
 
     bool update_theme(String theme_path, String theme_name);
     bool update_theme(String theme_path, String theme_name);
 
 
-    void set_hovered_window(Window*);
+    bool set_hovered_window(Window*);
     void deliver_mouse_event(Window& window, MouseEvent& event, bool process_double_click);
     void deliver_mouse_event(Window& window, MouseEvent& event, bool process_double_click);
 
 
     void did_popup_a_menu(Badge<Menu>);
     void did_popup_a_menu(Badge<Menu>);
@@ -234,6 +234,9 @@ public:
     int compositor_icon_scale() const;
     int compositor_icon_scale() const;
     void reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icons = true);
     void reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icons = true);
 
 
+    void reevaluate_hovered_window(Window* = nullptr);
+    Window* hovered_window() const { return m_hovered_window.ptr(); }
+
 private:
 private:
     NonnullRefPtr<Cursor> get_cursor(const String& name);
     NonnullRefPtr<Cursor> get_cursor(const String& name);