浏览代码

WindowServer: Expose window parent information and more modal improvements

* The parent information is necessary by the Taskbar to be able to
  determine a modal window's parent
* Minimize and maximize modal window stacks together
Tom 5 年之前
父节点
当前提交
bbdf0665fc

+ 10 - 1
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;

+ 1 - 1
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)

+ 7 - 3
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&)

+ 21 - 9
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)

+ 3 - 1
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 };
 

+ 1 - 1
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) =|
 

+ 40 - 27
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);
+    });
+}
+
 }

+ 30 - 0
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);