mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
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:
parent
bd830c2dfe
commit
2d29bfc89e
Notes:
sideshowbarker
2024-07-18 22:01:58 +09:00
Author: https://github.com/tomuta Commit: https://github.com/SerenityOS/serenity/commit/2d29bfc89ef Pull-request: https://github.com/SerenityOS/serenity/pull/5461 Reviewed-by: https://github.com/awesomekling
4 changed files with 60 additions and 5 deletions
|
@ -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>();
|
||||
}
|
||||
|
||||
|
|
|
@ -127,8 +127,10 @@ void Compositor::compose()
|
|||
|
||||
{
|
||||
auto& current_cursor = wm.active_cursor();
|
||||
if (m_current_cursor != ¤t_cursor)
|
||||
if (m_current_cursor != ¤t_cursor) {
|
||||
change_cursor(¤t_cursor);
|
||||
m_invalidated_cursor = m_invalidated_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_invalidated_any) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue