350cd296fa
These have been long deprecated over in clutter, and (via several vtables) simply forward the call to the equivalent ClutterActor methods Save ourselves the hassle and just use ClutterActor methods directly Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3010>
215 lines
6.7 KiB
JavaScript
215 lines
6.7 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
import GObject from 'gi://GObject';
|
|
import Pango from 'gi://Pango';
|
|
import Shell from 'gi://Shell';
|
|
import St from 'gi://St';
|
|
|
|
import * as BoxPointer from './boxpointer.js';
|
|
import * as Main from './main.js';
|
|
import * as Params from '../misc/params.js';
|
|
import * as PopupMenu from './popupMenu.js';
|
|
|
|
export class EntryMenu 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_child(this.actor);
|
|
this.actor.hide();
|
|
}
|
|
|
|
_makePasswordItem() {
|
|
let item = new PopupMenu.PopupMenuItem('');
|
|
item.connect('activate', this._onPasswordActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._passwordItem = item;
|
|
|
|
this._entry.bind_property('show-peek-icon',
|
|
this._passwordItem, 'visible',
|
|
GObject.BindingFlags.SYNC_CREATE);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* @param {St.Entry} entry
|
|
* @param {*} params
|
|
*/
|
|
export 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;
|
|
});
|
|
}
|
|
|
|
export const 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;
|
|
|
|
let seat = Clutter.get_default_backend().get_default_seat();
|
|
this._keymap = seat.get_keymap();
|
|
|
|
this.connect('notify::mapped', () => {
|
|
if (this.is_mapped()) {
|
|
this._keymap.connectObject(
|
|
'state-changed', () => this._sync(true), this);
|
|
} else {
|
|
this._keymap.disconnectObject(this);
|
|
}
|
|
|
|
this._sync(false);
|
|
});
|
|
}
|
|
|
|
_sync(animate) {
|
|
let capsLockOn = this._keymap.get_caps_lock_state();
|
|
|
|
this.remove_all_transitions();
|
|
|
|
const {naturalHeightSet} = this;
|
|
this.natural_height_set = false;
|
|
let [, height] = this.get_preferred_height(-1);
|
|
this.natural_height_set = naturalHeightSet;
|
|
|
|
this.ease({
|
|
height: capsLockOn ? height : 0,
|
|
opacity: capsLockOn ? 255 : 0,
|
|
duration: animate ? 200 : 0,
|
|
onComplete: () => {
|
|
if (capsLockOn)
|
|
this.height = -1;
|
|
},
|
|
});
|
|
}
|
|
});
|