14d7897a93
Braces are optional for single-line arrow functions, but there's a subtle difference: Without braces, the expression is implicitly used as return value; with braces, the function returns nothing unless there's an explicit return. We currently reflect that in our style by only omitting braces when the function is expected to have a return value, but that's not very obvious, not an important differentiation to make, and not easy to express in an automatic rule. So just omit braces consistently as mandated by gjs' coding style. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/608
173 lines
5.5 KiB
JavaScript
173 lines
5.5 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const { Clutter, 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;
|
|
|
|
this._passwordItem = null;
|
|
|
|
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;
|
|
}
|
|
|
|
get isPassword() {
|
|
return this._passwordItem != null;
|
|
}
|
|
|
|
set isPassword(v) {
|
|
if (v == this.isPassword)
|
|
return;
|
|
|
|
if (v) {
|
|
this._makePasswordItem();
|
|
this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD;
|
|
} else {
|
|
this._passwordItem.destroy();
|
|
this._passwordItem = null;
|
|
this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL;
|
|
}
|
|
}
|
|
|
|
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() {
|
|
let textHidden = (this._entry.clutter_text.password_char);
|
|
if (textHidden)
|
|
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() {
|
|
let visible = !!(this._entry.clutter_text.password_char);
|
|
this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf');
|
|
}
|
|
};
|
|
|
|
function _setMenuAlignment(entry, stageX) {
|
|
let [success, entryX, entryY] = 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, stageY] = 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 [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
|
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, { isPassword: false, actionMode: Shell.ActionMode.POPUP });
|
|
|
|
entry.menu = new EntryMenu(entry);
|
|
entry.menu.isPassword = params.isPassword;
|
|
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;
|
|
});
|
|
}
|