windowManager: add animations for fullscreen and unfullscreen
We use the newly introduced feature from Mutter to hook up our own fullscreen and unfullscreen animations. To give the illusion of a transition as smooth as possible, we create a snapshot of the current contents of the actor before its state is changed, and crossfade between the two states while the size changes. https://bugzilla.gnome.org/show_bug.cgi?id=707248
This commit is contained in:
parent
674ae262c8
commit
672ca8850a
@ -692,6 +692,7 @@ const WindowManager = new Lang.Class({
|
|||||||
this._minimizeWindowDone(shellwm, actor);
|
this._minimizeWindowDone(shellwm, actor);
|
||||||
this._mapWindowDone(shellwm, actor);
|
this._mapWindowDone(shellwm, actor);
|
||||||
this._destroyWindowDone(shellwm, actor);
|
this._destroyWindowDone(shellwm, actor);
|
||||||
|
this._sizeChangeWindowDone(shellwm, actor);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
|
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) {
|
_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);
|
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) {
|
_hasAttachedDialogs: function(window, ignoreWindow) {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
window.foreach_transient(function(win) {
|
window.foreach_transient(function(win) {
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
|
#include <meta/meta-shaped-texture.h>
|
||||||
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
|
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
|
||||||
@ -403,3 +404,57 @@ shell_util_text_insert_keyval (ClutterActor *actor,
|
|||||||
|
|
||||||
clutter_actor_event (actor, &event, FALSE);
|
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;
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <libsoup/soup.h>
|
#include <libsoup/soup.h>
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||||
#include <meta/meta-cursor-tracker.h>
|
#include <meta/meta-cursor-tracker.h>
|
||||||
|
#include <meta/meta-window-actor.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@ -50,6 +51,9 @@ gboolean shell_util_need_background_refresh (void);
|
|||||||
void shell_util_text_insert_keyval (ClutterActor *actor,
|
void shell_util_text_insert_keyval (ClutterActor *actor,
|
||||||
guint keyval);
|
guint keyval);
|
||||||
|
|
||||||
|
ClutterContent * shell_util_get_content_for_window_actor (MetaWindowActor *window_actor,
|
||||||
|
MetaRectangle *window_rect);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __SHELL_UTIL_H__ */
|
#endif /* __SHELL_UTIL_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user