[gnome-shell] Magnifier: Implement focus and caret tracking
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] Magnifier: Implement focus and caret tracking
- Date: Thu, 5 Sep 2013 17:19:17 +0000 (UTC)
commit 9d8f30f955fcd15a509a19310c0e946f13eca3a6
Author: Magdalen Berns <thisMagpie live com>
Date: Wed Sep 4 18:53:36 2013 +0100
Magnifier: Implement focus and caret tracking
A11y users who use the magnifier may have trouble
focusing when they're typing or trying to keynav.
Implement a new system so that they can have the
magnifier track the caret and focus instead instead
of just the mouse.
Bug https://bugzilla.gnome.org/show_bug.cgi?id=647074
js/Makefile.am | 1 +
js/ui/focusCaretTracker.js | 68 +++++++++++++++++++
js/ui/magnifier.js | 155 ++++++++++++++++++++++++++++++++++++++------
3 files changed, 204 insertions(+), 20 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index f9a1474..7df786f 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -55,6 +55,7 @@ nobase_dist_js_DATA = \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
+ ui/focusCaretTracker.js\
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
diff --git a/js/ui/focusCaretTracker.js b/js/ui/focusCaretTracker.js
new file mode 100644
index 0000000..c4c4d7f
--- /dev/null
+++ b/js/ui/focusCaretTracker.js
@@ -0,0 +1,68 @@
+/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2012 Inclusive Design Research Centre, OCAD University.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Joseph Scheuhammer <clown alum mit edu>
+ * Contributor:
+ * Magdalen Berns <m berns sms ed ac uk>
+ */
+
+const Atspi = imports.gi.Atspi;
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const CARETMOVED = 'object:text-caret-moved';
+const STATECHANGED = 'object:state-changed';
+
+const FocusCaretTracker = new Lang.Class({
+ Name: 'FocusCaretTracker',
+
+ _init: function() {
+ Atspi.init();
+ this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
+ },
+
+ _onChanged: function(event) {
+ let update = null;
+
+ if (event.type.indexOf(STATECHANGED) == 0)
+ update = 'focus-changed';
+ else if (event.type == CARETMOVED)
+ update = 'caret-moved';
+ if(update != null)
+ this.emit(update, event);
+ },
+
+ registerFocusListener: function() {
+ return this._atspiListener.register(STATECHANGED + ':focused') &&
+ this._atspiListener.register(STATECHANGED + ':selected');
+ },
+
+ registerCaretListener: function() {
+ return this._atspiListener.register(CARETMOVED);
+ },
+
+ deregisterFocusListener: function() {
+ return this._atspiListener.deregister(STATECHANGED + ':focused') &&
+ this._atspiListener.deregister(STATECHANGED + ':selected');
+ },
+
+ deregisterCaretListener: function() {
+ return this._atspiListener.deregister(CARETMOVED);
+ }
+});
+Signals.addSignalMethods(FocusCaretTracker.prototype);
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
index 7c68ea8..6f7fc56 100644
--- a/js/ui/magnifier.js
+++ b/js/ui/magnifier.js
@@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+const Atspi = imports.gi.Atspi;
const Clutter = imports.gi.Clutter;
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
@@ -10,6 +11,7 @@ const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
+const FocusCaretTracker = imports.ui.focusCaretTracker;
const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus;
const Params = imports.misc.params;
@@ -37,6 +39,8 @@ const CONTRAST_BLUE_KEY = 'contrast-blue';
const LENS_MODE_KEY = 'lens-mode';
const CLAMP_MODE_KEY = 'scroll-at-edges';
const MOUSE_TRACKING_KEY = 'mouse-tracking';
+const FOCUS_TRACKING_KEY = 'focus-tracking';
+const CARET_TRACKING_KEY = 'caret-tracking';
const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs';
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color';
@@ -449,6 +453,14 @@ const Magnifier = new Lang.Class({
if (aPref)
zoomRegion.setMouseTrackingMode(aPref);
+ aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
+ if (aPref)
+ zoomRegion.setFocusTrackingMode(aPref);
+
+ aPref = this._settings.get_enum(CARET_TRACKING_KEY);
+ if (aPref)
+ zoomRegion.setCaretTrackingMode(aPref);
+
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
if (aPref)
zoomRegion.setInvertLightness(aPref);
@@ -488,6 +500,10 @@ const Magnifier = new Lang.Class({
Lang.bind(this, this._updateClampMode));
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
Lang.bind(this, this._updateMouseTrackingMode));
+ this._settings.connect('changed::' + FOCUS_TRACKING_KEY,
+ Lang.bind(this, this._updateFocusTrackingMode));
+ this._settings.connect('changed::' + CARET_TRACKING_KEY,
+ Lang.bind(this, this._updateCaretTrackingMode));
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
Lang.bind(this, this._updateInvertLightness));
@@ -585,6 +601,24 @@ const Magnifier = new Lang.Class({
}
},
+ _updateFocusTrackingMode: function() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setFocusTrackingMode(
+ this._settings.get_enum(FOCUS_TRACKING_KEY)
+ );
+ }
+ },
+
+ _updateCaretTrackingMode: function() {
+ // Applies only to the first zoom region.
+ if (this._zoomRegions.length) {
+ this._zoomRegions[0].setCaretTrackingMode(
+ this._settings.get_enum(CARET_TRACKING_KEY)
+ );
+ }
+ },
+
_updateInvertLightness: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
@@ -623,7 +657,7 @@ const Magnifier = new Lang.Class({
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
this._zoomRegions[0].setContrast(contrast);
}
- },
+ }
});
Signals.addSignalMethods(Magnifier.prototype);
@@ -632,8 +666,11 @@ const ZoomRegion = new Lang.Class({
_init: function(magnifier, mouseSourceActor) {
this._magnifier = magnifier;
+ this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
+ this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
+ this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
this._clampScrollingAtEdges = false;
this._lensMode = false;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
@@ -659,9 +696,35 @@ const ZoomRegion = new Lang.Class({
this._xMagFactor = 1;
this._yMagFactor = 1;
this._followingCursor = false;
+ this._xFocus = 0;
+ this._yFocus = 0;
+ this._xCaret = 0;
+ this._yCaret = 0;
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
+ this._focusCaretTracker.connect('caret-moved',
+ Lang.bind(this, this._updateCaret));
+ this._focusCaretTracker.connect('focus-changed',
+ Lang.bind(this, this._updateFocus));
+ },
+
+ _updateFocus: function(caller, event) {
+ let component = event.source.get_component_iface();
+ if (!component || event.detail1 != 1)
+ return;
+ let extents = component.get_extents(Atspi.CoordType.SCREEN);
+ [this._xFocus, this._yFocus] = [extents.x, extents.y]
+ this._centerFromFocusPosition();
+ },
+
+ _updateCaret: function(caller, event) {
+ let text = event.source.get_text_iface();
+ if (!text)
+ return;
+ let extents = text.get_character_extents(text.get_caret_offset(), 0);
+ [this._xCaret, this._yCaret] = [extents.x, extents.y];
+ this._centerFromCaretPosition();
},
/**
@@ -733,6 +796,30 @@ const ZoomRegion = new Lang.Class({
},
/**
+ * setFocusTrackingMode
+ * @mode: One of the enum FocusTrackingMode values.
+ */
+ setFocusTrackingMode: function(mode) {
+ this._focusTrackingMode = mode;
+ if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
+ this._focusCaretTracker.deregisterFocusListener();
+ else
+ this._focusCaretTracker.registerFocusListener();
+ },
+
+ /**
+ * setCaretTrackingMode
+ * @mode: One of the enum CaretTrackingMode values.
+ */
+ setCaretTrackingMode: function(mode) {
+ this._caretTrackingMode = mode;
+ if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
+ this._focusCaretTracker.deregisterCaretListener();
+ else
+ this._focusCaretTracker.registerCaretListener();
+ },
+
+ /**
* setViewPort
* Sets the position and size of the ZoomRegion on screen.
* @viewPort: Object defining the position and size of the view port.
@@ -1243,19 +1330,47 @@ const ZoomRegion = new Lang.Class({
let yMouse = this._magnifier.yMouse;
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
- return this._centerFromMouseProportional(xMouse, yMouse);
+ return this._centerFromPointProportional(xMouse, yMouse);
}
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
- return this._centerFromMousePush(xMouse, yMouse);
+ return this._centerFromPointPush(xMouse, yMouse);
}
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
- return this._centerFromMouseCentered(xMouse, yMouse);
+ return this._centerFromPointCentered(xMouse, yMouse);
}
return null; // Should never be hit
},
- _centerFromMousePush: function(xMouse, yMouse) {
+ _centerFromCaretPosition: function() {
+ let xCaret = this._xCaret;
+ let yCaret = this._yCaret;
+
+ if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
+ [xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
+ else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
+ [xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
+ else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
+ [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
+
+ this.scrollContentsTo(xCaret, yCaret);
+ },
+
+ _centerFromFocusPosition: function() {
+ let xFocus = this._xFocus;
+ let yFocus = this._yFocus;
+
+ if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
+ [xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
+ else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
+ [xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
+ else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
+ [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
+
+ this.scrollContentsTo(xFocus, yFocus);
+ },
+
+ _centerFromPointPush: function(xPoint, yPoint) {
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
let xPos = xRoi + widthRoi / 2;
@@ -1263,20 +1378,20 @@ const ZoomRegion = new Lang.Class({
let xRoiRight = xRoi + widthRoi - cursorWidth;
let yRoiBottom = yRoi + heightRoi - cursorHeight;
- if (xMouse < xRoi)
- xPos -= (xRoi - xMouse);
- else if (xMouse > xRoiRight)
- xPos += (xMouse - xRoiRight);
+ if (xPoint < xRoi)
+ xPos -= (xRoi - xPoint);
+ else if (xPoint > xRoiRight)
+ xPos += (xPoint - xRoiRight);
- if (yMouse < yRoi)
- yPos -= (yRoi - yMouse);
- else if (yMouse > yRoiBottom)
- yPos += (yMouse - yRoiBottom);
+ if (yPoint < yRoi)
+ yPos -= (yRoi - yPoint);
+ else if (yPoint > yRoiBottom)
+ yPos += (yPoint - yRoiBottom);
return [xPos, yPos];
},
- _centerFromMouseProportional: function(xMouse, yMouse) {
+ _centerFromPointProportional: function(xPoint, yPoint) {
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
let halfScreenWidth = global.screen_width / 2;
let halfScreenHeight = global.screen_height / 2;
@@ -1285,16 +1400,16 @@ const ZoomRegion = new Lang.Class({
let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
let xPadding = unscaledPadding / this._xMagFactor;
let yPadding = unscaledPadding / this._yMagFactor;
- let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth; // -1 ... 1
- let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1
- let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding);
- let yPos = yMouse - yProportion * (heightRoi /2 - yPadding);
+ let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth; // -1 ... 1
+ let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1
+ let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding);
+ let yPos = yPoint - yProportion * (heightRoi /2 - yPadding);
return [xPos, yPos];
},
- _centerFromMouseCentered: function(xMouse, yMouse) {
- return [xMouse, yMouse];
+ _centerFromPointCentered: function(xPoint, yPoint) {
+ return [xPoint, yPoint];
},
_screenToViewPort: function(screenX, screenY) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]