Skip to content

Commit 4d1b6a7

Browse files
authored
WindowClone: display modal dialogs on top of parent window (#2850)
1 parent 5912df1 commit 4d1b6a7

5 files changed

Lines changed: 123 additions & 32 deletions

File tree

src/Widgets/MultitaskingView/MonitorClone.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class Gala.MonitorClone : Widget {
3333

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

36-
var windows = new WindowListModel (display, STACKING, true, monitor);
36+
var windows = new WindowListModel (display, STACKING, true, true, monitor);
3737

3838
window_container = new WindowCloneContainer (wm, windows, monitor_scale);
3939
window_container.add_constraint (new Clutter.BindConstraint (this, SIZE, 0.0f));

src/Widgets/MultitaskingView/WindowClone.vala

Lines changed: 95 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ public class Gala.WindowClone : Widget, RootTarget {
6767
private ulong check_confirm_dialog_cb = 0;
6868

6969
private Clutter.Actor clone_container;
70+
private Clutter.Actor child_clone_container;
7071
private Gala.CloseButton close_button;
7172
private ActiveShape active_shape;
7273
private Clutter.Actor window_icon;
7374
private Tooltip window_title;
75+
private GLib.ListModel window_list_model;
7476

7577
private GestureController gesture_controller;
7678

@@ -131,9 +133,14 @@ public class Gala.WindowClone : Widget, RootTarget {
131133
};
132134
bind_property ("monitor-scale", active_shape, "monitor-scale");
133135

134-
clone_container = new Clutter.Actor () {
135-
pivot_point = { 0.5f, 0.5f }
136-
};
136+
window_list_model = new WindowListModel (wm.get_display (), STACKING, true, false, -1, null, new ChildFilter (window));
137+
window_list_model.items_changed.connect_after (update_targets);
138+
139+
child_clone_container = new Clutter.Actor ();
140+
child_clone_container.bind_model (window_list_model, create_child_func);
141+
142+
clone_container = new Clutter.Actor ();
143+
clone_container.add_child (child_clone_container);
137144

138145
window_title = new Tooltip (monitor_scale);
139146
bind_property ("monitor-scale", window_title, "monitor-scale");
@@ -176,6 +183,18 @@ public class Gala.WindowClone : Widget, RootTarget {
176183
active_shape.restore_easing_state ();
177184
}
178185

186+
private static Clutter.Actor create_child_func (Object obj) requires (obj is Meta.Window) {
187+
unowned var child_window = (Meta.Window) obj;
188+
unowned var child_window_actor = (Meta.WindowActor) child_window.get_compositor_private ();
189+
190+
if (child_window_actor == null) {
191+
critical ("WindowClone: WindowListModel gave us a bad window");
192+
return new Clutter.Actor ();
193+
}
194+
195+
return new Clutter.Clone (child_window_actor);
196+
}
197+
179198
private void reallocate () {
180199
window_icon = new WindowIcon (window, WINDOW_ICON_SIZE, (int)Math.round (monitor_scale)) {
181200
visible = mode != SINGLE_APP_OVERVIEW
@@ -197,7 +216,7 @@ public class Gala.WindowClone : Widget, RootTarget {
197216
*/
198217
private void load_clone (Meta.WindowActor actor) {
199218
clone = new Clutter.Clone (actor);
200-
clone_container.add_child (clone);
219+
clone_container.insert_child_below (clone, null);
201220

202221
check_shadow_requirements ();
203222
}
@@ -276,6 +295,42 @@ public class Gala.WindowClone : Widget, RootTarget {
276295
add_target (new PropertyTarget (MULTITASKING_VIEW, window_title, "opacity", typeof (uint8), (uint8) 0u, (uint8) 255u));
277296

278297
add_target (new PropertyTarget (MULTITASKING_VIEW, close_button, "opacity", typeof (uint8), (uint8) 0u, (uint8) 255u));
298+
299+
var window_buffer_rect = window.get_buffer_rect ();
300+
var window_shadow_spread_x = window_rect.x - window_buffer_rect.x;
301+
var window_shadow_spread_y = window_rect.y - window_buffer_rect.y;
302+
303+
var i = 0u;
304+
for (unowned var child_clone = child_clone_container.get_first_child ();
305+
child_clone != null;
306+
child_clone = child_clone.get_next_sibling ()
307+
) {
308+
var child_window = (Meta.Window) window_list_model.get_item (i);
309+
310+
var child_buffer_rect = child_window.get_buffer_rect ();
311+
var child_frame_rect = child_window.get_frame_rect ();
312+
313+
var scale = 1.0f;
314+
if (child_frame_rect.width > window_rect.width || child_frame_rect.height > window_rect.height) {
315+
scale = float.min ((float) window_rect.width / child_frame_rect.width, (float) window_rect.height / child_frame_rect.height);
316+
317+
add_target (new PropertyTarget (MULTITASKING_VIEW, child_clone, "width", typeof (float), (float) child_buffer_rect.width, child_buffer_rect.width * scale));
318+
add_target (new PropertyTarget (MULTITASKING_VIEW, child_clone, "height", typeof (float), (float) child_buffer_rect.height, child_buffer_rect.height * scale));
319+
}
320+
321+
var child_parent_x_diff = child_buffer_rect.x - window_buffer_rect.x;
322+
var child_parent_y_diff = child_buffer_rect.y - window_buffer_rect.y;
323+
// Center the window
324+
var child_shadow_spread_x = (child_frame_rect.x - child_buffer_rect.x) * scale;
325+
var child_shadow_spread_y = (child_frame_rect.y - child_buffer_rect.y) * scale;
326+
var target_x = window_shadow_spread_x - child_shadow_spread_x + (window_rect.width - child_frame_rect.width * scale) / 2.0f;
327+
var target_y = window_shadow_spread_y - child_shadow_spread_y + (window_rect.height - child_frame_rect.height * scale) / 2.0f;
328+
329+
add_target (new PropertyTarget (MULTITASKING_VIEW, child_clone, "x", typeof (float), (float) child_parent_x_diff, target_x));
330+
add_target (new PropertyTarget (MULTITASKING_VIEW, child_clone, "y", typeof (float), (float) child_parent_y_diff, target_y));
331+
332+
i++;
333+
}
279334
}
280335

281336
public override void update_progress (Gala.GestureAction action, double progress) {
@@ -325,7 +380,7 @@ public class Gala.WindowClone : Widget, RootTarget {
325380

326381
unowned var display = wm.get_display ();
327382

328-
clone.set_scale (clone_scale_factor, clone_scale_factor);
383+
clone_container.set_scale (clone_scale_factor, clone_scale_factor);
329384

330385
Clutter.ActorBox shape_alloc = {
331386
-ACTIVE_SHAPE_SIZE,
@@ -418,14 +473,14 @@ public class Gala.WindowClone : Widget, RootTarget {
418473
drag_action.cancel ();
419474
}
420475

421-
if (clone != null) {
422-
clone.destroy ();
423-
}
476+
clone?.destroy ();
424477

425478
if (check_confirm_dialog_cb != 0) {
426479
SignalHandler.disconnect (window.get_display (), check_confirm_dialog_cb);
427480
check_confirm_dialog_cb = 0;
428481
}
482+
483+
child_clone_container.bind_model (null, (Clutter.ActorCreateChildFunc) null);
429484
}
430485

431486
private void actor_clicked (uint32 button, Clutter.InputDeviceType device_type = POINTER_DEVICE) {
@@ -443,18 +498,21 @@ public class Gala.WindowClone : Widget, RootTarget {
443498
private Clutter.Actor drag_begin (float click_x, float click_y) requires (drag_handle == null) {
444499
active_shape.hide ();
445500

446-
var scale = window_icon.width / clone.width;
501+
var scale = window_icon.width / clone_container.width;
447502
var duration = Utils.get_animation_duration (FADE_ANIMATION_DURATION);
448503

449504
float abs_x, abs_y;
450-
clone.get_transformed_position (out abs_x, out abs_y);
451-
clone.save_easing_state ();
452-
clone.set_easing_duration (duration);
453-
clone.set_easing_mode (Clutter.AnimationMode.EASE_IN_CUBIC);
454-
clone.set_pivot_point ((click_x - abs_x) / clone.width, (click_y - abs_y) / clone.height);
455-
clone.set_scale (scale, scale);
456-
clone.opacity = 0;
457-
clone.restore_easing_state ();
505+
clone_container.get_transformed_position (out abs_x, out abs_y);
506+
clone_container.save_easing_state ();
507+
clone_container.set_easing_duration (duration);
508+
clone_container.set_easing_mode (Clutter.AnimationMode.EASE_IN_CUBIC);
509+
clone_container.set_pivot_point (
510+
(click_x - abs_x) / clone_container.width,
511+
(click_y - abs_y) / clone_container.height
512+
);
513+
clone_container.set_scale (scale, scale);
514+
clone_container.opacity = 0;
515+
clone_container.restore_easing_state ();
458516

459517
get_transformed_position (out abs_x, out abs_y);
460518

@@ -565,13 +623,13 @@ public class Gala.WindowClone : Widget, RootTarget {
565623
drag_handle.set_position (target_x, target_y);
566624
drag_handle.restore_easing_state ();
567625

568-
clone.set_pivot_point (0.0f, 0.0f);
569-
clone.save_easing_state ();
570-
clone.set_easing_duration (duration);
571-
clone.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
572-
clone.set_scale (1, 1);
573-
clone.opacity = 255;
574-
clone.restore_easing_state ();
626+
clone_container.set_pivot_point (0.0f, 0.0f);
627+
clone_container.save_easing_state ();
628+
clone_container.set_easing_duration (duration);
629+
clone_container.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
630+
clone_container.set_scale (1, 1);
631+
clone_container.opacity = 255;
632+
clone_container.restore_easing_state ();
575633

576634
close_button.visible = true;
577635
window_title.visible = true;
@@ -645,4 +703,17 @@ public class Gala.WindowClone : Widget, RootTarget {
645703
});
646704
}
647705
}
706+
707+
private class ChildFilter : Gtk.Filter {
708+
public Meta.Window parent_window { private get; construct; }
709+
710+
public ChildFilter (Meta.Window parent_window) {
711+
Object (parent_window: parent_window);
712+
}
713+
714+
public override bool match (GLib.Object? item) requires (item is Meta.Window) {
715+
unowned var window = (Meta.Window) item;
716+
return parent_window.is_ancestor_of_transient (window);
717+
}
718+
}
648719
}

src/Widgets/MultitaskingView/WorkspaceClone.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public class Gala.WorkspaceClone : Widget {
137137
background = new FramedBackground (display);
138138
background.add_action (background_click_action);
139139

140-
windows = new WindowListModel (display, STACKING, true, display.get_primary_monitor (), workspace);
140+
windows = new WindowListModel (display, STACKING, true, true, display.get_primary_monitor (), workspace);
141141

142142
window_container = new WindowCloneContainer (wm, windows, monitor_scale) {
143143
width = monitor_geometry.width,

src/Widgets/WindowOverview.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public class Gala.WindowOverview : Root, RootTarget, ActivatableComponent {
120120
var scale = Utils.get_ui_scaling_factor (display, i);
121121

122122
var custom_filter = new Gtk.CustomFilter (window_filter_func);
123-
var model = new WindowListModel (display, STACKING, true, i, null, custom_filter);
123+
var model = new WindowListModel (display, STACKING, true, true, i, null, custom_filter);
124124
model.items_changed.connect (on_items_changed);
125125

126126
var window_clone_container = new WindowCloneContainer (wm, model, scale, mode) {

src/WindowListModel.vala

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public class Gala.WindowListModel : Object, ListModel {
2525
*/
2626
public bool normal_filter { get; construct set; }
2727

28+
/**
29+
* If true only present windows that are toplevel windows and not transients.
30+
*/
31+
public bool not_transient_filter { get; construct set; }
32+
2833
/**
2934
* If >= 0 only present windows that are on this monitor.
3035
*/
@@ -41,14 +46,21 @@ public class Gala.WindowListModel : Object, ListModel {
4146
private Gee.ArrayList<Meta.Window> windows;
4247

4348
public WindowListModel (
44-
Meta.Display display, SortMode sort_mode = NONE,
45-
bool normal_filter = false, int monitor_filter = -1,
49+
Meta.Display display,
50+
SortMode sort_mode = NONE,
51+
bool normal_filter = false,
52+
bool not_transient_filter = false,
53+
int monitor_filter = -1,
4654
Meta.Workspace? workspace_filter = null,
4755
Gtk.Filter? custom_filter = null
4856
) {
4957
Object (
50-
display: display, sort_mode: sort_mode, normal_filter: normal_filter,
51-
monitor_filter: monitor_filter, workspace_filter: workspace_filter,
58+
display: display,
59+
sort_mode: sort_mode,
60+
normal_filter: normal_filter,
61+
not_transient_filter: not_transient_filter,
62+
monitor_filter: monitor_filter,
63+
workspace_filter: workspace_filter,
5264
custom_filter: custom_filter
5365
);
5466
}
@@ -70,7 +82,7 @@ public class Gala.WindowListModel : Object, ListModel {
7082

7183
private void on_window_created (Meta.Window window) {
7284
window.unmanaging.connect (on_window_unmanaging);
73-
InternalUtils.wait_for_window_actor (window, (actor) => check_window (actor.meta_window));
85+
InternalUtils.wait_for_window_actor_visible (window, (actor) => check_window (actor.meta_window));
7486
}
7587

7688
private void on_window_unmanaging (Meta.Window window) {
@@ -102,6 +114,10 @@ public class Gala.WindowListModel : Object, ListModel {
102114
}
103115

104116
private bool should_present_window (Meta.Window window) {
117+
if (window.get_compositor_private () == null) {
118+
return false;
119+
}
120+
105121
if (monitor_filter >= 0 && monitor_filter != window.get_monitor ()) {
106122
return false;
107123
}
@@ -117,6 +133,10 @@ public class Gala.WindowListModel : Object, ListModel {
117133
return false;
118134
}
119135

136+
if (not_transient_filter && window.find_root_ancestor () != window) {
137+
return false;
138+
}
139+
120140
if (custom_filter != null) {
121141
return custom_filter.match (window);
122142
}

0 commit comments

Comments
 (0)