ui: Implement "window doesn't respond" dialog on gnome-shell

This does allow us to use ClutterActors instead of lousy legacy tools
meant for shell scripting.

https://bugzilla.gnome.org/show_bug.cgi?id=762083
This commit is contained in:
Carlos Garnacho 2017-01-19 19:52:18 +01:00
parent 593b4318a3
commit d220e353e0
7 changed files with 188 additions and 0 deletions

View File

@ -44,6 +44,7 @@
<file>ui/boxpointer.js</file>
<file>ui/calendar.js</file>
<file>ui/checkBox.js</file>
<file>ui/closeDialog.js</file>
<file>ui/ctrlAltTab.js</file>
<file>ui/dash.js</file>
<file>ui/dateMenu.js</file>

138
js/ui/closeDialog.js Normal file
View File

@ -0,0 +1,138 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Dialog = imports.ui.dialog;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const FROZEN_WINDOW_BRIGHTNESS = -0.3
const DIALOG_TRANSITION_TIME = 0.15
const CloseDialog = new Lang.Class({
Name: 'CloseDialog',
Extends: GObject.Object,
Implements: [ Meta.CloseDialog ],
Properties: {
'window': GObject.ParamSpec.override('window', Meta.CloseDialog)
},
_init: function (window) {
this.parent();
this._window = window;
this._dialog = null;
},
get window() {
return this._window;
},
set window(window) {
this._window = window;
},
_createDialogContent: function () {
let tracker = Shell.WindowTracker.get_default();
let windowApp = tracker.get_window_app(this._window);
/* Translators: %s is an application name */
let title = _("“%s” is not responding.").format(windowApp.get_name());
let subtitle = _("You may choose to wait a short while for it to " +
"continue or force the application to quit entirely.");
let icon = new Gio.ThemedIcon({ name: 'dialog-warning-symbolic' });
return new Dialog.MessageDialogContent({ icon, title, subtitle });
},
_initDialog: function () {
if (this._dialog)
return;
let windowActor = this._window.get_compositor_private();
this._dialog = new Dialog.Dialog(windowActor, 'close-dialog');
this._dialog.width = windowActor.width;
this._dialog.height = windowActor.height;
this._dialog.addContent(this._createDialogContent());
this._dialog.addButton({ label: _('Force Quit'),
action: Lang.bind(this, this._onClose),
default: true });
this._dialog.addButton({ label: _('Wait'),
action: Lang.bind(this, this._onWait),
key: Clutter.Escape });
global.focus_manager.add_group(this._dialog);
},
_addWindowEffect: function () {
// We set the effect on the surface actor, so the dialog itself
// (which is a child of the MetaWindowActor) does not get the
// effect applied itself.
let windowActor = this._window.get_compositor_private();
let surfaceActor = windowActor.get_first_child();
let effect = new Clutter.BrightnessContrastEffect();
effect.set_brightness(FROZEN_WINDOW_BRIGHTNESS);
surfaceActor.add_effect_with_name("gnome-shell-frozen-window", effect);
},
_removeWindowEffect: function () {
let windowActor = this._window.get_compositor_private();
let surfaceActor = windowActor.get_first_child();
surfaceActor.remove_effect_by_name("gnome-shell-frozen-window");
},
_onWait: function () {
this.response(Meta.CloseDialogResponse.WAIT);
},
_onClose: function () {
this.response(Meta.CloseDialogResponse.FORCE_CLOSE);
},
vfunc_show: function () {
if (this._dialog != null)
return;
this._addWindowEffect();
this._initDialog();
this._dialog.scale_y = 0;
this._dialog.set_pivot_point(0.5, 0.5);
Tweener.addTween(this._dialog,
{ scale_y: 1,
transition: 'linear',
time: DIALOG_TRANSITION_TIME,
onComplete: Lang.bind(this, function () {
Main.layoutManager.trackChrome(this._dialog, { affectsInputRegion: true });
})
});
},
vfunc_hide: function () {
if (this._dialog == null)
return;
let dialog = this._dialog;
this._dialog = null;
this._removeWindowEffect();
Tweener.addTween(dialog,
{ scale_y: 0,
transition: 'linear',
time: DIALOG_TRANSITION_TIME,
onComplete: Lang.bind(this, function () {
dialog.destroy();
})
});
},
vfunc_focus: function () {
if (this._dialog)
this._dialog.grab_key_focus();
}
});

View File

@ -20,6 +20,7 @@ const Tweener = imports.ui.tweener;
const WindowMenu = imports.ui.windowMenu;
const PadOsd = imports.ui.padOsd;
const EdgeDragAction = imports.ui.edgeDragAction;
const CloseDialog = imports.ui.closeDialog;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2;
@ -710,6 +711,7 @@ const WindowManager = new Lang.Class({
this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow));
this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding));
this._shellwm.connect('confirm-display-change', Lang.bind(this, this._confirmDisplayChange));
this._shellwm.connect('create-close-dialog', Lang.bind(this, this._createCloseDialog));
global.screen.connect('restacked', Lang.bind(this, this._syncStacking));
this._workspaceSwitcherPopup = null;
@ -1971,6 +1973,10 @@ const WindowManager = new Lang.Class({
dialog.open();
},
_createCloseDialog: function (shellwm, window) {
return new CloseDialog.CloseDialog(window);
},
_showResizePopup: function(display, show, rect, displayW, displayH) {
if (show) {
if (!this._resizePopup)

View File

@ -17,6 +17,7 @@ js/ui/appFavorites.js
js/ui/audioDeviceSelection.js
js/ui/backgroundMenu.js
js/ui/calendar.js
js/ui/closeDialog.js
js/ui/components/automountManager.js
js/ui/components/autorunManager.js
js/ui/components/keyring.js

View File

@ -88,6 +88,8 @@ static void gnome_shell_plugin_confirm_display_change (MetaPlugin *plugin);
static const MetaPluginInfo *gnome_shell_plugin_plugin_info (MetaPlugin *plugin);
static MetaCloseDialog * gnome_shell_plugin_create_close_dialog (MetaPlugin *plugin,
MetaWindow *window);
#define GNOME_TYPE_SHELL_PLUGIN (gnome_shell_plugin_get_type ())
#define GNOME_SHELL_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_SHELL_PLUGIN, GnomeShellPlugin))
@ -149,6 +151,8 @@ gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
plugin_class->confirm_display_change = gnome_shell_plugin_confirm_display_change;
plugin_class->plugin_info = gnome_shell_plugin_plugin_info;
plugin_class->create_close_dialog = gnome_shell_plugin_create_close_dialog;
}
static void
@ -425,3 +429,10 @@ MetaPluginInfo *gnome_shell_plugin_plugin_info (MetaPlugin *plugin)
return &info;
}
static MetaCloseDialog *
gnome_shell_plugin_create_close_dialog (MetaPlugin *plugin,
MetaWindow *window)
{
return _shell_wm_create_close_dialog (get_shell_wm (), window);
}

View File

@ -52,6 +52,9 @@ gboolean _shell_wm_filter_keybinding (ShellWM *wm,
void _shell_wm_confirm_display_change (ShellWM *wm);
MetaCloseDialog * _shell_wm_create_close_dialog (ShellWM *wm,
MetaWindow *window);
G_END_DECLS
#endif /* __SHELL_WM_PRIVATE_H__ */

View File

@ -33,6 +33,7 @@ enum
SHOW_WINDOW_MENU,
FILTER_KEYBINDING,
CONFIRM_DISPLAY_CHANGE,
CREATE_CLOSE_DIALOG,
LAST_SIGNAL
};
@ -168,6 +169,22 @@ shell_wm_class_init (ShellWMClass *klass)
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
/**
* ShellWM::create-close-dialog:
* @wm: The WM
* @window: The window to create the dialog for
*
* Creates a close dialog for the given window.
*
* Returns: (transfer full): The close dialog instance.
*/
shell_wm_signals[CREATE_CLOSE_DIALOG] =
g_signal_new ("create-close-dialog",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
META_TYPE_CLOSE_DIALOG, 1, META_TYPE_WINDOW);
}
void
@ -386,6 +403,17 @@ _shell_wm_confirm_display_change (ShellWM *wm)
g_signal_emit (wm, shell_wm_signals[CONFIRM_DISPLAY_CHANGE], 0);
}
MetaCloseDialog *
_shell_wm_create_close_dialog (ShellWM *wm,
MetaWindow *window)
{
MetaCloseDialog *dialog;
g_signal_emit (wm, shell_wm_signals[CREATE_CLOSE_DIALOG], 0, window, &dialog);
return dialog;
}
/**
* shell_wm_new:
* @plugin: the #MetaPlugin