1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 23:27:43 +00:00

LibGUI+WindowServer: Add a GResizeCorner widget.

This widget is automatically included in GStatusBar, but can be added in
any other place, too. When clicked (with the left button), it initiates a
window resize (using a WM request.)

In this patch I also fixed up some issues with override cursors being
cleared after the WindowServer finishes a drag or resize.
This commit is contained in:
Andreas Kling 2019-05-03 01:38:24 +02:00
parent 34c5db61aa
commit ea9a39a9f2
19 changed files with 189 additions and 26 deletions

View file

@ -55,6 +55,8 @@ enum class WSAPI_StandardCursor : unsigned char {
IBeam,
ResizeHorizontal,
ResizeVertical,
ResizeDiagonalTLBR,
ResizeDiagonalBLTR,
};
enum WSAPI_WMEventMask : unsigned {
@ -126,6 +128,7 @@ struct WSAPI_ServerMessage {
union {
struct {
int server_pid;
int your_client_id;
WSAPI_Rect screen_rect;
} greeting;
struct {
@ -211,6 +214,7 @@ struct WSAPI_ClientMessage {
SetWindowOverrideCursor,
WM_SetActiveWindow,
WM_SetWindowMinimized,
WM_StartWindowResize,
PopupMenu,
DismissMenu,
SetWindowIcon,

View file

@ -48,6 +48,7 @@ WSClientConnection::WSClientConnection(int fd)
WSAPI_ServerMessage message;
message.type = WSAPI_ServerMessage::Type::Greeting;
message.greeting.server_pid = getpid();
message.greeting.your_client_id = m_client_id;
message.greeting.screen_rect = WSScreen::the().rect();
post_message(message);
}
@ -645,6 +646,24 @@ void WSClientConnection::handle_request(const WSWMAPISetActiveWindowRequest& req
WSWindowManager::the().move_to_front_and_make_active(window);
}
void WSClientConnection::handle_request(const WSWMAPIStartWindowResizeRequest& request)
{
auto* client = WSClientConnection::from_client_id(request.target_client_id());
if (!client) {
post_error("WSWMAPIStartWindowResizeRequest: Bad client ID");
return;
}
auto it = client->m_windows.find(request.target_window_id());
if (it == client->m_windows.end()) {
post_error("WSWMAPIStartWindowResizeRequest: Bad window ID");
return;
}
auto& window = *(*it).value;
// FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button.
// Maybe the client should be allowed to specify what initiated this request?
WSWindowManager::the().start_window_resize(window, WSScreen::the().cursor_location(), MouseButton::Left);
}
void WSClientConnection::handle_request(const WSWMAPISetWindowMinimizedRequest& request)
{
auto* client = WSClientConnection::from_client_id(request.target_client_id());
@ -722,6 +741,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request)
return handle_request(static_cast<const WSWMAPISetActiveWindowRequest&>(request));
case WSEvent::WMAPISetWindowMinimizedRequest:
return handle_request(static_cast<const WSWMAPISetWindowMinimizedRequest&>(request));
case WSEvent::WMAPIStartWindowResizeRequest:
return handle_request(static_cast<const WSWMAPIStartWindowResizeRequest&>(request));
case WSEvent::APIPopupMenuRequest:
return handle_request(static_cast<const WSAPIPopupMenuRequest&>(request));
case WSEvent::APIDismissMenuRequest:

View file

@ -74,6 +74,7 @@ private:
void handle_request(const WSAPISetWindowOverrideCursorRequest&);
void handle_request(const WSWMAPISetActiveWindowRequest&);
void handle_request(const WSWMAPISetWindowMinimizedRequest&);
void handle_request(const WSWMAPIStartWindowResizeRequest&);
void handle_request(const WSAPIPopupMenuRequest&);
void handle_request(const WSAPIDismissMenuRequest&);

View file

@ -34,6 +34,10 @@ RetainPtr<WSCursor> WSCursor::create(WSStandardCursor standard_cursor)
return WSWindowManager::the().resize_horizontally_cursor();
case WSStandardCursor::ResizeVertical:
return WSWindowManager::the().resize_vertically_cursor();
case WSStandardCursor::ResizeDiagonalTLBR:
return WSWindowManager::the().resize_diagonally_tlbr_cursor();
case WSStandardCursor::ResizeDiagonalBLTR:
return WSWindowManager::the().resize_diagonally_bltr_cursor();
}
ASSERT_NOT_REACHED();
}

View file

@ -8,6 +8,8 @@ enum class WSStandardCursor {
IBeam,
ResizeHorizontal,
ResizeVertical,
ResizeDiagonalTLBR,
ResizeDiagonalBLTR,
};
class WSCursor : public Retainable<WSCursor> {

View file

@ -62,6 +62,7 @@ public:
APISetWindowOverrideCursorRequest,
WMAPISetActiveWindowRequest,
WMAPISetWindowMinimizedRequest,
WMAPIStartWindowResizeRequest,
APIPopupMenuRequest,
APIDismissMenuRequest,
__End_API_Client_Requests,
@ -104,6 +105,23 @@ private:
int m_client_id { 0 };
};
class WSWMAPIStartWindowResizeRequest : public WSAPIClientRequest {
public:
WSWMAPIStartWindowResizeRequest(int client_id, int target_client_id, int target_window_id)
: WSAPIClientRequest(WSEvent::WMAPIStartWindowResizeRequest, client_id)
, m_target_client_id(target_client_id)
, m_target_window_id(target_window_id)
{
}
int target_client_id() const { return m_target_client_id; }
int target_window_id() const { return m_target_window_id; }
private:
int m_target_client_id;
int m_target_window_id;
};
class WSWMAPISetActiveWindowRequest : public WSAPIClientRequest {
public:
WSWMAPISetActiveWindowRequest(int client_id, int target_client_id, int target_window_id)

View file

@ -250,6 +250,9 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
case WSAPI_ClientMessage::Type::WM_SetWindowMinimized:
post_event(client, make<WSWMAPISetWindowMinimizedRequest>(client_id, message.wm.client_id, message.wm.window_id, message.wm.minimized));
break;
case WSAPI_ClientMessage::Type::WM_StartWindowResize:
post_event(client, make<WSWMAPIStartWindowResizeRequest>(client_id, message.wm.client_id, message.wm.window_id));
break;
default:
break;
}

View file

@ -490,7 +490,7 @@ void WSWindowManager::start_window_drag(WSWindow& window, const WSMouseEvent& ev
invalidate(window);
}
void WSWindowManager::start_window_resize(WSWindow& window, const WSMouseEvent& event)
void WSWindowManager::start_window_resize(WSWindow& window, const Point& position, MouseButton button)
{
move_to_front_and_make_active(window);
constexpr ResizeDirection direction_for_hot_area[3][3] = {
@ -499,9 +499,9 @@ void WSWindowManager::start_window_resize(WSWindow& window, const WSMouseEvent&
{ ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight },
};
Rect outer_rect = window.frame().rect();
ASSERT(outer_rect.contains(event.position()));
int window_relative_x = event.x() - outer_rect.x();
int window_relative_y = event.y() - outer_rect.y();
ASSERT(outer_rect.contains(position));
int window_relative_x = position.x() - outer_rect.x();
int window_relative_y = position.y() - outer_rect.y();
int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3));
int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3));
m_resize_direction = direction_for_hot_area[hot_area_row][hot_area_column];
@ -513,15 +513,20 @@ void WSWindowManager::start_window_resize(WSWindow& window, const WSMouseEvent&
#ifdef RESIZE_DEBUG
printf("[WM] Begin resizing WSWindow{%p}\n", &window);
#endif
m_resizing_mouse_button = event.button();
m_resizing_mouse_button = button;
m_resize_window = window.make_weak_ptr();;
m_resize_origin = event.position();
m_resize_origin = position;
m_resize_window_original_rect = window.rect();
invalidate(window);
}
bool WSWindowManager::process_ongoing_window_drag(const WSMouseEvent& event, WSWindow*&)
void WSWindowManager::start_window_resize(WSWindow& window, const WSMouseEvent& event)
{
start_window_resize(window, event.position(), event.button());
}
bool WSWindowManager::process_ongoing_window_drag(const WSMouseEvent& event, WSWindow*& hovered_window)
{
if (!m_drag_window)
return false;
@ -530,6 +535,8 @@ bool WSWindowManager::process_ongoing_window_drag(const WSMouseEvent& event, WSW
printf("[WM] Finish dragging WSWindow{%p}\n", m_drag_window.ptr());
#endif
invalidate(*m_drag_window);
if (m_drag_window->rect().contains(event.position()))
hovered_window = m_drag_window;
m_drag_window = nullptr;
return true;
}
@ -539,12 +546,14 @@ bool WSWindowManager::process_ongoing_window_drag(const WSMouseEvent& event, WSW
#endif
Point pos = m_drag_window_origin.translated(event.position() - m_drag_origin);
m_drag_window->set_position_without_repaint(pos);
if (m_drag_window->rect().contains(event.position()))
hovered_window = m_drag_window;
return true;
}
return false;
}
bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, WSWindow*&)
bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, WSWindow*& hovered_window)
{
if (!m_resize_window)
return false;
@ -555,6 +564,8 @@ bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, W
#endif
WSEventLoop::the().post_event(*m_resize_window, make<WSResizeEvent>(m_resize_window->rect(), m_resize_window->rect()));
invalidate(*m_resize_window);
if (m_resize_window->rect().contains(event.position()))
hovered_window = m_resize_window;
m_resize_window = nullptr;
m_resizing_mouse_button = MouseButton::None;
return true;
@ -627,6 +638,9 @@ bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, W
new_rect.set_height(m_resize_window->base_size().height() + vertical_incs * m_resize_window->size_increment().height());
}
if (new_rect.contains(event.position()))
hovered_window = m_resize_window;
if (m_resize_window->rect() == new_rect)
return true;
#ifdef RESIZE_DEBUG
@ -644,14 +658,14 @@ void WSWindowManager::set_cursor_tracking_button(WSButton* button)
m_cursor_tracking_button = button ? button->make_weak_ptr() : nullptr;
}
void WSWindowManager::process_mouse_event(const WSMouseEvent& event, WSWindow*& event_window)
void WSWindowManager::process_mouse_event(const WSMouseEvent& event, WSWindow*& hovered_window)
{
event_window = nullptr;
hovered_window = nullptr;
if (process_ongoing_window_drag(event, event_window))
if (process_ongoing_window_drag(event, hovered_window))
return;
if (process_ongoing_window_resize(event, event_window))
if (process_ongoing_window_resize(event, hovered_window))
return;
if (m_cursor_tracking_button)
@ -687,7 +701,7 @@ void WSWindowManager::process_mouse_event(const WSMouseEvent& event, WSWindow*&
if (event.type() == WSEvent::MouseDown || event.type() == WSEvent::MouseUp)
close_current_menu();
} else {
event_window = &window;
hovered_window = &window;
auto translated_event = event.translated(-window.position());
window.event(translated_event);
}
@ -708,10 +722,12 @@ void WSWindowManager::process_mouse_event(const WSMouseEvent& event, WSWindow*&
// In those cases, the event is swallowed by the window manager.
if (window.type() == WSWindowType::Normal) {
if (m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseDown && event.button() == MouseButton::Left) {
hovered_window = &window;
start_window_drag(window, event);
return IterationDecision::Abort;
}
if (window.is_resizable() && m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseDown && event.button() == MouseButton::Right && !window.is_blocked_by_modal_window()) {
hovered_window = &window;
start_window_resize(window, event);
return IterationDecision::Abort;
}
@ -720,7 +736,7 @@ void WSWindowManager::process_mouse_event(const WSMouseEvent& event, WSWindow*&
if (window.rect().contains(event.position())) {
if (window.type() == WSWindowType::Normal && event.type() == WSEvent::MouseDown)
move_to_front_and_make_active(window);
event_window = &window;
hovered_window = &window;
if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&window)) {
auto translated_event = event.translated(-window.position());
window.event(translated_event);
@ -987,9 +1003,9 @@ void WSWindowManager::draw_cursor()
void WSWindowManager::event(CEvent& event)
{
if (static_cast<WSEvent&>(event).is_mouse_event()) {
WSWindow* event_window = nullptr;
process_mouse_event(static_cast<const WSMouseEvent&>(event), event_window);
set_hovered_window(event_window);
WSWindow* hovered_window = nullptr;
process_mouse_event(static_cast<const WSMouseEvent&>(event), hovered_window);
set_hovered_window(hovered_window);
return;
}

View file

@ -116,14 +116,16 @@ public:
void tell_wm_listeners_window_icon_changed(WSWindow&);
void tell_wm_listeners_window_rect_changed(WSWindow&);
void start_window_resize(WSWindow&, const Point&, MouseButton);
void start_window_resize(WSWindow&, const WSMouseEvent&);
private:
void process_mouse_event(const WSMouseEvent&, WSWindow*& event_window);
bool process_ongoing_window_resize(const WSMouseEvent&, WSWindow*& event_window);
bool process_ongoing_window_drag(const WSMouseEvent&, WSWindow*& event_window);
void process_mouse_event(const WSMouseEvent&, WSWindow*& hovered_window);
bool process_ongoing_window_resize(const WSMouseEvent&, WSWindow*& hovered_window);
bool process_ongoing_window_drag(const WSMouseEvent&, WSWindow*& hovered_window);
void handle_menu_mouse_event(WSMenu&, const WSMouseEvent&);
void handle_menubar_mouse_event(const WSMouseEvent&);
void handle_close_button_mouse_event(WSWindow&, const WSMouseEvent&);
void start_window_resize(WSWindow&, const WSMouseEvent&);
void start_window_drag(WSWindow&, const WSMouseEvent&);
void handle_client_request(const WSAPIClientRequest&);
void set_hovered_window(WSWindow*);