[gnome-shell] modalDialog: Add modal dialog base class



commit 47b8d16067180cb5c7ad86efb99819ad466312a4
Author: Ray Strode <rstrode redhat com>
Date:   Wed Oct 20 20:46:38 2010 -0400

    modalDialog: Add modal dialog base class
    
    This is a base class to make it easier to
    gain a consistent look for system modal dialogs.
    
    It handles creating a darkened backdrop behind the dialog, setting
    up buttons in the dialog, keynav, etc.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=637187

 data/theme/gnome-shell.css |   48 +++++++++
 js/Makefile.am             |    1 +
 js/ui/modalDialog.js       |  233 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 282 insertions(+), 0 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 344692a..1240c12 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1080,6 +1080,54 @@ StTooltip StLabel {
     border-radius: 8px;
 }
 
+/* Modal Dialogs */
+.modal-dialog {
+    font: 12pt sans-serif;
+    border-radius: 24px;
+    background-color: rgba(0.0, 0.0, 0.0, 0.9);
+    border: 2px solid #868686;
+    color: #ffffff;
+
+    padding-right: 42px;
+    padding-left: 42px;
+    padding-bottom: 30px;
+    padding-top: 30px;
+}
+
+.modal-dialog-button {
+    border: 1px solid #8b8b8b;
+    border-radius: 18px;
+    font: 14px sans-serif;
+
+    margin-left: 10px;
+    margin-right: 10px;
+
+    padding-left: 32px;
+    padding-right: 32px;
+    padding-top: 8px;
+    padding-bottom: 8px;
+
+    background-gradient-direction: vertical;
+    background-gradient-start: #29323b;
+    background-gradient-end: #121a24;
+}
+
+.modal-dialog-button:active,
+.modal-dialog-button:pressed {
+    border-color: #a5a5a5;
+    background-gradient-start: #121a24;
+    background-gradient-end: #29323b;
+}
+
+.modal-dialog-button:focus {
+    border: 2px solid #a5a5a5;
+
+    padding-left: 31px;
+    padding-right: 31px;
+    padding-top: 7px;
+    padding-bottom: 7px;
+}
+
 /* Run Dialog */
 .run-dialog-label {
     font: 12px sans-serif;
diff --git a/js/Makefile.am b/js/Makefile.am
index 3d87dec..3dce4f2 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -32,6 +32,7 @@ nobase_dist_js_DATA = 	\
 	ui/magnifierDBus.js	\
 	ui/main.js		\
 	ui/messageTray.js	\
+	ui/modalDialog.js	\
 	ui/notificationDaemon.js \
 	ui/overview.js		\
 	ui/panel.js		\
diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js
new file mode 100644
index 0000000..7b3f0d3
--- /dev/null
+++ b/js/ui/modalDialog.js
@@ -0,0 +1,233 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Pango = imports.gi.Pango;
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
+const Params = imports.misc.params;
+
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+
+const OPEN_AND_CLOSE_TIME = 0.1;
+const FADE_OUT_DIALOG_TIME = 1.0;
+
+const State = {
+    OPENED: 0,
+    CLOSED: 1,
+    OPENING: 2,
+    CLOSING: 3,
+    FADED_OUT: 4
+};
+
+function ModalDialog() {
+    this._init();
+}
+
+ModalDialog.prototype = {
+    _init: function(params) {
+        params = Params.parse(params, { styleClass: null });
+
+        this.state = State.CLOSED;
+
+        this._group = new St.Group({ visible: false,
+                                     x: 0,
+                                     y: 0 });
+        Main.uiGroup.add_actor(this._group);
+        global.focus_manager.add_group(this._group);
+        this._initialKeyFocus = this._group;
+
+        this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
+
+        this._actionKeys = {};
+        this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
+
+        this._lightbox = new Lightbox.Lightbox(this._group,
+                                               { inhibitEvents: true });
+
+        this._backgroundBin = new St.Bin();
+
+        this._group.add_actor(this._backgroundBin);
+        this._lightbox.highlight(this._backgroundBin);
+
+        this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
+                                                vertical:    true });
+        if (params.styleClass != null) {
+            this._dialogLayout.add_style_class_name(params.styleClass);
+        }
+        this._backgroundBin.child = this._dialogLayout;
+
+        this.contentLayout = new St.BoxLayout({ vertical: true });
+        this._dialogLayout.add(this.contentLayout,
+                               { x_fill:  true,
+                                 y_fill:  true,
+                                 x_align: St.Align.MIDDLE,
+                                 y_align: St.Align.START });
+
+        this._buttonLayout = new St.BoxLayout({ opacity:  220,
+                                                vertical: false });
+        this._dialogLayout.add(this._buttonLayout,
+                               { expand:  true,
+                                 x_align: St.Align.MIDDLE,
+                                 y_align: St.Align.END });
+    },
+
+    setButtons: function(buttons) {
+        this._buttonLayout.remove_all();
+        let i = 0;
+        for (let index in buttons) {
+            let buttonInfo = buttons[index];
+            let label = buttonInfo['label'];
+            let action = buttonInfo['action'];
+            let key = buttonInfo['key'];
+
+            let button = new St.Button({ style_class: 'modal-dialog-button',
+                                         reactive:    true,
+                                         can_focus:   true,
+                                         label:       label });
+
+            let x_alignment;
+            if (buttons.length == 1)
+                x_alignment = St.Align.END;
+            else if (i == 0)
+                x_alignment = St.Align.START;
+            else if (i == buttons.length - 1)
+                x_alignment = St.Align.END;
+            else
+                x_alignment = St.Align.MIDDLE;
+
+            this._initialKeyFocus = button;
+            this._buttonLayout.add(button,
+                                   { expand: true,
+                                     x_fill: false,
+                                     y_fill: false,
+                                     x_align: x_alignment,
+                                     y_align: St.Align.MIDDLE });
+
+            button.connect('clicked', action);
+
+            if (key)
+                this._actionKeys[key] = action;
+            i++;
+        }
+    },
+
+    _onKeyPressEvent: function(object, keyPressEvent) {
+        let symbol = keyPressEvent.get_key_symbol();
+        let action = this._actionKeys[symbol];
+
+        if (action)
+            action();
+    },
+
+    _onGroupDestroy: function() {
+        this.emit('destroy');
+    },
+
+    _fadeOpen: function() {
+        let monitor = global.get_focus_monitor();
+
+        this._backgroundBin.set_position(monitor.x, monitor.y);
+        this._backgroundBin.set_size(monitor.width, monitor.height);
+
+        this.state = State.OPENING;
+
+        this._dialogLayout.opacity = 255;
+        this._lightbox.show();
+        this._group.opacity = 0;
+        this._group.show();
+        Tweener.addTween(this._group,
+                         { opacity: 255,
+                           time: OPEN_AND_CLOSE_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   this._initialKeyFocus.grab_key_focus();
+                                   this.state = State.OPENED;
+                                   this.emit('opened');
+                               }),
+                         });
+    },
+
+    open: function(timestamp) {
+        if (this.state == State.OPENED || this.state == State.OPENING)
+            return true;
+
+        if (!Main.pushModal(this._group, timestamp))
+            return false;
+
+        global.stage.set_key_focus(this._group);
+
+        this._fadeOpen();
+        return true;
+    },
+
+    close: function(timestamp) {
+        if (this.state == State.CLOSED || this.state == State.CLOSING)
+            return;
+
+        let needsPopModal;
+
+        if (this.state == State.OPENED || this.state == State.OPENING)
+            needsPopModal = true;
+        else
+            needsPopModal = false;
+
+        this.state = State.CLOSING;
+
+        Tweener.addTween(this._group,
+                         { opacity: 0,
+                           time: OPEN_AND_CLOSE_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   this.state = State.CLOSED;
+                                   this._group.hide();
+
+                                   if (needsPopModal)
+                                       Main.popModal(this._group, timestamp);
+                               })
+                         });
+    },
+
+    // This method is like close, but fades the dialog out much slower,
+    // and leaves the lightbox in place. Once in the faded out state,
+    // the dialog can be brought back by an open call, or the lightbox
+    // can be dismissed by a close call.
+    //
+    // The main point of this method is to give some indication to the user
+    // that the dialog reponse has been acknowledged but will take a few
+    // moments before being processed.
+    // e.g., if a user clicked "Log Out" then the dialog should go away
+    // imediately, but the lightbox should remain until the logout is
+    // complete.
+    _fadeOutDialog: function(timestamp) {
+        if (this.state == State.CLOSED || this.state == State.CLOSING)
+            return;
+
+        if (this.state == State.FADED_OUT)
+            return;
+
+        Tweener.addTween(this._dialogLayout,
+                         { opacity: 0,
+                           time:    FADE_OUT_DIALOG_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   this.state = State.FADED_OUT;
+                                   Main.popModal(this._group, timestamp);
+                               })
+                         });
+    }
+};
+Signals.addSignalMethods(ModalDialog.prototype);



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