0b3fec22d7
Since the caps-lock warning adds a lot of spacing to dialogs and the lock screen, hide it by default and only show it when necessary. To make the transition smooth instead of just showing the label, animate it in using the height and opacity. Also add some bottom padding to the label so we can show or hide that padding, too. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/952
207 lines
6.5 KiB
JavaScript
207 lines
6.5 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/* exported addContextMenu CapsLockWarning */
|
|
|
|
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;
|
|
|
|
var EntryMenu = class extends PopupMenu.PopupMenu {
|
|
constructor(entry) {
|
|
super(entry, 0, St.Side.TOP);
|
|
|
|
this._entry = entry;
|
|
this._clipboard = St.Clipboard.get_default();
|
|
|
|
// Populate menu
|
|
let item;
|
|
item = new PopupMenu.PopupMenuItem(_("Copy"));
|
|
item.connect('activate', this._onCopyActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._copyItem = item;
|
|
|
|
item = new PopupMenu.PopupMenuItem(_("Paste"));
|
|
item.connect('activate', this._onPasteActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._pasteItem = item;
|
|
|
|
if (entry instanceof St.PasswordEntry)
|
|
this._makePasswordItem();
|
|
|
|
Main.uiGroup.add_actor(this.actor);
|
|
this.actor.hide();
|
|
}
|
|
|
|
_makePasswordItem() {
|
|
let item = new PopupMenu.PopupMenuItem('');
|
|
item.connect('activate', this._onPasswordActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._passwordItem = item;
|
|
}
|
|
|
|
open(animate) {
|
|
this._updatePasteItem();
|
|
this._updateCopyItem();
|
|
if (this._passwordItem)
|
|
this._updatePasswordItem();
|
|
|
|
super.open(animate);
|
|
this._entry.add_style_pseudo_class('focus');
|
|
|
|
let direction = St.DirectionType.TAB_FORWARD;
|
|
if (!this.actor.navigate_focus(null, direction, false))
|
|
this.actor.grab_key_focus();
|
|
}
|
|
|
|
_updateCopyItem() {
|
|
let selection = this._entry.clutter_text.get_selection();
|
|
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
|
|
selection && selection != '');
|
|
}
|
|
|
|
_updatePasteItem() {
|
|
this._clipboard.get_text(St.ClipboardType.CLIPBOARD,
|
|
(clipboard, text) => {
|
|
this._pasteItem.setSensitive(text && text != '');
|
|
});
|
|
}
|
|
|
|
_updatePasswordItem() {
|
|
if (!this._entry.password_visible)
|
|
this._passwordItem.label.set_text(_("Show Text"));
|
|
else
|
|
this._passwordItem.label.set_text(_("Hide Text"));
|
|
}
|
|
|
|
_onCopyActivated() {
|
|
let selection = this._entry.clutter_text.get_selection();
|
|
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
|
|
}
|
|
|
|
_onPasteActivated() {
|
|
this._clipboard.get_text(St.ClipboardType.CLIPBOARD,
|
|
(clipboard, text) => {
|
|
if (!text)
|
|
return;
|
|
this._entry.clutter_text.delete_selection();
|
|
let pos = this._entry.clutter_text.get_cursor_position();
|
|
this._entry.clutter_text.insert_text(text, pos);
|
|
});
|
|
}
|
|
|
|
_onPasswordActivated() {
|
|
this._entry.password_visible = !this._entry.password_visible;
|
|
}
|
|
};
|
|
|
|
function _setMenuAlignment(entry, stageX) {
|
|
let [success, entryX] = entry.transform_stage_point(stageX, 0);
|
|
if (success)
|
|
entry.menu.setSourceAlignment(entryX / entry.width);
|
|
}
|
|
|
|
function _onButtonPressEvent(actor, event, entry) {
|
|
if (entry.menu.isOpen) {
|
|
entry.menu.close(BoxPointer.PopupAnimation.FULL);
|
|
return Clutter.EVENT_STOP;
|
|
} else if (event.get_button() == 3) {
|
|
let [stageX] = event.get_coords();
|
|
_setMenuAlignment(entry, stageX);
|
|
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
|
return Clutter.EVENT_STOP;
|
|
}
|
|
return Clutter.EVENT_PROPAGATE;
|
|
}
|
|
|
|
function _onPopup(actor, entry) {
|
|
let cursorPosition = entry.clutter_text.get_cursor_position();
|
|
let [success, textX, textY_, lineHeight_] = entry.clutter_text.position_to_coords(cursorPosition);
|
|
if (success)
|
|
entry.menu.setSourceAlignment(textX / entry.width);
|
|
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
|
}
|
|
|
|
function addContextMenu(entry, params) {
|
|
if (entry.menu)
|
|
return;
|
|
|
|
params = Params.parse(params, { actionMode: Shell.ActionMode.POPUP });
|
|
|
|
entry.menu = new EntryMenu(entry);
|
|
entry._menuManager = new PopupMenu.PopupMenuManager(entry,
|
|
{ actionMode: params.actionMode });
|
|
entry._menuManager.addMenu(entry.menu);
|
|
|
|
// Add an event handler to both the entry and its clutter_text; the former
|
|
// so padding is included in the clickable area, the latter because the
|
|
// event processing of ClutterText prevents event-bubbling.
|
|
entry.clutter_text.connect('button-press-event', (actor, event) => {
|
|
_onButtonPressEvent(actor, event, entry);
|
|
});
|
|
entry.connect('button-press-event', (actor, event) => {
|
|
_onButtonPressEvent(actor, event, entry);
|
|
});
|
|
|
|
entry.connect('popup-menu', actor => _onPopup(actor, entry));
|
|
|
|
entry.connect('destroy', () => {
|
|
entry.menu.destroy();
|
|
entry.menu = null;
|
|
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.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();
|
|
|
|
this.remove_all_transitions();
|
|
|
|
this.natural_height_set = false;
|
|
let [, height] = this.get_preferred_height(-1);
|
|
this.natural_height_set = true;
|
|
|
|
this.ease({
|
|
height: capsLockOn ? height : 0,
|
|
opacity: capsLockOn ? 255 : 0,
|
|
duration: animate ? 200 : 0,
|
|
onComplete: () => {
|
|
if (capsLockOn)
|
|
this.height = -1;
|
|
},
|
|
});
|
|
}
|
|
});
|