diff --git a/src/ShellClients/ExtendedBehaviorWindow.vala b/src/ShellClients/ExtendedBehaviorWindow.vala new file mode 100644 index 000000000..9a28da2f2 --- /dev/null +++ b/src/ShellClients/ExtendedBehaviorWindow.vala @@ -0,0 +1,20 @@ +/* + * Copyright 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.ExtendedBehaviorWindow : ShellWindow { + public ExtendedBehaviorWindow (Meta.Window window) { + var target = new PropertyTarget (CUSTOM, window.get_compositor_private (), "opacity", typeof (uint), 255u, 0u); + Object (window: window, hide_target: target); + } + + protected override void get_window_position (Mtk.Rectangle window_rect, out int x, out int y) { + var monitor_rect = window.display.get_monitor_geometry (window.get_monitor ()); + + x = monitor_rect.x + (monitor_rect.width - window_rect.width) / 2; + y = monitor_rect.y + (monitor_rect.height - window_rect.height) / 2; + } +} diff --git a/src/ShellClients/PanelWindow.vala b/src/ShellClients/PanelWindow.vala index 01260f44e..4e60676ec 100644 --- a/src/ShellClients/PanelWindow.vala +++ b/src/ShellClients/PanelWindow.vala @@ -35,8 +35,11 @@ public class Gala.PanelWindow : ShellWindow, RootTarget { private GestureController workspace_gesture_controller; private WorkspaceHideTracker workspace_hide_tracker; + private int width = -1; + private int height = -1; + public PanelWindow (WindowManager wm, Meta.Window window, Pantheon.Desktop.Anchor anchor) { - Object (wm: wm, anchor: anchor, window: window, position: Position.from_anchor (anchor)); + Object (wm: wm, window: window, anchor: anchor); } construct { @@ -46,7 +49,7 @@ public class Gala.PanelWindow : ShellWindow, RootTarget { } }); - notify["anchor"].connect (() => position = Position.from_anchor (anchor)); + notify["anchor"].connect (position_window); unowned var workspace_manager = window.display.get_workspace_manager (); workspace_manager.workspace_added.connect (update_strut); @@ -71,8 +74,43 @@ public class Gala.PanelWindow : ShellWindow, RootTarget { workspace_hide_tracker.switching_workspace_progress_updated.connect ((value) => workspace_gesture_controller.progress = value); workspace_hide_tracker.window_state_changed_progress_updated.connect (workspace_gesture_controller.goto); + window.size_changed.connect (update_target); + notify["anchor"].connect (update_target); + update_target (); + add_gesture_controller (user_gesture_controller); add_gesture_controller (workspace_gesture_controller); + + var window_actor = (Meta.WindowActor) window.get_compositor_private (); + + window_actor.notify["width"].connect (update_clip); + window_actor.notify["height"].connect (update_clip); + window_actor.notify["translation-y"].connect (update_clip); + notify["anchor"].connect (update_clip); + } + + public Mtk.Rectangle get_custom_window_rect () { + var window_rect = window.get_frame_rect (); + + if (width > 0) { + window_rect.width = width; + } + + if (height > 0) { + window_rect.height = height; + + if (anchor == BOTTOM) { + var geom = window.display.get_monitor_geometry (window.get_monitor ()); + window_rect.y = geom.y + geom.height - height; + } + } + + return window_rect; + } + + public void set_size (int width, int height) { + this.width = width; + this.height = height; } public void request_visible_in_multitasking_view () { @@ -80,11 +118,6 @@ public class Gala.PanelWindow : ShellWindow, RootTarget { actor.add_action (new DragDropAction (DESTINATION, "multitaskingview-window")); } - protected override void update_target () { - base.update_target (); - workspace_hide_tracker.recalculate_all_workspaces (); - } - protected override double get_hidden_progress () { var user_workspace_hidden_progress = double.min ( user_gesture_controller.progress, @@ -214,4 +247,47 @@ public class Gala.PanelWindow : ShellWindow, RootTarget { return TOP; } } + + protected override void get_window_position (Mtk.Rectangle window_rect, out int x, out int y) { + var monitor_rect = window.display.get_monitor_geometry (window.display.get_primary_monitor ()); + switch (anchor) { + case TOP: + x = monitor_rect.x + (monitor_rect.width - window_rect.width) / 2; + y = monitor_rect.y; + break; + + case BOTTOM: + x = monitor_rect.x + (monitor_rect.width - window_rect.width) / 2; + y = monitor_rect.y + monitor_rect.height - window_rect.height; + break; + + default: + warning ("Unsupported anchor %s for PanelWindow", anchor.to_string ()); + x = 0; + y = 0; + break; + } + } + + private void update_target () { + var to_value = anchor == TOP ? -get_custom_window_rect ().height : get_custom_window_rect ().height; + hide_target = new PropertyTarget (CUSTOM, actor, "translation-y", typeof (float), 0f, (float) to_value); + + workspace_hide_tracker.recalculate_all_workspaces (); + } + + private void update_clip () { + var monitor_geom = window.display.get_monitor_geometry (window.get_monitor ()); + var window_actor = (Meta.WindowActor) window.get_compositor_private (); + + var y = window_actor.y + window_actor.translation_y; + + if (y + window_actor.height > monitor_geom.y + monitor_geom.height) { + window_actor.set_clip (0, 0, window_actor.width, monitor_geom.y + monitor_geom.height - y); + } else if (y < monitor_geom.y) { + window_actor.set_clip (0, monitor_geom.y - y, window_actor.width, window_actor.height); + } else { + window_actor.remove_clip (); + } + } } diff --git a/src/ShellClients/PositionedWindow.vala b/src/ShellClients/PositionedWindow.vala index 32fe55ad6..0b0bc39c8 100644 --- a/src/ShellClients/PositionedWindow.vala +++ b/src/ShellClients/PositionedWindow.vala @@ -5,39 +5,11 @@ * Authored by: Leonhard Kargl */ -public class Gala.PositionedWindow : Object { - public enum Position { - TOP, - BOTTOM, - CENTER; - - public static Position from_anchor (Pantheon.Desktop.Anchor anchor) { - if (anchor > 1) { - warning ("Position %s not supported yet", anchor.to_string ()); - return CENTER; - } - - return (Position) anchor; - } - } - +public abstract class Gala.PositionedWindow : Object { public Meta.Window window { get; construct; } - /** - * This may only be set after the window was shown. - * The initial position should only be given in the constructor. - */ - public Position position { get; construct set; } - public Variant? position_data { get; construct set; } - - private int width = -1; - private int height = -1; private ulong position_changed_id; - public PositionedWindow (Meta.Window window, Position position, Variant? position_data = null) { - Object (window: window, position: position, position_data: position_data); - } - construct { window.stick (); @@ -48,62 +20,18 @@ public class Gala.PositionedWindow : Object { unowned var monitor_manager = window.display.get_context ().get_backend ().get_monitor_manager (); monitor_manager.monitors_changed.connect (position_window); monitor_manager.monitors_changed_internal.connect (position_window); - - notify["position"].connect (position_window); - notify["position-data"].connect (position_window); } - public Mtk.Rectangle get_custom_window_rect () { + protected void position_window () { var window_rect = window.get_frame_rect (); - if (width > 0) { - window_rect.width = width; - } - - if (height > 0) { - window_rect.height = height; - - if (position == BOTTOM) { - var geom = window.display.get_monitor_geometry (window.get_monitor ()); - window_rect.y = geom.y + geom.height - height; - } - } - - return window_rect; - } - - public void set_size (int width, int height) { - this.width = width; - this.height = height; - } - - private void position_window () { int x = 0, y = 0; - var window_rect = window.get_frame_rect (); - unowned var display = window.display; - - switch (position) { - case CENTER: - var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; - y = monitor_geom.y + (monitor_geom.height - window_rect.height) / 2; - break; - - case TOP: - var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; - y = monitor_geom.y; - break; - - case BOTTOM: - var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; - y = monitor_geom.y + monitor_geom.height - window_rect.height; - break; - } + get_window_position (window_rect, out x, out y); SignalHandler.block (window, position_changed_id); window.move_frame (false, x, y); SignalHandler.unblock (window, position_changed_id); } + + protected abstract void get_window_position (Mtk.Rectangle window_rect, out int x, out int y); } diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index 25bd4a346..7dad2ce53 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -199,7 +199,7 @@ public class Gala.ShellClientsManager : Object, GestureTarget { } public void make_centered (Meta.Window window) requires (!is_itself_positioned (window)) { - positioned_windows[window] = new ShellWindow (window, CENTER); + positioned_windows[window] = new ExtendedBehaviorWindow (window); // connect_after so we make sure that any queued move is unqueued window.unmanaging.connect_after ((_window) => positioned_windows.remove (_window)); diff --git a/src/ShellClients/ShellWindow.vala b/src/ShellClients/ShellWindow.vala index 6a735e387..369a211fb 100644 --- a/src/ShellClients/ShellWindow.vala +++ b/src/ShellClients/ShellWindow.vala @@ -5,42 +5,18 @@ * Authored by: Leonhard Kargl */ -public class Gala.ShellWindow : PositionedWindow, GestureTarget { +public abstract class Gala.ShellWindow : PositionedWindow, GestureTarget { public bool restore_previous_x11_region { private get; set; default = false; } - private Meta.WindowActor window_actor; + /** + * A gesture target that will receive a CUSTOM update every time a gesture + * is propagated, with the progress gotten via {@link get_hidden_progress()} + */ + public GestureTarget hide_target { get; construct set; } + private double multitasking_view_progress = 0; private int animations_ongoing = 0; - private PropertyTarget property_target; - - public ShellWindow (Meta.Window window, Position position, Variant? position_data = null) { - base (window, position, position_data); - } - - construct { - window_actor = (Meta.WindowActor) window.get_compositor_private (); - - window_actor.notify["width"].connect (update_clip); - window_actor.notify["height"].connect (update_clip); - window_actor.notify["translation-y"].connect (update_clip); - notify["position"].connect (update_clip); - - window.size_changed.connect (update_target); - notify["position"].connect (update_target); - update_target (); - } - - protected virtual void update_target () { - property_target = new PropertyTarget ( - CUSTOM, window_actor, - get_animation_property (), - get_property_type (), - calculate_value (false), - calculate_value (true) - ); - } - public virtual void propagate (UpdateType update_type, GestureAction action, double progress) { switch (update_type) { case START: @@ -53,7 +29,7 @@ public class Gala.ShellWindow : PositionedWindow, GestureTarget { multitasking_view_progress = progress; } - property_target.propagate (UPDATE, CUSTOM, get_hidden_progress ()); + hide_target.propagate (UPDATE, CUSTOM, get_hidden_progress ()); break; case END: @@ -71,6 +47,8 @@ public class Gala.ShellWindow : PositionedWindow, GestureTarget { } private void update_visibility () { + unowned var window_actor = (Meta.WindowActor) window.get_compositor_private (); + var visible = get_hidden_progress () < 0.1; var animating = animations_ongoing > 0; @@ -104,63 +82,11 @@ public class Gala.ShellWindow : PositionedWindow, GestureTarget { return true; } - unowned var window_actor = (Meta.WindowActor) transient.get_compositor_private (); + unowned var transient_window_actor = (Meta.WindowActor) transient.get_compositor_private (); - window_actor.visible = visible && !animating; + transient_window_actor.visible = visible && !animating; return true; }); } - - private string get_animation_property () { - switch (position) { - case TOP: - case BOTTOM: - return "translation-y"; - default: - return "opacity"; - } - } - - private Type get_property_type () { - switch (position) { - case TOP: - case BOTTOM: - return typeof (float); - default: - return typeof (uint); - } - } - - private Value calculate_value (bool hidden) { - var custom_rect = get_custom_window_rect (); - - switch (position) { - case TOP: - return hidden ? -custom_rect.height : 0f; - case BOTTOM: - return hidden ? custom_rect.height : 0f; - default: - return hidden ? 0u : 255u; - } - } - - private void update_clip () { - if (position != TOP && position != BOTTOM) { - window_actor.remove_clip (); - return; - } - - var monitor_geom = window.display.get_monitor_geometry (window.get_monitor ()); - - var y = window_actor.y + window_actor.translation_y; - - if (y + window_actor.height > monitor_geom.y + monitor_geom.height) { - window_actor.set_clip (0, 0, window_actor.width, monitor_geom.y + monitor_geom.height - y); - } else if (y < monitor_geom.y) { - window_actor.set_clip (0, monitor_geom.y - y, window_actor.width, window_actor.height); - } else { - window_actor.remove_clip (); - } - } } diff --git a/src/meson.build b/src/meson.build index f3a35a6c8..08c81890f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -42,6 +42,7 @@ gala_bin_sources = files( 'HotCorners/Barrier.vala', 'HotCorners/HotCorner.vala', 'HotCorners/HotCornerManager.vala', + 'ShellClients/ExtendedBehaviorWindow.vala', 'ShellClients/HideTracker.vala', 'ShellClients/ManagedClient.vala', 'ShellClients/NotificationsClient.vala',