11/*
22 * SPDX-License-Identifier: GPL-3.0-or-later
3- * SPDX-FileCopyrightText: 2022-2023 elementary, Inc. (https://elementary.io)
3+ * SPDX-FileCopyrightText: 2022-2025 elementary, Inc. (https://elementary.io)
44 * 2014 Tom Beckmann
55 */
66
@@ -13,6 +13,7 @@ public class Gala.WindowClone : ActorTarget {
1313 private const int ACTIVE_SHAPE_SIZE = 12 ;
1414 private const int FADE_ANIMATION_DURATION = 200 ;
1515 private const int TITLE_MAX_WIDTH_MARGIN = 60 ;
16+ private const int CLOSE_TRANSLATION = 600 ;
1617
1718 /**
1819 * The window was selected. The MultitaskingView should consider activating
@@ -26,8 +27,7 @@ public class Gala.WindowClone : ActorTarget {
2627 */
2728 public signal void request_reposition ();
2829
29- public Meta . Display display { get ; construct; }
30-
30+ public WindowManager wm { get ; construct; }
3131 public Meta . Window window { get ; construct; }
3232
3333 /**
@@ -84,14 +84,17 @@ public class Gala.WindowClone : ActorTarget {
8484 private ulong check_confirm_dialog_cb = 0 ;
8585 private bool in_slot_animation = false ;
8686
87+ private Clutter . Actor clone_container;
8788 private Gala . CloseButton close_button;
8889 private ActiveShape active_shape;
8990 private Clutter . Actor window_icon;
9091 private Tooltip window_title;
9192
92- public WindowClone (Meta .Display display , Meta .Window window , float scale , bool overview_mode = false ) {
93+ private GestureController gesture_controller;
94+
95+ public WindowClone (WindowManager wm , Meta .Window window , float scale , bool overview_mode = false ) {
9396 Object (
94- display : display ,
97+ wm : wm ,
9598 window: window,
9699 monitor_scale_factor: scale,
97100 overview_mode: overview_mode
@@ -101,6 +104,9 @@ public class Gala.WindowClone : ActorTarget {
101104 construct {
102105 reactive = true ;
103106
107+ gesture_controller = new GestureController (CLOSE_WINDOW , this , wm);
108+ gesture_controller. enable_scroll (this , VERTICAL );
109+
104110 window. unmanaged. connect (unmanaged);
105111 window. notify[" fullscreen" ]. connect (check_shadow_requirements);
106112 window. notify[" maximized-horizontally" ]. connect (check_shadow_requirements);
@@ -126,13 +132,18 @@ public class Gala.WindowClone : ActorTarget {
126132 add_action (drag_action);
127133 }
128134
129- window_title = new Tooltip ();
130- window_title. opacity = 0 ;
131-
132135 active_shape = new ActiveShape ();
133136 active_shape. opacity = 0 ;
134137
138+ clone_container = new Clutter .Actor () {
139+ pivot_point = { 0.5f , 0.5f }
140+ };
141+
142+ window_title = new Tooltip ();
143+ window_title. opacity = 0 ;
144+
135145 add_child (active_shape);
146+ add_child (clone_container);
136147 add_child (window_title);
137148
138149 reallocate ();
@@ -183,13 +194,7 @@ public class Gala.WindowClone : ActorTarget {
183194 }
184195
185196 clone = new Clutter .Clone (actor);
186- clone. set_content_scaling_filters (TRILINEAR , TRILINEAR );
187- add_child (clone);
188-
189- set_child_below_sibling (active_shape, clone);
190- set_child_above_sibling (close_button, clone);
191- set_child_above_sibling (window_icon, clone);
192- set_child_above_sibling (window_title, clone);
197+ clone_container. add_child (clone);
193198
194199 check_shadow_requirements ();
195200 }
@@ -269,31 +274,61 @@ public class Gala.WindowClone : ActorTarget {
269274 update_hover_widgets (true );
270275 }
271276
277+ public override void update_progress (Gala .GestureAction action , double progress ) {
278+ if (action != CLOSE_WINDOW || slot == null || ! Meta . Prefs . get_gnome_animations ()) {
279+ return ;
280+ }
281+
282+ var target_translation_y = (float ) (- CLOSE_TRANSLATION * monitor_scale_factor * progress);
283+ var target_opacity = (uint ) (255 * (1 - progress));
284+
285+ clone_container. translation_y = target_translation_y;
286+ clone_container. opacity = target_opacity;
287+
288+ window_icon. translation_y = target_translation_y;
289+ window_icon. opacity = target_opacity;
290+
291+ window_title. translation_y = target_translation_y;
292+ window_title. opacity = target_opacity;
293+
294+ close_button. translation_y = target_translation_y;
295+ close_button. opacity = target_opacity;
296+ }
297+
272298 public override void end_progress (GestureAction action ) {
273299 update_hover_widgets (false );
300+
301+ if (action == CLOSE_WINDOW && get_current_commit (CLOSE_WINDOW ) > 0.5 && Meta . Prefs . get_gnome_animations ()) {
302+ close_window (Meta . CURRENT_TIME );
303+ }
274304 }
275305
276306 public override void allocate (Clutter .ActorBox box ) {
277307 base . allocate (box);
278308
309+ var input_rect = window. get_buffer_rect ();
310+ var outer_rect = window. get_frame_rect ();
311+ var clone_scale_factor = width / outer_rect. width;
312+
313+ // Compensate for invisible borders of the texture
314+ float clone_x = (input_rect. x - outer_rect. x) * clone_scale_factor;
315+ float clone_y = (input_rect. y - outer_rect. y) * clone_scale_factor;
316+
317+ var clone_container_alloc = InternalUtils . actor_box_from_rect (clone_x, clone_y, input_rect. width * clone_scale_factor, input_rect. height * clone_scale_factor);
318+ clone_container. allocate (clone_container_alloc);
319+
279320 if (clone == null || (drag_action != null && drag_action. dragging)) {
280321 return ;
281322 }
282323
283- var input_rect = window. get_buffer_rect ();
284- var outer_rect = window. get_frame_rect ();
285- var clone_scale_factor = width / outer_rect. width;
324+ unowned var display = wm. get_display ();
286325
287326 clone. set_scale (clone_scale_factor, clone_scale_factor);
288327
289328 float clone_width, clone_height;
290329 clone. get_preferred_size (null , null , out clone_width, out clone_height);
291330
292- // Compensate for invisible borders of the texture
293- float clone_x = (input_rect. x - outer_rect. x) * clone_scale_factor;
294- float clone_y = (input_rect. y - outer_rect. y) * clone_scale_factor;
295-
296- var clone_alloc = InternalUtils . actor_box_from_rect (clone_x, clone_y, clone_width, clone_height);
331+ var clone_alloc = InternalUtils . actor_box_from_rect (0 , 0 , clone_width, clone_height);
297332 clone. allocate (clone_alloc);
298333
299334 Clutter . ActorBox shape_alloc = {
@@ -380,15 +415,17 @@ public class Gala.WindowClone : ActorTarget {
380415 }
381416
382417 private void check_confirm_dialog (int monitor , Meta .Window new_window ) {
383- if (new_window. get_transient_for () == window) {
384- Idle . add (() = > {
418+ Idle . add (() = > {
419+ if (new_window. get_transient_for () == window) {
420+ gesture_controller. goto (0.0 );
385421 selected ();
386- return Source . REMOVE ;
387- });
388422
389- SignalHandler . disconnect (window. get_display (), check_confirm_dialog_cb);
390- check_confirm_dialog_cb = 0 ;
391- }
423+ SignalHandler . disconnect (window. get_display (), check_confirm_dialog_cb);
424+ check_confirm_dialog_cb = 0 ;
425+ }
426+
427+ return Source . REMOVE ;
428+ });
392429 }
393430
394431 /**
@@ -417,7 +454,7 @@ public class Gala.WindowClone : ActorTarget {
417454 if (button == Clutter . Button . PRIMARY ) {
418455 selected ();
419456 } else if (button == Clutter . Button . MIDDLE && device_type == POINTER_DEVICE ) {
420- close_window (display . get_current_time ());
457+ close_window (wm . get_display () . get_current_time ());
421458 }
422459 }
423460
@@ -476,7 +513,7 @@ public class Gala.WindowClone : ActorTarget {
476513 close_button. opacity = 0 ;
477514 window_title. opacity = 0 ;
478515
479- display . set_cursor (Meta . Cursor . DND_IN_DRAG );
516+ wm . get_display () . set_cursor (Meta . Cursor . DND_IN_DRAG );
480517
481518 return this ;
482519 }
@@ -529,7 +566,7 @@ public class Gala.WindowClone : ActorTarget {
529566 }
530567 }
531568
532- display . set_cursor (hovered ? Meta . Cursor . DND_MOVE: Meta . Cursor . DND_IN_DRAG );
569+ wm . get_display () . set_cursor (hovered ? Meta . Cursor . DND_MOVE: Meta . Cursor . DND_IN_DRAG );
533570 }
534571
535572 /**
@@ -538,8 +575,10 @@ public class Gala.WindowClone : ActorTarget {
538575 * otherwise we cancel the drag and animate back to our old place.
539576 */
540577 private void drag_end (Clutter .Actor destination ) {
578+ unowned var display = wm. get_display ();
579+
541580 Meta . Workspace workspace = null ;
542- var primary = window . get_display () . get_primary_monitor ();
581+ var primary = display . get_primary_monitor ();
543582
544583 active_shape. show ();
545584
@@ -628,7 +667,7 @@ public class Gala.WindowClone : ActorTarget {
628667
629668 request_reposition ();
630669
631- display . set_cursor (Meta . Cursor . DEFAULT );
670+ wm . get_display () . set_cursor (Meta . Cursor . DEFAULT );
632671
633672 if (duration > 0 ) {
634673 ulong handler = 0 ;
0 commit comments