Skip to content

Overlay window overview #2350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/DesktopIntegration.vala
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,12 @@ public class Gala.DesktopIntegration : GLib.Object {
throw new IOError.NOT_FOUND ("App not found");
}

var sorted_windows = wm.get_display ().sort_windows_by_stacking (app.get_windows ());

uint64[] window_ids = {};
foreach (var window in app.get_windows ()) {
foreach (var window in sorted_windows) {
window.raise_and_make_recent_on_workspace (window.get_workspace ());
window.focus (Meta.CURRENT_TIME);
window_ids += window.get_id ();
}

Expand Down
166 changes: 54 additions & 112 deletions src/Widgets/WindowOverview.vala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Copyright 2012 Tom Beckmann
* Copyright 2012 Rico Tzschichholz
* Copyright 2023 elementary, Inc. <https://elementary.io>
* Copyright 2025 elementary, Inc. <https://elementary.io>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Copyright 2025 elementary, Inc. <https://elementary.io>
* Copyright 2023-2025 elementary, Inc. <https://elementary.io>

* SPDX-License-Identifier: GPL-3.0-or-later
*/

Expand All @@ -14,10 +14,9 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {

private GestureController gesture_controller; // Currently not used for actual touchpad gestures but only as controller

private Clutter.Actor background;
private Clutter.Actor monitors;
private ModalProxy modal_proxy;
// the workspaces which we expose right now
private List<Meta.Workspace> workspaces;
private WindowCloneContainer window_clone_container;

public WindowOverview (WindowManager wm) {
Object (wm : wm);
Expand All @@ -29,15 +28,32 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
gesture_controller = new GestureController (MULTITASKING_VIEW, this, wm) {
enabled = false
};
}

background = new Clutter.Actor () {
#if HAS_MUTTER47
background_color = Cogl.Color.from_string ("black")
#else
background_color = Clutter.Color.from_string ("black")
#endif
};
background.add_constraint (new Clutter.BindConstraint (this, SIZE, 0));
add_child (background);

add_target (new PropertyTarget (MULTITASKING_VIEW, background, "opacity", typeof (uint), 0u, 150u));

monitors = new ActorTarget ();
add_child (monitors);

wm.get_display ().window_left_monitor.connect (window_left_monitor);
}

public override bool key_press_event (Clutter.Event event) {
if (!is_opened ()) {
return Clutter.EVENT_PROPAGATE;
}

return window_clone_container.key_press_event (event);
//TODO: Navigating between monitors
return get_child_at_index (wm.get_display ().get_primary_monitor ()).key_press_event (event);
}

public override bool button_release_event (Clutter.Event event) {
Expand All @@ -59,10 +75,8 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
* {@inheritDoc}
*/
public void open (HashTable<string,Variant>? hints = null) {
workspaces = new List<Meta.Workspace> ();
unowned var manager = wm.get_display ().get_workspace_manager ();
foreach (unowned var workspace in manager.get_workspaces ()) {
workspaces.append (workspace);
if (visible) {
return;
}

uint64[]? window_ids = null;
Expand All @@ -71,44 +85,23 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
}

var windows = new List<Meta.Window> ();
foreach (var workspace in workspaces) {
foreach (unowned var window in workspace.list_windows ()) {
if (window.window_type == Meta.WindowType.DOCK || NotificationStack.is_notification (window) ) {
continue;
}

if (window.window_type != Meta.WindowType.NORMAL &&
window.window_type != Meta.WindowType.DIALOG ||
window.is_attached_dialog () ||
(window_ids != null && !(window.get_id () in window_ids))
) {
unowned var actor = (Meta.WindowActor) window.get_compositor_private ();
actor.hide ();

continue;
}

// skip windows that are on all workspace except we're currently
// processing the workspace it actually belongs to
if (window.on_all_workspaces && window.get_workspace () != workspace) {
continue;
}

windows.append (window);
foreach (unowned var window_actor in wm.get_display ().get_window_actors ()) {
var window = window_actor.meta_window;
if (ShellClientsManager.get_instance ().is_positioned_window (window) ||
window.window_type != NORMAL && window.window_type != DIALOG ||
window.is_attached_dialog () ||
window_ids != null && !(window.get_id () in window_ids)
) {
continue;
}

windows.append (window);
}

if (windows.is_empty ()) {
return;
}

foreach (var workspace in workspaces) {
workspace.window_added.connect (add_window);
workspace.window_removed.connect (remove_window);
}

wm.get_display ().window_left_monitor.connect (window_left_monitor);

grab_key_focus ();

modal_proxy = wm.push_modal (this);
Expand All @@ -121,7 +114,7 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
var geometry = display.get_monitor_geometry (i);
var scale = display.get_monitor_scale (i);

window_clone_container = new WindowCloneContainer (wm, scale, true) {
var window_clone_container = new WindowCloneContainer (wm, scale, true) {
padding_top = TOP_GAP,
padding_left = BORDER,
padding_right = BORDER,
Expand All @@ -134,7 +127,7 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
window_clone_container.window_selected.connect (thumb_selected);
window_clone_container.requested_close.connect (() => close ());

add_child (window_clone_container);
monitors.add_child (window_clone_container);
}

visible = true;
Expand All @@ -143,7 +136,7 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
unowned var actor = (Meta.WindowActor) window.get_compositor_private ();
actor.hide ();

unowned var container = (WindowCloneContainer) get_child_at_index (window.get_monitor ());
unowned var container = (WindowCloneContainer) monitors.get_child_at_index (window.get_monitor ());
if (container == null) {
continue;
}
Expand Down Expand Up @@ -175,60 +168,6 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
return true;
}

private void window_left_monitor (int num, Meta.Window window) {
unowned var container = (WindowCloneContainer) get_child_at_index (num);
if (container == null) {
return;
}

// make sure the window belongs to one of our workspaces
foreach (var workspace in workspaces) {
if (window.located_on_workspace (workspace)) {
container.remove_window (window);
break;
}
}
}

private void add_window (Meta.Window window) {
if (!visible) {
return;
}
if (window.window_type == Meta.WindowType.DOCK || NotificationStack.is_notification (window)) {
return;
}
if (window.window_type != Meta.WindowType.NORMAL &&
window.window_type != Meta.WindowType.DIALOG ||
window.is_attached_dialog ()) {
unowned var actor = (Meta.WindowActor) window.get_compositor_private ();
actor.hide ();

return;
}

unowned var container = (WindowCloneContainer) get_child_at_index (window.get_monitor ());
if (container == null) {
return;
}

// make sure the window belongs to one of our workspaces
foreach (var workspace in workspaces) {
if (window.located_on_workspace (workspace)) {
container.add_window (window);
break;
}
}
}

private void remove_window (Meta.Window window) {
unowned var container = (WindowCloneContainer) get_child_at_index (window.get_monitor ());
if (container == null) {
return;
}

container.remove_window (window);
}

private void thumb_selected (Meta.Window window) {
if (window.get_workspace () == wm.get_display ().get_workspace_manager ().get_active_workspace ()) {
window.activate (window.get_display ().get_current_time ());
Expand All @@ -244,6 +183,17 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
}
}

private void window_left_monitor (int monitor, Meta.Window window) {
if (!visible) {
return;
}

var container = (WindowCloneContainer) monitors.get_child_at_index (monitor);
if (container != null) {
container.remove_window (window);
}
}

/**
* {@inheritDoc}
*/
Expand All @@ -252,22 +202,14 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
return;
}

foreach (var workspace in workspaces) {
workspace.window_added.disconnect (add_window);
workspace.window_removed.disconnect (remove_window);
}
wm.get_display ().window_left_monitor.disconnect (window_left_monitor);

Clutter.Threads.Timeout.add (MultitaskingView.ANIMATION_DURATION, () => {
cleanup ();

return Source.REMOVE;
});

gesture_controller.goto (0);
}

private void cleanup () {
public override void end_progress (GestureAction action) {
if (action != MULTITASKING_VIEW || get_current_commit (MULTITASKING_VIEW) > 0.5) {
return;
}

visible = false;

wm.pop_modal (modal_proxy);
Expand All @@ -278,6 +220,6 @@ public class Gala.WindowOverview : ActorTarget, ActivatableComponent {
}
}

destroy_all_children ();
monitors.remove_all_children ();
}
}
10 changes: 5 additions & 5 deletions src/WindowManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,17 @@ namespace Gala {
Meta.KeyBinding.set_custom_handler ("switch-group-backward", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
}

// Add the remaining components that should be on top
shell_group = new Clutter.Actor ();
ui_group.add_child (shell_group);

if (plugin_manager.window_overview_provider == null
|| (window_overview = (plugin_manager.get_plugin (plugin_manager.window_overview_provider) as ActivatableComponent)) == null
) {
window_overview = new WindowOverview (this);
ui_group.add_child ((Clutter.Actor) window_overview);
}

// Add the remaining components that should be on top
shell_group = new Clutter.Actor ();
ui_group.add_child (shell_group);

var feedback_group = display.get_compositor ().get_feedback_group ();
stage.remove_child (feedback_group);
ui_group.add_child (feedback_group);
Expand Down Expand Up @@ -1427,7 +1427,7 @@ namespace Gala {
return;
}

if (!Meta.Prefs.get_gnome_animations ()) {
if (!Meta.Prefs.get_gnome_animations () || !actor.visible) {
destroy_completed (actor);

if (window.window_type == Meta.WindowType.NORMAL) {
Expand Down