From a969e829549cc018322fdcdb7a0e840599e780a5 Mon Sep 17 00:00:00 2001 From: Maxim Ermilov Date: Sat, 11 Sep 2010 05:30:50 +0400 Subject: [PATCH] Attach dialogs to windows with visual effects Override the new mutter preference /apps/mutter/general/attach_modal_dialogs to attach modal dialogs to their parent window. Animate the modal dialogs expanding from the top of the parent window. Slowly dim the parent window after the dialog comes up. https://bugzilla.gnome.org/show_bug.cgi?id=612726 --- data/Makefile.am | 4 + data/gnome-shell.schemas | 15 +++ data/shaders/dim-window.glsl | 26 ++++++ js/ui/windowManager.js | 171 ++++++++++++++++++++++++++++++++++- src/gnome-shell-plugin.c | 2 + 5 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 data/shaders/dim-window.glsl diff --git a/data/Makefile.am b/data/Makefile.am index 5d54a5bbf..1f894f954 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -70,6 +70,10 @@ menudir = $(sysconfdir)/xdg/menus menu_DATA = \ gs-applications.menu +shadersdir = $(pkgdatadir)/shaders +shaders_DATA = \ + shaders/dim-window.glsl + install-data-local: GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(gconfschema_DATA) diff --git a/data/gnome-shell.schemas b/data/gnome-shell.schemas index 72c4751e9..d6a7dbfe6 100644 --- a/data/gnome-shell.schemas +++ b/data/gnome-shell.schemas @@ -2,6 +2,21 @@ + + /schemas/desktop/gnome/shell/windows/attach_modal_dialogs + /desktop/gnome/shell/windows/attach_modal_dialogs + gnome-shell + bool + true + + Attach modal dialog to the parent window + + This key overrides /apps/mutter/general/attach_modal_dialogs when + running GNOME Shell. + + + + /schemas/desktop/gnome/shell/windows/button_layout /desktop/gnome/shell/windows/button_layout diff --git a/data/shaders/dim-window.glsl b/data/shaders/dim-window.glsl new file mode 100644 index 000000000..9099b041e --- /dev/null +++ b/data/shaders/dim-window.glsl @@ -0,0 +1,26 @@ +#version 100 +uniform sampler2D sampler0; +uniform float fraction; +uniform float height; +const float c = -0.2; +const float border_max_height = 60.0; + +mat4 contrast = mat4 (1.0 + c, 0.0, 0.0, 0.0, + 0.0, 1.0 + c, 0.0, 0.0, + 0.0, 0.0, 1.0 + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +vec4 off = vec4(0.633, 0.633, 0.633, 0); +void main() +{ + vec4 color = texture2D(sampler0, gl_TexCoord[0].st); + float y = height * gl_TexCoord[0][1]; + + // To reduce contrast, blend with a mid gray + gl_FragColor = color * contrast - off * c; + + // We only fully dim at a distance of BORDER_MAX_HEIGHT from the edge and + // when the fraction is 1.0. For other locations and fractions we linearly + // interpolate back to the original undimmed color. + gl_FragColor = color + (gl_FragColor - color) * min(y / border_max_height, 1.0); + gl_FragColor = color + (gl_FragColor - color) * fraction; +} diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 6d0ea4411..8b96bb2be 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -1,6 +1,8 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; const Lang = imports.lang; const Meta = imports.gi.Meta; const St = imports.gi.St; @@ -11,6 +13,68 @@ const Main = imports.ui.main; const Tweener = imports.ui.tweener; const WINDOW_ANIMATION_TIME = 0.25; +const DIM_TIME = 0.500; +const UNDIM_TIME = 0.250; + +var dimShader = undefined; + +function getDimShader() { + if (dimShader === null) + return null; + if (!dimShader) { + let [success, source, length] = GLib.file_get_contents(global.datadir + + '/shaders/dim-window.glsl'); + try { + let shader = new Clutter.Shader(); + shader.set_fragment_source(source, -1); + shader.compile(); + + dimShader = shader; + } catch (e) { + log(e.message); + dimShader = null; + } + } + return dimShader; +} + +function WindowDimmer(actor) { + this._init(actor); +} + +WindowDimmer.prototype = { + _init: function(actor) { + this.actor = actor; + }, + + set dimFraction(fraction) { + this._dimFraction = fraction; + let shader = getDimShader(); + if (!Meta.prefs_get_attach_modal_dialogs() || !shader) { + this.actor.set_shader(null); + return; + } + if (fraction > 0.01) { + this.actor.set_shader(shader); + this.actor.set_shader_param_float('height', this.actor.get_height()); + this.actor.set_shader_param_float('fraction', fraction); + } else + this.actor.set_shader(null); + }, + + get dimFraction() { + return this._dimFraction; + }, + + _dimFraction: 0.0 +}; + +function getWindowDimmer(texture) { + if (!texture._windowDimmer) + texture._windowDimmer = new WindowDimmer(texture); + + return texture._windowDimmer; +} function WindowManager() { this._init(); @@ -147,7 +211,75 @@ WindowManager.prototype = { _unmaximizeWindowDone : function(shellwm, actor) { }, + _parentHasOtherAttachedDialog: function(parent, self) { + var count = 0; + parent.foreach_transient(function(win) { + if (win.get_window_type() == Meta.CompWindowType.MODAL_DIALOG && win != self) + count++; + return false; + }); + return count != 0; + }, + + _dimParentWindow: function(actor) { + let meta = actor.get_meta_window(); + let parent = meta.get_transient_for(); + if (!parent) + return; + let parentActor = parent.get_compositor_private(); + if (!parentActor) + return; + if (!this._parentHasOtherAttachedDialog(parent, meta)) { + let texture = parentActor.get_texture(); + Tweener.addTween(getWindowDimmer(texture), + { dimFraction: 1.0, + time: DIM_TIME, + transition: 'linear' + }); + } + }, + + _undimParentWindow: function(actor) { + let meta = actor.get_meta_window(); + let parent = meta.get_transient_for(); + if (!parent) + return; + let parentActor = parent.get_compositor_private(); + if (!parentActor) + return; + if (!this._parentHasOtherAttachedDialog(parent, meta)) { + let texture = parentActor.get_texture(); + Tweener.addTween(getWindowDimmer(texture), + { dimFraction: 0.0, + time: UNDIM_TIME, + transition: 'linear' + }); + } + }, + _mapWindow : function(shellwm, actor) { + if (this._shouldAnimate() && actor + && actor.get_window_type() == Meta.CompWindowType.MODAL_DIALOG + && Meta.prefs_get_attach_modal_dialogs() + && actor.get_meta_window().get_transient_for()) { + actor.set_scale(1.0, 0.0); + actor.show(); + this._mapping.push(actor); + + Tweener.addTween(actor, + { scale_y: 1, + time: WINDOW_ANIMATION_TIME, + transition: "easeOutQuad", + onComplete: this._mapWindowDone, + onCompleteScope: this, + onCompleteParams: [shellwm, actor], + onOverwrite: this._mapWindowOverwrite, + onOverwriteScope: this, + onOverwriteParams: [shellwm, actor] + }); + this._dimParentWindow(actor); + return; + } if (!this._shouldAnimate(actor)) { shellwm.completed_map(actor); return; @@ -185,11 +317,46 @@ WindowManager.prototype = { } }, - _destroyWindow : function(shellwm, actor) { + _destroyWindowOverwrite : function(shellwm, actor) { + if (this._removeEffect(this._mapping, actor)) { + shellwm.completed_destroy(actor); + } + }, + + _destroyWindow : function(shellwm, actor) { + while (actor && this._shouldAnimate() + && actor.get_window_type() == Meta.CompWindowType.MODAL_DIALOG + && actor.get_meta_window().get_transient_for()) { + this._undimParentWindow(actor); + if (!Meta.prefs_get_attach_modal_dialogs()) + break; + actor.set_scale(1.0, 1.0); + actor.show(); + this._mapping.push(actor); + + Tweener.addTween(actor, + { scale_y: 0, + time: WINDOW_ANIMATION_TIME, + transition: "easeOutQuad", + onComplete: this._destroyWindowDone, + onCompleteScope: this, + onCompleteParams: [shellwm, actor], + onOverwrite: this._destroyWindowOverwrite, + onOverwriteScope: this, + onOverwriteParams: [shellwm, actor] + }); + return; + } shellwm.completed_destroy(actor); }, - + _destroyWindowDone : function(shellwm, actor) { + if (actor && actor.get_window_type() == Meta.CompWindowType.MODAL_DIALOG && + actor.get_meta_window().get_transient_for()) { + if (this._removeEffect(this._mapping, actor)) { + shellwm.completed_destroy(actor); + } + } }, _switchWorkspace : function(shellwm, from, to, direction) { diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c index c6ac75278..3b78bd80f 100644 --- a/src/gnome-shell-plugin.c +++ b/src/gnome-shell-plugin.c @@ -154,6 +154,8 @@ static void gnome_shell_plugin_init (GnomeShellPlugin *shell_plugin) { g_setenv ("XDG_MENU_PREFIX", "gs-", TRUE); + meta_prefs_override_preference_location ("/apps/mutter/general/attach_modal_dialogs", + "/desktop/gnome/shell/windows/attach_modal_dialogs"); meta_prefs_override_preference_location ("/apps/metacity/general/button_layout", "/desktop/gnome/shell/windows/button_layout"); }