diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 591875f8c..71e713d2b 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -47,6 +47,7 @@
ui/ctrlAltTab.js
ui/dash.js
ui/dateMenu.js
+ ui/dialog.js
ui/dnd.js
ui/edgeDragAction.js
ui/endSessionDialog.js
diff --git a/js/ui/dialog.js b/js/ui/dialog.js
new file mode 100644
index 000000000..941ef7536
--- /dev/null
+++ b/js/ui/dialog.js
@@ -0,0 +1,131 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+const Lang = imports.lang;
+
+const Dialog = new Lang.Class({
+ Name: 'Dialog',
+ Extends: St.Widget,
+
+ _init: function (parentActor, styleClass) {
+ this.parent({ layout_manager: new Clutter.BinLayout() });
+ this.connect('destroy', Lang.bind(this, this._onDestroy));
+
+ this._pressedKey = null;
+ this._buttonKeys = {};
+ this._createDialog();
+ this.add_child(this._dialog);
+
+ if (styleClass != null)
+ this._dialog.add_style_class_name(styleClass);
+
+ this._parentActor = parentActor;
+ this._eventId = this._parentActor.connect('event', Lang.bind(this, this._modalEventHandler));
+ this._parentActor.add_child(this);
+ },
+
+ _createDialog: function () {
+ this._dialog = new St.BoxLayout({ style_class: 'modal-dialog',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ vertical: true });
+
+ // modal dialogs are fixed width and grow vertically; set the request
+ // mode accordingly so wrapped labels are handled correctly during
+ // size requests.
+ this._dialog.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
+
+ this.contentLayout = new St.BoxLayout({ vertical: true,
+ style_class: "modal-dialog-content-box" });
+ this._dialog.add(this.contentLayout,
+ { expand: true,
+ x_fill: true,
+ y_fill: true,
+ x_align: St.Align.MIDDLE,
+ y_align: St.Align.START });
+
+ this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout({ homogeneous:true }) });
+ this._dialog.add(this.buttonLayout,
+ { x_align: St.Align.MIDDLE,
+ y_align: St.Align.START });
+ },
+
+ _onDestroy: function () {
+ if (this._eventId != 0)
+ this._parentActor.disconnect(this._eventId);
+ this._eventId = 0;
+ },
+
+ _modalEventHandler: function (actor, event) {
+ if (event.type() == Clutter.EventType.KEY_PRESS) {
+ this._pressedKey = event.get_key_symbol();
+ } else if (event.type() == Clutter.EventType.KEY_RELEASE) {
+ let pressedKey = this._pressedKey;
+ this._pressedKey = null;
+
+ let symbol = event.get_key_symbol();
+ if (symbol != pressedKey)
+ return Clutter.EVENT_PROPAGATE;
+
+ let buttonInfo = this._buttonKeys[symbol];
+ if (!buttonInfo)
+ return Clutter.EVENT_PROPAGATE;
+
+ let { button, action } = buttonInfo;
+
+ if (action && button.reactive) {
+ action();
+ return Clutter.EVENT_STOP;
+ }
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ },
+
+ addContent: function (actor) {
+ this.contentLayout.add (actor, { expand: true });
+ },
+
+ addButton: function (buttonInfo) {
+ let { label, action, key } = buttonInfo;
+ let isDefault = buttonInfo['default'];
+ let keys;
+
+ if (key)
+ keys = [key];
+ else if (isDefault)
+ keys = [Clutter.KEY_Return, Clutter.KEY_KP_Enter, Clutter.KEY_ISO_Enter];
+ else
+ keys = [];
+
+ let button = new St.Button({ style_class: 'modal-dialog-linked-button',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ reactive: true,
+ can_focus: true,
+ x_expand: true,
+ y_expand: true,
+ label: label });
+ button.connect('clicked', action);
+
+ buttonInfo['button'] = button;
+
+ if (isDefault)
+ button.add_style_pseudo_class('default');
+
+ if (!this._initialKeyFocusDestroyId)
+ this._initialKeyFocus = button;
+
+ for (let i in keys)
+ this._buttonKeys[keys[i]] = buttonInfo;
+
+ this.buttonLayout.add_actor(button);
+
+ return button;
+ },
+
+ clearButtons: function () {
+ this.buttonLayout.destroy_all_children();
+ this._buttonKeys = {};
+ },
+});
diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js
index ac773056d..ed1b92f9d 100644
--- a/js/ui/modalDialog.js
+++ b/js/ui/modalDialog.js
@@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
const Params = imports.misc.params;
+const Dialog = imports.ui.dialog;
const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
@@ -61,11 +62,6 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
- this._pressedKey = null;
- this._buttonKeys = {};
- this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
- this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
-
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
x_fill: true, y_fill: true });
@@ -73,17 +69,9 @@ const ModalDialog = new Lang.Class({
this._backgroundBin.add_constraint(this._monitorConstraint);
this._group.add_actor(this._backgroundBin);
- this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
- x_align: Clutter.ActorAlign.CENTER,
- y_align: Clutter.ActorAlign.CENTER,
- vertical: true });
- // modal dialogs are fixed width and grow vertically; set the request
- // mode accordingly so wrapped labels are handled correctly during
- // size requests.
- this.dialogLayout.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
-
- if (params.styleClass != null)
- this.dialogLayout.add_style_class_name(params.styleClass);
+ this.dialogLayout = new Dialog.Dialog(this.backgroundStack, params.styleClass);
+ this.contentLayout = this.dialogLayout.contentLayout;
+ this.buttonLayout = this.dialogLayout.buttonLayout;
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
@@ -94,22 +82,6 @@ const ModalDialog = new Lang.Class({
this._eventBlocker = new Clutter.Actor({ reactive: true });
this.backgroundStack.add_actor(this._eventBlocker);
}
- this.backgroundStack.add_actor(this.dialogLayout);
-
-
- this.contentLayout = new St.BoxLayout({ vertical: true,
- style_class: "modal-dialog-content-box" });
- this.dialogLayout.add(this.contentLayout,
- { expand: true,
- x_fill: true,
- y_fill: true,
- x_align: St.Align.MIDDLE,
- y_align: St.Align.START });
-
- this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout ({ homogeneous:true }) });
- this.dialogLayout.add(this.buttonLayout,
- { x_align: St.Align.MIDDLE,
- y_align: St.Align.END });
global.focus_manager.add_group(this.dialogLayout);
this._initialKeyFocus = this.dialogLayout;
@@ -122,8 +94,7 @@ const ModalDialog = new Lang.Class({
},
clearButtons: function() {
- this.buttonLayout.destroy_all_children();
- this._buttonKeys = {};
+ this.dialogLayout.clearButtons();
},
setButtons: function(buttons) {
@@ -146,72 +117,8 @@ const ModalDialog = new Lang.Class({
}
},
- addButton: function(buttonInfo) {
- let label = buttonInfo['label']
- let action = buttonInfo['action'];
- let key = buttonInfo['key'];
- let isDefault = buttonInfo['default'];
-
- let keys;
-
- if (key)
- keys = [key];
- else if (isDefault)
- keys = [Clutter.KEY_Return, Clutter.KEY_KP_Enter, Clutter.KEY_ISO_Enter];
- else
- keys = [];
-
- let button = new St.Button({ style_class: 'modal-dialog-linked-button',
- button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
- reactive: true,
- can_focus: true,
- x_expand: true,
- y_expand: true,
- label: label });
- button.connect('clicked', action);
-
- buttonInfo['button'] = button;
-
- if (isDefault)
- button.add_style_pseudo_class('default');
-
- if (!this._initialKeyFocusDestroyId)
- this._initialKeyFocus = button;
-
- for (let i in keys)
- this._buttonKeys[keys[i]] = buttonInfo;
-
- this.buttonLayout.add_actor(button);
-
- return button;
- },
-
- _onKeyPressEvent: function(object, event) {
- this._pressedKey = event.get_key_symbol();
- return Clutter.EVENT_PROPAGATE;
- },
-
- _onKeyReleaseEvent: function(object, event) {
- let pressedKey = this._pressedKey;
- this._pressedKey = null;
-
- let symbol = event.get_key_symbol();
- if (symbol != pressedKey)
- return Clutter.EVENT_PROPAGATE;
-
- let buttonInfo = this._buttonKeys[symbol];
- if (!buttonInfo)
- return Clutter.EVENT_PROPAGATE;
-
- let button = buttonInfo['button'];
- let action = buttonInfo['action'];
-
- if (action && button.reactive) {
- action();
- return Clutter.EVENT_STOP;
- }
-
- return Clutter.EVENT_PROPAGATE;
+ addButton: function (buttonInfo) {
+ return this.dialogLayout.addButton(buttonInfo);
},
_onGroupDestroy: function() {