diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 7c96d0654..718f72f18 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -692,6 +692,7 @@ const WindowManager = new Lang.Class({ this._minimizeWindowDone(shellwm, actor); this._mapWindowDone(shellwm, actor); this._destroyWindowDone(shellwm, actor); + this._sizeChangeWindowDone(shellwm, actor); })); this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace)); @@ -1218,9 +1219,124 @@ const WindowManager = new Lang.Class({ }, _sizeChangeWindow : function(shellwm, actor, whichChange, oldFrameRect, oldBufferRect) { + let types = [Meta.WindowType.NORMAL]; + if (!this._shouldAnimateActor(actor, types)) { + shellwm.completed_size_change(actor); + return; + } + + if (whichChange == Meta.SizeChange.FULLSCREEN) + this._fullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect); + else if (whichChange == Meta.SizeChange.UNFULLSCREEN) + this._unfullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect); + else + shellwm.completed_size_change(actor); + }, + + _fullscreenWindow: function(shellwm, actor, oldFrameRect, oldBufferRect) { + let targetRect = actor.meta_window.get_frame_rect(); + let actorContent = Shell.util_get_content_for_window_actor(actor, oldFrameRect); + let actorClone = new St.Widget({ content: actorContent }); + actorClone.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + actorClone.set_position(oldFrameRect.x, oldFrameRect.y); + actorClone.set_size(oldFrameRect.width, oldFrameRect.height); + Main.uiGroup.add_actor(actorClone); + + actor.__fullscreenClone = actorClone; + + let scaleX = targetRect.width / oldFrameRect.width; + let scaleY = targetRect.height / oldFrameRect.height; + + Tweener.addTween(actorClone, + { x: targetRect.x, + y: targetRect.y, + scaleX: scaleX, + scaleY: scaleY, + opacity: 0, + time: WINDOW_ANIMATION_TIME, + transition: 'easeOutQuad', + onComplete: this._sizeChangeWindowDone, + onCompleteScope: this, + onCompleteParams: [shellwm, actor] + }); + + actor.translation_x = actor.x; + actor.translation_y = actor.y; + actor.scaleX = 1 / scaleX; + actor.scaleY = 1 / scaleY; + + Tweener.addTween(actor, + { scaleX: 1.0, + scaleY: 1.0, + translation_x: 0, + translation_y: 0, + time: WINDOW_ANIMATION_TIME, + transition: 'easeOutQuad' + }); + shellwm.completed_size_change(actor); }, + _unfullscreenWindow: function(shellwm, actor, oldFrameRect, oldBufferRect) { + let targetRect = actor.meta_window.get_frame_rect(); + let actorContent = Shell.util_get_content_for_window_actor(actor, oldFrameRect); + let actorClone = new St.Widget({ content: actorContent }); + actorClone.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + actorClone.set_position(oldFrameRect.x, oldFrameRect.y); + actorClone.set_size(oldFrameRect.width, oldFrameRect.height); + Main.uiGroup.add_actor(actorClone); + + actor.__unfullscreenClone = actorClone; + + let scaleX = targetRect.width / oldFrameRect.width; + let scaleY = targetRect.height / oldFrameRect.height; + + Tweener.addTween(actorClone, + { x: targetRect.x, + y: targetRect.y, + scaleX: scaleX, + scaleY: scaleY, + opacity: 0, + time: WINDOW_ANIMATION_TIME, + transition: 'easeOutQuad', + onComplete: this._sizeChangeWindowDone, + onCompleteScope: this, + onCompleteParams: [shellwm, actor] + }); + + actor.translation_x = -actor.x; + actor.translation_y = -actor.y; + actor.scaleX = 1 / scaleX; + actor.scaleY = 1 / scaleY; + + Tweener.addTween(actor, + { scaleX: 1.0, + scaleY: 1.0, + translation_x: 0, + translation_y: 0, + time: WINDOW_ANIMATION_TIME, + transition: 'easeOutQuad' + }); + + shellwm.completed_size_change(actor); + }, + + _sizeChangeWindowDone: function(shellwm, actor) { + Tweener.removeTweens(actor); + + let actorClone = actor.__fullscreenClone; + if (actorClone) { + actorClone.destroy(); + delete actor.__fullscreenClone; + } + + actorClone = actor.__unfullscreenClone; + if (actorClone) { + actorClone.destroy(); + delete actor.__unfullscreenClone; + } + }, + _hasAttachedDialogs: function(window, ignoreWindow) { var count = 0; window.foreach_transient(function(win) { diff --git a/src/shell-util.c b/src/shell-util.c index 33de0200f..5cd225822 100644 --- a/src/shell-util.c +++ b/src/shell-util.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifdef HAVE__NL_TIME_FIRST_WEEKDAY @@ -403,3 +404,57 @@ shell_util_text_insert_keyval (ClutterActor *actor, clutter_actor_event (actor, &event, FALSE); } + +static gboolean +canvas_draw_cb (ClutterContent *content, + cairo_t *cr, + gint width, + gint height, + gpointer user_data) +{ + cairo_surface_t *surface = user_data; + + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + + return FALSE; +} + +/** + * shell_util_get_content_for_window_actor: + * @window_actor: a #MetaWindowActor + * @window_rect: a #MetaRectangle + * + * Returns: (transfer full): a new #ClutterContent + */ +ClutterContent * +shell_util_get_content_for_window_actor (MetaWindowActor *window_actor, + MetaRectangle *window_rect) +{ + ClutterActor *texture; + ClutterContent *content; + cairo_surface_t *surface; + cairo_rectangle_int_t clip; + gfloat actor_x, actor_y; + + texture = meta_window_actor_get_texture (window_actor); + clutter_actor_get_position (CLUTTER_ACTOR (window_actor), &actor_x, &actor_y); + + clip.x = window_rect->x - (gint) actor_x; + clip.y = window_rect->y - (gint) actor_y; + clip.width = window_rect->width; + clip.height = window_rect->height; + + surface = meta_shaped_texture_get_image (META_SHAPED_TEXTURE (texture), + &clip); + + content = clutter_canvas_new (); + clutter_canvas_set_size (CLUTTER_CANVAS (content), + clip.width, clip.height); + g_signal_connect (content, "draw", + G_CALLBACK (canvas_draw_cb), surface); + clutter_content_invalidate (content); + cairo_surface_destroy (surface); + + return content; +} diff --git a/src/shell-util.h b/src/shell-util.h index 4474a0d6d..80a1dc75a 100644 --- a/src/shell-util.h +++ b/src/shell-util.h @@ -8,6 +8,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -50,6 +51,9 @@ gboolean shell_util_need_background_refresh (void); void shell_util_text_insert_keyval (ClutterActor *actor, guint keyval); +ClutterContent * shell_util_get_content_for_window_actor (MetaWindowActor *window_actor, + MetaRectangle *window_rect); + G_END_DECLS #endif /* __SHELL_UTIL_H__ */