// -*- 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 Pango = imports.gi.Pango; 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 = {}; }, }); const MessageDialogContent = new Lang.Class({ Name: 'MessageDialogContent', Extends: St.BoxLayout, Properties: { 'icon': GObject.ParamSpec.object('icon', 'icon', 'icon', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, Gio.Icon.$gtype), 'title': GObject.ParamSpec.string('title', 'title', 'title', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, null), 'subtitle': GObject.ParamSpec.string('subtitle', 'subtitle', 'subtitle', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, null), 'body': GObject.ParamSpec.string('body', 'body', 'body', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, null) }, _init: function(params) { this._icon = new St.Icon({ y_align: Clutter.ActorAlign.START }); this._title = new St.Label({ style_class: 'headline' }); this._subtitle = new St.Label(); this._body = new St.Label(); ['icon', 'title', 'subtitle', 'body'].forEach(prop => { this[`_${prop}`].add_style_class_name(`message-dialog-${prop}`); }); let textProps = { ellipsize_mode: Pango.EllipsizeMode.NONE, line_wrap: true }; Object.assign(this._subtitle.clutter_text, textProps); Object.assign(this._body.clutter_text, textProps); if (!params.hasOwnProperty('style_class')) params.style_class = 'message-dialog-main-layout'; this.parent(params); this.messageBox = new St.BoxLayout({ style_class: 'message-dialog-content', x_expand: true, vertical: true }); this.messageBox.add_actor(this._title); this.messageBox.add_actor(this._subtitle); this.messageBox.add_actor(this._body); this.add_actor(this._icon); this.add_actor(this.messageBox); }, get icon() { return this._icon.gicon; }, get title() { return this._title.text; }, get subtitle() { return this._subtitle.text; }, get body() { return this._body.text; }, set icon(icon) { Object.assign(this._icon, { gicon: icon, visible: icon != null }); this.notify('icon'); }, set title(title) { this._setLabel(this._title, 'title', title); }, set subtitle(subtitle) { this._setLabel(this._subtitle, 'subtitle', subtitle); }, set body(body) { this._setLabel(this._body, 'body', body); }, _setLabel(label, prop, value) { Object.assign(label, { text: value || '', visible: value != null }); this.notify(prop); }, insertBeforeBody: function(actor) { this.messageBox.insert_child_below(actor, this._body); } });