diff --git a/src/ShellClients/PanelClone.vala b/src/ShellClients/PanelClone.vala deleted file mode 100644 index 056e73002..000000000 --- a/src/ShellClients/PanelClone.vala +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2024 elementary, Inc. (https://elementary.io) - * SPDX-License-Identifier: GPL-3.0-or-later - * - * Authored by: Leonhard Kargl - */ - -public class Gala.PanelClone : Object { - private const int ANIMATION_DURATION = 250; - - public WindowManager wm { get; construct; } - public unowned PanelWindow panel { get; construct; } - - public Pantheon.Desktop.HideMode hide_mode { - get { - return hide_tracker == null ? Pantheon.Desktop.HideMode.NEVER : hide_tracker.hide_mode; - } - set { - if (value == NEVER) { - hide_tracker = null; - show (); - return; - } else if (hide_tracker == null) { - hide_tracker = new HideTracker (wm.get_display (), panel); - hide_tracker.hide.connect (hide); - hide_tracker.show.connect (show); - } - - hide_tracker.hide_mode = value; - } - } - - public bool panel_hidden { get; private set; default = true; } - - private Meta.WindowActor actor; - - private GestureTracker default_gesture_tracker; - - private HideTracker? hide_tracker; - - public PanelClone (WindowManager wm, PanelWindow panel) { - Object (wm: wm, panel: panel); - } - - construct { - default_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION); - - actor = (Meta.WindowActor) panel.window.get_compositor_private (); - - notify["panel-hidden"].connect (() => { - // When hidden changes schedule an update to make sure it's actually - // correct since things might have changed during the animation - if (hide_tracker != null) { - hide_tracker.schedule_update (); - } - }); - - wm.get_display ().in_fullscreen_changed.connect (check_hide); - - Idle.add_once (() => { - if (hide_mode == NEVER) { - show (); - } else { - hide_tracker.schedule_update (); - } - }); - } - - private float calculate_translation_y (bool hidden) { - switch (panel.anchor) { - case TOP: - return hidden ? -actor.height : 0; - case BOTTOM: - return hidden ? actor.height : 0; - default: - return 0; - } - } - - private void hide () { - if (panel_hidden || default_gesture_tracker.recognizing) { - return; - } - - if (!Meta.Util.is_wayland_compositor ()) { - Utils.x11_set_window_pass_through (panel.window); - } - - if (panel.anchor != TOP && panel.anchor != BOTTOM) { - warning ("Animated hide not supported for side yet."); - return; - } - - InternalUtils.update_transients_visible (panel.window, false); - - new GesturePropertyTransition ( - actor, default_gesture_tracker, "translation-y", null, calculate_translation_y (true) - ).start (false, () => InternalUtils.update_transients_visible (panel.window, !panel_hidden)); - - default_gesture_tracker.add_success_callback (false, () => panel_hidden = true); - } - - private void show () { - if (!panel_hidden || default_gesture_tracker.recognizing || wm.get_display ().get_monitor_in_fullscreen (panel.window.get_monitor ())) { - return; - } - - if (!Meta.Util.is_wayland_compositor ()) { - Utils.x11_unset_window_pass_through (panel.window); - } - - new GesturePropertyTransition ( - actor, default_gesture_tracker, "translation-y", null, calculate_translation_y (false) - ).start (false, () => InternalUtils.update_transients_visible (panel.window, !panel_hidden)); - - default_gesture_tracker.add_success_callback (false, () => panel_hidden = false); - } - - private void check_hide () { - if (wm.get_display ().get_monitor_in_fullscreen (panel.window.get_monitor ())) { - hide (); - } else if (hide_mode == NEVER) { - show (); - } else { - hide_tracker.update_overlap (); - } - } -} diff --git a/src/ShellClients/PanelWindow.vala b/src/ShellClients/PanelWindow.vala index e7e13feac..cc4f6d5bc 100644 --- a/src/ShellClients/PanelWindow.vala +++ b/src/ShellClients/PanelWindow.vala @@ -5,22 +5,44 @@ * Authored by: Leonhard Kargl */ -public class Gala.PanelWindow : Object { +public class Gala.PanelWindow : ShellWindow { + private const int ANIMATION_DURATION = 250; + private static HashTable window_struts = new HashTable (null, null); public WindowManager wm { get; construct; } - public Meta.Window window { get; construct; } public Pantheon.Desktop.Anchor anchor { get; construct set; } - private WindowPositioner window_positioner; + public Pantheon.Desktop.HideMode hide_mode { + get { + return hide_tracker == null ? Pantheon.Desktop.HideMode.NEVER : hide_tracker.hide_mode; + } + set { + if (value == NEVER) { + hide_tracker = null; + show (); + make_exclusive (); + return; + } else if (hide_tracker == null) { + unmake_exclusive (); + + hide_tracker = new HideTracker (wm.get_display (), this); + hide_tracker.hide.connect (hide); + hide_tracker.show.connect (show); + } + + hide_tracker.hide_mode = value; + } + } - private PanelClone clone; + private GestureTracker default_gesture_tracker; + private HideTracker? hide_tracker; private int width = -1; private int height = -1; public PanelWindow (WindowManager wm, Meta.Window window, Pantheon.Desktop.Anchor anchor) { - Object (wm: wm, window: window, anchor: anchor); + Object (wm: wm, anchor: anchor, window: window, position: Position.from_anchor (anchor)); } construct { @@ -30,22 +52,34 @@ public class Gala.PanelWindow : Object { } }); - window.stick (); - - clone = new PanelClone (wm, this); - - unowned var display = wm.get_display (); - - window_positioner = new WindowPositioner (display, window, WindowPositioner.Position.from_anchor (anchor)); - - notify["anchor"].connect (() => window_positioner.position = WindowPositioner.Position.from_anchor (anchor)); + notify["anchor"].connect (() => position = Position.from_anchor (anchor)); - unowned var workspace_manager = display.get_workspace_manager (); + unowned var workspace_manager = window.display.get_workspace_manager (); workspace_manager.workspace_added.connect (update_strut); workspace_manager.workspace_removed.connect (update_strut); window.size_changed.connect (update_strut); window.position_changed.connect (update_strut); + + default_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION); + + notify["hidden"].connect (() => { + // When hidden changes schedule an update to make sure it's actually + // correct since things might have changed during the animation + if (hide_tracker != null) { + hide_tracker.schedule_update (); + } + }); + + window.display.in_fullscreen_changed.connect (() => { + if (wm.get_display ().get_monitor_in_fullscreen (window.get_monitor ())) { + hide (); + } else if (hide_mode == NEVER) { + show (); + } else { + hide_tracker.update_overlap (); + } + }); } #if HAS_MUTTER45 @@ -78,14 +112,16 @@ public class Gala.PanelWindow : Object { update_strut (); } - public void set_hide_mode (Pantheon.Desktop.HideMode hide_mode) { - clone.hide_mode = hide_mode; + private void hide () { + add_state (CUSTOM_HIDDEN, default_gesture_tracker); + } - if (hide_mode == NEVER) { - make_exclusive (); - } else { - unmake_exclusive (); + private void show () { + if (window.display.get_monitor_in_fullscreen (window.get_monitor ())) { + return; } + + remove_state (CUSTOM_HIDDEN, default_gesture_tracker); } private void make_exclusive () { @@ -93,7 +129,7 @@ public class Gala.PanelWindow : Object { } private void update_strut () { - if (clone.hide_mode != NEVER) { + if (hide_mode != NEVER) { return; } diff --git a/src/ShellClients/WindowPositioner.vala b/src/ShellClients/PositionedWindow.vala similarity index 85% rename from src/ShellClients/WindowPositioner.vala rename to src/ShellClients/PositionedWindow.vala index daf838f14..53cf9b5fa 100644 --- a/src/ShellClients/WindowPositioner.vala +++ b/src/ShellClients/PositionedWindow.vala @@ -5,7 +5,7 @@ * Authored by: Leonhard Kargl */ -public class Gala.WindowPositioner : Object { +public class Gala.PositionedWindow : Object { public enum Position { TOP, BOTTOM, @@ -21,7 +21,6 @@ public class Gala.WindowPositioner : Object { } } - public Meta.Display display { get; construct; } public Meta.Window window { get; construct; } /** * This may only be set after the window was shown. @@ -30,8 +29,8 @@ public class Gala.WindowPositioner : Object { public Position position { get; construct set; } public Variant? position_data { get; construct set; } - public WindowPositioner (Meta.Display display, Meta.Window window, Position position, Variant? position_data = null) { - Object (display: display, window: window, position: position, position_data: position_data); + public PositionedWindow (Meta.Window window, Position position, Variant? position_data = null) { + Object (window: window, position: position, position_data: position_data); } construct { @@ -41,7 +40,7 @@ public class Gala.WindowPositioner : Object { window.position_changed.connect (position_window); window.shown.connect (position_window); - unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager (); + 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); @@ -51,8 +50,8 @@ public class Gala.WindowPositioner : Object { 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: diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index 31e5b5c71..ee099253c 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -26,7 +26,7 @@ public class Gala.ShellClientsManager : Object { private ManagedClient[] protocol_clients = {}; private GLib.HashTable panel_windows = new GLib.HashTable (null, null); - private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); + private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); private ShellClientsManager (WindowManager wm) { Object (wm: wm); @@ -180,16 +180,36 @@ public class Gala.ShellClientsManager : Object { return; } - panel_windows[window].set_hide_mode (hide_mode); + panel_windows[window].hide_mode = hide_mode; } public void make_centered (Meta.Window window) requires (!is_itself_positioned (window)) { - positioned_windows[window] = new WindowPositioner (wm.get_display (), window, CENTER); + positioned_windows[window] = new ShellWindow (window, CENTER); // connect_after so we make sure that any queued move is unqueued window.unmanaging.connect_after ((_window) => positioned_windows.remove (_window)); } + public void add_state (ShellWindow.State state, GestureTracker gesture_tracker) { + foreach (var window in positioned_windows.get_values ()) { + window.add_state (state, gesture_tracker); + } + + foreach (var window in panel_windows.get_values ()) { + window.add_state (state, gesture_tracker); + } + } + + public void remove_state (ShellWindow.State state, GestureTracker gesture_tracker) { + foreach (var window in positioned_windows.get_values ()) { + window.remove_state (state, gesture_tracker); + } + + foreach (var window in panel_windows.get_values ()) { + window.remove_state (state, gesture_tracker); + } + } + private bool is_itself_positioned (Meta.Window window) { return (window in positioned_windows) || (window in panel_windows) || NotificationStack.is_notification (window); } diff --git a/src/ShellClients/ShellWindow.vala b/src/ShellClients/ShellWindow.vala new file mode 100644 index 000000000..963fa5592 --- /dev/null +++ b/src/ShellClients/ShellWindow.vala @@ -0,0 +1,85 @@ +/* + * Copyright 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.ShellWindow : PositionedWindow { + [Flags] + public enum State { + CUSTOM_HIDDEN, + MULTITASKING_VIEW, + DESKTOP + } + + private const State HIDING_STATES = CUSTOM_HIDDEN | MULTITASKING_VIEW; + + private Meta.WindowActor actor; + private State current_state = DESKTOP; + + private bool gesture_ongoing = false; + + public ShellWindow (Meta.Window window, Position position, Variant? position_data = null) { + base (window, position, position_data); + } + + construct { + actor = (Meta.WindowActor) window.get_compositor_private (); + } + + public void add_state (State state, GestureTracker gesture_tracker) { + animate (current_state | state, gesture_tracker); + } + + public void remove_state (State state, GestureTracker gesture_tracker) { + animate (current_state & ~state, gesture_tracker); + } + + private void animate (State new_state, GestureTracker gesture_tracker) { + if (new_state == current_state || gesture_ongoing) { + return; + } + + gesture_ongoing = true; + + if (!Meta.Util.is_wayland_compositor ()) { + Utils.x11_set_window_pass_through (window); + } + + InternalUtils.update_transients_visible (window, false); + + new GesturePropertyTransition ( + actor, gesture_tracker, get_animation_property (), null, calculate_value ((new_state & HIDING_STATES) != 0) + ).start (true, () => InternalUtils.update_transients_visible (window, (current_state & HIDING_STATES) == 0)); + + gesture_tracker.add_success_callback (false, (percentage, completions) => { + if (completions != 0) { + current_state = new_state; + } + + gesture_ongoing = false; + }); + } + + private string get_animation_property () { + switch (position) { + case TOP: + case BOTTOM: + return "translation-y"; + default: + return "opacity"; + } + } + + private Value calculate_value (bool hidden) { + switch (position) { + case TOP: + return hidden ? -actor.height : 0f; + case BOTTOM: + return hidden ? actor.height : 0f; + default: + return hidden ? 0u : 255u; + } + } +} diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index c98d83f00..2ed1b646d 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -39,7 +39,6 @@ namespace Gala { private IconGroupContainer icon_groups; private Clutter.Actor workspaces; - private Clutter.Actor dock_clones; private Clutter.Actor primary_monitor_container; private Clutter.BrightnessContrastEffect brightness_effect; @@ -83,8 +82,6 @@ namespace Gala { icon_groups = new IconGroupContainer (display.get_monitor_scale (display.get_primary_monitor ())); - dock_clones = new Clutter.Actor (); - brightness_effect = new Clutter.BrightnessContrastEffect (); update_brightness_effect (); @@ -101,7 +98,6 @@ namespace Gala { primary_monitor_container.add_child (icon_groups); primary_monitor_container.add_child (workspaces); add_child (primary_monitor_container); - add_child (dock_clones); unowned var manager = display.get_workspace_manager (); manager.workspace_added.connect (add_workspace); @@ -631,7 +627,6 @@ namespace Gala { wm.background_group.hide (); wm.window_group.hide (); wm.top_window_group.hide (); - wm.shell_group.hide (); show (); grab_key_focus (); @@ -676,9 +671,9 @@ namespace Gala { } if (opening) { - show_docks (with_gesture, is_cancel_animation); + ShellClientsManager.get_instance ().add_state (MULTITASKING_VIEW, multitasking_gesture_tracker); } else { - hide_docks (with_gesture, is_cancel_animation); + ShellClientsManager.get_instance ().remove_state (MULTITASKING_VIEW, multitasking_gesture_tracker); } GestureTracker.OnEnd on_animation_end = (percentage, completions) => { @@ -694,9 +689,6 @@ namespace Gala { wm.background_group.show (); wm.window_group.show (); wm.top_window_group.show (); - wm.shell_group.show (); - - dock_clones.destroy_all_children (); wm.pop_modal (modal_proxy); } @@ -718,107 +710,6 @@ namespace Gala { } } - private void show_docks (bool with_gesture, bool is_cancel_animation) { - unowned GLib.List window_actors = display.get_window_actors (); - foreach (unowned Meta.WindowActor actor in window_actors) { - const int MAX_OFFSET = 200; - - if (actor.is_destroyed () || !actor.visible || actor.translation_y != 0) { - continue; - } - - unowned Meta.Window window = actor.get_meta_window (); - var monitor = window.get_monitor (); - - if (window.window_type != Meta.WindowType.DOCK) { - continue; - } - - if (NotificationStack.is_notification (window)) { - continue; - } - - if (display.get_monitor_in_fullscreen (monitor)) { - continue; - } - - var monitor_geom = display.get_monitor_geometry (monitor); - - var window_geom = window.get_frame_rect (); - var top = monitor_geom.y + MAX_OFFSET > window_geom.y; - var bottom = monitor_geom.y + monitor_geom.height - MAX_OFFSET < window_geom.y; - - if (!top && !bottom) { - continue; - } - - var initial_x = actor.x; - var initial_y = actor.y; - var target_y = (top) - ? actor.y - actor.height - : actor.y + actor.height; - - var clone = new SafeWindowClone (window, true); - dock_clones.add_child (clone); - - clone.set_position (initial_x, initial_y); - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var y = GestureTracker.animation_value (initial_y, target_y, percentage); - clone.y = y; - }; - - GestureTracker.OnEnd on_animation_end = (percentage, completions) => { - if (completions == 0) { - return; - } - - clone.save_easing_state (); - clone.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - clone.set_easing_duration ((!is_cancel_animation && AnimationsSettings.get_enable_animations ()) ? ANIMATION_DURATION : 0); - clone.y = target_y; - clone.restore_easing_state (); - }; - - if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, 1, 0); - } else { - multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); - } - } - } - - private void hide_docks (bool with_gesture, bool is_cancel_animation) { - foreach (unowned var child in dock_clones.get_children ()) { - var dock = (Clutter.Clone) child; - var initial_y = dock.y; - var target_y = dock.source.y; - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var y = GestureTracker.animation_value (initial_y, target_y, percentage); - dock.y = y; - }; - - GestureTracker.OnEnd on_animation_end = (percentage, completions) => { - if (completions == 0) { - return; - } - - dock.save_easing_state (); - dock.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - dock.set_easing_duration (AnimationsSettings.get_animation_duration (ANIMATION_DURATION)); - dock.y = target_y; - dock.restore_easing_state (); - }; - - if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, 1, 0); - } else { - multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); - } - } - } - private bool keybinding_filter (Meta.KeyBinding binding) { var action = Meta.Prefs.get_keybinding_action (binding.get_name ()); diff --git a/src/meson.build b/src/meson.build index 9660dd2b8..90152df37 100644 --- a/src/meson.build +++ b/src/meson.build @@ -46,10 +46,10 @@ gala_bin_sources = files( 'ShellClients/HideTracker.vala', 'ShellClients/ManagedClient.vala', 'ShellClients/NotificationsClient.vala', - 'ShellClients/PanelClone.vala', 'ShellClients/PanelWindow.vala', + 'ShellClients/PositionedWindow.vala', 'ShellClients/ShellClientsManager.vala', - 'ShellClients/WindowPositioner.vala', + 'ShellClients/ShellWindow.vala', 'Widgets/DwellClickTimer.vala', 'Widgets/IconGroup.vala', 'Widgets/IconGroupContainer.vala',