ShellClients: Allow force hiding all panels

This is e.g. used by the multitasking view.
It also supports hiding with gesture
This commit is contained in:
Leonhard Kargl 2024-12-08 15:46:34 +01:00 committed by Leonhard
parent 4f38089f5f
commit 0f0b50a300
7 changed files with 87 additions and 168 deletions

View File

@ -10,7 +10,7 @@
* with easing without a gesture. Respects the enable animation setting. * with easing without a gesture. Respects the enable animation setting.
*/ */
public class Gala.GesturePropertyTransition : Object { public class Gala.GesturePropertyTransition : Object {
public delegate void DoneCallback (); public delegate void DoneCallback (bool cancel_action);
/** /**
* The actor whose property will be animated. * The actor whose property will be animated.
@ -90,13 +90,13 @@ public class Gala.GesturePropertyTransition : Object {
if (actual_from_value.type () != current_value.type ()) { if (actual_from_value.type () != current_value.type ()) {
warning ("from_value of type %s is not of the same type as the property %s which is %s. Can't animate.", from_value.type_name (), property, current_value.type_name ()); warning ("from_value of type %s is not of the same type as the property %s which is %s. Can't animate.", from_value.type_name (), property, current_value.type_name ());
finish (); finish (true);
return; return;
} }
if (current_value.type () != to_value.type ()) { if (current_value.type () != to_value.type ()) {
warning ("to_value of type %s is not of the same type as the property %s which is %s. Can't animate.", to_value.type_name (), property, current_value.type_name ()); warning ("to_value of type %s is not of the same type as the property %s which is %s. Can't animate.", to_value.type_name (), property, current_value.type_name ());
finish (); finish (true);
return; return;
} }
@ -118,9 +118,9 @@ public class Gala.GesturePropertyTransition : Object {
unowned var transition = actor.get_transition (property); unowned var transition = actor.get_transition (property);
if (transition == null) { if (transition == null) {
finish (); finish (cancel_action);
} else { } else {
transition.stopped.connect (finish); transition.stopped.connect (() => finish (cancel_action));
} }
}; };
@ -147,9 +147,9 @@ public class Gala.GesturePropertyTransition : Object {
} }
} }
private void finish () { private void finish (bool cancel_action) {
if (done_callback != null) { if (done_callback != null) {
done_callback (); done_callback (cancel_action);
} }
unref (); unref ();

View File

@ -66,6 +66,8 @@ public class Gala.GestureTracker : Object {
*/ */
public bool enabled { get; set; default = true; } public bool enabled { get; set; default = true; }
public bool recognizing { get; private set; }
/** /**
* Emitted when a new gesture is detected. * Emitted when a new gesture is detected.
* If the receiving code needs to handle this gesture, it should call to connect_handlers to * If the receiving code needs to handle this gesture, it should call to connect_handlers to
@ -212,6 +214,7 @@ public class Gala.GestureTracker : Object {
on_begin (percentage); on_begin (percentage);
} }
recognizing = true;
previous_percentage = percentage; previous_percentage = percentage;
previous_time = elapsed_time; previous_time = elapsed_time;
} }
@ -248,6 +251,7 @@ public class Gala.GestureTracker : Object {
} }
disconnect_all_handlers (); disconnect_all_handlers ();
recognizing = false;
previous_percentage = 0; previous_percentage = 0;
previous_time = 0; previous_time = 0;
percentage_delta = 0; percentage_delta = 0;

View File

@ -6,12 +6,13 @@
*/ */
public class Gala.HideTracker : Object { public class Gala.HideTracker : Object {
private const int ANIMATION_DURATION = 250;
private const int BARRIER_OFFSET = 50; // Allow hot corner trigger private const int BARRIER_OFFSET = 50; // Allow hot corner trigger
private const int UPDATE_TIMEOUT = 200; private const int UPDATE_TIMEOUT = 200;
private const int HIDE_DELAY = 500; private const int HIDE_DELAY = 500;
public signal void hide (); public signal void hide (GestureTracker gesture_tracker, bool with_gesture);
public signal void show (); public signal void show (GestureTracker gesture_tracker, bool with_gesture);
public Meta.Display display { get; construct; } public Meta.Display display { get; construct; }
public unowned PanelWindow panel { get; construct; } public unowned PanelWindow panel { get; construct; }
@ -19,6 +20,7 @@ public class Gala.HideTracker : Object {
public Pantheon.Desktop.HideMode hide_mode { get; set; } public Pantheon.Desktop.HideMode hide_mode { get; set; }
private Clutter.PanAction pan_action; private Clutter.PanAction pan_action;
private GestureTracker gesture_tracker; // Placeholder that will replace pan_action once the pan_backend gets merged
private bool hovered = false; private bool hovered = false;
@ -79,6 +81,8 @@ public class Gala.HideTracker : Object {
display.get_workspace_manager ().active_workspace_changed.connect (schedule_update); display.get_workspace_manager ().active_workspace_changed.connect (schedule_update);
gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
pan_action = new Clutter.PanAction () { pan_action = new Clutter.PanAction () {
n_touch_points = 1, n_touch_points = 1,
pan_axis = X_AXIS pan_axis = X_AXIS
@ -146,13 +150,13 @@ public class Gala.HideTracker : Object {
} }
update_timeout_id = Timeout.add (UPDATE_TIMEOUT, () => { update_timeout_id = Timeout.add (UPDATE_TIMEOUT, () => {
update_overlap (); update_overlap (gesture_tracker, false);
update_timeout_id = 0; update_timeout_id = 0;
return Source.REMOVE; return Source.REMOVE;
}); });
} }
private void update_overlap () { public void update_overlap (GestureTracker gesture_tracker, bool with_gesture) {
overlap = false; overlap = false;
focus_overlap = false; focus_overlap = false;
focus_maximized_overlap = false; focus_maximized_overlap = false;
@ -185,25 +189,25 @@ public class Gala.HideTracker : Object {
focus_maximized_overlap = VERTICAL in window.get_maximized (); focus_maximized_overlap = VERTICAL in window.get_maximized ();
} }
update_hidden (); update_hidden (gesture_tracker, with_gesture);
} }
private void update_hidden () { private void update_hidden (GestureTracker gesture_tracker, bool with_gesture) {
switch (hide_mode) { switch (hide_mode) {
case MAXIMIZED_FOCUS_WINDOW: case MAXIMIZED_FOCUS_WINDOW:
toggle_display (focus_maximized_overlap); toggle_display (focus_maximized_overlap, gesture_tracker, with_gesture);
break; break;
case OVERLAPPING_FOCUS_WINDOW: case OVERLAPPING_FOCUS_WINDOW:
toggle_display (focus_overlap); toggle_display (focus_overlap, gesture_tracker, with_gesture);
break; break;
case OVERLAPPING_WINDOW: case OVERLAPPING_WINDOW:
toggle_display (overlap); toggle_display (overlap, gesture_tracker, with_gesture);
break; break;
case ALWAYS: case ALWAYS:
toggle_display (true); toggle_display (true, gesture_tracker, with_gesture);
break; break;
default: default:
@ -212,7 +216,11 @@ public class Gala.HideTracker : Object {
} }
} }
private void toggle_display (bool should_hide) { private void toggle_display (bool should_hide, GestureTracker gesture_tracker, bool with_gesture) {
if (display.get_monitor_in_fullscreen (panel.window.get_monitor ())) {
return;
}
#if HAS_MUTTER45 #if HAS_MUTTER45
hovered = panel.window.has_pointer (); hovered = panel.window.has_pointer ();
#else #else
@ -222,7 +230,7 @@ public class Gala.HideTracker : Object {
if (should_hide && !hovered && !panel.window.has_focus ()) { if (should_hide && !hovered && !panel.window.has_focus ()) {
trigger_hide (); trigger_hide ();
} else { } else {
trigger_show (); trigger_show (gesture_tracker, with_gesture);
} }
} }
@ -241,7 +249,7 @@ public class Gala.HideTracker : Object {
} }
hide_timeout_id = Timeout.add_once (HIDE_DELAY, () => { hide_timeout_id = Timeout.add_once (HIDE_DELAY, () => {
hide (); hide (gesture_tracker, false);
hide_timeout_id = 0; hide_timeout_id = 0;
}); });
} }
@ -253,9 +261,9 @@ public class Gala.HideTracker : Object {
} }
} }
private void trigger_show () { private void trigger_show (GestureTracker gesture_tracker, bool with_gesture) {
reset_hide_timeout (); reset_hide_timeout ();
show (); show (gesture_tracker, with_gesture);
} }
private bool check_valid_gesture () { private bool check_valid_gesture () {
@ -281,7 +289,7 @@ public class Gala.HideTracker : Object {
if (delta_y < 0) { // Only allow swipes upwards if (delta_y < 0) { // Only allow swipes upwards
panel.window.focus (pan_action.get_last_event (0).get_time ()); panel.window.focus (pan_action.get_last_event (0).get_time ());
trigger_show (); trigger_show (gesture_tracker, false);
} }
return false; return false;
@ -325,7 +333,7 @@ public class Gala.HideTracker : Object {
int.MAX int.MAX
); );
barrier.trigger.connect (trigger_show); barrier.trigger.connect (() => trigger_show (gesture_tracker, false));
} }
#if HAS_MUTTER45 #if HAS_MUTTER45
@ -346,6 +354,6 @@ public class Gala.HideTracker : Object {
int.MAX int.MAX
); );
barrier.trigger.connect (trigger_show); barrier.trigger.connect (() => trigger_show (gesture_tracker, false));
} }
} }

View File

@ -18,7 +18,7 @@ public class Gala.PanelClone : Object {
set { set {
if (value == NEVER) { if (value == NEVER) {
hide_tracker = null; hide_tracker = null;
show (); show (new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION), false);
return; return;
} else if (hide_tracker == null) { } else if (hide_tracker == null) {
hide_tracker = new HideTracker (wm.get_display (), panel); hide_tracker = new HideTracker (wm.get_display (), panel);
@ -35,6 +35,9 @@ public class Gala.PanelClone : Object {
private SafeWindowClone clone; private SafeWindowClone clone;
private Meta.WindowActor actor; private Meta.WindowActor actor;
private GestureTracker? last_gesture_tracker;
private bool force_hide = false;
private HideTracker? hide_tracker; private HideTracker? hide_tracker;
public PanelClone (WindowManager wm, PanelWindow panel) { public PanelClone (WindowManager wm, PanelWindow panel) {
@ -72,7 +75,7 @@ public class Gala.PanelClone : Object {
Idle.add_once (() => { Idle.add_once (() => {
if (hide_mode == NEVER) { if (hide_mode == NEVER) {
show (); show (new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION), false);
} else { } else {
hide_tracker.schedule_update (); hide_tracker.schedule_update ();
} }
@ -114,17 +117,13 @@ public class Gala.PanelClone : Object {
} }
} }
private int get_animation_duration () { private void hide (GestureTracker gesture_tracker, bool with_gesture) {
var fullscreen = wm.get_display ().get_monitor_in_fullscreen (panel.window.get_monitor ()); if (panel_hidden || last_gesture_tracker != null && last_gesture_tracker.recognizing) {
var should_animate = AnimationsSettings.get_enable_animations () && !wm.workspace_view.is_opened () && !fullscreen;
return should_animate ? ANIMATION_DURATION : 0;
}
private void hide () {
if (panel_hidden) {
return; return;
} }
last_gesture_tracker = gesture_tracker;
panel_hidden = true; panel_hidden = true;
if (!Meta.Util.is_wayland_compositor ()) { if (!Meta.Util.is_wayland_compositor ()) {
@ -138,37 +137,43 @@ public class Gala.PanelClone : Object {
clone.visible = true; clone.visible = true;
clone.save_easing_state (); new GesturePropertyTransition (clone, gesture_tracker, "y", null, calculate_clone_y (true)).start (with_gesture);
clone.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
clone.set_easing_duration (get_animation_duration ());
clone.y = calculate_clone_y (true);
clone.restore_easing_state ();
} }
private void show () { private void show (GestureTracker gesture_tracker, bool with_gesture) {
if (!panel_hidden) { if (!panel_hidden || force_hide || last_gesture_tracker != null && last_gesture_tracker.recognizing) {
return; return;
} }
last_gesture_tracker = gesture_tracker;
if (!Meta.Util.is_wayland_compositor ()) { if (!Meta.Util.is_wayland_compositor ()) {
Utils.x11_unset_window_pass_through (panel.window); Utils.x11_unset_window_pass_through (panel.window);
} }
clone.save_easing_state (); new GesturePropertyTransition (clone, gesture_tracker, "y", null, calculate_clone_y (false)).start (with_gesture, (cancel_action) => {
clone.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); if (!cancel_action) {
clone.set_easing_duration (get_animation_duration ()); // We need the small timeout because in the case that we're showing in sync with the multitasking view
clone.y = calculate_clone_y (false); // we have to make sure that we hide the clone after the multitasking view was already hidden otherwise
clone.restore_easing_state (); // it flickers because the actual window actors are hidden by the multitasking view
Timeout.add_once (10, () => {
force_hide = false;
clone.visible = false;
panel_hidden = false;
});
}
});
}
unowned var y_transition = clone.get_transition ("y"); public void set_force_hide (bool force_hide, GestureTracker gesture_tracker, bool with_gesture) {
if (y_transition != null) { this.force_hide = force_hide;
y_transition.completed.connect (() => {
clone.visible = false; if (force_hide) {
panel_hidden = false; hide (gesture_tracker, with_gesture);
}); } else if (hide_mode == NEVER) {
show (gesture_tracker, with_gesture);
} else { } else {
clone.visible = false; hide_tracker.update_overlap (gesture_tracker, with_gesture);
panel_hidden = false;
} }
} }
} }

View File

@ -143,4 +143,8 @@ public class Gala.PanelWindow : Object {
return TOP; return TOP;
} }
} }
public void set_force_hide (bool force_hide, GestureTracker gesture_tracker, bool with_gesture) {
clone.set_force_hide (force_hide, gesture_tracker, with_gesture);
}
} }

View File

@ -207,6 +207,12 @@ public class Gala.ShellClientsManager : Object {
return positioned; return positioned;
} }
public void set_force_hide_panels (bool force_hide, GestureTracker gesture_tracker, bool with_gesture) {
foreach (var panel in panel_windows.get_values ()) {
panel.set_force_hide (force_hide, gesture_tracker, with_gesture);
}
}
//X11 only //X11 only
private void parse_mutter_hints (Meta.Window window) requires (!Meta.Util.is_wayland_compositor ()) { private void parse_mutter_hints (Meta.Window window) requires (!Meta.Util.is_wayland_compositor ()) {
if (window.mutter_hints == null) { if (window.mutter_hints == null) {

View File

@ -39,7 +39,6 @@ namespace Gala {
private IconGroupContainer icon_groups; private IconGroupContainer icon_groups;
private Clutter.Actor workspaces; private Clutter.Actor workspaces;
private Clutter.Actor dock_clones;
private Clutter.Actor primary_monitor_container; private Clutter.Actor primary_monitor_container;
private Clutter.BrightnessContrastEffect brightness_effect; private Clutter.BrightnessContrastEffect brightness_effect;
@ -81,8 +80,6 @@ namespace Gala {
icon_groups = new IconGroupContainer (display.get_monitor_scale (display.get_primary_monitor ())); icon_groups = new IconGroupContainer (display.get_monitor_scale (display.get_primary_monitor ()));
dock_clones = new Clutter.Actor ();
brightness_effect = new Clutter.BrightnessContrastEffect (); brightness_effect = new Clutter.BrightnessContrastEffect ();
update_brightness_effect (); update_brightness_effect ();
@ -99,7 +96,6 @@ namespace Gala {
primary_monitor_container.add_child (icon_groups); primary_monitor_container.add_child (icon_groups);
primary_monitor_container.add_child (workspaces); primary_monitor_container.add_child (workspaces);
add_child (primary_monitor_container); add_child (primary_monitor_container);
add_child (dock_clones);
unowned var manager = display.get_workspace_manager (); unowned var manager = display.get_workspace_manager ();
manager.workspace_added.connect (add_workspace); manager.workspace_added.connect (add_workspace);
@ -701,14 +697,13 @@ namespace Gala {
} }
if (opening) { if (opening) {
show_docks (with_gesture, is_cancel_animation); ShellClientsManager.get_instance ().set_force_hide_panels (true, multitasking_gesture_tracker, with_gesture);
} else { } else {
hide_docks (with_gesture, is_cancel_animation); ShellClientsManager.get_instance ().set_force_hide_panels (false, multitasking_gesture_tracker, with_gesture);
} }
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => {
var animation_duration = cancel_action ? 0 : ANIMATION_DURATION; Timeout.add (calculated_duration, () => {
Timeout.add (animation_duration, () => {
if (!opening) { if (!opening) {
foreach (var container in window_containers_monitors) { foreach (var container in window_containers_monitors) {
container.visible = false; container.visible = false;
@ -720,8 +715,6 @@ namespace Gala {
wm.window_group.show (); wm.window_group.show ();
wm.top_window_group.show (); wm.top_window_group.show ();
dock_clones.destroy_all_children ();
wm.pop_modal (modal_proxy); wm.pop_modal (modal_proxy);
} }
@ -736,113 +729,12 @@ namespace Gala {
}; };
if (!with_gesture) { if (!with_gesture) {
on_animation_end (1, false, 0); on_animation_end (1, false, is_cancel_animation ? 0 : ANIMATION_DURATION);
} else { } else {
multitasking_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end); multitasking_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end);
} }
} }
private void show_docks (bool with_gesture, bool is_cancel_animation) {
unowned GLib.List<Meta.WindowActor> 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) {
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, cancel_action) => {
if (cancel_action) {
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, false, 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, cancel_action) => {
if (cancel_action) {
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, false, 0);
} else {
multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
}
}
}
private bool keybinding_filter (Meta.KeyBinding binding) { private bool keybinding_filter (Meta.KeyBinding binding) {
var action = Meta.Prefs.get_keybinding_action (binding.get_name ()); var action = Meta.Prefs.get_keybinding_action (binding.get_name ());