diff options
-rw-r--r-- | Libraries/LibGUI/Event.h | 11 | ||||
-rw-r--r-- | Libraries/LibGUI/WindowServerConnection.cpp | 2 | ||||
-rw-r--r-- | Services/WindowServer/ClientConnection.cpp | 10 | ||||
-rw-r--r-- | Services/WindowServer/Window.cpp | 30 | ||||
-rw-r--r-- | Services/WindowServer/Window.h | 4 | ||||
-rw-r--r-- | Services/WindowServer/WindowClient.ipc | 2 | ||||
-rw-r--r-- | Services/WindowServer/WindowManager.cpp | 67 | ||||
-rw-r--r-- | Services/WindowServer/WindowManager.h | 30 |
8 files changed, 113 insertions, 43 deletions
diff --git a/Libraries/LibGUI/Event.h b/Libraries/LibGUI/Event.h index 1fe9205892..f5ab95ce14 100644 --- a/Libraries/LibGUI/Event.h +++ b/Libraries/LibGUI/Event.h @@ -114,31 +114,40 @@ public: class WMWindowStateChangedEvent : public WMEvent { public: - WMWindowStateChangedEvent(int client_id, int window_id, const StringView& title, const Gfx::IntRect& rect, bool is_active, WindowType window_type, bool is_minimized, bool is_frameless, int progress) + WMWindowStateChangedEvent(int client_id, int window_id, int parent_client_id, int parent_window_id, const StringView& title, const Gfx::IntRect& rect, bool is_active, bool is_modal, WindowType window_type, bool is_minimized, bool is_frameless, int progress) : WMEvent(Event::Type::WM_WindowStateChanged, client_id, window_id) + , m_parent_client_id(parent_client_id) + , m_parent_window_id(parent_window_id) , m_title(title) , m_rect(rect) , m_window_type(window_type) , m_active(is_active) + , m_modal(is_modal) , m_minimized(is_minimized) , m_frameless(is_frameless) , m_progress(progress) { } + int parent_client_id() const { return m_parent_client_id; } + int parent_window_id() const { return m_parent_window_id; } String title() const { return m_title; } Gfx::IntRect rect() const { return m_rect; } bool is_active() const { return m_active; } + bool is_modal() const { return m_modal; } WindowType window_type() const { return m_window_type; } bool is_minimized() const { return m_minimized; } bool is_frameless() const { return m_frameless; } int progress() const { return m_progress; } private: + int m_parent_client_id; + int m_parent_window_id; String m_title; Gfx::IntRect m_rect; WindowType m_window_type; bool m_active; + bool m_modal; bool m_minimized; bool m_frameless; int m_progress; diff --git a/Libraries/LibGUI/WindowServerConnection.cpp b/Libraries/LibGUI/WindowServerConnection.cpp index 1f29afe18a..35eae09015 100644 --- a/Libraries/LibGUI/WindowServerConnection.cpp +++ b/Libraries/LibGUI/WindowServerConnection.cpp @@ -273,7 +273,7 @@ void WindowServerConnection::handle(const Messages::WindowClient::MenuItemActiva void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowStateChanged& message) { if (auto* window = Window::from_window_id(message.wm_id())) - Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(message.client_id(), message.window_id(), message.title(), message.rect(), message.is_active(), static_cast<WindowType>(message.window_type()), message.is_minimized(), message.is_frameless(), message.progress())); + Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(message.client_id(), message.window_id(), message.parent_client_id(), message.parent_window_id(), message.title(), message.rect(), message.is_active(), message.is_modal(), static_cast<WindowType>(message.window_type()), message.is_minimized(), message.is_frameless(), message.progress())); } void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRectChanged& message) diff --git a/Services/WindowServer/ClientConnection.cpp b/Services/WindowServer/ClientConnection.cpp index d3eaf9ee4a..42a58c1037 100644 --- a/Services/WindowServer/ClientConnection.cpp +++ b/Services/WindowServer/ClientConnection.cpp @@ -630,7 +630,7 @@ void ClientConnection::handle(const Messages::WindowServer::WM_SetActiveWindow& return; } auto& window = *(*it).value; - window.set_minimized(false); + WindowManager::the().minimize_windows(window, false); WindowManager::the().move_to_front_and_make_active(window); } @@ -647,7 +647,11 @@ void ClientConnection::handle(const Messages::WindowServer::WM_PopupWindowMenu& return; } auto& window = *(*it).value; - window.popup_window_menu(message.screen_position(), WindowMenuDefaultAction::BasedOnWindowState); + if (auto* blocked_by_modal = window.is_blocked_by_modal_window()) { + blocked_by_modal->popup_window_menu(message.screen_position(), WindowMenuDefaultAction::BasedOnWindowState); + } else { + window.popup_window_menu(message.screen_position(), WindowMenuDefaultAction::BasedOnWindowState); + } } void ClientConnection::handle(const Messages::WindowServer::WM_StartWindowResize& request) @@ -681,7 +685,7 @@ void ClientConnection::handle(const Messages::WindowServer::WM_SetWindowMinimize return; } auto& window = *(*it).value; - window.set_minimized(message.minimized()); + WindowManager::the().minimize_windows(window, message.minimized()); } OwnPtr<Messages::WindowServer::GreetResponse> ClientConnection::handle(const Messages::WindowServer::Greet&) diff --git a/Services/WindowServer/Window.cpp b/Services/WindowServer/Window.cpp index afb0f8d449..e6387014b4 100644 --- a/Services/WindowServer/Window.cpp +++ b/Services/WindowServer/Window.cpp @@ -214,11 +214,10 @@ void Window::set_minimized(bool minimized) return; if (minimized && !m_minimizable) return; - if (is_blocked_by_modal_window()) - return; m_minimized = minimized; update_menu_item_text(PopupMenuItem::Minimize); - start_minimize_animation(); + if (!is_blocked_by_modal_window()) + start_minimize_animation(); if (!minimized) request_update({ {}, size() }); invalidate(); @@ -256,8 +255,6 @@ void Window::set_maximized(bool maximized) return; if (maximized && !is_resizable()) return; - if (is_blocked_by_modal_window()) - return; set_tiled(WindowTileType::None); m_maximized = maximized; update_menu_item_text(PopupMenuItem::Maximize); @@ -446,14 +443,12 @@ void Window::ensure_window_menu() m_window_menu->on_item_activation = [&](auto& item) { switch (item.identifier()) { case 1: - set_minimized(!m_minimized); + WindowManager::the().minimize_windows(*this, !m_minimized); if (!m_minimized) WindowManager::the().move_to_front_and_make_active(*this); break; case 2: - set_maximized(!m_maximized); - if (m_minimized) - set_minimized(false); + WindowManager::the().maximize_windows(*this, !m_maximized); WindowManager::the().move_to_front_and_make_active(*this); break; case 3: @@ -613,6 +608,23 @@ bool Window::is_accessory_of(Window& window) const return parent_window() == &window; } +void Window::modal_unparented() +{ + m_modal = false; + WindowManager::the().notify_modal_unparented(*this); +} + +bool Window::is_modal() const +{ + if (!m_modal) + return false; + if (!m_parent_window) { + const_cast<Window*>(this)->modal_unparented(); + return false; + } + return true; +} + void Window::set_progress(int progress) { if (m_progress == progress) diff --git a/Services/WindowServer/Window.h b/Services/WindowServer/Window.h index c7033853b8..0fc84bf853 100644 --- a/Services/WindowServer/Window.h +++ b/Services/WindowServer/Window.h @@ -145,7 +145,8 @@ public: bool is_visible() const { return m_visible; } void set_visible(bool); - bool is_modal() const { return m_modal && m_parent_window; } + bool is_modal() const; + bool is_modal_dont_unparent() const { return m_modal && m_parent_window; } Gfx::IntRect rect() const { return m_rect; } void set_rect(const Gfx::IntRect&); @@ -260,6 +261,7 @@ private: void add_child_window(Window&); void add_accessory_window(Window&); void ensure_window_menu(); + void modal_unparented(); ClientConnection* m_client { nullptr }; diff --git a/Services/WindowServer/WindowClient.ipc b/Services/WindowServer/WindowClient.ipc index 53d8dc2747..0c5056db53 100644 --- a/Services/WindowServer/WindowClient.ipc +++ b/Services/WindowServer/WindowClient.ipc @@ -23,7 +23,7 @@ endpoint WindowClient = 4 ScreenRectChanged(Gfx::IntRect rect) =| WM_WindowRemoved(i32 wm_id, i32 client_id, i32 window_id) =| - WM_WindowStateChanged(i32 wm_id, i32 client_id, i32 window_id, bool is_active, bool is_minimized, bool is_frameless, i32 window_type, [UTF8] String title, Gfx::IntRect rect, i32 progress) =| + WM_WindowStateChanged(i32 wm_id, i32 client_id, i32 window_id, i32 parent_client_id, i32 parent_window_id, bool is_active, bool is_minimized, bool is_modal, bool is_frameless, i32 window_type, [UTF8] String title, Gfx::IntRect rect, i32 progress) =| WM_WindowIconBitmapChanged(i32 wm_id, i32 client_id, i32 window_id, i32 icon_buffer_id, Gfx::IntSize icon_size) =| WM_WindowRectChanged(i32 wm_id, i32 client_id, i32 window_id, Gfx::IntRect rect) =| diff --git a/Services/WindowServer/WindowManager.cpp b/Services/WindowServer/WindowManager.cpp index 97093a4a75..aad228b0a7 100644 --- a/Services/WindowServer/WindowManager.cpp +++ b/Services/WindowServer/WindowManager.cpp @@ -220,31 +220,14 @@ void WindowManager::move_to_front_and_make_active(Window& window) do_move_to_front(wnd, make_active, make_input); }; - auto* blocking_modal_window = window.is_blocked_by_modal_window(); - if (blocking_modal_window || window.is_modal()) { - // If a window that is currently blocked by a modal child is being - // brought to the front, bring the entire stack of modal windows - // to the front and activate the modal window. Also set the - // active input window to that same window (which would pull - // active input from any accessory window) - Vector<Window*> modal_stack; - auto* modal_stack_top = blocking_modal_window ? blocking_modal_window : &window; - for (auto* parent = modal_stack_top->parent_window(); parent; parent = parent->parent_window()) { - if (parent->is_blocked_by_modal_window() != blocking_modal_window) - break; - modal_stack.append(parent); - if (!parent->is_modal()) - break; - } - if (!modal_stack.is_empty()) { - for (size_t i = modal_stack.size(); i > 0; i--) { - move_window_to_front(*modal_stack[i - 1], false, false); - } - } - move_window_to_front(*modal_stack_top, true, true); - } else { - move_window_to_front(window, true, true); - } + // If a window that is currently blocked by a modal child is being + // brought to the front, bring the entire stack of modal windows + // to the front and activate the modal window. Also set the + // active input window to that same window (which would pull + // active input from any accessory window) + for_each_window_in_modal_stack(window, [&](auto& w, bool is_stack_top) { + move_window_to_front(w, is_stack_top, is_stack_top); + }); } void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input) @@ -287,7 +270,7 @@ void WindowManager::remove_window(Window& window) for_each_window_listening_to_wm_events([&window](Window& listener) { if (!(listener.wm_event_mask() & WMEventMask::WindowRemovals)) return IterationDecision::Continue; - if (!window.is_internal()) + if (!window.is_internal() && !window.is_modal()) listener.client()->post_message(Messages::WindowClient::WM_WindowRemoved(listener.window_id(), window.client_id(), window.window_id())); return IterationDecision::Continue; }); @@ -299,7 +282,8 @@ void WindowManager::tell_wm_listener_about_window(Window& listener, Window& wind return; if (window.is_internal()) return; - listener.client()->post_message(Messages::WindowClient::WM_WindowStateChanged(listener.window_id(), window.client_id(), window.window_id(), window.is_active(), window.is_minimized(), window.is_frameless(), (i32)window.type(), window.title(), window.rect(), window.progress())); + auto* parent = window.parent_window(); + listener.client()->post_message(Messages::WindowClient::WM_WindowStateChanged(listener.window_id(), window.client_id(), window.window_id(), parent ? parent->client_id() : -1, parent ? parent->window_id() : -1, window.is_active(), window.is_minimized(), window.is_modal_dont_unparent(), window.is_frameless(), (i32)window.type(), window.title(), window.rect(), window.progress())); } void WindowManager::tell_wm_listener_about_window_rect(Window& listener, Window& window) @@ -366,6 +350,19 @@ void WindowManager::notify_title_changed(Window& window) tell_wm_listeners_window_state_changed(window); } +void WindowManager::notify_modal_unparented(Window& window) +{ + if (window.type() != WindowType::Normal) + return; +#ifdef WINDOWMANAGER_DEBUG + dbg() << "[WM] Modal Window{" << &window << "} was unparented"; +#endif + if (m_switcher.is_visible()) + m_switcher.refresh(); + + tell_wm_listeners_window_state_changed(window); +} + void WindowManager::notify_rect_changed(Window& window, const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect) { UNUSED_PARAM(old_rect); @@ -1405,4 +1402,20 @@ void WindowManager::did_popup_a_menu(Badge<Menu>) m_active_input_tracking_window = nullptr; } +void WindowManager::minimize_windows(Window& window, bool minimized) +{ + for_each_window_in_modal_stack(window, [&](auto& w, bool) { + w.set_minimized(minimized); + }); +} + +void WindowManager::maximize_windows(Window& window, bool maximized) +{ + for_each_window_in_modal_stack(window, [&](auto& w, bool) { + w.set_maximized(maximized); + if (w.is_minimized()) + w.set_minimized(false); + }); +} + } diff --git a/Services/WindowServer/WindowManager.h b/Services/WindowServer/WindowManager.h index 08f15c0b77..2fac08daef 100644 --- a/Services/WindowServer/WindowManager.h +++ b/Services/WindowServer/WindowManager.h @@ -88,6 +88,7 @@ public: void remove_window(Window&); void notify_title_changed(Window&); + void notify_modal_unparented(Window&); void notify_rect_changed(Window&, const Gfx::IntRect& oldRect, const Gfx::IntRect& newRect); void notify_minimization_state_changed(Window&); void notify_opacity_changed(Window&); @@ -181,6 +182,35 @@ public: void start_menu_doubleclick(Window& window, const MouseEvent& event); bool is_menu_doubleclick(Window& window, const MouseEvent& event) const; + void minimize_windows(Window&, bool); + void maximize_windows(Window&, bool); + + template<typename Function> + void for_each_window_in_modal_stack(Window& window, Function f) + { + auto* blocking_modal_window = window.is_blocked_by_modal_window(); + if (blocking_modal_window || window.is_modal()) { + Vector<Window*> modal_stack; + auto* modal_stack_top = blocking_modal_window ? blocking_modal_window : &window; + for (auto* parent = modal_stack_top->parent_window(); parent; parent = parent->parent_window()) { + if (parent->is_blocked_by_modal_window() != modal_stack_top) + break; + modal_stack.append(parent); + if (!parent->is_modal()) + break; + } + if (!modal_stack.is_empty()) { + for (size_t i = modal_stack.size(); i > 0; i--) { + f(*modal_stack[i - 1], false); + } + } + f(*modal_stack_top, true); + } else { + // Not a modal window stack, just "iterate" over this window + f(window, true); + } + } + private: NonnullRefPtr<Cursor> get_cursor(const String& name); NonnullRefPtr<Cursor> get_cursor(const String& name, const Gfx::IntPoint& hotspot); |