[gnome-shell/T27795: 17/138] Add a dialog to force quit applications



commit 0b85369a19f94ed864ec0c48fc648b7dc09c67cb
Author: Mario Sanchez Prada <mario endlessm com>
Date:   Mon Sep 11 17:30:53 2017 +0100

    Add a dialog to force quit applications
    
    This is an escape hatch for users who are used to Ctrl+Alt+Del allowing
    them to quit applications on their computer.

 data/org.gnome.shell.gschema.xml.in       |   8 ++
 data/theme/gnome-shell-sass/_endless.scss |  39 ++++++++
 js/js-resources.gresource.xml             |   2 +
 js/ui/forceAppExitDialog.js               | 142 ++++++++++++++++++++++++++++++
 js/ui/windowManager.js                    |  16 ++++
 po/POTFILES.in                            |   2 +
 6 files changed, 209 insertions(+)
---
diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in
index 99221a7dd2..2d807ca582 100644
--- a/data/org.gnome.shell.gschema.xml.in
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -186,6 +186,14 @@
       <default>["&lt;Super&gt;9"]</default>
       <summary>Switch to application 9</summary>
     </key>
+
+    <!-- Endless-specific keys beyond this point -->
+
+    <key name="show-force-app-exit-dialog" type="as">
+      <default>["&lt;Ctrl&gt;&lt;Alt&gt;Delete"]</default>
+      <summary>Keybinding that shows the force app exit dialog</summary>
+      <description></description>
+    </key>
   </schema>
 
   <schema id="org.gnome.shell.app-switcher"
diff --git a/data/theme/gnome-shell-sass/_endless.scss b/data/theme/gnome-shell-sass/_endless.scss
index 996a831ce0..fdab2e0d03 100644
--- a/data/theme/gnome-shell-sass/_endless.scss
+++ b/data/theme/gnome-shell-sass/_endless.scss
@@ -16,3 +16,42 @@
 #lockDialogGroup {
   background: #12282e url(resource:///org/gnome/shell/theme/noise-texture.png);
 }
+
+// Force Apps dialog
+
+.force-app-exit-dialog {
+    max-height: 500px;
+    min-height: 450px;
+    min-width: 470px;
+    border: 1px solid rgba(238, 238, 236, 0.2);
+
+    .force-app-exit-dialog-content {
+        spacing: 20px;
+        padding: 20px 17px;
+
+        .force-app-exit-dialog-header {
+            font-weight: bold;
+        }
+
+        .force-app-exit-dialog-subtitle {
+            color: #ccc;
+            font-size: 12pt;
+            max-width: 370px;
+        }
+
+        .force-app-exit-dialog-scroll-view {
+            border: 2px solid #666;
+            border-radius: 6px;
+            height: 288px;
+
+            .force-app-exit-dialog-item {
+                border: solid #242424;
+                padding: 16px;
+                spacing: 16px;
+                border-bottom-width: 1px;
+                &:hover { background-color: rgba(255,255,255,0.1); }
+                &:selected { background-color: #333; }
+            }
+        }
+    }
+}
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 64205e05f0..6616b3dae0 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -139,6 +139,8 @@
     <file>ui/status/thunderbolt.js</file>
 
     <!-- Endless-specific files beyond this point -->
+
+    <file>ui/forceAppExitDialog.js</file>
     <file>ui/monitor.js</file>
   </gresource>
 </gresources>
diff --git a/js/ui/forceAppExitDialog.js b/js/ui/forceAppExitDialog.js
new file mode 100644
index 0000000000..b93bd84c65
--- /dev/null
+++ b/js/ui/forceAppExitDialog.js
@@ -0,0 +1,142 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const { Clutter, GObject, Gtk, Shell, St } = imports.gi;
+
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+
+const GNOME_SYSTEM_MONITOR_DESKTOP_ID = 'gnome-system-monitor.desktop';
+const ICON_SIZE = 32;
+
+const ForceAppExitDialogItem = GObject.registerClass({
+    Signals: { 'selected': {} },
+}, class ForceAppExitDialogItem extends St.BoxLayout {
+    _init(app) {
+        super._init({
+            style_class: 'force-app-exit-dialog-item',
+            can_focus: true,
+            reactive: true,
+            track_hover: true,
+        });
+        this.app = app;
+
+        this.connect('key-focus-in', () => this.emit('selected'));
+        let action = new Clutter.ClickAction();
+        action.connect('clicked', this.grab_key_focus.bind(this));
+        this.add_action(action);
+
+        this._icon = this.app.create_icon_texture(ICON_SIZE);
+        this.add(this._icon);
+
+        this._label = new St.Label({
+            text: this.app.get_name(),
+            y_expand: true,
+            y_align: Clutter.ActorAlign.CENTER,
+        });
+        this.label_actor = this._label;
+        this.add(this._label);
+    }
+});
+
+var ForceAppExitDialog = class extends ModalDialog.ModalDialog {
+    constructor() {
+        super({ styleClass: 'force-app-exit-dialog' });
+
+        let title = new St.Label({
+            style_class: 'force-app-exit-dialog-header',
+            text: _("Quit applications"),
+        });
+
+        this.contentLayout.style_class = 'force-app-exit-dialog-content';
+        this.contentLayout.add(title);
+
+        let subtitle = new St.Label({
+            style_class: 'force-app-exit-dialog-subtitle',
+            text: _("If an application doesn't respond for a while, select its name and click Quit 
Application."),
+        });
+        subtitle.clutter_text.line_wrap = true;
+        this.contentLayout.add(subtitle, {
+            x_fill: false,
+            x_align: St.Align.START,
+        });
+
+        this._itemBox = new St.BoxLayout({ vertical: true });
+        this._scrollView = new St.ScrollView({
+            style_class: 'force-app-exit-dialog-scroll-view',
+            hscrollbar_policy: Gtk.PolicyType.NEVER,
+            vscrollbar_policy: Gtk.PolicyType.AUTOMATIC,
+            overlay_scrollbars: true,
+            x_expand: true,
+            y_expand: true,
+        });
+        this._scrollView.add_actor(this._itemBox);
+
+        this.contentLayout.add(this._scrollView, { expand: true });
+
+        this._cancelButton = this.addButton({
+            action: this.close.bind(this),
+            label: _("Cancel"),
+            key: Clutter.Escape,
+        });
+
+        let appSystem = Shell.AppSystem.get_default();
+        if (appSystem.lookup_app(GNOME_SYSTEM_MONITOR_DESKTOP_ID))
+            this.addButton({
+                action: this._launchSystemMonitor.bind(this),
+                label: _("System Monitor")
+            }, {
+                x_align: St.Align.END,
+            });
+
+        this._quitButton = this.addButton({
+            action: this._quitApp.bind(this),
+            label: _("Quit Application"),
+            key: Clutter.Return,
+        }, {
+            expand: true,
+            x_fill: false,
+            x_align: St.Align.END,
+        });
+
+        appSystem.get_running().forEach(app => {
+            let item = new ForceAppExitDialogItem(app);
+            item.connect('selected', this._selectApp.bind(this));
+            this._itemBox.add_child(item);
+        });
+
+        this._selectedAppItem = null;
+        this._updateSensitivity();
+    }
+
+    _updateSensitivity() {
+        let quitSensitive = this._selectedAppItem != null;
+        this._quitButton.reactive = quitSensitive;
+        this._quitButton.can_focus = quitSensitive;
+    }
+
+    _launchSystemMonitor() {
+        let appSystem = Shell.AppSystem.get_default();
+        let systemMonitor = appSystem.lookup_app(GNOME_SYSTEM_MONITOR_DESKTOP_ID);
+        systemMonitor.activate();
+
+        this.close();
+        Main.overview.hide();
+    }
+
+    _quitApp() {
+        let app = this._selectedAppItem.app;
+        app.request_quit();
+        this.close();
+    }
+
+    _selectApp(appItem) {
+        if (this._selectedAppItem)
+            this._selectedAppItem.remove_style_pseudo_class('selected');
+
+        this._selectedAppItem = appItem;
+        this._updateSensitivity();
+
+        if (this._selectedAppItem)
+            this._selectedAppItem.add_style_pseudo_class('selected');
+    }
+};
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index 11837c46a7..77e24d8336 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -7,6 +7,7 @@ const Signals = imports.signals;
 const AltTab = imports.ui.altTab;
 const AppFavorites = imports.ui.appFavorites;
 const Dialog = imports.ui.dialog;
+const ForceAppExitDialog = imports.ui.forceAppExitDialog;
 const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
 const InhibitShortcutsDialog = imports.ui.inhibitShortcutsDialog;
 const Main = imports.ui.main;
@@ -932,6 +933,13 @@ var WindowManager = class {
                                         Shell.ActionMode.OVERVIEW,
                                         this._startSwitcher.bind(this));
 
+        this.addKeybinding('show-force-app-exit-dialog',
+                           new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
+                           Meta.KeyBindingFlags.NONE,
+                           Shell.ActionMode.NORMAL |
+                           Shell.ActionMode.OVERVIEW,
+                           this._showForceAppExitDialog.bind(this));
+
         this.addKeybinding('open-application-menu',
                            new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
                            Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
@@ -2081,6 +2089,14 @@ var WindowManager = class {
             app.activate();
     }
 
+    _showForceAppExitDialog() {
+        if (!Main.sessionMode.hasOverview)
+            return;
+
+        let dialog = new ForceAppExitDialog.ForceAppExitDialog();
+        dialog.open();
+    }
+
     _toggleAppMenu() {
         Main.panel.toggleAppMenu();
     }
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 33be57b529..050ca10a72 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -92,3 +92,5 @@ src/shell-polkit-authentication-agent.c
 src/shell-util.c
 # Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get 
it.
 subprojects/gvc/gvc-mixer-control.c
+# Endless-specific files beyond this point
+js/ui/forceAppExitDialog.js


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]