Jelajahi Sumber

WindowServer+LibGUI: Allow specifying a "launch origin" for new windows

The launch_origin_rect parameter to create_window() specifies where on
screen the window was launched from. It's optional, but if you provide
it, the new window will have a short wireframe animation from the origin
to the initial window frame rect.

GUI::Window looks for the "__libgui_launch_origin_rect" environment
variable. Put your launch origin rect in there with the format
"<x>,<y>,<width>,<height>" and the first GUI::Window shown by the app
will use that as the launch origin rect.

Also it looks pretty neat, although I'm sure we can improve it. :^)
Andreas Kling 4 tahun lalu
induk
melakukan
6a132d8672

+ 16 - 1
Userland/Libraries/LibGUI/Window.cpp

@@ -122,6 +122,20 @@ void Window::show()
 
     m_window_id = s_window_id_allocator.allocate();
 
+    Gfx::IntRect launch_origin_rect;
+    if (auto* launch_origin_rect_string = getenv("__libgui_launch_origin_rect")) {
+        auto parts = StringView(launch_origin_rect_string).split_view(',');
+        if (parts.size() == 4) {
+            launch_origin_rect = Gfx::IntRect {
+                parts[0].to_int().value_or(0),
+                parts[1].to_int().value_or(0),
+                parts[2].to_int().value_or(0),
+                parts[3].to_int().value_or(0),
+            };
+        }
+        unsetenv("__libgui_launch_origin_rect");
+    }
+
     WindowServerConnection::the().async_create_window(
         m_window_id,
         m_rect_when_windowless,
@@ -141,7 +155,8 @@ void Window::show()
         m_resize_aspect_ratio,
         (i32)m_window_type,
         m_title_when_windowless,
-        parent_window ? parent_window->window_id() : 0);
+        parent_window ? parent_window->window_id() : 0,
+        launch_origin_rect);
     m_visible = true;
 
     apply_icon();

+ 5 - 1
Userland/Services/WindowServer/ClientConnection.cpp

@@ -496,7 +496,8 @@ void ClientConnection::create_window(i32 window_id, Gfx::IntRect const& rect,
     bool auto_position, bool has_alpha_channel, bool modal, bool minimizable, bool resizable,
     bool fullscreen, bool frameless, bool accessory, float opacity, float alpha_hit_threshold,
     Gfx::IntSize const& base_size, Gfx::IntSize const& size_increment, Gfx::IntSize const& minimum_size,
-    Optional<Gfx::IntSize> const& resize_aspect_ratio, i32 type, String const& title, i32 parent_window_id)
+    Optional<Gfx::IntSize> const& resize_aspect_ratio, i32 type, String const& title, i32 parent_window_id,
+    Gfx::IntRect const& launch_origin_rect)
 {
     Window* parent_window = nullptr;
     if (parent_window_id) {
@@ -519,6 +520,9 @@ void ClientConnection::create_window(i32 window_id, Gfx::IntRect const& rect,
 
     auto window = Window::construct(*this, (WindowType)type, window_id, modal, minimizable, frameless, resizable, fullscreen, accessory, parent_window);
 
+    if (!launch_origin_rect.is_empty())
+        window->start_launch_animation(launch_origin_rect);
+
     window->set_has_alpha_channel(has_alpha_channel);
     window->set_title(title);
     if (!fullscreen) {

+ 1 - 1
Userland/Services/WindowServer/ClientConnection.h

@@ -101,7 +101,7 @@ private:
     virtual void update_menu_item(i32, i32, i32, String const&, bool, bool, bool, bool, String const&) override;
     virtual void create_window(i32, Gfx::IntRect const&, bool, bool, bool,
         bool, bool, bool, bool, bool, float, float, Gfx::IntSize const&, Gfx::IntSize const&, Gfx::IntSize const&,
-        Optional<Gfx::IntSize> const&, i32, String const&, i32) override;
+        Optional<Gfx::IntSize> const&, i32, String const&, i32, Gfx::IntRect const&) override;
     virtual Messages::WindowServer::DestroyWindowResponse destroy_window(i32) override;
     virtual void set_window_title(i32, String const&) override;
     virtual Messages::WindowServer::GetWindowTitleResponse get_window_title(i32) override;

+ 20 - 0
Userland/Services/WindowServer/Window.cpp

@@ -365,6 +365,26 @@ void Window::start_minimize_animation()
     m_animation->start();
 }
 
+void Window::start_launch_animation(Gfx::IntRect const& launch_origin_rect)
+{
+    m_animation = Animation::create();
+    m_animation->set_length(150);
+    m_animation->on_update = [this, launch_origin_rect](float progress, Gfx::Painter& painter, Screen& screen, Gfx::DisjointRectSet& flush_rects) {
+        Gfx::PainterStateSaver saver(painter);
+        painter.set_draw_op(Gfx::Painter::DrawOp::Invert);
+
+        auto rect = interpolate_rect(launch_origin_rect, frame().rect(), progress);
+
+        painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted
+        flush_rects.add(rect.intersected(screen.rect()));
+        Compositor::the().invalidate_screen(rect);
+    };
+    m_animation->on_stop = [this] {
+        m_animation = nullptr;
+    };
+    m_animation->start();
+}
+
 void Window::set_opacity(float opacity)
 {
     if (m_opacity == opacity)

+ 2 - 0
Userland/Services/WindowServer/Window.h

@@ -259,6 +259,8 @@ public:
     bool has_taskbar_rect() const { return m_have_taskbar_rect; };
     void start_minimize_animation();
 
+    void start_launch_animation(Gfx::IntRect const&);
+
     Gfx::IntRect tiled_rect(Screen*, WindowTileType) const;
     void recalculate_rect();
 

+ 2 - 1
Userland/Services/WindowServer/WindowServer.ipc

@@ -44,7 +44,8 @@ endpoint WindowServer
         Optional<Gfx::IntSize> resize_aspect_ratio,
         i32 type,
         [UTF8] String title,
-        i32 parent_window_id) =|
+        i32 parent_window_id,
+        Gfx::IntRect launch_origin_rect) =|
 
     destroy_window(i32 window_id) => (Vector<i32> destroyed_window_ids)