2016-06-22 17:57:12 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import Atk from 'gi://Atk';
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
|
|
import GDesktopEnums from 'gi://GDesktopEnums';
|
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import GObject from 'gi://GObject';
|
|
|
|
import Meta from 'gi://Meta';
|
|
|
|
import Pango from 'gi://Pango';
|
|
|
|
import Rsvg from 'gi://Rsvg';
|
|
|
|
import St from 'gi://St';
|
2023-06-08 04:52:46 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as Signals from '../misc/signals.js';
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as Main from './main.js';
|
|
|
|
import * as PopupMenu from './popupMenu.js';
|
|
|
|
import * as Layout from './layout.js';
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import {loadInterfaceXML} from '../misc/fileUtils.js';
|
2018-09-06 00:55:20 +00:00
|
|
|
|
2023-08-06 22:34:20 +00:00
|
|
|
const ACTIVE_COLOR = '#729fcf';
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
const LTR = 0;
|
|
|
|
const RTL = 1;
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const PadChooser = GObject.registerClass({
|
2023-08-06 22:40:20 +00:00
|
|
|
Signals: {'pad-selected': {param_types: [Clutter.InputDevice.$gtype]}},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class PadChooser extends St.Button {
|
|
|
|
_init(device, groupDevices) {
|
|
|
|
super._init({
|
|
|
|
style_class: 'pad-chooser-button',
|
|
|
|
toggle_mode: true,
|
|
|
|
});
|
2017-02-13 14:12:28 +00:00
|
|
|
this.currentDevice = device;
|
|
|
|
this._padChooserMenu = null;
|
|
|
|
|
2019-10-17 21:40:24 +00:00
|
|
|
let arrow = new St.Icon({
|
|
|
|
style_class: 'popup-menu-arrow',
|
|
|
|
icon_name: 'pan-down-symbolic',
|
|
|
|
accessible_role: Atk.Role.ARROW,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_child(arrow);
|
2017-02-13 14:12:28 +00:00
|
|
|
this._ensureMenu(groupDevices);
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
2019-09-10 05:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_clicked() {
|
|
|
|
if (this.get_checked()) {
|
|
|
|
if (this._padChooserMenu != null)
|
|
|
|
this._padChooserMenu.open(true);
|
|
|
|
else
|
|
|
|
this.set_checked(false);
|
|
|
|
} else {
|
|
|
|
this._padChooserMenu.close(true);
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-02-13 14:12:28 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_ensureMenu(devices) {
|
2019-07-16 09:24:13 +00:00
|
|
|
this._padChooserMenu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP);
|
2017-10-31 00:38:18 +00:00
|
|
|
this._padChooserMenu.connect('menu-closed', () => {
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_checked(false);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2017-02-13 14:12:28 +00:00
|
|
|
this._padChooserMenu.actor.hide();
|
|
|
|
Main.uiGroup.add_actor(this._padChooserMenu.actor);
|
|
|
|
|
2022-04-23 17:53:46 +00:00
|
|
|
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
|
|
|
this._menuManager.addMenu(this._padChooserMenu);
|
|
|
|
|
2017-02-13 14:12:28 +00:00
|
|
|
for (let i = 0; i < devices.length; i++) {
|
|
|
|
let device = devices[i];
|
2023-08-07 00:51:19 +00:00
|
|
|
if (device === this.currentDevice)
|
2017-02-13 14:12:28 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
this._padChooserMenu.addAction(device.get_device_name(), () => {
|
|
|
|
this.emit('pad-selected', device);
|
|
|
|
});
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-02-13 14:12:28 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDestroy() {
|
2017-02-13 14:12:28 +00:00
|
|
|
this._padChooserMenu.destroy();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-02-13 14:12:28 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
update(devices) {
|
2017-02-13 14:12:28 +00:00
|
|
|
if (this._padChooserMenu)
|
|
|
|
this._padChooserMenu.actor.destroy();
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_checked(false);
|
2017-02-13 14:12:28 +00:00
|
|
|
this._ensureMenu(devices);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2017-02-13 14:12:28 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const KeybindingEntry = GObject.registerClass({
|
2023-08-06 22:40:20 +00:00
|
|
|
Signals: {'keybinding-edited': {param_types: [GObject.TYPE_STRING]}},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class KeybindingEntry extends St.Entry {
|
|
|
|
_init() {
|
2023-06-08 04:53:07 +00:00
|
|
|
super._init({hint_text: _('New shortcut…'), style: 'width: 10em'});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2019-09-10 05:42:48 +00:00
|
|
|
vfunc_captured_event(event) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (event.type() !== Clutter.EventType.KEY_PRESS)
|
2016-06-22 17:57:12 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
2022-12-06 12:38:48 +00:00
|
|
|
const str = Meta.accelerator_name(
|
|
|
|
event.get_state(), event.get_key_symbol());
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_text(str);
|
2016-06-22 17:57:12 +00:00
|
|
|
this.emit('keybinding-edited', str);
|
|
|
|
return Clutter.EVENT_STOP;
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const ActionComboBox = GObject.registerClass({
|
2023-08-06 22:40:20 +00:00
|
|
|
Signals: {'action-selected': {param_types: [GObject.TYPE_INT]}},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class ActionComboBox extends St.Button {
|
|
|
|
_init() {
|
2023-08-06 22:40:20 +00:00
|
|
|
super._init({style_class: 'button'});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_toggle_mode(true);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
const boxLayout = new Clutter.BoxLayout({
|
|
|
|
orientation: Clutter.Orientation.HORIZONTAL,
|
|
|
|
spacing: 6,
|
|
|
|
});
|
2023-08-06 22:40:20 +00:00
|
|
|
let box = new St.Widget({layout_manager: boxLayout});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_child(box);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._label = new St.Label({style_class: 'combo-box-label'});
|
2019-01-29 01:18:52 +00:00
|
|
|
box.add_child(this._label);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
const arrow = new St.Icon({
|
|
|
|
style_class: 'popup-menu-arrow',
|
|
|
|
icon_name: 'pan-down-symbolic',
|
|
|
|
accessible_role: Atk.Role.ARROW,
|
|
|
|
y_expand: true,
|
|
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
box.add_child(arrow);
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this._editMenu = new PopupMenu.PopupMenu(this, 0, St.Side.TOP);
|
2017-10-31 00:38:18 +00:00
|
|
|
this._editMenu.connect('menu-closed', () => {
|
2019-07-16 09:24:13 +00:00
|
|
|
this.set_checked(false);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
this._editMenu.actor.hide();
|
|
|
|
Main.uiGroup.add_actor(this._editMenu.actor);
|
|
|
|
|
2022-04-23 17:51:11 +00:00
|
|
|
this._editMenuManager = new PopupMenu.PopupMenuManager(this);
|
|
|
|
this._editMenuManager.addMenu(this._editMenu);
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
this._actionLabels = new Map();
|
2022-12-16 21:33:10 +00:00
|
|
|
this._actionLabels.set(GDesktopEnums.PadButtonAction.NONE, _('App defined'));
|
|
|
|
this._actionLabels.set(GDesktopEnums.PadButtonAction.HELP, _('Show on-screen help'));
|
|
|
|
this._actionLabels.set(GDesktopEnums.PadButtonAction.SWITCH_MONITOR, _('Switch monitor'));
|
|
|
|
this._actionLabels.set(GDesktopEnums.PadButtonAction.KEYBINDING, _('Assign keystroke'));
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-07-12 16:37:18 +00:00
|
|
|
this._buttonItems = [];
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
for (let [action, label] of this._actionLabels.entries()) {
|
|
|
|
let selectedAction = action;
|
2017-10-31 00:38:18 +00:00
|
|
|
let item = this._editMenu.addAction(label, () => {
|
|
|
|
this._onActionSelected(selectedAction);
|
|
|
|
});
|
2017-07-12 16:37:18 +00:00
|
|
|
|
|
|
|
/* These actions only apply to pad buttons */
|
2023-08-07 00:51:19 +00:00
|
|
|
if (selectedAction === GDesktopEnums.PadButtonAction.HELP ||
|
|
|
|
selectedAction === GDesktopEnums.PadButtonAction.SWITCH_MONITOR)
|
2017-07-12 16:37:18 +00:00
|
|
|
this._buttonItems.push(item);
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.setAction(GDesktopEnums.PadButtonAction.NONE);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onActionSelected(action) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this.setAction(action);
|
|
|
|
this.popdown();
|
|
|
|
this.emit('action-selected', action);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setAction(action) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._label.set_text(this._actionLabels.get(action));
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
popup() {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._editMenu.open(true);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
popdown() {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._editMenu.close(true);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2019-09-10 05:42:48 +00:00
|
|
|
vfunc_clicked() {
|
2019-07-16 09:24:13 +00:00
|
|
|
if (this.get_checked())
|
2016-06-22 17:57:12 +00:00
|
|
|
this.popup();
|
|
|
|
else
|
|
|
|
this.popdown();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-07-12 16:37:18 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setButtonActionsActive(active) {
|
2019-01-28 00:42:00 +00:00
|
|
|
this._buttonItems.forEach(item => item.setSensitive(active));
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const ActionEditor = GObject.registerClass({
|
2023-08-06 22:40:20 +00:00
|
|
|
Signals: {'done': {}},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class ActionEditor extends St.Widget {
|
|
|
|
_init() {
|
2020-03-29 21:51:13 +00:00
|
|
|
const boxLayout = new Clutter.BoxLayout({
|
|
|
|
orientation: Clutter.Orientation.HORIZONTAL,
|
|
|
|
spacing: 12,
|
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
super._init({layout_manager: boxLayout});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
this._actionComboBox = new ActionComboBox();
|
2017-12-02 00:27:35 +00:00
|
|
|
this._actionComboBox.connect('action-selected', this._onActionSelected.bind(this));
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(this._actionComboBox);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
this._keybindingEdit = new KeybindingEntry();
|
2017-12-02 00:27:35 +00:00
|
|
|
this._keybindingEdit.connect('keybinding-edited', this._onKeybindingEdited.bind(this));
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(this._keybindingEdit);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
this._doneButton = new St.Button({
|
|
|
|
label: _('Done'),
|
|
|
|
style_class: 'button',
|
|
|
|
x_expand: false,
|
|
|
|
});
|
2017-12-02 00:27:35 +00:00
|
|
|
this._doneButton.connect('clicked', this._onEditingDone.bind(this));
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(this._doneButton);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateKeybindingEntryState() {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._currentAction === GDesktopEnums.PadButtonAction.KEYBINDING) {
|
2019-07-16 09:24:13 +00:00
|
|
|
this._keybindingEdit.set_text(this._currentKeybinding);
|
|
|
|
this._keybindingEdit.show();
|
|
|
|
this._keybindingEdit.grab_key_focus();
|
2016-06-22 17:57:12 +00:00
|
|
|
} else {
|
2019-07-16 09:24:13 +00:00
|
|
|
this._keybindingEdit.hide();
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setSettings(settings, action) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._buttonSettings = settings;
|
|
|
|
|
|
|
|
this._currentAction = this._buttonSettings.get_enum('action');
|
|
|
|
this._currentKeybinding = this._buttonSettings.get_string('keybinding');
|
|
|
|
this._actionComboBox.setAction(this._currentAction);
|
|
|
|
this._updateKeybindingEntryState();
|
2017-07-12 16:37:18 +00:00
|
|
|
|
2023-05-16 16:06:40 +00:00
|
|
|
let isButton = action === null;
|
2017-07-12 16:37:18 +00:00
|
|
|
this._actionComboBox.setButtonActionsActive(isButton);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
close() {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._actionComboBox.popdown();
|
2019-07-16 09:24:13 +00:00
|
|
|
this.hide();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onKeybindingEdited(entry, keybinding) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._currentKeybinding = keybinding;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onActionSelected(menu, action) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._currentAction = action;
|
|
|
|
this._updateKeybindingEntryState();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_storeSettings() {
|
2016-06-22 17:57:12 +00:00
|
|
|
if (!this._buttonSettings)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let keybinding = null;
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._currentAction === GDesktopEnums.PadButtonAction.KEYBINDING)
|
2016-06-22 17:57:12 +00:00
|
|
|
keybinding = this._currentKeybinding;
|
|
|
|
|
|
|
|
this._buttonSettings.set_enum('action', this._currentAction);
|
|
|
|
|
|
|
|
if (keybinding)
|
|
|
|
this._buttonSettings.set_string('keybinding', keybinding);
|
|
|
|
else
|
|
|
|
this._buttonSettings.reset('keybinding');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onEditingDone() {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._storeSettings();
|
|
|
|
this.close();
|
|
|
|
this.emit('done');
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
const PadDiagram = GObject.registerClass({
|
2019-01-29 18:15:23 +00:00
|
|
|
Properties: {
|
2023-08-06 23:45:22 +00:00
|
|
|
'left-handed': GObject.ParamSpec.boolean(
|
|
|
|
'left-handed', 'left-handed', 'Left handed',
|
|
|
|
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
|
|
|
|
false),
|
|
|
|
'image': GObject.ParamSpec.string(
|
|
|
|
'image', 'image', 'Image',
|
|
|
|
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
|
|
|
|
null),
|
|
|
|
'editor-actor': GObject.ParamSpec.object(
|
|
|
|
'editor-actor', 'editor-actor', 'Editor actor',
|
|
|
|
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
|
|
|
|
Clutter.Actor.$gtype),
|
2019-01-29 18:15:23 +00:00
|
|
|
},
|
2017-10-31 01:23:39 +00:00
|
|
|
}, class PadDiagram extends St.DrawingArea {
|
2017-10-31 00:03:21 +00:00
|
|
|
_init(params) {
|
2016-06-22 17:57:12 +00:00
|
|
|
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/pad-osd.css');
|
2019-01-31 14:08:00 +00:00
|
|
|
let [success_, css] = file.load_contents(null);
|
2017-07-05 22:05:22 +00:00
|
|
|
this._curEdited = null;
|
2021-08-12 14:38:57 +00:00
|
|
|
this._css = new TextDecoder().decode(css);
|
2016-06-22 17:57:12 +00:00
|
|
|
this._labels = [];
|
|
|
|
this._activeButtons = [];
|
2017-10-31 01:23:39 +00:00
|
|
|
super._init(params);
|
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
get image() {
|
|
|
|
return this._imagePath;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
set image(imagePath) {
|
|
|
|
let originalHandle = Rsvg.Handle.new_from_file(imagePath);
|
|
|
|
let dimensions = originalHandle.get_dimensions();
|
|
|
|
this._imageWidth = dimensions.width;
|
|
|
|
this._imageHeight = dimensions.height;
|
|
|
|
|
|
|
|
this._imagePath = imagePath;
|
|
|
|
this._handle = this._composeStyledDiagram();
|
2020-05-28 12:03:13 +00:00
|
|
|
this._initLabels();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2021-01-30 01:03:07 +00:00
|
|
|
get editorActor() {
|
2016-06-22 17:57:12 +00:00
|
|
|
return this._editorActor;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2021-01-30 01:03:07 +00:00
|
|
|
set editorActor(actor) {
|
2016-06-22 17:57:12 +00:00
|
|
|
actor.hide();
|
|
|
|
this._editorActor = actor;
|
|
|
|
this.add_actor(actor);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-28 12:03:13 +00:00
|
|
|
_initLabels() {
|
|
|
|
let i = 0;
|
2020-05-29 13:43:55 +00:00
|
|
|
for (i = 0; ; i++) {
|
2023-05-16 16:06:40 +00:00
|
|
|
if (!this._addLabel(null, i))
|
2020-05-28 12:03:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-29 13:43:55 +00:00
|
|
|
for (i = 0; ; i++) {
|
2023-05-16 16:06:40 +00:00
|
|
|
if (!this._addLabel(Meta.PadFeatureType.RING, i, Meta.PadDirection.CW) ||
|
|
|
|
!this._addLabel(Meta.PadFeatureType.RING, i, Meta.PadDirection.CCW))
|
2020-05-28 12:03:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-29 13:43:55 +00:00
|
|
|
for (i = 0; ; i++) {
|
2023-05-16 16:06:40 +00:00
|
|
|
if (!this._addLabel(Meta.PadFeatureType.STRIP, i, Meta.PadDirection.UP) ||
|
|
|
|
!this._addLabel(Meta.PadFeatureType.STRIP, i, Meta.PadDirection.DOWN))
|
2020-05-28 12:03:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_wrappingSvgHeader() {
|
2019-08-19 19:38:51 +00:00
|
|
|
return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
|
|
|
|
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
|
|
|
|
'xmlns:xi="http://www.w3.org/2001/XInclude" ' +
|
2022-02-07 14:14:06 +00:00
|
|
|
`width="${this._imageWidth}" height="${this._imageHeight}"> ` +
|
2019-08-19 19:38:51 +00:00
|
|
|
'<style type="text/css">';
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_wrappingSvgFooter() {
|
2020-03-24 04:02:20 +00:00
|
|
|
return '%s%s%s'.format(
|
|
|
|
'</style>',
|
|
|
|
'<xi:include href="%s" />'.format(this._imagePath),
|
|
|
|
'</svg>');
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_cssString() {
|
2016-06-22 17:57:12 +00:00
|
|
|
let css = this._css;
|
|
|
|
|
|
|
|
for (let i = 0; i < this._activeButtons.length; i++) {
|
|
|
|
let ch = String.fromCharCode('A'.charCodeAt() + this._activeButtons[i]);
|
2022-02-07 14:14:06 +00:00
|
|
|
css += `.${ch}.Leader { stroke: ${ACTIVE_COLOR} !important; }`;
|
|
|
|
css += `.${ch}.Button { stroke: ${ACTIVE_COLOR} !important; fill: ${ACTIVE_COLOR} !important; }`;
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return css;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_composeStyledDiagram() {
|
2016-06-22 17:57:12 +00:00
|
|
|
let svgData = '';
|
|
|
|
|
|
|
|
if (!GLib.file_test(this._imagePath, GLib.FileTest.EXISTS))
|
|
|
|
return null;
|
|
|
|
|
|
|
|
svgData += this._wrappingSvgHeader();
|
|
|
|
svgData += this._cssString();
|
|
|
|
svgData += this._wrappingSvgFooter();
|
|
|
|
|
2019-04-26 11:03:42 +00:00
|
|
|
let istream = new Gio.MemoryInputStream();
|
|
|
|
istream.add_bytes(new GLib.Bytes(svgData));
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2019-04-26 11:03:42 +00:00
|
|
|
return Rsvg.Handle.new_from_stream_sync(istream,
|
2021-01-30 00:03:04 +00:00
|
|
|
Gio.File.new_for_path(this._imagePath), 0, null);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateDiagramScale() {
|
2016-06-22 17:57:12 +00:00
|
|
|
[this._actorWidth, this._actorHeight] = this.get_size();
|
|
|
|
let dimensions = this._handle.get_dimensions();
|
|
|
|
let scaleX = this._actorWidth / dimensions.width;
|
|
|
|
let scaleY = this._actorHeight / dimensions.height;
|
|
|
|
this._scale = Math.min(scaleX, scaleY);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_allocateChild(child, x, y, direction) {
|
2019-02-01 13:41:55 +00:00
|
|
|
let [, natHeight] = child.get_preferred_height(-1);
|
|
|
|
let [, natWidth] = child.get_preferred_width(natHeight);
|
2016-06-22 17:57:12 +00:00
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
|
2020-05-29 14:59:46 +00:00
|
|
|
// I miss Cairo.Matrix
|
|
|
|
let dimensions = this._handle.get_dimensions();
|
|
|
|
x = x * this._scale + this._actorWidth / 2 - dimensions.width / 2 * this._scale;
|
|
|
|
y = y * this._scale + this._actorHeight / 2 - dimensions.height / 2 * this._scale;
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (direction === LTR) {
|
2016-06-22 17:57:12 +00:00
|
|
|
childBox.x1 = x;
|
|
|
|
childBox.x2 = x + natWidth;
|
|
|
|
} else {
|
|
|
|
childBox.x1 = x - natWidth;
|
|
|
|
childBox.x2 = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
childBox.y1 = y - natHeight / 2;
|
|
|
|
childBox.y2 = y + natHeight / 2;
|
2020-05-28 11:34:57 +00:00
|
|
|
child.allocate(childBox);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
vfunc_allocate(box) {
|
|
|
|
super.vfunc_allocate(box);
|
2020-05-29 14:59:46 +00:00
|
|
|
if (this._handle === null)
|
|
|
|
return;
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
this._updateDiagramScale();
|
|
|
|
|
|
|
|
for (let i = 0; i < this._labels.length; i++) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {label, x, y, arrangement} = this._labels[i];
|
2016-06-22 17:57:12 +00:00
|
|
|
this._allocateChild(label, x, y, arrangement);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._editorActor && this._curEdited) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {x, y, arrangement} = this._curEdited;
|
2016-06-22 17:57:12 +00:00
|
|
|
this._allocateChild(this._editorActor, x, y, arrangement);
|
|
|
|
}
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
vfunc_repaint() {
|
2016-06-22 17:57:12 +00:00
|
|
|
if (this._handle == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this._scale == null)
|
|
|
|
this._updateDiagramScale();
|
|
|
|
|
|
|
|
let [width, height] = this.get_surface_size();
|
|
|
|
let dimensions = this._handle.get_dimensions();
|
|
|
|
let cr = this.get_context();
|
|
|
|
|
|
|
|
cr.save();
|
2019-01-29 01:27:05 +00:00
|
|
|
cr.translate(width / 2, height / 2);
|
2016-06-22 17:57:12 +00:00
|
|
|
cr.scale(this._scale, this._scale);
|
2021-01-30 00:03:04 +00:00
|
|
|
if (this.leftHanded)
|
2016-06-22 17:57:12 +00:00
|
|
|
cr.rotate(Math.PI);
|
2019-01-29 01:27:05 +00:00
|
|
|
cr.translate(-dimensions.width / 2, -dimensions.height / 2);
|
2016-06-22 17:57:12 +00:00
|
|
|
this._handle.render_cairo(cr);
|
|
|
|
cr.restore();
|
|
|
|
cr.$dispose();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getItemLabelCoords(labelName, leaderName) {
|
2016-06-22 17:57:12 +00:00
|
|
|
if (this._handle == null)
|
|
|
|
return [false];
|
|
|
|
|
2022-04-30 12:09:37 +00:00
|
|
|
const [labelFound, labelPos] = this._handle.get_position_sub(`#${labelName}`);
|
|
|
|
const [, labelSize] = this._handle.get_dimensions_sub(`#${labelName}`);
|
|
|
|
if (!labelFound)
|
2016-06-22 17:57:12 +00:00
|
|
|
return [false];
|
|
|
|
|
2022-04-30 12:09:37 +00:00
|
|
|
const [leaderFound, leaderPos] = this._handle.get_position_sub(`#${leaderName}`);
|
|
|
|
const [, leaderSize] = this._handle.get_dimensions_sub(`#${leaderName}`);
|
|
|
|
if (!leaderFound)
|
2016-06-22 17:57:12 +00:00
|
|
|
return [false];
|
|
|
|
|
2022-04-30 12:09:37 +00:00
|
|
|
let direction;
|
|
|
|
if (labelPos.x > leaderPos.x + leaderSize.width)
|
2016-06-22 17:57:12 +00:00
|
|
|
direction = LTR;
|
|
|
|
else
|
|
|
|
direction = RTL;
|
|
|
|
|
2022-04-30 12:09:37 +00:00
|
|
|
let pos = {x: labelPos.x, y: labelPos.y + labelSize.height};
|
2021-01-30 00:03:04 +00:00
|
|
|
if (this.leftHanded) {
|
2016-06-22 17:57:12 +00:00
|
|
|
direction = 1 - direction;
|
|
|
|
pos.x = this._imageWidth - pos.x;
|
|
|
|
pos.y = this._imageHeight - pos.y;
|
|
|
|
}
|
|
|
|
|
2020-05-29 14:59:46 +00:00
|
|
|
return [true, pos.x, pos.y, direction];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-28 17:54:25 +00:00
|
|
|
_getButtonLabels(button) {
|
2016-06-22 17:57:12 +00:00
|
|
|
let ch = String.fromCharCode('A'.charCodeAt() + button);
|
2022-02-07 14:14:06 +00:00
|
|
|
const labelName = `Label${ch}`;
|
|
|
|
const leaderName = `Leader${ch}`;
|
2020-05-28 17:54:25 +00:00
|
|
|
return [labelName, leaderName];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-28 17:54:25 +00:00
|
|
|
_getRingLabels(number, dir) {
|
2017-08-03 19:59:26 +00:00
|
|
|
let numStr = number > 0 ? (number + 1).toString() : '';
|
2023-05-16 16:06:40 +00:00
|
|
|
let dirStr = dir === Meta.PadDirection.CW ? 'CW' : 'CCW';
|
2022-02-07 14:14:06 +00:00
|
|
|
const labelName = `LabelRing${numStr}${dirStr}`;
|
|
|
|
const leaderName = `LeaderRing${numStr}${dirStr}`;
|
2020-05-28 17:54:25 +00:00
|
|
|
return [labelName, leaderName];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-28 17:54:25 +00:00
|
|
|
_getStripLabels(number, dir) {
|
2016-06-22 17:57:12 +00:00
|
|
|
let numStr = number > 0 ? (number + 1).toString() : '';
|
2023-05-16 16:06:40 +00:00
|
|
|
let dirStr = dir === Meta.PadDirection.UP ? 'Up' : 'Down';
|
2022-02-07 14:14:06 +00:00
|
|
|
const labelName = `LabelStrip${numStr}${dirStr}`;
|
|
|
|
const leaderName = `LeaderStrip${numStr}${dirStr}`;
|
2020-05-28 17:54:25 +00:00
|
|
|
return [labelName, leaderName];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-28 17:54:25 +00:00
|
|
|
_getLabelCoords(action, idx, dir) {
|
2023-05-16 16:06:40 +00:00
|
|
|
if (action === Meta.PadFeatureType.RING)
|
2020-05-28 17:54:25 +00:00
|
|
|
return this._getItemLabelCoords(...this._getRingLabels(idx, dir));
|
2023-05-16 16:06:40 +00:00
|
|
|
else if (action === Meta.PadFeatureType.STRIP)
|
2020-05-28 17:54:25 +00:00
|
|
|
return this._getItemLabelCoords(...this._getStripLabels(idx, dir));
|
2023-05-16 16:06:40 +00:00
|
|
|
else
|
|
|
|
return this._getItemLabelCoords(...this._getButtonLabels(idx));
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_invalidateSvg() {
|
2016-06-22 17:57:12 +00:00
|
|
|
if (this._handle == null)
|
|
|
|
return;
|
|
|
|
this._handle = this._composeStyledDiagram();
|
|
|
|
this.queue_repaint();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
activateButton(button) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._activeButtons.push(button);
|
|
|
|
this._invalidateSvg();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
deactivateButton(button) {
|
2016-06-22 17:57:12 +00:00
|
|
|
for (let i = 0; i < this._activeButtons.length; i++) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._activeButtons[i] === button)
|
2016-06-22 17:57:12 +00:00
|
|
|
this._activeButtons.splice(i, 1);
|
|
|
|
}
|
|
|
|
this._invalidateSvg();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-05-29 13:53:33 +00:00
|
|
|
_addLabel(action, idx, dir) {
|
2020-05-29 14:07:28 +00:00
|
|
|
let [found, x, y, arrangement] = this._getLabelCoords(action, idx, dir);
|
2020-05-29 13:43:55 +00:00
|
|
|
if (!found)
|
|
|
|
return false;
|
|
|
|
|
2020-05-28 12:03:13 +00:00
|
|
|
let label = new St.Label();
|
2023-08-06 22:40:20 +00:00
|
|
|
this._labels.push({label, action, idx, dir, x, y, arrangement});
|
2016-06-22 17:57:12 +00:00
|
|
|
this.add_actor(label);
|
2020-05-29 13:43:55 +00:00
|
|
|
return true;
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2019-12-16 11:39:49 +00:00
|
|
|
updateLabels(getText) {
|
|
|
|
for (let i = 0; i < this._labels.length; i++) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {label, action, idx, dir} = this._labels[i];
|
2019-12-16 11:39:49 +00:00
|
|
|
let str = getText(action, idx, dir);
|
|
|
|
label.set_text(str);
|
|
|
|
}
|
2020-05-29 14:05:57 +00:00
|
|
|
|
|
|
|
this.queue_relayout();
|
2019-12-16 11:39:49 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_applyLabel(label, action, idx, dir, str) {
|
2020-05-29 14:05:57 +00:00
|
|
|
if (str !== null)
|
2017-07-05 22:03:12 +00:00
|
|
|
label.set_text(str);
|
|
|
|
label.show();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2017-07-05 22:03:12 +00:00
|
|
|
|
2023-05-16 16:11:45 +00:00
|
|
|
stopEdition(str) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._editorActor.hide();
|
|
|
|
|
|
|
|
if (this._curEdited) {
|
2023-08-06 22:40:20 +00:00
|
|
|
const {label, action, idx, dir} = this._curEdited;
|
2017-07-05 22:03:12 +00:00
|
|
|
this._applyLabel(label, action, idx, dir, str);
|
2016-06-22 17:57:12 +00:00
|
|
|
this._curEdited = null;
|
|
|
|
}
|
2020-05-29 14:05:57 +00:00
|
|
|
|
|
|
|
this.queue_relayout();
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
startEdition(action, idx, dir) {
|
2016-06-22 17:57:12 +00:00
|
|
|
let editedLabel;
|
2017-07-05 22:05:22 +00:00
|
|
|
|
|
|
|
if (this._curEdited)
|
|
|
|
return;
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < this._labels.length; i++) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (action === this._labels[i].action &&
|
|
|
|
idx === this._labels[i].idx && dir === this._labels[i].dir) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._curEdited = this._labels[i];
|
2020-05-29 13:53:33 +00:00
|
|
|
editedLabel = this._curEdited.label;
|
2016-06-22 17:57:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._curEdited == null)
|
|
|
|
return;
|
|
|
|
this._editorActor.show();
|
|
|
|
editedLabel.hide();
|
2020-05-29 14:05:57 +00:00
|
|
|
this.queue_relayout();
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export const PadOsd = GObject.registerClass({
|
2019-11-01 19:01:31 +00:00
|
|
|
Signals: {
|
2023-08-06 22:40:20 +00:00
|
|
|
'pad-selected': {param_types: [Clutter.InputDevice.$gtype]},
|
2019-08-20 21:43:54 +00:00
|
|
|
'closed': {},
|
|
|
|
},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class PadOsd extends St.BoxLayout {
|
|
|
|
_init(padDevice, settings, imagePath, editionMode, monitorIndex) {
|
|
|
|
super._init({
|
|
|
|
style_class: 'pad-osd-window',
|
|
|
|
vertical: true,
|
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
2019-08-20 21:43:54 +00:00
|
|
|
reactive: true,
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
this.padDevice = padDevice;
|
2019-01-29 01:27:05 +00:00
|
|
|
this._groupPads = [padDevice];
|
2016-06-22 17:57:12 +00:00
|
|
|
this._settings = settings;
|
|
|
|
this._imagePath = imagePath;
|
|
|
|
this._editionMode = editionMode;
|
2017-02-13 14:25:20 +00:00
|
|
|
this._padChooser = null;
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2019-10-05 10:36:52 +00:00
|
|
|
let seat = Clutter.get_default_backend().get_default_seat();
|
2021-08-15 22:36:59 +00:00
|
|
|
seat.connectObject(
|
|
|
|
'device-added', (_seat, device) => {
|
|
|
|
if (device.get_device_type() === Clutter.InputDeviceType.PAD_DEVICE &&
|
|
|
|
this.padDevice.is_grouped(device)) {
|
|
|
|
this._groupPads.push(device);
|
|
|
|
this._updatePadChooser();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'device-removed', (_seat, device) => {
|
|
|
|
// If the device is being removed, destroy the padOsd.
|
|
|
|
if (device === this.padDevice) {
|
|
|
|
this.destroy();
|
|
|
|
} else if (this._groupPads.includes(device)) {
|
|
|
|
// Or update the pad chooser if the device belongs to
|
|
|
|
// the same group.
|
|
|
|
this._groupPads.splice(this._groupPads.indexOf(device), 1);
|
|
|
|
this._updatePadChooser();
|
|
|
|
}
|
|
|
|
}, this);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2019-10-05 10:36:52 +00:00
|
|
|
seat.list_devices().forEach(device => {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (device !== this.padDevice &&
|
|
|
|
device.get_device_type() === Clutter.InputDeviceType.PAD_DEVICE &&
|
2017-02-13 10:55:24 +00:00
|
|
|
this.padDevice.is_grouped(device))
|
|
|
|
this._groupPads.push(device);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2017-02-13 10:55:24 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
|
|
Main.uiGroup.add_actor(this);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
|
|
|
this._monitorIndex = monitorIndex;
|
2023-08-06 22:40:20 +00:00
|
|
|
let constraint = new Layout.MonitorConstraint({index: monitorIndex});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_constraint(constraint);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
this._titleBox = new St.BoxLayout({
|
|
|
|
style_class: 'pad-osd-title-box',
|
|
|
|
vertical: false,
|
|
|
|
x_expand: false,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(this._titleBox);
|
2017-02-13 14:11:22 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
const labelBox = new St.BoxLayout({
|
|
|
|
style_class: 'pad-osd-title-menu-box',
|
|
|
|
vertical: true,
|
|
|
|
});
|
2017-02-13 14:11:22 +00:00
|
|
|
this._titleBox.add_actor(labelBox);
|
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
this._titleLabel = new St.Label({
|
|
|
|
style: 'font-side: larger; font-weight: bold;',
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2020-05-28 11:36:25 +00:00
|
|
|
this._titleLabel.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
2016-06-22 17:57:12 +00:00
|
|
|
this._titleLabel.clutter_text.set_text(padDevice.get_device_name());
|
2017-02-13 14:11:22 +00:00
|
|
|
labelBox.add_actor(this._titleLabel);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-08-06 22:40:20 +00:00
|
|
|
this._tipLabel = new St.Label({x_align: Clutter.ActorAlign.CENTER});
|
2017-02-13 14:11:22 +00:00
|
|
|
labelBox.add_actor(this._tipLabel);
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-02-13 14:25:20 +00:00
|
|
|
this._updatePadChooser();
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
this._actionEditor = new ActionEditor();
|
2017-12-02 00:27:35 +00:00
|
|
|
this._actionEditor.connect('done', this._endActionEdition.bind(this));
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
this._padDiagram = new PadDiagram({
|
|
|
|
image: this._imagePath,
|
|
|
|
left_handed: settings.get_boolean('left-handed'),
|
|
|
|
editor_actor: this._actionEditor,
|
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(this._padDiagram);
|
2020-05-28 12:03:13 +00:00
|
|
|
this._updateActionLabels();
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
const buttonBox = new St.Widget({
|
|
|
|
layout_manager: new Clutter.BinLayout(),
|
|
|
|
x_expand: true,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_actor(buttonBox);
|
2019-10-17 21:27:27 +00:00
|
|
|
this._editButton = new St.Button({
|
|
|
|
label: _('Edit…'),
|
|
|
|
style_class: 'button',
|
|
|
|
can_focus: true,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2017-10-31 00:38:18 +00:00
|
|
|
this._editButton.connect('clicked', () => {
|
|
|
|
this.setEditionMode(true);
|
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
buttonBox.add_actor(this._editButton);
|
|
|
|
|
|
|
|
this._syncEditionMode();
|
2021-11-25 09:49:42 +00:00
|
|
|
this._grab = Main.pushModal(this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updatePadChooser() {
|
2017-02-13 14:25:20 +00:00
|
|
|
if (this._groupPads.length > 1) {
|
|
|
|
if (this._padChooser == null) {
|
2019-01-29 01:18:52 +00:00
|
|
|
this._padChooser = new PadChooser(this.padDevice, this._groupPads);
|
2017-10-31 00:38:18 +00:00
|
|
|
this._padChooser.connect('pad-selected', (chooser, pad) => {
|
2017-02-13 14:25:20 +00:00
|
|
|
this._requestForOtherPad(pad);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this._titleBox.add_child(this._padChooser);
|
2017-02-13 14:25:20 +00:00
|
|
|
} else {
|
|
|
|
this._padChooser.update(this._groupPads);
|
|
|
|
}
|
|
|
|
} else if (this._padChooser != null) {
|
|
|
|
this._padChooser.destroy();
|
|
|
|
this._padChooser = null;
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-02-13 14:25:20 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_requestForOtherPad(pad) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (pad === this.padDevice || !this._groupPads.includes(pad))
|
2017-02-13 14:25:20 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
let editionMode = this._editionMode;
|
|
|
|
this.destroy();
|
|
|
|
global.display.request_pad_osd(pad, editionMode);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-02-13 14:25:20 +00:00
|
|
|
|
2023-05-16 16:06:40 +00:00
|
|
|
_getActionText(type, number, dir) {
|
|
|
|
let str;
|
|
|
|
if (type === Meta.PadFeatureType.RING || type === Meta.PadFeatureType.STRIP)
|
|
|
|
str = global.display.get_pad_feature_label(this.padDevice, type, dir, number);
|
|
|
|
else
|
|
|
|
str = global.display.get_pad_button_label(this.padDevice, number);
|
|
|
|
|
2020-08-12 18:59:01 +00:00
|
|
|
return str ?? _('None');
|
2019-12-16 11:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateActionLabels() {
|
|
|
|
this._padDiagram.updateLabels(this._getActionText.bind(this));
|
|
|
|
}
|
|
|
|
|
2021-11-24 16:42:41 +00:00
|
|
|
vfunc_captured_event(event) {
|
2019-12-16 11:39:49 +00:00
|
|
|
let isModeSwitch =
|
2023-08-07 00:51:19 +00:00
|
|
|
(event.type() === Clutter.EventType.PAD_BUTTON_PRESS ||
|
|
|
|
event.type() === Clutter.EventType.PAD_BUTTON_RELEASE) &&
|
2019-12-16 11:39:49 +00:00
|
|
|
this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
if (event.type() === Clutter.EventType.PAD_BUTTON_PRESS &&
|
|
|
|
event.get_source_device() === this.padDevice) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._padDiagram.activateButton(event.get_button());
|
|
|
|
|
2017-07-05 22:05:22 +00:00
|
|
|
/* Buttons that switch between modes cannot be edited */
|
|
|
|
if (this._editionMode && !isModeSwitch)
|
2016-06-22 17:57:12 +00:00
|
|
|
this._startButtonActionEdition(event.get_button());
|
|
|
|
return Clutter.EVENT_STOP;
|
2023-08-07 00:51:19 +00:00
|
|
|
} else if (event.type() === Clutter.EventType.PAD_BUTTON_RELEASE &&
|
|
|
|
event.get_source_device() === this.padDevice) {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._padDiagram.deactivateButton(event.get_button());
|
2019-12-16 11:39:49 +00:00
|
|
|
|
|
|
|
if (isModeSwitch) {
|
|
|
|
this._endActionEdition();
|
|
|
|
this._updateActionLabels();
|
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
return Clutter.EVENT_STOP;
|
2023-08-07 00:51:19 +00:00
|
|
|
} else if (event.type() === Clutter.EventType.KEY_PRESS &&
|
2019-11-05 19:37:28 +00:00
|
|
|
(!this._editionMode || event.get_key_symbol() === Clutter.KEY_Escape)) {
|
2017-07-05 22:05:22 +00:00
|
|
|
if (this._editedAction != null)
|
|
|
|
this._endActionEdition();
|
2016-06-22 17:57:12 +00:00
|
|
|
else
|
|
|
|
this.destroy();
|
|
|
|
return Clutter.EVENT_STOP;
|
2023-08-07 00:51:19 +00:00
|
|
|
} else if (event.get_source_device() === this.padDevice &&
|
|
|
|
event.type() === Clutter.EventType.PAD_STRIP) {
|
2017-07-05 22:05:22 +00:00
|
|
|
if (this._editionMode) {
|
2019-01-31 14:08:00 +00:00
|
|
|
let [retval_, number, mode] = event.get_pad_event_details();
|
2023-05-16 16:06:40 +00:00
|
|
|
this._startStripActionEdition(number, Meta.PadDirection.UP, mode);
|
2017-07-05 22:05:22 +00:00
|
|
|
}
|
2023-08-07 00:51:19 +00:00
|
|
|
} else if (event.get_source_device() === this.padDevice &&
|
|
|
|
event.type() === Clutter.EventType.PAD_RING) {
|
2017-07-05 22:05:22 +00:00
|
|
|
if (this._editionMode) {
|
2019-01-31 14:08:00 +00:00
|
|
|
let [retval_, number, mode] = event.get_pad_event_details();
|
2023-05-16 16:06:40 +00:00
|
|
|
this._startRingActionEdition(number, Meta.PadDirection.CCW, mode);
|
2017-07-05 22:05:22 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
|
|
|
|
2017-02-13 14:25:20 +00:00
|
|
|
// If the event comes from another pad in the same group,
|
|
|
|
// show the OSD for it.
|
2018-07-14 20:56:22 +00:00
|
|
|
if (this._groupPads.includes(event.get_source_device())) {
|
2017-02-13 14:25:20 +00:00
|
|
|
this._requestForOtherPad(event.get_source_device());
|
|
|
|
return Clutter.EVENT_STOP;
|
|
|
|
}
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_syncEditionMode() {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._editButton.set_reactive(!this._editionMode);
|
|
|
|
this._editButton.save_easing_state();
|
|
|
|
this._editButton.set_easing_duration(200);
|
|
|
|
this._editButton.set_opacity(this._editionMode ? 128 : 255);
|
|
|
|
this._editButton.restore_easing_state();
|
|
|
|
|
|
|
|
let title;
|
|
|
|
|
|
|
|
if (this._editionMode) {
|
2023-08-06 22:34:20 +00:00
|
|
|
title = _('Press a button to configure');
|
|
|
|
this._tipLabel.set_text(_('Press Esc to exit'));
|
2016-06-22 17:57:12 +00:00
|
|
|
} else {
|
|
|
|
title = this.padDevice.get_device_name();
|
2023-08-06 22:34:20 +00:00
|
|
|
this._tipLabel.set_text(_('Press any key to exit'));
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 11:38:36 +00:00
|
|
|
this._titleLabel.set_text(title);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_isEditedAction(type, number, dir) {
|
2017-07-05 22:05:22 +00:00
|
|
|
if (!this._editedAction)
|
|
|
|
return false;
|
|
|
|
|
2023-08-07 00:51:19 +00:00
|
|
|
return this._editedAction.type === type &&
|
|
|
|
this._editedAction.number === number &&
|
|
|
|
this._editedAction.dir === dir;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-07-05 22:05:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_endActionEdition() {
|
2016-06-22 17:57:12 +00:00
|
|
|
this._actionEditor.close();
|
|
|
|
|
2017-07-05 22:05:22 +00:00
|
|
|
if (this._editedAction != null) {
|
2023-05-16 16:11:45 +00:00
|
|
|
const {type, number, dir, mode} = this._editedAction;
|
2023-05-16 16:06:40 +00:00
|
|
|
const str = this._getActionText(type, number, dir);
|
2017-07-05 22:05:22 +00:00
|
|
|
|
2023-05-16 16:11:45 +00:00
|
|
|
const hasNextAction =
|
|
|
|
type === Meta.PadFeatureType.RING && dir === Meta.PadDirection.CCW ||
|
|
|
|
type === Meta.PadFeatureType.STRIP && dir === Meta.PadDirection.UP;
|
|
|
|
|
|
|
|
this._padDiagram.stopEdition(str);
|
2017-07-05 22:05:22 +00:00
|
|
|
this._editedAction = null;
|
2023-05-16 16:11:45 +00:00
|
|
|
|
|
|
|
// Maybe follow up on next ring/strip direction
|
|
|
|
if (hasNextAction) {
|
|
|
|
if (type === Meta.PadFeatureType.RING)
|
|
|
|
this._startRingActionEdition(number, Meta.PadDirection.CW, mode);
|
|
|
|
else
|
|
|
|
this._startStripActionEdition(number, Meta.PadDirection.DOWN, mode);
|
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
}
|
|
|
|
|
2017-07-05 22:05:22 +00:00
|
|
|
this._editedActionSettings = null;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_startActionEdition(key, type, number, dir, mode) {
|
2017-07-05 22:05:22 +00:00
|
|
|
if (this._isEditedAction(type, number, dir))
|
2016-06-22 17:57:12 +00:00
|
|
|
return;
|
|
|
|
|
2017-07-05 22:05:22 +00:00
|
|
|
this._endActionEdition();
|
2023-08-06 22:40:20 +00:00
|
|
|
this._editedAction = {type, number, dir, mode};
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2020-03-24 04:02:20 +00:00
|
|
|
const settingsPath = `${this._settings.path}${key}/`;
|
2023-08-06 23:45:22 +00:00
|
|
|
this._editedActionSettings = Gio.Settings.new_with_path(
|
|
|
|
'org.gnome.desktop.peripherals.tablet.pad-button',
|
|
|
|
settingsPath);
|
2017-07-12 16:37:18 +00:00
|
|
|
this._actionEditor.setSettings(this._editedActionSettings, type);
|
2017-07-05 22:05:22 +00:00
|
|
|
this._padDiagram.startEdition(type, number, dir);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-07-05 22:05:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_startButtonActionEdition(button) {
|
2017-07-05 22:05:22 +00:00
|
|
|
let ch = String.fromCharCode('A'.charCodeAt() + button);
|
2022-02-07 14:14:06 +00:00
|
|
|
let key = `button${ch}`;
|
2023-05-16 16:06:40 +00:00
|
|
|
this._startActionEdition(key, null, button);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-07-05 22:05:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_startRingActionEdition(ring, dir, mode) {
|
2017-07-05 22:05:22 +00:00
|
|
|
let ch = String.fromCharCode('A'.charCodeAt() + ring);
|
2023-05-16 16:06:40 +00:00
|
|
|
const key = `ring${ch}-${dir === Meta.PadDirection.CCW ? 'ccw' : 'cw'}-mode-${mode}`;
|
|
|
|
this._startActionEdition(key, Meta.PadFeatureType.RING, ring, dir, mode);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-07-05 22:05:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_startStripActionEdition(strip, dir, mode) {
|
2017-07-05 22:05:22 +00:00
|
|
|
let ch = String.fromCharCode('A'.charCodeAt() + strip);
|
2023-05-16 16:06:40 +00:00
|
|
|
const key = `strip${ch}-${dir === Meta.PadDirection.UP ? 'up' : 'down'}-mode-${mode}`;
|
|
|
|
this._startActionEdition(key, Meta.PadFeatureType.STRIP, strip, dir, mode);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setEditionMode(editionMode) {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (this._editionMode === editionMode)
|
2016-06-22 17:57:12 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
this._editionMode = editionMode;
|
|
|
|
this._syncEditionMode();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDestroy() {
|
2021-11-25 09:49:42 +00:00
|
|
|
Main.popModal(this._grab);
|
|
|
|
this._grab = null;
|
2016-06-22 17:57:12 +00:00
|
|
|
this._actionEditor.close();
|
|
|
|
|
|
|
|
this.emit('closed');
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2018-09-06 00:55:20 +00:00
|
|
|
const PadOsdIface = loadInterfaceXML('org.gnome.Shell.Wacom.PadOsd');
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
export class PadOsdService extends Signals.EventEmitter {
|
2017-10-31 01:19:44 +00:00
|
|
|
constructor() {
|
2022-07-04 22:30:44 +00:00
|
|
|
super();
|
|
|
|
|
2016-06-22 17:57:12 +00:00
|
|
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(PadOsdIface, this);
|
|
|
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Wacom');
|
|
|
|
Gio.DBus.session.own_name('org.gnome.Shell.Wacom.PadOsd', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
ShowAsync(params, invocation) {
|
2016-06-22 17:57:12 +00:00
|
|
|
let [deviceNode, editionMode] = params;
|
2019-10-05 10:36:52 +00:00
|
|
|
let seat = Clutter.get_default_backend().get_default_seat();
|
|
|
|
let devices = seat.list_devices();
|
2016-06-22 17:57:12 +00:00
|
|
|
let padDevice = null;
|
|
|
|
|
2017-10-31 00:38:18 +00:00
|
|
|
devices.forEach(device => {
|
2023-08-07 00:51:19 +00:00
|
|
|
if (deviceNode === device.get_device_node() &&
|
|
|
|
device.get_device_type() === Clutter.InputDeviceType.PAD_DEVICE)
|
2016-06-22 17:57:12 +00:00
|
|
|
padDevice = device;
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2016-06-22 17:57:12 +00:00
|
|
|
|
2018-01-19 09:45:50 +00:00
|
|
|
if (padDevice == null) {
|
2023-08-06 23:45:22 +00:00
|
|
|
invocation.return_error_literal(
|
|
|
|
Gio.IOErrorEnum,
|
|
|
|
Gio.IOErrorEnum.CANCELLED,
|
|
|
|
'Invalid params');
|
2016-06-22 17:57:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
global.display.request_pad_osd(padDevice, editionMode);
|
|
|
|
invocation.return_value(null);
|
|
|
|
}
|
2023-07-10 09:53:00 +00:00
|
|
|
}
|