mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 12:38:12 +00:00
LibGUI: Implement enter/leave events (with WindowServer support.)
Windows now learn when the mouse cursor leaves or enters them. Use this to implement GWidget::{enter,leave}_event() and use that to implement the CoolBar button effect. :^)
This commit is contained in:
parent
af7eb5c89c
commit
bf30502560
14 changed files with 103 additions and 5 deletions
|
@ -89,3 +89,14 @@ void GButton::mouseup_event(GMouseEvent& event)
|
||||||
GWidget::mouseup_event(event);
|
GWidget::mouseup_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GButton::enter_event(GEvent&)
|
||||||
|
{
|
||||||
|
m_hovered = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GButton::leave_event(GEvent&)
|
||||||
|
{
|
||||||
|
m_hovered = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ private:
|
||||||
virtual void mousedown_event(GMouseEvent&) override;
|
virtual void mousedown_event(GMouseEvent&) override;
|
||||||
virtual void mouseup_event(GMouseEvent&) override;
|
virtual void mouseup_event(GMouseEvent&) override;
|
||||||
virtual void mousemove_event(GMouseEvent&) override;
|
virtual void mousemove_event(GMouseEvent&) override;
|
||||||
|
virtual void enter_event(GEvent&) override;
|
||||||
|
virtual void leave_event(GEvent&) override;
|
||||||
|
|
||||||
virtual const char* class_name() const override { return "GButton"; }
|
virtual const char* class_name() const override { return "GButton"; }
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,14 @@ public:
|
||||||
MouseMove,
|
MouseMove,
|
||||||
MouseDown,
|
MouseDown,
|
||||||
MouseUp,
|
MouseUp,
|
||||||
|
Enter,
|
||||||
|
Leave,
|
||||||
KeyDown,
|
KeyDown,
|
||||||
KeyUp,
|
KeyUp,
|
||||||
Timer,
|
Timer,
|
||||||
DeferredDestroy,
|
DeferredDestroy,
|
||||||
|
WindowEntered,
|
||||||
|
WindowLeft,
|
||||||
WindowBecameInactive,
|
WindowBecameInactive,
|
||||||
WindowBecameActive,
|
WindowBecameActive,
|
||||||
FocusIn,
|
FocusIn,
|
||||||
|
|
|
@ -134,6 +134,11 @@ void GEventLoop::handle_window_close_request_event(const WSAPI_ServerMessage&, G
|
||||||
post_event(&window, make<GEvent>(GEvent::WindowCloseRequest));
|
post_event(&window, make<GEvent>(GEvent::WindowCloseRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GEventLoop::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window)
|
||||||
|
{
|
||||||
|
post_event(&window, make<GEvent>(message.type == WSAPI_ServerMessage::Type::WindowEntered ? GEvent::WindowEntered : GEvent::WindowLeft));
|
||||||
|
}
|
||||||
|
|
||||||
void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window)
|
void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window)
|
||||||
{
|
{
|
||||||
#ifdef GEVENTLOOP_DEBUG
|
#ifdef GEVENTLOOP_DEBUG
|
||||||
|
@ -294,6 +299,10 @@ void GEventLoop::wait_for_event()
|
||||||
case WSAPI_ServerMessage::Type::KeyUp:
|
case WSAPI_ServerMessage::Type::KeyUp:
|
||||||
handle_key_event(event, *window);
|
handle_key_event(event, *window);
|
||||||
break;
|
break;
|
||||||
|
case WSAPI_ServerMessage::Type::WindowEntered:
|
||||||
|
case WSAPI_ServerMessage::Type::WindowLeft:
|
||||||
|
handle_window_entered_or_left_event(event, *window);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ private:
|
||||||
void handle_window_activation_event(const WSAPI_ServerMessage&, GWindow&);
|
void handle_window_activation_event(const WSAPI_ServerMessage&, GWindow&);
|
||||||
void handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow&);
|
void handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow&);
|
||||||
void handle_menu_event(const WSAPI_ServerMessage&);
|
void handle_menu_event(const WSAPI_ServerMessage&);
|
||||||
|
void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&);
|
||||||
void get_next_timer_expiration(timeval&);
|
void get_next_timer_expiration(timeval&);
|
||||||
|
|
||||||
struct QueuedEvent {
|
struct QueuedEvent {
|
||||||
|
|
|
@ -63,6 +63,10 @@ void GWidget::event(GEvent& event)
|
||||||
return mousedown_event(static_cast<GMouseEvent&>(event));
|
return mousedown_event(static_cast<GMouseEvent&>(event));
|
||||||
case GEvent::MouseUp:
|
case GEvent::MouseUp:
|
||||||
return mouseup_event(static_cast<GMouseEvent&>(event));
|
return mouseup_event(static_cast<GMouseEvent&>(event));
|
||||||
|
case GEvent::Enter:
|
||||||
|
return enter_event(event);
|
||||||
|
case GEvent::Leave:
|
||||||
|
return leave_event(event);
|
||||||
default:
|
default:
|
||||||
return GObject::event(event);
|
return GObject::event(event);
|
||||||
}
|
}
|
||||||
|
@ -173,6 +177,14 @@ void GWidget::focusout_event(GEvent&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GWidget::enter_event(GEvent&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GWidget::leave_event(GEvent&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void GWidget::update()
|
void GWidget::update()
|
||||||
{
|
{
|
||||||
update(rect());
|
update(rect());
|
||||||
|
|
|
@ -43,6 +43,8 @@ public:
|
||||||
virtual void mouseup_event(GMouseEvent&);
|
virtual void mouseup_event(GMouseEvent&);
|
||||||
virtual void focusin_event(GEvent&);
|
virtual void focusin_event(GEvent&);
|
||||||
virtual void focusout_event(GEvent&);
|
virtual void focusout_event(GEvent&);
|
||||||
|
virtual void enter_event(GEvent&);
|
||||||
|
virtual void leave_event(GEvent&);
|
||||||
|
|
||||||
Rect relative_rect() const { return m_relative_rect; }
|
Rect relative_rect() const { return m_relative_rect; }
|
||||||
Point relative_position() const { return m_relative_rect.location(); }
|
Point relative_position() const { return m_relative_rect.location(); }
|
||||||
|
|
|
@ -157,6 +157,7 @@ void GWindow::event(GEvent& event)
|
||||||
auto result = m_main_widget->hit_test(mouse_event.x(), mouse_event.y());
|
auto result = m_main_widget->hit_test(mouse_event.x(), mouse_event.y());
|
||||||
auto local_event = make<GMouseEvent>(event.type(), Point { result.localX, result.localY }, mouse_event.buttons(), mouse_event.button());
|
auto local_event = make<GMouseEvent>(event.type(), Point { result.localX, result.localY }, mouse_event.buttons(), mouse_event.button());
|
||||||
ASSERT(result.widget);
|
ASSERT(result.widget);
|
||||||
|
set_hovered_widget(result.widget);
|
||||||
return result.widget->event(*local_event);
|
return result.widget->event(*local_event);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -203,6 +204,11 @@ void GWindow::event(GEvent& event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.type() == GEvent::WindowLeft) {
|
||||||
|
set_hovered_widget(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GObject::event(event);
|
GObject::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,3 +307,17 @@ void GWindow::set_opacity(float opacity)
|
||||||
m_opacity_when_windowless = opacity;
|
m_opacity_when_windowless = opacity;
|
||||||
GEventLoop::main().post_message_to_server(request);
|
GEventLoop::main().post_message_to_server(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GWindow::set_hovered_widget(GWidget* widget)
|
||||||
|
{
|
||||||
|
if (widget == m_hovered_widget.ptr())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_hovered_widget)
|
||||||
|
GEventLoop::main().post_event(m_hovered_widget.ptr(), make<GEvent>(GEvent::Leave));
|
||||||
|
|
||||||
|
m_hovered_widget = widget ? widget->make_weak_ptr() : nullptr;
|
||||||
|
|
||||||
|
if (m_hovered_widget)
|
||||||
|
GEventLoop::main().post_event(m_hovered_widget.ptr(), make<GEvent>(GEvent::Enter));
|
||||||
|
}
|
||||||
|
|
|
@ -64,6 +64,10 @@ public:
|
||||||
bool should_exit_app_on_close() const { return m_should_exit_app_on_close; }
|
bool should_exit_app_on_close() const { return m_should_exit_app_on_close; }
|
||||||
void set_should_exit_app_on_close(bool b) { m_should_exit_app_on_close = b; }
|
void set_should_exit_app_on_close(bool b) { m_should_exit_app_on_close = b; }
|
||||||
|
|
||||||
|
GWidget* hovered_widget() { return m_hovered_widget.ptr(); }
|
||||||
|
const GWidget* hovered_widget() const { return m_hovered_widget.ptr(); }
|
||||||
|
void set_hovered_widget(GWidget*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual const char* class_name() const override { return "GWindow"; }
|
virtual const char* class_name() const override { return "GWindow"; }
|
||||||
|
|
||||||
|
@ -73,6 +77,7 @@ private:
|
||||||
GWidget* m_main_widget { nullptr };
|
GWidget* m_main_widget { nullptr };
|
||||||
GWidget* m_focused_widget { nullptr };
|
GWidget* m_focused_widget { nullptr };
|
||||||
WeakPtr<GWidget> m_global_cursor_tracking_widget;
|
WeakPtr<GWidget> m_global_cursor_tracking_widget;
|
||||||
|
WeakPtr<GWidget> m_hovered_widget;
|
||||||
Rect m_rect_when_windowless;
|
Rect m_rect_when_windowless;
|
||||||
String m_title_when_windowless;
|
String m_title_when_windowless;
|
||||||
Vector<Rect> m_pending_paint_event_rects;
|
Vector<Rect> m_pending_paint_event_rects;
|
||||||
|
|
|
@ -62,6 +62,8 @@ struct WSAPI_ServerMessage {
|
||||||
MouseMove,
|
MouseMove,
|
||||||
MouseDown,
|
MouseDown,
|
||||||
MouseUp,
|
MouseUp,
|
||||||
|
WindowEntered,
|
||||||
|
WindowLeft,
|
||||||
KeyDown,
|
KeyDown,
|
||||||
KeyUp,
|
KeyUp,
|
||||||
WindowActivated,
|
WindowActivated,
|
||||||
|
|
|
@ -14,6 +14,8 @@ public:
|
||||||
MouseMove,
|
MouseMove,
|
||||||
MouseDown,
|
MouseDown,
|
||||||
MouseUp,
|
MouseUp,
|
||||||
|
WindowEntered,
|
||||||
|
WindowLeft,
|
||||||
KeyDown,
|
KeyDown,
|
||||||
KeyUp,
|
KeyUp,
|
||||||
WindowActivated,
|
WindowActivated,
|
||||||
|
|
|
@ -94,6 +94,12 @@ void WSWindow::on_message(WSMessage& message)
|
||||||
server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button());
|
server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button());
|
||||||
server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons();
|
server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons();
|
||||||
break;
|
break;
|
||||||
|
case WSMessage::WindowEntered:
|
||||||
|
server_message.type = WSAPI_ServerMessage::Type::WindowEntered;
|
||||||
|
break;
|
||||||
|
case WSMessage::WindowLeft:
|
||||||
|
server_message.type = WSAPI_ServerMessage::Type::WindowLeft;
|
||||||
|
break;
|
||||||
case WSMessage::KeyDown:
|
case WSMessage::KeyDown:
|
||||||
server_message.type = WSAPI_ServerMessage::Type::KeyDown;
|
server_message.type = WSAPI_ServerMessage::Type::KeyDown;
|
||||||
server_message.key.character = static_cast<WSKeyEvent&>(message).character();
|
server_message.key.character = static_cast<WSKeyEvent&>(message).character();
|
||||||
|
|
|
@ -488,8 +488,10 @@ void WSWindowManager::handle_close_button_mouse_event(WSWindow& window, WSMouseE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSWindowManager::process_mouse_event(WSMouseEvent& event)
|
void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_window)
|
||||||
{
|
{
|
||||||
|
event_window = nullptr;
|
||||||
|
|
||||||
if (event.type() == WSMessage::MouseUp && event.button() == MouseButton::Left) {
|
if (event.type() == WSMessage::MouseUp && event.button() == MouseButton::Left) {
|
||||||
if (m_drag_window) {
|
if (m_drag_window) {
|
||||||
#ifdef DRAG_DEBUG
|
#ifdef DRAG_DEBUG
|
||||||
|
@ -561,6 +563,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event)
|
||||||
move_to_front(window);
|
move_to_front(window);
|
||||||
set_active_window(&window);
|
set_active_window(&window);
|
||||||
}
|
}
|
||||||
|
event_window = &window;
|
||||||
// FIXME: Should we just alter the coordinates of the existing MouseEvent and pass it through?
|
// FIXME: Should we just alter the coordinates of the existing MouseEvent and pass it through?
|
||||||
Point position { event.x() - window.rect().x(), event.y() - window.rect().y() };
|
Point position { event.x() - window.rect().x(), event.y() - window.rect().y() };
|
||||||
auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
|
auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
|
||||||
|
@ -753,8 +756,12 @@ void WSWindowManager::draw_cursor()
|
||||||
|
|
||||||
void WSWindowManager::on_message(WSMessage& message)
|
void WSWindowManager::on_message(WSMessage& message)
|
||||||
{
|
{
|
||||||
if (message.is_mouse_event())
|
if (message.is_mouse_event()) {
|
||||||
return process_mouse_event(static_cast<WSMouseEvent&>(message));
|
WSWindow* event_window = nullptr;
|
||||||
|
process_mouse_event(static_cast<WSMouseEvent&>(message), event_window);
|
||||||
|
set_hovered_window(event_window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.is_key_event()) {
|
if (message.is_key_event()) {
|
||||||
// FIXME: This is a good place to hook key events globally. :)
|
// FIXME: This is a good place to hook key events globally. :)
|
||||||
|
@ -795,6 +802,20 @@ void WSWindowManager::set_active_window(WSWindow* window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSWindowManager::set_hovered_window(WSWindow* window)
|
||||||
|
{
|
||||||
|
if (m_hovered_window.ptr() == window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_hovered_window)
|
||||||
|
WSMessageLoop::the().post_message(m_hovered_window.ptr(), make<WSMessage>(WSMessage::WindowLeft));
|
||||||
|
|
||||||
|
m_hovered_window = window ? window->make_weak_ptr() : nullptr;
|
||||||
|
|
||||||
|
if (m_hovered_window)
|
||||||
|
WSMessageLoop::the().post_message(m_hovered_window.ptr(), make<WSMessage>(WSMessage::WindowEntered));
|
||||||
|
}
|
||||||
|
|
||||||
void WSWindowManager::invalidate()
|
void WSWindowManager::invalidate()
|
||||||
{
|
{
|
||||||
m_dirty_rects.clear_with_capacity();
|
m_dirty_rects.clear_with_capacity();
|
||||||
|
|
|
@ -71,7 +71,7 @@ public:
|
||||||
void set_resolution(int width, int height);
|
void set_resolution(int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process_mouse_event(WSMouseEvent&);
|
void process_mouse_event(WSMouseEvent&, WSWindow*& event_window);
|
||||||
void handle_menu_mouse_event(WSMenu&, WSMouseEvent&);
|
void handle_menu_mouse_event(WSMenu&, WSMouseEvent&);
|
||||||
void handle_menubar_mouse_event(WSMouseEvent&);
|
void handle_menubar_mouse_event(WSMouseEvent&);
|
||||||
void handle_titlebar_mouse_event(WSWindow&, WSMouseEvent&);
|
void handle_titlebar_mouse_event(WSWindow&, WSMouseEvent&);
|
||||||
|
@ -79,6 +79,7 @@ private:
|
||||||
void handle_client_request(WSAPIClientRequest&);
|
void handle_client_request(WSAPIClientRequest&);
|
||||||
|
|
||||||
void set_active_window(WSWindow*);
|
void set_active_window(WSWindow*);
|
||||||
|
void set_hovered_window(WSWindow*);
|
||||||
template<typename Callback> IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback);
|
template<typename Callback> IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback);
|
||||||
template<typename Callback> IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback);
|
template<typename Callback> IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback);
|
||||||
template<typename Callback> IterationDecision for_each_visible_window_from_front_to_back(Callback);
|
template<typename Callback> IterationDecision for_each_visible_window_from_front_to_back(Callback);
|
||||||
|
@ -111,7 +112,7 @@ private:
|
||||||
InlineLinkedList<WSWindow> m_windows_in_order;
|
InlineLinkedList<WSWindow> m_windows_in_order;
|
||||||
|
|
||||||
WeakPtr<WSWindow> m_active_window;
|
WeakPtr<WSWindow> m_active_window;
|
||||||
|
WeakPtr<WSWindow> m_hovered_window;
|
||||||
WeakPtr<WSWindow> m_drag_window;
|
WeakPtr<WSWindow> m_drag_window;
|
||||||
|
|
||||||
Point m_drag_origin;
|
Point m_drag_origin;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue