summaryrefslogtreecommitdiffstats
path: root/js/ui/pointerA11yTimeout.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/ui/pointerA11yTimeout.js')
-rw-r--r--js/ui/pointerA11yTimeout.js134
1 files changed, 134 insertions, 0 deletions
diff --git a/js/ui/pointerA11yTimeout.js b/js/ui/pointerA11yTimeout.js
new file mode 100644
index 0000000..263cc3e
--- /dev/null
+++ b/js/ui/pointerA11yTimeout.js
@@ -0,0 +1,134 @@
+/* exported PointerA11yTimeout */
+const { Clutter, GObject, Meta, St } = imports.gi;
+const Main = imports.ui.main;
+const Cairo = imports.cairo;
+
+const SUCCESS_ZOOM_OUT_DURATION = 150;
+
+var PieTimer = GObject.registerClass({
+ Properties: {
+ 'angle': GObject.ParamSpec.double(
+ 'angle', 'angle', 'angle',
+ GObject.ParamFlags.READWRITE,
+ 0, 2 * Math.PI, 0),
+ },
+}, class PieTimer extends St.DrawingArea {
+ _init() {
+ this._angle = 0;
+ super._init({
+ style_class: 'pie-timer',
+ opacity: 0,
+ visible: false,
+ can_focus: false,
+ reactive: false,
+ });
+
+ this.set_pivot_point(0.5, 0.5);
+ }
+
+ get angle() {
+ return this._angle;
+ }
+
+ set angle(angle) {
+ if (this._angle == angle)
+ return;
+
+ this._angle = angle;
+ this.notify('angle');
+ this.queue_repaint();
+ }
+
+ 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 startAngle = 3 * Math.PI / 2;
+ let endAngle = startAngle + this._angle;
+
+ let cr = this.get_context();
+ cr.setLineCap(Cairo.LineCap.ROUND);
+ cr.setLineJoin(Cairo.LineJoin.ROUND);
+ cr.translate(width / 2, height / 2);
+
+ if (this._angle < 2 * Math.PI)
+ cr.moveTo(0, 0);
+
+ cr.arc(0, 0, radius - borderWidth, startAngle, endAngle);
+
+ if (this._angle < 2 * Math.PI)
+ 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) {
+ this.x = x - this.width / 2;
+ this.y = y - this.height / 2;
+ this.show();
+
+ this.ease({
+ opacity: 255,
+ duration: duration / 4,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+
+ this.ease_property('angle', 2 * Math.PI, {
+ duration,
+ mode: Clutter.AnimationMode.LINEAR,
+ onComplete: this._onTransitionComplete.bind(this),
+ });
+ }
+
+ _onTransitionComplete() {
+ this.ease({
+ scale_x: 2,
+ scale_y: 2,
+ opacity: 0,
+ duration: SUCCESS_ZOOM_OUT_DURATION,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onStopped: () => this.destroy(),
+ });
+ }
+});
+
+var PointerA11yTimeout = class PointerA11yTimeout {
+ constructor() {
+ let seat = Clutter.get_default_backend().get_default_seat();
+
+ seat.connect('ptr-a11y-timeout-started', (o, device, type, timeout) => {
+ let [x, y] = global.get_pointer();
+
+ this._pieTimer = new PieTimer();
+ Main.uiGroup.add_actor(this._pieTimer);
+ Main.uiGroup.set_child_above_sibling(this._pieTimer, null);
+
+ this._pieTimer.start(x, y, timeout);
+
+ if (type == Clutter.PointerA11yTimeoutType.GESTURE)
+ global.display.set_cursor(Meta.Cursor.CROSSHAIR);
+ });
+
+ seat.connect('ptr-a11y-timeout-stopped', (o, device, type, clicked) => {
+ if (!clicked)
+ this._pieTimer.destroy();
+
+ if (type == Clutter.PointerA11yTimeoutType.GESTURE)
+ global.display.set_cursor(Meta.Cursor.DEFAULT);
+ });
+ }
+};