1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 03:57:43 +00:00

WindowServer+LibGUI: Handle mouse wheel deltas in the mouse event stream.

The wheel events will end up in GWidget::mousewheel_event(GMouseEvent&)
on the client-side. This patch also implements basic wheel scrolling in
GScrollableWidget via this mechanism. :^)
This commit is contained in:
Andreas Kling 2019-05-13 19:52:57 +02:00
parent dae8eb6454
commit dab9901235
13 changed files with 58 additions and 25 deletions

View file

@ -19,6 +19,7 @@ public:
MouseMove, MouseMove,
MouseDown, MouseDown,
MouseUp, MouseUp,
MouseWheel,
Enter, Enter,
Leave, Leave,
KeyDown, KeyDown,
@ -44,7 +45,6 @@ public:
explicit GEvent(Type type) : CEvent(type) { } explicit GEvent(Type type) : CEvent(type) { }
virtual ~GEvent() { } virtual ~GEvent() { }
bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseUp; }
bool is_key_event() const { return type() == KeyUp || type() == KeyDown; } bool is_key_event() const { return type() == KeyUp || type() == KeyDown; }
bool is_paint_event() const { return type() == Paint; } bool is_paint_event() const { return type() == Paint; }
}; };
@ -244,12 +244,13 @@ private:
class GMouseEvent final : public GEvent { class GMouseEvent final : public GEvent {
public: public:
GMouseEvent(Type type, const Point& position, unsigned buttons, GMouseButton button, unsigned modifiers) GMouseEvent(Type type, const Point& position, unsigned buttons, GMouseButton button, unsigned modifiers, int wheel_delta)
: GEvent(type) : GEvent(type)
, m_position(position) , m_position(position)
, m_buttons(buttons) , m_buttons(buttons)
, m_button(button) , m_button(button)
, m_modifiers(modifiers) , m_modifiers(modifiers)
, m_wheel_delta(wheel_delta)
{ {
} }
@ -259,10 +260,12 @@ public:
GMouseButton button() const { return m_button; } GMouseButton button() const { return m_button; }
unsigned buttons() const { return m_buttons; } unsigned buttons() const { return m_buttons; }
unsigned modifiers() const { return m_modifiers; } unsigned modifiers() const { return m_modifiers; }
int wheel_delta() const { return m_wheel_delta; }
private: private:
Point m_position; Point m_position;
unsigned m_buttons { 0 }; unsigned m_buttons { 0 };
GMouseButton m_button { GMouseButton::None }; GMouseButton m_button { GMouseButton::None };
unsigned m_modifiers { 0 }; unsigned m_modifiers { 0 };
int m_wheel_delta { 0 };
}; };

View file

@ -152,13 +152,14 @@ void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& win
void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window) void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window)
{ {
#ifdef GEVENTLOOP_DEBUG #ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%x MouseEvent %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); dbgprintf("WID=%x MouseEvent %d,%d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y, event.mouse.wheel_delta);
#endif #endif
GMouseEvent::Type type; GMouseEvent::Type type;
switch (event.type) { switch (event.type) {
case WSAPI_ServerMessage::Type::MouseMove: type = GEvent::MouseMove; break; case WSAPI_ServerMessage::Type::MouseMove: type = GEvent::MouseMove; break;
case WSAPI_ServerMessage::Type::MouseUp: type = GEvent::MouseUp; break; case WSAPI_ServerMessage::Type::MouseUp: type = GEvent::MouseUp; break;
case WSAPI_ServerMessage::Type::MouseDown: type = GEvent::MouseDown; break; case WSAPI_ServerMessage::Type::MouseDown: type = GEvent::MouseDown; break;
case WSAPI_ServerMessage::Type::MouseWheel: type = GEvent::MouseWheel; break;
default: ASSERT_NOT_REACHED(); break; default: ASSERT_NOT_REACHED(); break;
} }
GMouseButton button { GMouseButton::None }; GMouseButton button { GMouseButton::None };
@ -169,7 +170,7 @@ void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& w
case WSAPI_MouseButton::Middle: button = GMouseButton::Middle; break; case WSAPI_MouseButton::Middle: button = GMouseButton::Middle; break;
default: ASSERT_NOT_REACHED(); break; default: ASSERT_NOT_REACHED(); break;
} }
post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers)); post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers, event.mouse.wheel_delta));
} }
void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event) void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event)
@ -276,6 +277,7 @@ void GEventLoop::process_unprocessed_bundles()
case WSAPI_ServerMessage::Type::MouseDown: case WSAPI_ServerMessage::Type::MouseDown:
case WSAPI_ServerMessage::Type::MouseUp: case WSAPI_ServerMessage::Type::MouseUp:
case WSAPI_ServerMessage::Type::MouseMove: case WSAPI_ServerMessage::Type::MouseMove:
case WSAPI_ServerMessage::Type::MouseWheel:
handle_mouse_event(event, *window); handle_mouse_event(event, *window);
break; break;
case WSAPI_ServerMessage::Type::WindowActivated: case WSAPI_ServerMessage::Type::WindowActivated:

View file

@ -27,6 +27,12 @@ GScrollableWidget::~GScrollableWidget()
{ {
} }
void GScrollableWidget::mousewheel_event(GMouseEvent& event)
{
// FIXME: The wheel delta multiplier should probably come from... somewhere?
vertical_scrollbar().set_value(vertical_scrollbar().value() + event.wheel_delta() * 20);
}
void GScrollableWidget::resize_event(GResizeEvent& event) void GScrollableWidget::resize_event(GResizeEvent& event)
{ {
auto inner_rect = frame_inner_rect_for_size(event.size()); auto inner_rect = frame_inner_rect_for_size(event.size());

View file

@ -39,6 +39,7 @@ public:
protected: protected:
explicit GScrollableWidget(GWidget* parent); explicit GScrollableWidget(GWidget* parent);
virtual void resize_event(GResizeEvent&) override; virtual void resize_event(GResizeEvent&) override;
virtual void mousewheel_event(GMouseEvent&) override;
virtual void did_scroll() { } virtual void did_scroll() { }
void set_content_size(const Size&); void set_content_size(const Size&);
void set_size_occupied_by_fixed_elements(const Size&); void set_size_occupied_by_fixed_elements(const Size&);

View file

@ -86,6 +86,8 @@ void GWidget::event(CEvent& event)
return handle_mousedown_event(static_cast<GMouseEvent&>(event)); return handle_mousedown_event(static_cast<GMouseEvent&>(event));
case GEvent::MouseUp: case GEvent::MouseUp:
return handle_mouseup_event(static_cast<GMouseEvent&>(event)); return handle_mouseup_event(static_cast<GMouseEvent&>(event));
case GEvent::MouseWheel:
return mousewheel_event(static_cast<GMouseEvent&>(event));
case GEvent::Enter: case GEvent::Enter:
return handle_enter_event(event); return handle_enter_event(event);
case GEvent::Leave: case GEvent::Leave:
@ -261,6 +263,10 @@ void GWidget::mousemove_event(GMouseEvent&)
{ {
} }
void GWidget::mousewheel_event(GMouseEvent&)
{
}
void GWidget::context_menu_event(GContextMenuEvent&) void GWidget::context_menu_event(GContextMenuEvent&)
{ {
} }

View file

@ -58,6 +58,7 @@ public:
virtual void mousemove_event(GMouseEvent&); virtual void mousemove_event(GMouseEvent&);
virtual void mousedown_event(GMouseEvent&); virtual void mousedown_event(GMouseEvent&);
virtual void mouseup_event(GMouseEvent&); virtual void mouseup_event(GMouseEvent&);
virtual void mousewheel_event(GMouseEvent&);
virtual void click_event(GMouseEvent&); virtual void click_event(GMouseEvent&);
virtual void doubleclick_event(GMouseEvent&); virtual void doubleclick_event(GMouseEvent&);
virtual void context_menu_event(GContextMenuEvent&); virtual void context_menu_event(GContextMenuEvent&);

View file

@ -170,19 +170,19 @@ void GWindow::set_override_cursor(GStandardCursor cursor)
void GWindow::event(CEvent& event) void GWindow::event(CEvent& event)
{ {
if (event.type() == GEvent::MouseUp || event.type() == GEvent::MouseDown || event.type() == GEvent::MouseMove) { if (event.type() == GEvent::MouseUp || event.type() == GEvent::MouseDown || event.type() == GEvent::MouseMove || event.type() == GEvent::MouseWheel) {
auto& mouse_event = static_cast<GMouseEvent&>(event); auto& mouse_event = static_cast<GMouseEvent&>(event);
if (m_global_cursor_tracking_widget) { if (m_global_cursor_tracking_widget) {
auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect(); 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() }; 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()); 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->event(*local_event);
return; return;
} }
if (m_automatic_cursor_tracking_widget) { if (m_automatic_cursor_tracking_widget) {
auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect(); 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() }; 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()); 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->event(*local_event);
if (mouse_event.buttons() == 0) if (mouse_event.buttons() == 0)
m_automatic_cursor_tracking_widget = nullptr; m_automatic_cursor_tracking_widget = nullptr;
@ -190,16 +190,14 @@ void GWindow::event(CEvent& event)
} }
if (!m_main_widget) if (!m_main_widget)
return; return;
if (m_main_widget) { auto result = m_main_widget->hit_test(mouse_event.position());
auto result = m_main_widget->hit_test(mouse_event.position()); auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), result.local_position, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta());
auto local_event = make<GMouseEvent>((GEvent::Type)event.type(), result.local_position, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers()); ASSERT(result.widget);
ASSERT(result.widget); set_hovered_widget(result.widget);
set_hovered_widget(result.widget); if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget)
if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget) m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr();
m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr(); if (result.widget != m_global_cursor_tracking_widget.ptr())
if (result.widget != m_global_cursor_tracking_widget.ptr()) return result.widget->event(*local_event);
return result.widget->event(*local_event);
}
return; return;
} }

View file

@ -74,6 +74,7 @@ struct WSAPI_ServerMessage {
MouseMove, MouseMove,
MouseDown, MouseDown,
MouseUp, MouseUp,
MouseWheel,
WindowEntered, WindowEntered,
WindowLeft, WindowLeft,
KeyDown, KeyDown,
@ -155,6 +156,7 @@ struct WSAPI_ServerMessage {
WSAPI_MouseButton button; WSAPI_MouseButton button;
unsigned buttons; unsigned buttons;
byte modifiers; byte modifiers;
int wheel_delta;
} mouse; } mouse;
struct { struct {
char character; char character;

View file

@ -18,6 +18,7 @@ public:
MouseMove, MouseMove,
MouseDown, MouseDown,
MouseUp, MouseUp,
MouseWheel,
WindowEntered, WindowEntered,
WindowLeft, WindowLeft,
KeyDown, KeyDown,
@ -74,7 +75,7 @@ public:
virtual ~WSEvent() { } virtual ~WSEvent() { }
bool is_client_request() const { return type() > __Begin_API_Client_Requests && type() < __End_API_Client_Requests; } bool is_client_request() const { return type() > __Begin_API_Client_Requests && type() < __End_API_Client_Requests; }
bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseUp; } bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseUp || type() == MouseWheel; }
bool is_key_event() const { return type() == KeyUp || type() == KeyDown; } bool is_key_event() const { return type() == KeyUp || type() == KeyDown; }
}; };
@ -718,12 +719,13 @@ private:
class WSMouseEvent final : public WSEvent { class WSMouseEvent final : public WSEvent {
public: public:
WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button, unsigned modifiers) WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button, unsigned modifiers, int wheel_delta = 0)
: WSEvent(type) : WSEvent(type)
, m_position(position) , m_position(position)
, m_buttons(buttons) , m_buttons(buttons)
, m_button(button) , m_button(button)
, m_modifiers(modifiers) , m_modifiers(modifiers)
, m_wheel_delta(wheel_delta)
{ {
} }
@ -733,14 +735,16 @@ public:
MouseButton button() const { return m_button; } MouseButton button() const { return m_button; }
unsigned buttons() const { return m_buttons; } unsigned buttons() const { return m_buttons; }
unsigned modifiers() const { return m_modifiers; } unsigned modifiers() const { return m_modifiers; }
int wheel_delta() const { return m_wheel_delta; }
WSMouseEvent translated(const Point& delta) const { return WSMouseEvent((Type)type(), m_position.translated(delta), m_buttons, m_button, m_modifiers); } WSMouseEvent translated(const Point& delta) const { return WSMouseEvent((Type)type(), m_position.translated(delta), m_buttons, m_button, m_modifiers, m_wheel_delta); }
private: private:
Point m_position; Point m_position;
unsigned m_buttons { 0 }; unsigned m_buttons { 0 };
MouseButton m_button { MouseButton::None }; MouseButton m_button { MouseButton::None };
unsigned m_modifiers { 0 }; unsigned m_modifiers { 0 };
int m_wheel_delta { 0 };
}; };
class WSResizeEvent final : public WSEvent { class WSResizeEvent final : public WSEvent {

View file

@ -62,6 +62,7 @@ void WSEventLoop::drain_mouse()
unsigned prev_buttons = screen.mouse_button_state(); unsigned prev_buttons = screen.mouse_button_state();
int dx = 0; int dx = 0;
int dy = 0; int dy = 0;
int dz = 0;
unsigned buttons = prev_buttons; unsigned buttons = prev_buttons;
for (;;) { for (;;) {
MousePacket packet; MousePacket packet;
@ -73,15 +74,17 @@ void WSEventLoop::drain_mouse()
dx += packet.dx; dx += packet.dx;
dy += -packet.dy; dy += -packet.dy;
dz += packet.dz;
if (buttons != prev_buttons) { if (buttons != prev_buttons) {
screen.on_receive_mouse_data(dx, dy, buttons); screen.on_receive_mouse_data(dx, dy, dz, buttons);
dx = 0; dx = 0;
dy = 0; dy = 0;
dz = 0;
prev_buttons = buttons; prev_buttons = buttons;
} }
} }
if (dx || dy) if (dx || dy || dz)
screen.on_receive_mouse_data(dx, dy, buttons); screen.on_receive_mouse_data(dx, dy, dz, buttons);
} }
void WSEventLoop::drain_keyboard() void WSEventLoop::drain_keyboard()

View file

@ -58,7 +58,7 @@ void WSScreen::set_resolution(int width, int height)
m_cursor_location.constrain(rect()); m_cursor_location.constrain(rect());
} }
void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons) void WSScreen::on_receive_mouse_data(int dx, int dy, int dz, unsigned buttons)
{ {
auto prev_location = m_cursor_location; auto prev_location = m_cursor_location;
m_cursor_location.move_by(dx, dy); m_cursor_location.move_by(dx, dy);
@ -80,6 +80,11 @@ void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons)
WSEventLoop::the().post_event(WSWindowManager::the(), move(message)); WSEventLoop::the().post_event(WSWindowManager::the(), move(message));
} }
if (dz) {
auto message = make<WSMouseEvent>(WSEvent::MouseWheel, m_cursor_location, buttons, MouseButton::None, m_modifiers, dz);
WSEventLoop::the().post_event(WSWindowManager::the(), move(message));
}
if (m_cursor_location != prev_location) if (m_cursor_location != prev_location)
WSWindowManager::the().invalidate_cursor(); WSWindowManager::the().invalidate_cursor();
} }

View file

@ -26,7 +26,7 @@ public:
Point cursor_location() const { return m_cursor_location; } Point cursor_location() const { return m_cursor_location; }
unsigned mouse_button_state() const { return m_mouse_button_state; } unsigned mouse_button_state() const { return m_mouse_button_state; }
void on_receive_mouse_data(int dx, int dy, unsigned buttons); void on_receive_mouse_data(int dx, int dy, int dz, unsigned buttons);
void on_receive_keyboard_data(KeyEvent); void on_receive_keyboard_data(KeyEvent);
private: private:

View file

@ -95,6 +95,7 @@ void WSWindow::handle_mouse_event(const WSMouseEvent& event)
case WSEvent::MouseMove: server_message.type = WSAPI_ServerMessage::Type::MouseMove; break; case WSEvent::MouseMove: server_message.type = WSAPI_ServerMessage::Type::MouseMove; break;
case WSEvent::MouseDown: server_message.type = WSAPI_ServerMessage::Type::MouseDown; break; case WSEvent::MouseDown: server_message.type = WSAPI_ServerMessage::Type::MouseDown; break;
case WSEvent::MouseUp: server_message.type = WSAPI_ServerMessage::Type::MouseUp; break; case WSEvent::MouseUp: server_message.type = WSAPI_ServerMessage::Type::MouseUp; break;
case WSEvent::MouseWheel: server_message.type = WSAPI_ServerMessage::Type::MouseWheel; break;
default: ASSERT_NOT_REACHED(); default: ASSERT_NOT_REACHED();
} }
@ -102,6 +103,7 @@ void WSWindow::handle_mouse_event(const WSMouseEvent& event)
server_message.mouse.button = to_api(event.button()); server_message.mouse.button = to_api(event.button());
server_message.mouse.buttons = event.buttons(); server_message.mouse.buttons = event.buttons();
server_message.mouse.modifiers = event.modifiers(); server_message.mouse.modifiers = event.modifiers();
server_message.mouse.wheel_delta = event.wheel_delta();
m_client->post_message(server_message); m_client->post_message(server_message);
} }