WindowOverview and WindowCloneContainer: Cleanup (#1587)

* Code cleanup
 * Fixes an issue with window stacking in window overview where windows on other workspaces were higher than windows on current workspace
 * Prevents switching workspaces when window overview is opened
This commit is contained in:
Leo 2023-03-28 16:13:03 +09:00 committed by GitHub
parent 4c78a15883
commit f75159568a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 174 additions and 164 deletions

View File

@ -314,8 +314,34 @@ namespace Gala {
return result; return result;
} }
/*
* Sorts the windows by stacking order so that the window on active workspaces come first.
*/
public static SList<weak Meta.Window> sort_windows (Meta.Display display, List<Meta.Window> windows) {
var windows_on_active_workspace = new SList<Meta.Window> ();
var windows_on_other_workspaces = new SList<Meta.Window> ();
unowned var active_workspace = display.get_workspace_manager ().get_active_workspace ();
foreach (unowned var window in windows) {
if (window.get_workspace () == active_workspace) {
windows_on_active_workspace.append (window);
} else {
windows_on_other_workspaces.append (window);
}
}
var sorted_windows = new SList<weak Meta.Window> ();
var windows_on_active_workspace_sorted = display.sort_windows_by_stacking (windows_on_active_workspace);
windows_on_active_workspace_sorted.reverse ();
var windows_on_other_workspaces_sorted = display.sort_windows_by_stacking (windows_on_other_workspaces);
windows_on_other_workspaces_sorted.reverse ();
sorted_windows.concat ((owned) windows_on_active_workspace_sorted);
sorted_windows.concat ((owned) windows_on_other_workspaces_sorted);
return sorted_windows;
}
public static inline bool get_window_is_normal (Meta.Window window) { public static inline bool get_window_is_normal (Meta.Window window) {
switch (window.get_window_type ()) { switch (window.window_type) {
case Meta.WindowType.NORMAL: case Meta.WindowType.NORMAL:
case Meta.WindowType.DIALOG: case Meta.WindowType.DIALOG:
case Meta.WindowType.MODAL_DIALOG: case Meta.WindowType.MODAL_DIALOG:

View File

@ -31,23 +31,18 @@ namespace Gala {
public GestureTracker? gesture_tracker { get; construct; } public GestureTracker? gesture_tracker { get; construct; }
public bool overview_mode { get; construct; } public bool overview_mode { get; construct; }
private bool opened; private bool opened = false;
/** /**
* The window that is currently selected via keyboard shortcuts. It is not * The window that is currently selected via keyboard shortcuts. It is not
* necessarily the same as the active window. * necessarily the same as the active window.
*/ */
private WindowClone? current_window; private WindowClone? current_window = null;
public WindowCloneContainer (WindowManager wm, GestureTracker? gesture_tracker, bool overview_mode = false) { public WindowCloneContainer (WindowManager wm, GestureTracker? gesture_tracker, bool overview_mode = false) {
Object (wm: wm, gesture_tracker: gesture_tracker, overview_mode: overview_mode); Object (wm: wm, gesture_tracker: gesture_tracker, overview_mode: overview_mode);
} }
construct {
opened = false;
current_window = null;
}
/** /**
* Create a WindowClone for a MetaWindow and add it to the group * Create a WindowClone for a MetaWindow and add it to the group
* *
@ -55,27 +50,24 @@ namespace Gala {
*/ */
public void add_window (Meta.Window window) { public void add_window (Meta.Window window) {
unowned Meta.Display display = window.get_display (); unowned Meta.Display display = window.get_display ();
var children = get_children ();
GLib.SList<Meta.Window> windows = new GLib.SList<Meta.Window> (); var windows = new List<Meta.Window> ();
foreach (unowned Clutter.Actor child in children) { foreach (unowned var child in get_children ()) {
unowned WindowClone tw = (WindowClone) child; unowned var clone = (WindowClone) child;
windows.prepend (tw.window); windows.append (clone.window);
} }
windows.prepend (window); windows.append (window);
windows.reverse ();
var windows_ordered = display.sort_windows_by_stacking (windows); var windows_ordered = InternalUtils.sort_windows (display, windows);
var new_window = new WindowClone (wm, window, gesture_tracker, overview_mode); var new_window = new WindowClone (wm, window, gesture_tracker, overview_mode);
new_window.selected.connect (window_selected_cb); new_window.selected.connect (window_selected_cb);
new_window.destroy.connect (window_destroyed); new_window.destroy.connect (window_destroyed);
new_window.request_reposition.connect (() => reflow ()); new_window.request_reposition.connect (window_request_reposition);
var added = false;
unowned Meta.Window? target = null; unowned Meta.Window? target = null;
foreach (unowned Meta.Window w in windows_ordered) { foreach (unowned var w in windows_ordered) {
if (w != window) { if (w != window) {
target = w; target = w;
continue; continue;
@ -83,19 +75,19 @@ namespace Gala {
break; break;
} }
foreach (unowned Clutter.Actor child in children) { // top most or no other children
unowned WindowClone tw = (WindowClone) child; if (target == null) {
if (target == tw.window) { add_child (new_window);
insert_child_above (new_window, tw); }
added = true;
foreach (unowned var child in get_children ()) {
unowned var clone = (WindowClone) child;
if (target == clone.window) {
insert_child_below (new_window, clone);
break; break;
} }
} }
// top most or no other children
if (!added)
add_child (new_window);
reflow (); reflow ();
} }
@ -103,7 +95,7 @@ namespace Gala {
* Find and remove the WindowClone for a MetaWindow * Find and remove the WindowClone for a MetaWindow
*/ */
public void remove_window (Meta.Window window) { public void remove_window (Meta.Window window) {
foreach (var child in get_children ()) { foreach (unowned var child in get_children ()) {
if (((WindowClone) child).window == window) { if (((WindowClone) child).window == window) {
remove_child (child); remove_child (child);
reflow (); reflow ();
@ -112,43 +104,42 @@ namespace Gala {
} }
} }
private void window_selected_cb (WindowClone tiled) { private void window_selected_cb (WindowClone clone) {
window_selected (tiled.window); window_selected (clone.window);
} }
private void window_destroyed (Clutter.Actor actor) { private void window_destroyed (Clutter.Actor actor) {
var window = actor as WindowClone; unowned var clone = (WindowClone) actor;
if (window == null)
return;
window.destroy.disconnect (window_destroyed); clone.destroy.disconnect (window_destroyed);
window.selected.disconnect (window_selected_cb); clone.selected.disconnect (window_selected_cb);
clone.request_reposition.disconnect (window_request_reposition);
Idle.add (() => { reflow ();
reflow (); }
return false;
}); private void window_request_reposition () {
reflow ();
} }
/** /**
* Sort the windows z-order by their actual stacking to make intersections * Sort the windows z-order by their actual stacking to make intersections
* during animations correct. * during animations correct.
*/ */
public void restack_windows (Meta.Display display) { public void restack_windows () {
var children = get_children (); var children = get_children ();
GLib.SList<Meta.Window> windows = new GLib.SList<Meta.Window> (); var windows = new List<Meta.Window> ();
foreach (unowned Clutter.Actor child in children) { foreach (unowned Clutter.Actor child in children) {
unowned WindowClone tw = (WindowClone) child; windows.prepend (((WindowClone) child).window);
windows.prepend (tw.window);
} }
var windows_ordered = display.sort_windows_by_stacking (windows); var windows_ordered = InternalUtils.sort_windows (wm.get_display (), windows);
windows_ordered.reverse (); windows_ordered.reverse ();
foreach (unowned Meta.Window window in windows_ordered) { var i = 0;
var i = 0; foreach (unowned var window in windows_ordered) {
foreach (unowned Clutter.Actor child in children) { foreach (unowned var child in children) {
if (((WindowClone) child).window == window) { if (((WindowClone) child).window == window) {
set_child_at_index (child, i); set_child_at_index (child, i);
children.remove (child); children.remove (child);
@ -164,17 +155,19 @@ namespace Gala {
* the resulting spots. * the resulting spots.
*/ */
public void reflow (bool with_gesture = false, bool is_cancel_animation = false) { public void reflow (bool with_gesture = false, bool is_cancel_animation = false) {
if (!opened) if (!opened) {
return; return;
var windows = new List<InternalUtils.TilableWindow?> ();
foreach (var child in get_children ()) {
unowned WindowClone window = (WindowClone) child;
windows.prepend ({ window.window.get_frame_rect (), window });
} }
if (windows.length () < 1) var windows = new List<InternalUtils.TilableWindow?> ();
foreach (unowned var child in get_children ()) {
unowned var clone = (WindowClone) child;
windows.prepend ({ clone.window.get_frame_rect (), clone });
}
if (windows.is_empty ()) {
return; return;
}
// make sure the windows are always in the same order so the algorithm // make sure the windows are always in the same order so the algorithm
// doesn't give us different slots based on stacking order, which can lead // doesn't give us different slots based on stacking order, which can lead
@ -195,9 +188,9 @@ namespace Gala {
var window_positions = InternalUtils.calculate_grid_placement (area, windows); var window_positions = InternalUtils.calculate_grid_placement (area, windows);
foreach (var tilable in window_positions) { foreach (var tilable in window_positions) {
unowned WindowClone window = (WindowClone) tilable.id; unowned var clone = (WindowClone) tilable.id;
window.take_slot (tilable.rect, with_gesture, is_cancel_animation); clone.take_slot (tilable.rect, with_gesture, is_cancel_animation);
window.place_widgets (tilable.rect.width, tilable.rect.height); clone.place_widgets (tilable.rect.width, tilable.rect.height);
} }
} }
@ -208,8 +201,9 @@ namespace Gala {
* @param direction The MetaMotionDirection in which to search for windows for. * @param direction The MetaMotionDirection in which to search for windows for.
*/ */
public void select_next_window (Meta.MotionDirection direction) { public void select_next_window (Meta.MotionDirection direction) {
if (get_n_children () < 1) if (get_n_children () < 1) {
return; return;
}
if (current_window == null) { if (current_window == null) {
current_window = (WindowClone) get_child_at_index (0); current_window = (WindowClone) get_child_at_index (0);
@ -219,63 +213,76 @@ namespace Gala {
var current_rect = current_window.slot; var current_rect = current_window.slot;
WindowClone? closest = null; WindowClone? closest = null;
foreach (var window in get_children ()) { foreach (unowned var child in get_children ()) {
if (window == current_window) if (child == current_window) {
continue; continue;
}
var window_rect = ((WindowClone) window).slot; var window_rect = ((WindowClone) child).slot;
switch (direction) { switch (direction) {
case Meta.MotionDirection.LEFT: case Meta.MotionDirection.LEFT:
if (window_rect.x > current_rect.x) if (window_rect.x > current_rect.x) {
continue; continue;
}
// test for vertical intersection // test for vertical intersection
if (window_rect.y + window_rect.height > current_rect.y if (window_rect.y + window_rect.height > current_rect.y
&& window_rect.y < current_rect.y + current_rect.height) { && window_rect.y < current_rect.y + current_rect.height) {
if (closest == null if (closest == null
|| closest.slot.x < window_rect.x) || closest.slot.x < window_rect.x) {
closest = (WindowClone) window;
closest = (WindowClone) child;
}
} }
break; break;
case Meta.MotionDirection.RIGHT: case Meta.MotionDirection.RIGHT:
if (window_rect.x < current_rect.x) if (window_rect.x < current_rect.x) {
continue; continue;
}
// test for vertical intersection // test for vertical intersection
if (window_rect.y + window_rect.height > current_rect.y if (window_rect.y + window_rect.height > current_rect.y
&& window_rect.y < current_rect.y + current_rect.height) { && window_rect.y < current_rect.y + current_rect.height) {
if (closest == null if (closest == null
|| closest.slot.x > window_rect.x) || closest.slot.x > window_rect.x) {
closest = (WindowClone) window;
closest = (WindowClone) child;
}
} }
break; break;
case Meta.MotionDirection.UP: case Meta.MotionDirection.UP:
if (window_rect.y > current_rect.y) if (window_rect.y > current_rect.y) {
continue; continue;
}
// test for horizontal intersection // test for horizontal intersection
if (window_rect.x + window_rect.width > current_rect.x if (window_rect.x + window_rect.width > current_rect.x
&& window_rect.x < current_rect.x + current_rect.width) { && window_rect.x < current_rect.x + current_rect.width) {
if (closest == null if (closest == null
|| closest.slot.y < window_rect.y) || closest.slot.y < window_rect.y) {
closest = (WindowClone) window;
closest = (WindowClone) child;
}
} }
break; break;
case Meta.MotionDirection.DOWN: case Meta.MotionDirection.DOWN:
if (window_rect.y < current_rect.y) if (window_rect.y < current_rect.y) {
continue; continue;
}
// test for horizontal intersection // test for horizontal intersection
if (window_rect.x + window_rect.width > current_rect.x if (window_rect.x + window_rect.width > current_rect.x
&& window_rect.x < current_rect.x + current_rect.width) { && window_rect.x < current_rect.x + current_rect.width) {
if (closest == null if (closest == null
|| closest.slot.y > window_rect.y) || closest.slot.y > window_rect.y) {
closest = (WindowClone) window;
closest = (WindowClone) child;
}
} }
break; break;
default: default:
@ -283,11 +290,13 @@ namespace Gala {
} }
} }
if (closest == null) if (closest == null) {
return; return;
}
if (current_window != null) if (current_window != null) {
current_window.active = false; current_window.active = false;
}
closest.active = true; closest.active = true;
current_window = closest; current_window = closest;
@ -318,9 +327,9 @@ namespace Gala {
// hide the highlight when opened // hide the highlight when opened
if (selected_window != null) { if (selected_window != null) {
foreach (var child in get_children ()) { foreach (var child in get_children ()) {
unowned WindowClone tiled_window = (WindowClone) child; unowned var clone = (WindowClone) child;
if (tiled_window.window == selected_window) { if (clone.window == selected_window) {
current_window = tiled_window; current_window = clone;
break; break;
} }
} }

View File

@ -12,9 +12,7 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
public WindowManager wm { get; construct; } public WindowManager wm { get; construct; }
private Meta.Display display;
private ModalProxy modal_proxy; private ModalProxy modal_proxy;
private bool ready;
// the workspaces which we expose right now // the workspaces which we expose right now
private List<Meta.Workspace> workspaces; private List<Meta.Workspace> workspaces;
@ -24,19 +22,10 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
} }
construct { construct {
display = wm.get_display ();
display.get_workspace_manager ().workspace_switched.connect (() => { close (); });
display.restacked.connect (restack_windows);
visible = false; visible = false;
ready = true;
reactive = true; reactive = true;
} }
~WindowOverview () {
display.restacked.disconnect (restack_windows);
}
public override bool key_press_event (Clutter.KeyEvent event) { public override bool key_press_event (Clutter.KeyEvent event) {
if (event.keyval == Clutter.Key.Escape) { if (event.keyval == Clutter.Key.Escape) {
close (); close ();
@ -62,93 +51,73 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public bool is_opened () { public bool is_opened () {
return visible; return visible;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
* You may specify 'all-windows' in hints to expose all windows * You may specify 'all-windows' in hints to expose all windows
*/ */
public void open (HashTable<string,Variant>? hints = null) { public void open (HashTable<string,Variant>? hints = null) {
if (!ready) {
return;
}
if (visible) {
close ();
return;
}
var all_windows = hints != null && "all-windows" in hints; var all_windows = hints != null && "all-windows" in hints;
var used_windows = new SList<Meta.Window> ();
workspaces = new List<Meta.Workspace> (); workspaces = new List<Meta.Workspace> ();
unowned var manager = wm.get_display ().get_workspace_manager ();
unowned Meta.WorkspaceManager manager = display.get_workspace_manager ();
if (all_windows) { if (all_windows) {
for (int i = 0; i < manager.get_n_workspaces (); i++) { foreach (unowned var workspace in manager.get_workspaces ()) {
workspaces.append (manager.get_workspace_by_index (i)); workspaces.append (workspace);
} }
} else { } else {
workspaces.append (manager.get_active_workspace ()); workspaces.append (manager.get_active_workspace ());
} }
var windows = new List<Meta.Window> ();
foreach (var workspace in workspaces) { foreach (var workspace in workspaces) {
foreach (var window in workspace.list_windows ()) { foreach (unowned var window in workspace.list_windows ()) {
if (window.window_type == Meta.WindowType.DOCK) {
continue;
}
if (window.window_type != Meta.WindowType.NORMAL && if (window.window_type != Meta.WindowType.NORMAL &&
window.window_type != Meta.WindowType.DOCK &&
window.window_type != Meta.WindowType.DIALOG || window.window_type != Meta.WindowType.DIALOG ||
window.is_attached_dialog ()) { window.is_attached_dialog ()) {
unowned var actor = (Meta.WindowActor) window.get_compositor_private (); unowned var actor = (Meta.WindowActor) window.get_compositor_private ();
if (actor != null) { actor.hide ();
actor.hide ();
}
continue;
}
if (window.window_type == Meta.WindowType.DOCK) {
continue; continue;
} }
// skip windows that are on all workspace except we're currently // skip windows that are on all workspace except we're currently
// processing the workspace it actually belongs to // processing the workspace it actually belongs to
if (window.is_on_all_workspaces () && window.get_workspace () != workspace) { if (window.on_all_workspaces && window.get_workspace () != workspace) {
continue; continue;
} }
used_windows.append (window); windows.append (window);
} }
} }
var n_windows = used_windows.length (); if (windows.is_empty ()) {
if (n_windows == 0) {
return; return;
} }
ready = false;
foreach (var workspace in workspaces) { foreach (var workspace in workspaces) {
workspace.window_added.connect (add_window); workspace.window_added.connect (add_window);
workspace.window_removed.connect (remove_window); workspace.window_removed.connect (remove_window);
} }
display.window_left_monitor.connect (window_left_monitor); wm.get_display ().window_left_monitor.connect (window_left_monitor);
// sort windows by stacking order
var windows = display.sort_windows_by_stacking (used_windows);
grab_key_focus (); grab_key_focus ();
modal_proxy = wm.push_modal (this); modal_proxy = wm.push_modal (this);
modal_proxy.set_keybinding_filter (keybinding_filter); modal_proxy.set_keybinding_filter (keybinding_filter);
visible = true; for (var i = 0; i < wm.get_display ().get_n_monitors (); i++) {
var geometry = wm.get_display ().get_monitor_geometry (i);
for (var i = 0; i < display.get_n_monitors (); i++) {
var geometry = display.get_monitor_geometry (i);
var container = new WindowCloneContainer (wm, null, true) { var container = new WindowCloneContainer (wm, null, true) {
padding_top = TOP_GAP, padding_top = TOP_GAP,
@ -163,11 +132,11 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
add_child (container); add_child (container);
} }
foreach (var window in windows) { visible = true;
foreach (unowned var window in windows) {
unowned var actor = (Meta.WindowActor) window.get_compositor_private (); unowned var actor = (Meta.WindowActor) window.get_compositor_private ();
if (actor != null) { actor.hide ();
actor.hide ();
}
unowned var container = (WindowCloneContainer) get_child_at_index (window.get_monitor ()); unowned var container = (WindowCloneContainer) get_child_at_index (window.get_monitor ());
if (container == null) { if (container == null) {
@ -175,13 +144,8 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
} }
container.add_window (window); container.add_window (window);
container.open ();
} }
foreach (var child in get_children ()) {
((WindowCloneContainer) child).open ();
}
ready = true;
} }
private bool keybinding_filter (Meta.KeyBinding binding) { private bool keybinding_filter (Meta.KeyBinding binding) {
@ -198,9 +162,9 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
return true; return true;
} }
private void restack_windows (Meta.Display display) { private void restack_windows () {
foreach (var child in get_children ()) { foreach (var child in get_children ()) {
((WindowCloneContainer) child).restack_windows (display); ((WindowCloneContainer) child).restack_windows ();
} }
} }
@ -220,8 +184,18 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
} }
private void add_window (Meta.Window window) { private void add_window (Meta.Window window) {
if (!visible if (!visible) {
|| (window.window_type != Meta.WindowType.NORMAL && window.window_type != Meta.WindowType.DIALOG)) { return;
}
if (window.window_type == Meta.WindowType.DOCK) {
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; return;
} }
@ -249,42 +223,41 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
} }
private void thumb_selected (Meta.Window window) { private void thumb_selected (Meta.Window window) {
if (window.get_workspace () == display.get_workspace_manager ().get_active_workspace ()) { if (window.get_workspace () == wm.get_display ().get_workspace_manager ().get_active_workspace ()) {
window.activate (display.get_current_time ()); window.activate (window.get_display ().get_current_time ());
close (); close ();
} else { } else {
close (); close ();
//wait for the animation to finish before switching
Timeout.add (400, () => { // wait for the animation to finish before switching
window.get_workspace ().activate_with_focus (window, display.get_current_time ()); Timeout.add (MultitaskingView.ANIMATION_DURATION, () => {
window.get_workspace ().activate_with_focus (window, window.get_display ().get_current_time ());
return Source.REMOVE; return Source.REMOVE;
}); });
} }
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void close (HashTable<string,Variant>? hints = null) { public void close (HashTable<string,Variant>? hints = null) {
if (!visible || !ready) { if (!visible) {
return; return;
} }
restack_windows ();
foreach (var workspace in workspaces) { foreach (var workspace in workspaces) {
workspace.window_added.disconnect (add_window); workspace.window_added.disconnect (add_window);
workspace.window_removed.disconnect (remove_window); workspace.window_removed.disconnect (remove_window);
} }
wm.get_display ().window_left_monitor.disconnect (window_left_monitor);
display.window_left_monitor.disconnect (window_left_monitor); foreach (unowned var child in get_children ()) {
ready = false;
wm.pop_modal (modal_proxy);
foreach (var child in get_children ()) {
((WindowCloneContainer) child).close (); ((WindowCloneContainer) child).close ();
} }
Clutter.Threads.Timeout.add (300, () => { Clutter.Threads.Timeout.add (MultitaskingView.ANIMATION_DURATION, () => {
cleanup (); cleanup ();
return Source.REMOVE; return Source.REMOVE;
@ -292,10 +265,11 @@ public class Gala.WindowOverview : Clutter.Actor, ActivatableComponent {
} }
private void cleanup () { private void cleanup () {
ready = true;
visible = false; visible = false;
foreach (var window in display.get_workspace_manager ().get_active_workspace ().list_windows ()) { wm.pop_modal (modal_proxy);
foreach (var window in wm.get_display ().get_workspace_manager ().get_active_workspace ().list_windows ()) {
if (window.showing_on_its_workspace ()) { if (window.showing_on_its_workspace ()) {
((Clutter.Actor) window.get_compositor_private ()).show (); ((Clutter.Actor) window.get_compositor_private ()).show ();
} }

View File

@ -1724,7 +1724,8 @@ namespace Gala {
|| AnimationDuration.WORKSPACE_SWITCH == 0 || AnimationDuration.WORKSPACE_SWITCH == 0
|| (direction != Meta.MotionDirection.LEFT && direction != Meta.MotionDirection.RIGHT) || (direction != Meta.MotionDirection.LEFT && direction != Meta.MotionDirection.RIGHT)
|| animating_switch_workspace || animating_switch_workspace
|| workspace_view.is_opened ()) { || workspace_view.is_opened ()
|| window_overview.is_opened ()) {
animating_switch_workspace = false; animating_switch_workspace = false;
switch_workspace_completed (); switch_workspace_completed ();
return; return;