diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss index a382ce5..e79f093 100644 --- a/data/theme/gnome-shell-sass/_common.scss +++ b/data/theme/gnome-shell-sass/_common.scss @@ -94,6 +94,14 @@ StEntry { } } +.caps-lock-warning-label { + padding-bottom: 8px; + padding-left: 6.2em; + @include fontsize($font-size - 1); + color: $warning_color; +} + + /* Scrollbars */ diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js index 71069e9..3ce9fd0 100644 --- a/js/gdm/authPrompt.js +++ b/js/gdm/authPrompt.js @@ -113,6 +113,9 @@ var AuthPrompt = class { this._entry.grab_key_focus(); + this._capsLockWarningLabel = new ShellEntry.CapsLockWarning(); + this.actor.add_child(this._capsLockWarningLabel); + this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', scale_x: 0 }); @@ -432,6 +435,7 @@ var AuthPrompt = class { setPasswordChar(passwordChar) { this._entry.clutter_text.set_password_char(passwordChar); this._entry.menu.isPassword = passwordChar != ''; + this._capsLockWarningLabel.visible = passwordChar !== ''; } setQuestion(question) { diff --git a/js/ui/components/keyring.js b/js/ui/components/keyring.js index 0d9f1e4..3512fb6 100644 --- a/js/ui/components/keyring.js +++ b/js/ui/components/keyring.js @@ -128,6 +128,12 @@ var KeyringDialog = class extends ModalDialog.ModalDialog { this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null); this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null); + if (this._passwordEntry || this._confirmEntry) { + this._capsLockWarningLabel = new ShellEntry.CapsLockWarning(); + layout.attach(this._capsLockWarningLabel, 1, row, 1, 1); + row++; + } + if (this.prompt.choice_visible) { let choice = new CheckBox.CheckBox(); this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE); diff --git a/js/ui/components/networkAgent.js b/js/ui/components/networkAgent.js index f871c73..32d40fb 100644 --- a/js/ui/components/networkAgent.js +++ b/js/ui/components/networkAgent.js @@ -95,6 +95,14 @@ var NetworkSecretDialog = class extends ModalDialog.ModalDialog { secret.entry.clutter_text.set_password_char('\u25cf'); } + if (this._content.secrets.some(s => s.password)) { + this._capsLockWarningLabel = new ShellEntry.CapsLockWarning(); + if (rtl) + layout.attach(this._capsLockWarningLabel, 0, pos, 1, 1); + else + layout.attach(this._capsLockWarningLabel, 1, pos, 1, 1); + } + contentBox.messageBox.add(secretTable); if (flags & NM.SecretAgentGetSecretsFlags.WPS_PBC_ACTIVE) { diff --git a/js/ui/components/polkitAgent.js b/js/ui/components/polkitAgent.js index 21feb40..734a217 100644 --- a/js/ui/components/polkitAgent.js +++ b/js/ui/components/polkitAgent.js @@ -108,6 +108,8 @@ var AuthenticationDialog = class extends ModalDialog.ModalDialog { this.setInitialKeyFocus(this._passwordEntry); this._passwordBox.hide(); + this._capsLockWarningLabel = new ShellEntry.CapsLockWarning({ style_class: 'prompt-dialog-caps-lock-warning' }); + content.messageBox.add(this._capsLockWarningLabel); this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' }); this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; diff --git a/js/ui/overview.js b/js/ui/overview.js index dc6ad18..5bad4cb 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -80,6 +80,7 @@ var Overview = class { constructor() { this._overviewCreated = false; this._initCalled = false; + this._visible = false; Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this._sessionUpdated(); diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js index 79f1aad..4a30b22 100644 --- a/js/ui/shellEntry.js +++ b/js/ui/shellEntry.js @@ -1,11 +1,12 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -const { Clutter, Shell, St } = imports.gi; +const { Clutter, GObject, Pango, Shell, St } = imports.gi; const BoxPointer = imports.ui.boxpointer; const Main = imports.ui.main; const Params = imports.misc.params; const PopupMenu = imports.ui.popupMenu; +const Tweener = imports.ui.tweener; var EntryMenu = class extends PopupMenu.PopupMenu { constructor(entry) { @@ -170,3 +171,58 @@ function addContextMenu(entry, params) { entry._menuManager = null; }); } + +var CapsLockWarning = GObject.registerClass( +class CapsLockWarning extends St.Label { + _init(params) { + let defaultParams = { style_class: 'caps-lock-warning-label' }; + super._init(Object.assign(defaultParams, params)); + + this.text = _('Caps lock is on.'); + + this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + this.clutter_text.line_wrap = true; + + this._keymap = Clutter.get_default_backend().get_keymap(); + this._stateChangedId = 0; + + this.connect('notify::mapped', () => { + if (this.is_mapped()) { + this._stateChangedId = this._keymap.connect('state-changed', + () => this._sync(true)); + } else { + this._keymap.disconnect(this._stateChangedId); + this._stateChangedId = 0; + } + + this._sync(false); + }); + + this.connect('destroy', () => { + if (this._stateChangedId) + this._keymap.disconnect(this._stateChangedId); + }); + } + + _sync(animate) { + let capsLockOn = this._keymap.get_caps_lock_state(); + + Tweener.removeTweens(this); + + const naturalHeightSet = this.natural_height_set; + this.natural_height_set = false; + let [, height] = this.get_preferred_height(-1); + this.natural_height_set = naturalHeightSet; + + Tweener.addTween(this, { + height: capsLockOn ? height : 0, + opacity: capsLockOn ? 255 : 0, + time: animate ? 0.2 : 0, + transition: 'easeOutQuad', + onComplete: () => { + if (capsLockOn) + this.height = -1; + }, + }); + } +}); diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js index f976f40..3a2377d 100644 --- a/js/ui/shellMountOperation.js +++ b/js/ui/shellMountOperation.js @@ -305,6 +305,9 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog { this._passwordBox.add(this._passwordEntry, {expand: true }); this.setInitialKeyFocus(this._passwordEntry); + this._capsLockWarningLabel = new ShellEntry.CapsLockWarning(); + content.messageBox.add(this._capsLockWarningLabel); + this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label', text: _("Sorry, that didn’t work. Please try again.") }); this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;