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.
This commit is contained in:
Tom 2021-02-21 11:56:59 -07:00 committed by Andreas Kling
parent bd830c2dfe
commit 2d29bfc89e
Notes: sideshowbarker 2024-07-18 22:01:58 +09:00
4 changed files with 60 additions and 5 deletions

View file

@ -603,6 +603,8 @@ void ClientConnection::handle(const Messages::WindowServer::DidFinishPainting& m
auto& window = *(*it).value;
for (auto& rect : message.rects())
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();
}
@ -661,7 +663,8 @@ OwnPtr<Messages::WindowServer::SetWindowCursorResponse> ClientConnection::handle
return {};
}
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>();
}

View file

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

View file

@ -1043,6 +1043,52 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
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()
{
if (m_resize_candidate)
@ -1315,10 +1361,10 @@ void WindowManager::set_active_window(Window* window, bool make_input)
Compositor::the().invalidate_occlusions();
}
void WindowManager::set_hovered_window(Window* window)
bool WindowManager::set_hovered_window(Window* window)
{
if (m_hovered_window == window)
return;
return false;
if (m_hovered_window)
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)
Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowEntered));
return true;
}
const ClientConnection* WindowManager::active_client() const

View file

@ -189,7 +189,7 @@ public:
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 did_popup_a_menu(Badge<Menu>);
@ -234,6 +234,9 @@ public:
int compositor_icon_scale() const;
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:
NonnullRefPtr<Cursor> get_cursor(const String& name);