diff --git a/js/ui/windowmanager.js b/js/ui/windowmanager.js index d143efe14..83dde3d5b 100644 --- a/js/ui/windowmanager.js +++ b/js/ui/windowmanager.js @@ -1,29 +1,125 @@ -/* -*- mode: js2; js2-basic-offset: 4; -*- */ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil; -*- */ const Clutter = imports.gi.Clutter; const Mainloop = imports.mainloop; +const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; +const Tweener = imports.tweener.tweener; const Main = imports.ui.main; +const SWITCH_ANIMATION_TIME = 0.5; + function WindowManager() { this._init(); } WindowManager.prototype = { _init : function() { - let global = Shell.global_get(); - let shellwm = global.window_manager; + let me = this; - shellwm.connect('switch-workspace', - function(o, from, to, direction) { - let actors = shellwm.get_switch_workspace_actors(); - for (let i = 0; i < actors.length; i++) { - if (actors[i].get_workspace() == from) - actors[i].hide(); - else if (actors[i].get_workspace() == to) - actors[i].show(); - } - }); + this._global = Shell.global_get(); + this._shellwm = this._global.window_manager; + + this._switchData = null; + this._shellwm.connect('switch-workspace', + function(o, from, to, direction) { + let actors = me._shellwm.get_switch_workspace_actors(); + me.switchWorkspace(actors, from, to, direction); + }); + this._shellwm.connect('kill-switch-workspace', + function(o) { + me.switchWorkspaceDone(); + }); + }, + + switchWorkspace : function(windows, from, to, direction) { + /* @direction is the direction that the "camera" moves, so the + * screen contents have to move one screen's worth in the + * opposite direction. + */ + let xDest = 0, yDest = 0; + + if (direction == Meta.MotionDirection.UP || + direction == Meta.MotionDirection.UP_LEFT || + direction == Meta.MotionDirection.UP_RIGHT) + yDest = this._global.screen_height; + else if (direction == Meta.MotionDirection.DOWN || + direction == Meta.MotionDirection.DOWN_LEFT || + direction == Meta.MotionDirection.DOWN_RIGHT) + yDest = -this._global.screen_height; + + if (direction == Meta.MotionDirection.LEFT || + direction == Meta.MotionDirection.UP_LEFT || + direction == Meta.MotionDirection.DOWN_LEFT) + xDest = this._global.screen_width; + else if (direction == Meta.MotionDirection.RIGHT || + direction == Meta.MotionDirection.UP_RIGHT || + direction == Meta.MotionDirection.DOWN_RIGHT) + xDest = -this._global.screen_width; + + let switchData = {}; + this._switchData = switchData; + switchData.inGroup = new Clutter.Group(); + switchData.outGroup = new Clutter.Group(); + switchData.windows = []; + + let wgroup = this._global.window_group; + wgroup.add_actor(switchData.inGroup); + wgroup.add_actor(switchData.outGroup); + + for (let i = 0; i < windows.length; i++) { + let window = windows[i]; + if (window.get_workspace() == from) { + switchData.windows.push({ window: window, + parent: window.get_parent() }); + window.reparent(switchData.outGroup); + } else if (window.get_workspace() == to) { + switchData.windows.push({ window: window, + parent: window.get_parent() }); + window.reparent(switchData.inGroup); + window.show_all(); + } + } + + switchData.inGroup.set_position(-xDest, -yDest); + switchData.inGroup.raise_top(); + + Tweener.addTween(switchData.outGroup, + { x: xDest, + y: yDest, + time: SWITCH_ANIMATION_TIME, + transition: "easeOutBack", + onComplete: this.switchWorkspaceDone + }); + Tweener.addTween(switchData.inGroup, + { x: 0, + y: 0, + time: SWITCH_ANIMATION_TIME, + transition: "easeOutBack" + }); + }, + + switchWorkspaceDone : function() { + let switchData = this._switchData; + if (!switchData) + return; + this._switchData = null; + + for (let i = 0; i < switchData.windows.length; i++) { + let w = switchData.windows[i]; + if (w.window.get_parent() == switchData.outGroup) { + w.window.reparent(w.parent); + w.window.hide(); + } else + w.window.reparent(w.parent); + } + Tweener.removeTweens(switchData.inGroup); + Tweener.removeTweens(switchData.outGroup); + switchData.inGroup.destroy(); + switchData.outGroup.destroy(); + + this._shellwm.completed_switch_workspace(); } + }; diff --git a/src/shell-wm.c b/src/shell-wm.c index a7d2b3ca8..34ba3a7c3 100644 --- a/src/shell-wm.c +++ b/src/shell-wm.c @@ -7,6 +7,7 @@ struct _ShellWM { GObject parent; + MutterPlugin *plugin; GList *switch_workspace_actors; }; @@ -104,9 +105,65 @@ shell_wm_switch_workspace (const GList **actors, shell_wm_set_switch_workspace_actors (wm, (GList *)*actors); g_signal_emit (wm, shell_wm_signals[SWITCH_WORKSPACE], 0, from, to, direction); +} + +/** + * shell_wm_get_switch_workspace_actors: + * @wm: the #ShellWM + * + * A workaround for a missing feature in gobject-introspection. Returns + * the list of windows involved in a switch-workspace operation (which + * cannot be passed directly to the signal handler because there's no + * way to annotate the element-type of a signal parameter.) + * + * Return value: (element-type MutterWindow) (transfer full): the list + * of windows + **/ +GList * +shell_wm_get_switch_workspace_actors (ShellWM *wm) +{ + GList *l; + + for (l = wm->switch_workspace_actors; l; l = l->next) + g_object_ref (l->data); + return g_list_copy (wm->switch_workspace_actors); +} + +static void +shell_wm_set_switch_workspace_actors (ShellWM *wm, GList *actors) +{ + const GList *l; + + for (l = wm->switch_workspace_actors; l; l = l->next) + g_object_unref (l->data); + g_list_free (wm->switch_workspace_actors); + wm->switch_workspace_actors = g_list_copy (actors); + for (l = wm->switch_workspace_actors; l; l = l->next) + g_object_ref (l->data); +} + +/** + * shell_wm_completed_switch_workspace: + * @wm: the ShellWM + * + * The plugin must call this when it has finished switching the + * workspace. + **/ +void +shell_wm_completed_switch_workspace (ShellWM *wm) +{ + g_return_if_fail (wm->switch_workspace_actors != NULL); + + /* mutter_plugin_effect_completed() requires us to pass a window, + * though it doesn't matter *which* window in this case. + */ + mutter_plugin_effect_completed (wm->plugin, + wm->switch_workspace_actors->data, + MUTTER_PLUGIN_SWITCH_WORKSPACE); shell_wm_set_switch_workspace_actors (wm, NULL); } + static void shell_wm_kill_effect (MutterWindow *actor, gulong events) @@ -141,6 +198,11 @@ shell_wm_kill_effect (MutterWindow *actor, ShellWM * shell_wm_new (MutterPlugin *plugin) { + ShellWM *wm; + + wm = g_object_new (SHELL_TYPE_WM, NULL); + wm->plugin = plugin; + #ifdef NOT_YET plugin->minimize = shell_wm_minimize; plugin->maximize = shell_wm_maximize; @@ -151,44 +213,5 @@ shell_wm_new (MutterPlugin *plugin) plugin->switch_workspace = shell_wm_switch_workspace; plugin->kill_effect = shell_wm_kill_effect; - return g_object_new (SHELL_TYPE_WM, NULL); -} - -/** - * shell_wm_get_switch_workspace_actors: - * @wm: the #ShellWM - * - * A workaround for a missing feature in gobject-introspection. Returns - * the list of windows involved in a switch-workspace operation (which - * cannot be passed directly to the signal handler because there's no - * way to annotate the element-type of a signal parameter.) - * - * You must call this from the #ShellWM::switch-workspace signal - * handler itself; if you need the value again later, you must save a - * copy yourself. - * - * Return value: (element-type MutterWindow) (transfer full): the list - * of windows - **/ -GList * -shell_wm_get_switch_workspace_actors (ShellWM *wm) -{ - GList *l; - - for (l = wm->switch_workspace_actors; l; l = l->next) - g_object_ref (l->data); - return g_list_copy (wm->switch_workspace_actors); -} - -static void -shell_wm_set_switch_workspace_actors (ShellWM *wm, GList *actors) -{ - const GList *l; - - for (l = wm->switch_workspace_actors; l; l = l->next) - g_object_unref (l->data); - g_list_free (wm->switch_workspace_actors); - wm->switch_workspace_actors = g_list_copy (actors); - for (l = wm->switch_workspace_actors; l; l = l->next) - g_object_ref (l->data); + return wm; } diff --git a/src/shell-wm.h b/src/shell-wm.h index 54d81a32e..d448239aa 100644 --- a/src/shell-wm.h +++ b/src/shell-wm.h @@ -27,6 +27,7 @@ GType shell_wm_get_type (void) G_GNUC_CONST; ShellWM *shell_wm_new (MutterPlugin *plugin); GList *shell_wm_get_switch_workspace_actors (ShellWM *wm); +void shell_wm_completed_switch_workspace (ShellWM *wm); G_END_DECLS