accessibility: Add pointer accessibility support
Adds the UI part for the pointer accessibility features. The various timeouts running are notified using a pie-timer showing under the pointer. For dwell-click type selection, we use a drop-down menu. Users can use the dwell-click to select the next type of dwell click to be emitted. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/474
This commit is contained in:
parent
14d9839ed3
commit
5ace4682bf
@ -1195,6 +1195,15 @@ StScrollBar {
|
||||
}
|
||||
}
|
||||
|
||||
// Pointer accessibility notifications
|
||||
.pie-timer {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
-pie-border-width: 3px;
|
||||
-pie-border-color: $selected_bg_color;
|
||||
-pie-background-color: lighten(transparentize($selected_bg_color, 0.7), 40%);
|
||||
}
|
||||
|
||||
/* NETWORK DIALOGS */
|
||||
|
||||
.nm-dialog {
|
||||
|
@ -82,6 +82,7 @@
|
||||
<file>ui/pageIndicators.js</file>
|
||||
<file>ui/panel.js</file>
|
||||
<file>ui/panelMenu.js</file>
|
||||
<file>ui/pointerA11yTimeout.js</file>
|
||||
<file>ui/pointerWatcher.js</file>
|
||||
<file>ui/popupMenu.js</file>
|
||||
<file>ui/remoteSearch.js</file>
|
||||
@ -122,6 +123,7 @@
|
||||
|
||||
<file>ui/status/accessibility.js</file>
|
||||
<file>ui/status/brightness.js</file>
|
||||
<file>ui/status/dwellClick.js</file>
|
||||
<file>ui/status/location.js</file>
|
||||
<file>ui/status/keyboard.js</file>
|
||||
<file>ui/status/nightLight.js</file>
|
||||
|
@ -38,6 +38,7 @@ const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const KbdA11yDialog = imports.ui.kbdA11yDialog;
|
||||
const LocatePointer = imports.ui.locatePointer;
|
||||
const PointerA11yTimeout = imports.ui.pointerA11yTimeout;
|
||||
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
|
||||
@ -82,6 +83,7 @@ let _cssStylesheet = null;
|
||||
let _a11ySettings = null;
|
||||
let _themeResource = null;
|
||||
let _oskResource = null;
|
||||
let pointerA11yTimeout = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
if (sessionMode.isPrimary)
|
||||
@ -189,6 +191,8 @@ function _initializeUI() {
|
||||
layoutManager.init();
|
||||
overview.init();
|
||||
|
||||
pointerA11yTimeout = new PointerA11yTimeout.PointerA11yTimeout();
|
||||
|
||||
_a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
|
||||
|
||||
global.display.connect('overlay-key', () => {
|
||||
|
@ -817,6 +817,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator,
|
||||
};
|
||||
|
||||
var Panel = GObject.registerClass(
|
||||
|
104
js/ui/pointerA11yTimeout.js
Normal file
104
js/ui/pointerA11yTimeout.js
Normal file
@ -0,0 +1,104 @@
|
||||
const { Clutter, GLib, GObject, Meta, St } = imports.gi;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
const Cairo = imports.cairo;
|
||||
|
||||
const ANIMATION_STEPS = 36.;
|
||||
|
||||
var PieTimer = GObject.registerClass(
|
||||
class PieTimer extends St.DrawingArea {
|
||||
_init() {
|
||||
this._x = 0;
|
||||
this._y = 0;
|
||||
this._startTime = 0;
|
||||
this._duration = 0;
|
||||
super._init( { style_class: 'pie-timer',
|
||||
visible: false,
|
||||
can_focus: false,
|
||||
reactive: false });
|
||||
}
|
||||
|
||||
vfunc_repaint() {
|
||||
let node = this.get_theme_node();
|
||||
let backgroundColor = node.get_color('-pie-background-color');
|
||||
let borderColor = node.get_color('-pie-border-color');
|
||||
let borderWidth = node.get_length('-pie-border-width');
|
||||
let [width, height] = this.get_surface_size();
|
||||
let radius = Math.min(width / 2, height / 2);
|
||||
|
||||
let currentTime = GLib.get_monotonic_time() / 1000.0;
|
||||
let ellapsed = currentTime - this._startTime;
|
||||
let angle = (ellapsed / this._duration) * 2 * Math.PI;
|
||||
let startAngle = 3 * Math.PI / 2;
|
||||
let endAngle = startAngle + angle;
|
||||
|
||||
let cr = this.get_context();
|
||||
cr.setLineCap(Cairo.LineCap.ROUND);
|
||||
cr.setLineJoin(Cairo.LineJoin.ROUND);
|
||||
cr.translate(width / 2, height / 2);
|
||||
|
||||
cr.moveTo(0, 0);
|
||||
cr.arc(0, 0, radius - borderWidth, startAngle, endAngle);
|
||||
cr.lineTo(0, 0);
|
||||
cr.closePath();
|
||||
|
||||
cr.setLineWidth(0);
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
cr.fillPreserve();
|
||||
|
||||
cr.setLineWidth(borderWidth);
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
cr.stroke();
|
||||
|
||||
cr.$dispose();
|
||||
}
|
||||
|
||||
start(x, y, duration) {
|
||||
Tweener.removeTweens(this);
|
||||
|
||||
this.x = x - this.width / 2;
|
||||
this.y = y - this.height / 2;
|
||||
this.show();
|
||||
Main.uiGroup.set_child_above_sibling(this, null);
|
||||
|
||||
this._startTime = GLib.get_monotonic_time() / 1000.0;
|
||||
this._duration = duration;
|
||||
|
||||
Tweener.addTween(this,
|
||||
{ opacity: 255,
|
||||
time: duration / 1000,
|
||||
transition: 'easeOutQuad',
|
||||
onUpdateScope: this,
|
||||
onUpdate() { this.queue_repaint() },
|
||||
onCompleteScope: this,
|
||||
onComplete() { this.stop(); }
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
Tweener.removeTweens(this);
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
var PointerA11yTimeout = class PointerA11yTimeout {
|
||||
constructor() {
|
||||
let manager = Clutter.DeviceManager.get_default();
|
||||
let pieTimer = new PieTimer();
|
||||
|
||||
Main.uiGroup.add_actor(pieTimer);
|
||||
|
||||
manager.connect('ptr-a11y-timeout-started', (manager, device, type, timeout) => {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
pieTimer.start(x, y, timeout);
|
||||
if (type == Clutter.PointerA11yTimeoutType.GESTURE)
|
||||
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
|
||||
});
|
||||
|
||||
manager.connect('ptr-a11y-timeout-stopped', (manager, device, type) => {
|
||||
pieTimer.stop();
|
||||
if (type == Clutter.PointerA11yTimeoutType.GESTURE)
|
||||
global.display.set_cursor(Meta.Cursor.DEFAULT);
|
||||
});
|
||||
}
|
||||
};
|
@ -48,7 +48,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'aggregateMenu']
|
||||
right: ['dwellClick', 'a11y', 'keyboard', 'aggregateMenu']
|
||||
},
|
||||
panelStyle: 'login-screen'
|
||||
},
|
||||
@ -73,7 +73,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: [],
|
||||
center: [],
|
||||
right: ['a11y', 'keyboard', 'aggregateMenu']
|
||||
right: ['dwellClick', 'a11y', 'keyboard', 'aggregateMenu']
|
||||
},
|
||||
panelStyle: 'unlock-screen'
|
||||
},
|
||||
@ -101,7 +101,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'aggregateMenu']
|
||||
right: ['dwellClick', 'a11y', 'keyboard', 'aggregateMenu']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
86
js/ui/status/dwellClick.js
Normal file
86
js/ui/status/dwellClick.js
Normal file
@ -0,0 +1,86 @@
|
||||
const { Clutter, Gio, GLib, GObject, St } = imports.gi;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const MOUSE_A11Y_SCHEMA = 'org.gnome.desktop.a11y.mouse';
|
||||
const KEY_DWELL_CLICK_ENABLED = 'dwell-click-enabled';
|
||||
const KEY_DWELL_MODE = 'dwell-mode';
|
||||
const DWELL_MODE_WINDOW = 'window';
|
||||
const DWELL_CLICK_MODES = {
|
||||
primary: {
|
||||
name: _("Single Click"),
|
||||
icon: 'pointer-primary-click-symbolic',
|
||||
type: Clutter.PointerA11yDwellClickType.PRIMARY
|
||||
},
|
||||
double: {
|
||||
name: _("Double Click"),
|
||||
icon: 'pointer-double-click-symbolic',
|
||||
type: Clutter.PointerA11yDwellClickType.DOUBLE
|
||||
},
|
||||
drag: {
|
||||
name: _("Drag"),
|
||||
icon: 'pointer-drag-symbolic',
|
||||
type: Clutter.PointerA11yDwellClickType.DRAG
|
||||
},
|
||||
secondary: {
|
||||
name: _("Secondary Click"),
|
||||
icon: 'pointer-secondary-click-symbolic',
|
||||
type: Clutter.PointerA11yDwellClickType.SECONDARY
|
||||
},
|
||||
};
|
||||
|
||||
var DwellClickIndicator = GObject.registerClass(
|
||||
class DwellClickIndicator extends PanelMenu.Button {
|
||||
_init() {
|
||||
super._init(0.0, _("Dwell Click"));
|
||||
|
||||
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
this._icon = new St.Icon({ style_class: 'system-status-icon',
|
||||
icon_name: 'pointer-primary-click-symbolic' });
|
||||
this._hbox.add_child(this._icon);
|
||||
this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
|
||||
|
||||
this.add_child(this._hbox);
|
||||
|
||||
this._a11ySettings = new Gio.Settings({ schema_id: MOUSE_A11Y_SCHEMA });
|
||||
this._a11ySettings.connect('changed::' + KEY_DWELL_CLICK_ENABLED, this._syncMenuVisibility.bind(this));
|
||||
this._a11ySettings.connect('changed::' + KEY_DWELL_MODE, this._syncMenuVisibility.bind(this));
|
||||
|
||||
this._deviceManager = Clutter.DeviceManager.get_default();
|
||||
this._deviceManager.connect('ptr-a11y-dwell-click-type-changed', this._updateClickType.bind(this));
|
||||
|
||||
this._addDwellAction(DWELL_CLICK_MODES.primary);
|
||||
this._addDwellAction(DWELL_CLICK_MODES.double);
|
||||
this._addDwellAction(DWELL_CLICK_MODES.drag);
|
||||
this._addDwellAction(DWELL_CLICK_MODES.secondary);
|
||||
|
||||
this._setClickType(DWELL_CLICK_MODES.primary);
|
||||
this._syncMenuVisibility();
|
||||
}
|
||||
|
||||
_syncMenuVisibility() {
|
||||
this.visible =
|
||||
(this._a11ySettings.get_boolean(KEY_DWELL_CLICK_ENABLED) &&
|
||||
this._a11ySettings.get_string(KEY_DWELL_MODE) == DWELL_MODE_WINDOW);
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
_addDwellAction(mode) {
|
||||
this.menu.addAction(mode.name, this._setClickType.bind(this, mode), mode.icon);
|
||||
}
|
||||
|
||||
_updateClickType(manager, click_type) {
|
||||
for (let mode in DWELL_CLICK_MODES) {
|
||||
if (DWELL_CLICK_MODES[mode].type == click_type)
|
||||
this._icon.icon_name = DWELL_CLICK_MODES[mode].icon;
|
||||
}
|
||||
}
|
||||
|
||||
_setClickType(mode) {
|
||||
this._deviceManager.set_pointer_a11y_dwell_click_type(mode.type);
|
||||
this._icon.icon_name = mode.icon;
|
||||
}
|
||||
});
|
@ -54,6 +54,7 @@ js/ui/shellMountOperation.js
|
||||
js/ui/status/accessibility.js
|
||||
js/ui/status/bluetooth.js
|
||||
js/ui/status/brightness.js
|
||||
js/ui/status/dwellClick.js
|
||||
js/ui/status/keyboard.js
|
||||
js/ui/status/location.js
|
||||
js/ui/status/network.js
|
||||
|
Loading…
Reference in New Issue
Block a user