Skip to content

Commit 76b5800

Browse files
authored
Introduce a WindowListModel (#2561)
Closes #2353 Introduce a window list model that allows us to collect the filtering and sorting code for different views of the current window list in a single place Since this window list model now also guarantees that all windows that appear in it have a valid actor i.e. get_compositor_private won't return null this will allow for a bunch of clean up in a follow up :) This should also help quite a bit with #2522 to allow more deduplication there
1 parent 79c9ff1 commit 76b5800

File tree

8 files changed

+240
-295
lines changed

8 files changed

+240
-295
lines changed

src/Widgets/MultitaskingView/MonitorClone.vala

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -33,42 +33,20 @@ public class Gala.MonitorClone : ActorTarget {
3333

3434
background = new BackgroundManager (display, monitor, false);
3535

36-
window_container = new WindowCloneContainer (wm, monitor_scale);
36+
var windows = new WindowListModel (display, STACKING, true, monitor);
37+
38+
window_container = new WindowCloneContainer (wm, windows, monitor_scale);
3739
window_container.add_constraint (new Clutter.BindConstraint (this, SIZE, 0.0f));
3840
window_container.window_selected.connect ((w) => { window_selected (w); });
3941
bind_property ("monitor-scale", window_container, "monitor-scale");
4042

41-
display.window_entered_monitor.connect (window_entered);
42-
display.window_left_monitor.connect (window_left);
43-
44-
#if HAS_MUTTER48
45-
unowned GLib.List<Meta.WindowActor> window_actors = display.get_compositor ().get_window_actors ();
46-
#else
47-
unowned GLib.List<Meta.WindowActor> window_actors = display.get_window_actors ();
48-
#endif
49-
foreach (unowned Meta.WindowActor window_actor in window_actors) {
50-
if (window_actor.is_destroyed ())
51-
continue;
52-
53-
unowned Meta.Window window = window_actor.get_meta_window ();
54-
if (window.get_monitor () == monitor) {
55-
window_entered (monitor, window);
56-
}
57-
}
58-
5943
add_child (background);
6044
add_child (window_container);
6145

6246
var drop = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
6347
add_action (drop);
6448
}
6549

66-
~MonitorClone () {
67-
unowned var display = wm.get_display ();
68-
display.window_entered_monitor.disconnect (window_entered);
69-
display.window_left_monitor.disconnect (window_left);
70-
}
71-
7250
/**
7351
* Make sure the MonitorClone is at the location of the monitor on the stage
7452
*/
@@ -82,18 +60,4 @@ public class Gala.MonitorClone : ActorTarget {
8260

8361
monitor_scale = display.get_monitor_scale (monitor);
8462
}
85-
86-
private void window_left (int window_monitor, Meta.Window window) {
87-
if (window_monitor != monitor)
88-
return;
89-
90-
window_container.remove_window (window);
91-
}
92-
93-
private void window_entered (int window_monitor, Meta.Window window) {
94-
if (window_monitor != monitor || window.window_type != Meta.WindowType.NORMAL)
95-
return;
96-
97-
window_container.add_window (window);
98-
}
9963
}

src/Widgets/MultitaskingView/WindowClone.vala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,8 +440,6 @@ public class Gala.WindowClone : ActorTarget, RootTarget {
440440
SignalHandler.disconnect (window.get_display (), check_confirm_dialog_cb);
441441
check_confirm_dialog_cb = 0;
442442
}
443-
444-
destroy ();
445443
}
446444

447445
private void actor_clicked (uint32 button, Clutter.InputDeviceType device_type = POINTER_DEVICE) {

src/Widgets/MultitaskingView/WindowCloneContainer.vala

Lines changed: 37 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
public class Gala.WindowCloneContainer : ActorTarget {
1111
public signal void window_selected (Meta.Window window);
1212
public signal void requested_close ();
13-
public signal void last_window_closed ();
1413

1514
public int padding_top { get; set; default = 12; }
1615
public int padding_left { get; set; default = 12; }
1716
public int padding_right { get; set; default = 12; }
1817
public int padding_bottom { get; set; default = 12; }
1918

2019
public WindowManager wm { get; construct; }
20+
public WindowListModel windows { get; construct; }
2121
public float monitor_scale { get; construct set; }
2222
public bool overview_mode { get; construct; }
2323

@@ -29,78 +29,54 @@ public class Gala.WindowCloneContainer : ActorTarget {
2929
*/
3030
private unowned WindowClone? current_window = null;
3131

32-
public WindowCloneContainer (WindowManager wm, float monitor_scale, bool overview_mode = false) {
33-
Object (wm: wm, monitor_scale: monitor_scale, overview_mode: overview_mode);
32+
public WindowCloneContainer (WindowManager wm, WindowListModel windows, float monitor_scale, bool overview_mode = false) {
33+
Object (wm: wm, windows: windows, monitor_scale: monitor_scale, overview_mode: overview_mode);
3434
}
3535

36-
/**
37-
* Create a WindowClone for a Meta.Window and add it to the group
38-
*
39-
* @param window The window for which to create the WindowClone for
40-
*/
41-
public void add_window (Meta.Window window) {
42-
var windows = new List<Meta.Window> ();
43-
windows.append (window);
44-
foreach (unowned var clone in (GLib.List<weak WindowClone>) get_children ()) {
45-
windows.append (clone.window);
46-
}
47-
48-
var new_window = new WindowClone (wm, window, monitor_scale, overview_mode);
49-
new_window.selected.connect ((_new_window) => window_selected (_new_window.window));
50-
new_window.request_reposition.connect (() => reflow (false));
51-
new_window.destroy.connect ((_new_window) => {
52-
// make sure to release reference if the window is selected
53-
if (_new_window == current_window) {
54-
select_next_window (Meta.MotionDirection.RIGHT, false);
55-
}
56-
57-
// if window is still selected, reset the selection
58-
if (_new_window == current_window) {
59-
current_window = null;
60-
}
36+
construct {
37+
on_items_changed (0, 0, windows.get_n_items ());
38+
windows.items_changed.connect (on_items_changed);
39+
}
6140

62-
reflow (false);
63-
});
64-
bind_property ("monitor-scale", new_window, "monitor-scale");
41+
private void on_items_changed (uint position, uint removed, uint added) {
42+
// Used to make sure we only construct new window clones for windows that are really new
43+
// and not when only the position changed (e.g. when sorted)
44+
var to_remove = new HashTable<Meta.Window, WindowClone> (null, null);
6545

66-
unowned Meta.Window? target = null;
67-
foreach (unowned var w in sort_windows (windows)) {
68-
if (w != window) {
69-
target = w;
70-
continue;
71-
}
72-
break;
46+
for (uint i = 0; i < removed; i++) {
47+
var window_clone = (WindowClone) get_child_at_index ((int) position);
48+
to_remove[window_clone.window] = window_clone;
49+
remove_child (window_clone);
7350
}
7451

75-
// top most or no other children
76-
if (target == null) {
77-
add_child (new_window);
78-
}
52+
for (int i = (int) position; i < position + added; i++) {
53+
var window = (Meta.Window) windows.get_item (i);
7954

80-
foreach (unowned var clone in (GLib.List<weak WindowClone>) get_children ()) {
81-
if (target == clone.window) {
82-
insert_child_below (new_window, clone);
83-
break;
55+
WindowClone? clone = to_remove.take (window);
56+
57+
if (clone == null) {
58+
clone = new WindowClone (wm, window, monitor_scale, overview_mode);
59+
clone.selected.connect ((_clone) => window_selected (_clone.window));
60+
clone.request_reposition.connect (() => reflow (false));
61+
bind_property ("monitor-scale", clone, "monitor-scale");
8462
}
63+
64+
insert_child_at_index (clone, i);
8565
}
8666

87-
reflow (false);
88-
}
67+
// Make sure we release the reference on the window
68+
if (current_window != null && current_window.window in to_remove) {
69+
select_next_window (RIGHT, false);
8970

90-
/**
91-
* Find and remove the WindowClone for a MetaWindow
92-
*/
93-
public void remove_window (Meta.Window window) {
94-
foreach (unowned var clone in (GLib.List<weak WindowClone>) get_children ()) {
95-
if (clone.window == window) {
96-
remove_child (clone);
97-
reflow (false);
98-
break;
71+
// There is no next window so select nothing
72+
if (current_window.window in to_remove) {
73+
current_window = null;
9974
}
10075
}
10176

102-
if (get_n_children () == 0) {
103-
last_window_closed ();
77+
// Don't reflow if only the sorting changed
78+
if (to_remove.size () > 0 || added != removed) {
79+
reflow (false);
10480
}
10581
}
10682

@@ -120,10 +96,10 @@ public class Gala.WindowCloneContainer : ActorTarget {
12096
}
12197
}
12298

123-
restack_windows ();
99+
windows.sort ();
124100
reflow (true);
125101
} else if (action == MULTITASKING_VIEW) { // If we are open we only want to restack when we close
126-
restack_windows ();
102+
windows.sort ();
127103
}
128104
}
129105

@@ -142,34 +118,6 @@ public class Gala.WindowCloneContainer : ActorTarget {
142118
}
143119
}
144120

145-
/**
146-
* Sort the windows z-order by their actual stacking to make intersections
147-
* during animations correct.
148-
*/
149-
private void restack_windows () {
150-
var children = (GLib.List<weak WindowClone>) get_children ();
151-
152-
var windows = new GLib.List<Meta.Window> ();
153-
foreach (unowned var clone in children) {
154-
windows.prepend (clone.window);
155-
}
156-
157-
var windows_ordered = sort_windows (windows);
158-
windows_ordered.reverse ();
159-
160-
var i = 0;
161-
foreach (unowned var window in windows_ordered) {
162-
foreach (unowned var clone in children) {
163-
if (clone.window == window) {
164-
set_child_at_index (clone, i);
165-
children.remove (clone);
166-
i++;
167-
break;
168-
}
169-
}
170-
}
171-
}
172-
173121
/**
174122
* Recalculate the tiling positions of the windows and animate them to the resulting spots.
175123
*/
@@ -350,35 +298,6 @@ public class Gala.WindowCloneContainer : ActorTarget {
350298
current_window = closest;
351299
}
352300

353-
/**
354-
* Sorts the windows by stacking order so that the window on active workspaces come first.
355-
*/
356-
private GLib.SList<weak Meta.Window> sort_windows (GLib.List<Meta.Window> windows) {
357-
unowned var display = wm.get_display ();
358-
359-
var windows_on_active_workspace = new GLib.SList<Meta.Window> ();
360-
var windows_on_other_workspaces = new GLib.SList<Meta.Window> ();
361-
unowned var active_workspace = display.get_workspace_manager ().get_active_workspace ();
362-
foreach (unowned var window in windows) {
363-
if (window.get_workspace () == active_workspace) {
364-
windows_on_active_workspace.append (window);
365-
} else {
366-
windows_on_other_workspaces.append (window);
367-
}
368-
}
369-
370-
var sorted_windows = new GLib.SList<weak Meta.Window> ();
371-
var windows_on_active_workspace_sorted = display.sort_windows_by_stacking (windows_on_active_workspace);
372-
windows_on_active_workspace_sorted.reverse ();
373-
var windows_on_other_workspaces_sorted = display.sort_windows_by_stacking (windows_on_other_workspaces);
374-
windows_on_other_workspaces_sorted.reverse ();
375-
sorted_windows.concat ((owned) windows_on_active_workspace_sorted);
376-
sorted_windows.concat ((owned) windows_on_other_workspaces_sorted);
377-
378-
return sorted_windows;
379-
}
380-
381-
382301
// Code ported from KWin present windows effect
383302
// https://projects.kde.org/projects/kde/kde-workspace/repository/revisions/master/entry/kwin/effects/presentwindows/presentwindows.cpp
384303

src/Widgets/MultitaskingView/WorkspaceClone.vala

Lines changed: 8 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public class Gala.WorkspaceClone : ActorTarget {
121121
public WindowCloneContainer window_container { get; private set; }
122122

123123
private BackgroundManager background;
124+
private WindowListModel windows;
124125
private uint hover_activate_timeout = 0;
125126

126127
public WorkspaceClone (WindowManager wm, Meta.Workspace workspace, float monitor_scale) {
@@ -136,7 +137,9 @@ public class Gala.WorkspaceClone : ActorTarget {
136137
background = new FramedBackground (display);
137138
background.add_action (background_click_action);
138139

139-
window_container = new WindowCloneContainer (wm, monitor_scale) {
140+
windows = new WindowListModel (display, STACKING, true, display.get_primary_monitor (), workspace);
141+
142+
window_container = new WindowCloneContainer (wm, windows, monitor_scale) {
140143
width = monitor_geometry.width,
141144
height = monitor_geometry.height,
142145
};
@@ -164,79 +167,20 @@ public class Gala.WorkspaceClone : ActorTarget {
164167
}
165168
});
166169

167-
display.window_entered_monitor.connect (window_entered_monitor);
168-
display.window_left_monitor.connect (window_left_monitor);
169-
workspace.window_added.connect (add_window);
170-
workspace.window_removed.connect (window_container.remove_window);
171-
172170
add_child (background);
173171
add_child (window_container);
174172

175-
// add existing windows
176-
foreach (var window in workspace.list_windows ()) {
177-
add_window (window);
178-
}
179-
180-
var static_windows = StaticWindowContainer.get_instance (display);
181-
static_windows.window_changed.connect (on_window_static_changed);
182-
183173
unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
184174
monitor_manager.monitors_changed.connect (update_targets);
185175
notify["monitor-scale"].connect (update_targets);
186176
update_targets ();
187177
}
188178

189179
~WorkspaceClone () {
190-
unowned var display = workspace.get_display ();
191-
192-
display.window_entered_monitor.disconnect (window_entered_monitor);
193-
display.window_left_monitor.disconnect (window_left_monitor);
194-
workspace.window_added.disconnect (add_window);
195-
workspace.window_removed.disconnect (window_container.remove_window);
196-
197180
background.destroy ();
198181
window_container.destroy ();
199182
}
200183

201-
/**
202-
* Add a window to the WindowCloneContainer if it belongs to this workspace and this monitor.
203-
*/
204-
private void add_window (Meta.Window window) {
205-
if (window.window_type != NORMAL ||
206-
window.get_workspace () != workspace ||
207-
StaticWindowContainer.get_instance (workspace.get_display ()).is_static (window) ||
208-
!window.is_on_primary_monitor ()
209-
) {
210-
return;
211-
}
212-
213-
foreach (var child in (GLib.List<weak WindowClone>) window_container.get_children ()) {
214-
if (child.window == window) {
215-
return;
216-
}
217-
}
218-
219-
window_container.add_window (window);
220-
}
221-
222-
private void window_entered_monitor (Meta.Display display, int monitor, Meta.Window window) {
223-
add_window (window);
224-
}
225-
226-
private void window_left_monitor (Meta.Display display, int monitor, Meta.Window window) {
227-
if (monitor == display.get_primary_monitor ()) {
228-
window_container.remove_window (window);
229-
}
230-
}
231-
232-
private void on_window_static_changed (Meta.Window window, bool is_static) {
233-
if (is_static) {
234-
window_container.remove_window (window);
235-
} else {
236-
add_window (window);
237-
}
238-
}
239-
240184
public void update_size (Mtk.Rectangle monitor_geometry) {
241185
if (window_container.width != monitor_geometry.width || window_container.height != monitor_geometry.height) {
242186
window_container.set_size (monitor_geometry.width, monitor_geometry.height);
@@ -248,8 +192,11 @@ public class Gala.WorkspaceClone : ActorTarget {
248192
remove_all_targets ();
249193

250194
unowned var display = workspace.get_display ();
195+
var primary = display.get_primary_monitor ();
196+
197+
windows.monitor_filter = primary;
251198

252-
var monitor = display.get_monitor_geometry (display.get_primary_monitor ());
199+
var monitor = display.get_monitor_geometry (primary);
253200

254201
var scale = (float)(monitor.height - Utils.scale_to_int (TOP_OFFSET + BOTTOM_OFFSET, monitor_scale)) / monitor.height;
255202
var pivot_y = Utils.scale_to_int (TOP_OFFSET, monitor_scale) / (monitor.height - monitor.height * scale);

0 commit comments

Comments
 (0)