diff --git a/src/Expo.vala b/src/Expo.vala index b5d8d124..68168f2e 100644 --- a/src/Expo.vala +++ b/src/Expo.vala @@ -61,7 +61,7 @@ namespace Gala * Code borrowed from native window placement GS extension * http://git.gnome.org/browse/gnome-shell-extensions/tree/extensions/native-window-placement/extension.js **/ - const int GAPS = 5; + const int GAPS = 15; const int MAX_TRANSLATIONS = 5000; const int ACCURACY = 20; const int BORDER = 10; @@ -92,7 +92,7 @@ namespace Gala Meta.Rectangle area = {(int)Math.floorf (geom.x + x_gap / 2), (int)Math.floorf (geom.y + 20 + y_gap), (int)Math.floorf (geom.width - x_gap), - (int)Math.floorf (geom.height - 80 - y_gap)}; + (int)Math.floorf (geom.height - 100 - y_gap)}; Meta.Rectangle bounds = {area.x, area.y, area.width, area.height}; @@ -229,9 +229,12 @@ namespace Gala //animate the windows and icons to the calculated positions clone.icon.x = rects.nth_data (i).x + Math.floorf (clone.width * scale / 2.0f - clone.icon.width / 2.0f); - clone.icon.y = rects.nth_data (i).y + Math.floorf (clone.height * scale - 30.0f); + clone.icon.y = rects.nth_data (i).y + Math.floorf (clone.height * scale - 50.0f); clone.icon.get_parent ().set_child_above_sibling (clone.icon, null); + clone.close_button.x = rects.nth_data (i).x - 12; + clone.close_button.y = rects.nth_data (i).y - 12; + clone.animate (Clutter.AnimationMode.EASE_OUT_CUBIC, 250, scale_x:scale, scale_y:scale, x:rects.nth_data (i).x+0.0f, y:rects.nth_data (i).y+0.0f) .completed.connect (() => ready = true ); clone.icon.opacity = 0; @@ -284,6 +287,7 @@ namespace Gala clone.y = actor.y; clone.selected.connect (selected); + clone.reposition.connect (reposition); add_child (clone); } @@ -291,6 +295,13 @@ namespace Gala calculate_places (get_children ()); } + void reposition (ExposedWindow removed) + { + var children = get_children ().copy (); + children.remove (removed); + calculate_places (children); + } + void selected (Window window) { window.activate (screen.get_display ().get_current_time ()); diff --git a/src/Widgets/ExposedWindow.vala b/src/Widgets/ExposedWindow.vala index c3d01a8d..0cf9f856 100644 --- a/src/Widgets/ExposedWindow.vala +++ b/src/Widgets/ExposedWindow.vala @@ -16,16 +16,19 @@ // using Meta; +using Clutter; namespace Gala { - public class ExposedWindow : Clutter.Actor + public class ExposedWindow : Actor { public weak Window window; - Clutter.Clone clone; + Clone clone; public GtkClutter.Texture icon; + public GtkClutter.Texture close_button; public signal void selected (Window window); + public signal void reposition (); public ExposedWindow (Window _window) { @@ -34,23 +37,77 @@ namespace Gala reactive = true; var actor = window.get_compositor_private () as WindowActor; - clone = new Clutter.Clone (actor.get_texture ()); + clone = new Clone (actor.get_texture ()); icon = new GtkClutter.Texture (); icon.scale_x = 0.0f; icon.scale_y = 0.0f; - icon.scale_gravity = Clutter.Gravity.CENTER; + icon.scale_gravity = Gravity.CENTER; try { icon.set_from_pixbuf (Utils.get_icon_for_window (window, 64)); } catch (Error e) { warning (e.message); } + close_button = new GtkClutter.Texture (); + close_button.reactive = true; + close_button.visible = false; + close_button.scale_x = 0.0f; + close_button.scale_y = 0.0f; + close_button.scale_gravity = Gravity.CENTER; + close_button.button_press_event.connect (close_clicked); + + try { + close_button.set_from_pixbuf (Granite.Widgets.get_close_pixbuf ()); + } catch (Error e) { warning (e.message); } + add_child (clone); - Compositor.get_stage_for_screen (window.get_screen ()).add_child (icon); + var stage = Compositor.get_stage_for_screen (window.get_screen ()); + stage.add_child (icon); + stage.add_child (close_button); } - public override bool button_press_event (Clutter.ButtonEvent event) + bool close_clicked (ButtonEvent event) + { + if (event.button != 1) + return false; + + //make sure we dont see a window closing animation in the background + (window.get_compositor_private () as Actor).opacity = 0; + get_parent ().set_child_below_sibling (this, null); + animate (AnimationMode.EASE_IN_CUBIC, 200, depth : -50.0f, opacity : 0).completed.connect (() => { + destroy (); + window.delete (window.get_screen ().get_display ().get_current_time ()); + }); + + close_button.destroy (); + icon.destroy (); + + reposition (); + + return true; + } + + public override bool enter_event (CrossingEvent event) + { + close_button.visible = true; + close_button.animate (AnimationMode.EASE_OUT_ELASTIC, 400, scale_x : 1.0f, scale_y : 1.0f); + + return true; + } + + public override bool leave_event (CrossingEvent event) + { + if (event.related == close_button) + return false; + + close_button.animate (AnimationMode.EASE_IN_QUAD, 400, scale_x : 0.0f, scale_y : 0.0f) + .completed.connect (() => close_button.visible = false ); + + return true; + } + + public override bool button_press_event (ButtonEvent event) { get_parent ().set_child_above_sibling (this, null); selected (window); @@ -60,7 +117,7 @@ namespace Gala public void close (bool do_animate=true) { - unowned Rectangle rect = window.get_outer_rect (); + unowned Meta.Rectangle rect = window.get_outer_rect (); //FIXME need to subtract 10 here to remove jump for most windows, but adds jump for maximized ones float delta = window.maximized_horizontally || window.maximized_vertically ? 0 : 10; @@ -73,20 +130,22 @@ namespace Gala icon.detach_animation (); if (do_animate) { - icon.animate (Clutter.AnimationMode.EASE_IN_CUBIC, 100, scale_x:0.0f, scale_y:0.0f).completed.connect ( () => { + icon.animate (AnimationMode.EASE_IN_CUBIC, 100, scale_x:0.0f, scale_y:0.0f).completed.connect ( () => { icon.destroy (); }); - animate (Clutter.AnimationMode.EASE_OUT_CUBIC, 250, scale_x:1.0f, scale_y:1.0f, x:dest_x, y:dest_y).completed.connect (() => { - (window.get_compositor_private () as Clutter.Actor).show (); + animate (AnimationMode.EASE_OUT_CUBIC, 250, scale_x:1.0f, scale_y:1.0f, x:dest_x, y:dest_y).completed.connect (() => { + (window.get_compositor_private () as Actor).show (); destroy (); }); } else { - (window.get_compositor_private () as Clutter.Actor).show (); + (window.get_compositor_private () as Actor).show (); destroy (); icon.destroy (); } + + close_button.destroy (); } } }