Skip to content

Commit b83fe2e

Browse files
anaximenorcalixte
andauthored
mouse-click-effects@anaximeno: Version 1.0.0 (#748)
* Ensure proper initialization before enable * Add pointer watcher Imported from the mouse-shake-zoom@rcalixte extension Co-Authored-By: Rick Calixte <10281587+rcalixte@users.noreply.github.com> * Add structure to use pointer watch on the extension * Fix const import name * Re-organize settings layout * Update settings schema * Bump version * Add Idle Monitor * Made some updates Bind some settings Made some refactoring * Update mouse movement tracker implementation * Add option to hide or persist icon on mouse stopped * Improve handling when changing mouse tracker icon * Add new icon named Aim * Set idle tracker feature for a future update * Update translation texts * Update settings defaults --------- Co-authored-by: Rick Calixte <10281587+rcalixte@users.noreply.github.com>
1 parent a9fbee2 commit b83fe2e

File tree

16 files changed

+1199
-211
lines changed

16 files changed

+1199
-211
lines changed

mouse-click-effects@anaximeno/files/mouse-click-effects@anaximeno/5.4/constants.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@
2020
const UUID = "mouse-click-effects@anaximeno";
2121

2222
const PAUSE_EFFECTS_KEY = `${UUID}-bind-pause-effects`;
23+
24+
const CLICK_DEBOUNCE_MS = 10;
25+
26+
const POINTER_WATCH_MS = 10;
27+
28+
const MOUSE_PARADE_DELAY_MS = 250;
29+
30+
const IDLE_TIME = 1000;

mouse-click-effects@anaximeno/files/mouse-click-effects@anaximeno/5.4/extension.js

Lines changed: 154 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ const ByteArray = imports.byteArray;
2424
const { Atspi, GLib, Gio } = imports.gi;
2525
const { ClickAnimationFactory, ClickAnimationModes } = require("./clickAnimations.js");
2626
const { Debouncer } = require("./helpers.js");
27-
const { UUID, PAUSE_EFFECTS_KEY } = require("./constants.js");
28-
27+
const { UUID, PAUSE_EFFECTS_KEY, CLICK_DEBOUNCE_MS, POINTER_WATCH_MS, IDLE_TIME } = require("./constants.js");
28+
const { IdleMonitor } = require("./idleMonitor.js");
29+
const { MouseMovementTracker } = require("./mouseMovementTracker.js");
2930

3031
Gettext.bindtextdomain(UUID, `${GLib.get_home_dir()}/.local/share/locale`);
3132

@@ -37,11 +38,13 @@ function _(text) {
3738

3839

3940
const ClickType = Object.freeze({
40-
LEFT: "left_click",
41-
MIDDLE: "middle_click",
42-
RIGHT: "right_click",
41+
LEFT: "left_click",
42+
MIDDLE: "middle_click",
43+
RIGHT: "right_click",
4344
PAUSE_ON: "pause_on",
4445
PAUSE_OFF: "pause_off",
46+
MOUSE_IDLE: "mouse_idle",
47+
MOUSE_MOV: "mouse_mov",
4548
});
4649

4750

@@ -52,6 +55,7 @@ class MouseClickEffects {
5255
this.pause_icon_path = `${this.app_icons_dir}/extra/pause.svg`;
5356
this.settings = this._setup_settings(this.metadata.uuid);
5457
this.data_dir = this._init_data_dir(this.metadata.uuid);
58+
this.colored_icon_store = {};
5559

5660
this.clickAnimator = ClickAnimationFactory.createForMode(this.animation_mode);
5761

@@ -61,15 +65,18 @@ class MouseClickEffects {
6165
return;
6266
}
6367
this.animate_click(...args);
64-
}, 10);
68+
}, CLICK_DEBOUNCE_MS);
6569

6670
this.listener = Atspi.EventListener.new(this.on_mouse_click.bind(this));
71+
this.idleMonitor = null;
72+
73+
this.mouse_movement_tracker = null;
6774

68-
this.colored_icon_store = {};
6975
this.enabled = false;
76+
this.set_active(false);
7077
}
7178

72-
_init_data_dir(uuid) {
79+
_init_data_dir(uuid) {
7380
let data_dir = `${GLib.get_user_cache_dir()}/${uuid}`;
7481

7582
if (GLib.mkdir_with_parents(`${data_dir}/icons`, 0o777) < 0) {
@@ -92,12 +99,45 @@ class MouseClickEffects {
9299
{
93100
key: "icon-mode",
94101
value: "icon_mode",
95-
cb: this.update_colored_icons,
102+
cb: () => {
103+
this.update_colored_icons();
104+
if (this.mouse_movement_tracker) {
105+
let icon = this.get_click_icon(
106+
this.icon_mode,
107+
ClickType.MOUSE_MOV,
108+
this.mouse_movement_color,
109+
);
110+
this.mouse_movement_tracker.update({
111+
icon: icon,
112+
});
113+
}
114+
},
96115
},
97116
{
98117
key: "size",
99118
value: "size",
100-
cb: null,
119+
cb: () => {
120+
if (this.mouse_movement_tracker) {
121+
this.mouse_movement_tracker.update({
122+
size: this.size,
123+
});
124+
}
125+
},
126+
},
127+
{
128+
key: "idle-animation-mode",
129+
value: "idle_animation_mode",
130+
cb: null, // TODO
131+
},
132+
{
133+
key: "idle-animation-period",
134+
value: "idle_animation_period",
135+
cb: null, // TODO
136+
},
137+
{
138+
key: "idle-animation-delay",
139+
value: "idle_animation_delay",
140+
cb: null, // TODO
101141
},
102142
{
103143
key: "left-click-effect-enabled",
@@ -119,6 +159,27 @@ class MouseClickEffects {
119159
value: "pause_animation_effects_enabled",
120160
cb: null,
121161
},
162+
{
163+
key: "mouse-movement-tracker-enabled",
164+
value: "mouse_movement_tracker_enabled",
165+
cb: () => this.set_active(this.enabled),
166+
},
167+
{
168+
key: "mouse-movement-tracker-persist-on-stopped-enabled",
169+
value: "mouse_movement_tracker_persist_on_stopped_enabled",
170+
cb: () => {
171+
if (this.mouse_movement_tracker) {
172+
this.mouse_movement_tracker.update({
173+
persist_on_stopped: this.mouse_movement_tracker_persist_on_stopped_enabled,
174+
});
175+
}
176+
},
177+
},
178+
{
179+
key: "mouse-idle-watcher-enabled",
180+
value: "mouse_idle_watcher_enabled",
181+
cb: null, // TODO
182+
},
122183
{
123184
key: "left-click-color",
124185
value: "left_click_color",
@@ -134,10 +195,38 @@ class MouseClickEffects {
134195
value: "right_click_color",
135196
cb: this.update_colored_icons,
136197
},
198+
{
199+
key: "mouse-movement-color",
200+
value: "mouse_movement_color",
201+
cb: () => {
202+
this.update_colored_icons();
203+
if (this.mouse_movement_tracker) {
204+
let icon = this.get_click_icon(
205+
this.icon_mode,
206+
ClickType.MOUSE_MOV,
207+
this.mouse_movement_color,
208+
);
209+
this.mouse_movement_tracker.update({
210+
icon: icon,
211+
});
212+
}
213+
},
214+
},
215+
{
216+
key: "mouse-idle-watcher-color",
217+
value: "mouse_idle_watcher_color",
218+
cb: this.update_colored_icons,
219+
},
137220
{
138221
key: "general-opacity",
139222
value: "general_opacity",
140-
cb: null,
223+
cb: () => {
224+
if (this.mouse_movement_tracker) {
225+
this.mouse_movement_tracker.update({
226+
opacity: this.general_opacity,
227+
});
228+
}
229+
},
141230
},
142231
{
143232
key: "animation-mode",
@@ -156,13 +245,13 @@ class MouseClickEffects {
156245
},
157246
];
158247

159-
bindings.forEach(b => settings.bind(
248+
bindings.forEach(b => settings.bind(
160249
b.key,
161250
b.value,
162251
b.cb ? (...args) => b.cb.call(this, ...args) : null,
163252
));
164253

165-
return settings;
254+
return settings;
166255
}
167256

168257
enable() {
@@ -177,12 +266,12 @@ class MouseClickEffects {
177266

178267
set_keybindings() {
179268
this.unset_keybindings();
180-
Main.keybindingManager.addHotKey(
269+
Main.keybindingManager.addHotKey(
181270
PAUSE_EFFECTS_KEY,
182271
this.pause_effects_binding,
183-
this.on_pause_toggled.bind(this),
272+
this.on_pause_toggled.bind(this),
184273
);
185-
}
274+
}
186275

187276
on_pause_toggled() {
188277
this.set_active(!this.enabled);
@@ -197,22 +286,22 @@ class MouseClickEffects {
197286
}
198287
}
199288

200-
get_click_icon(mode, click_type, color) {
201-
let name = `${mode}_${click_type}_${color}`;
202-
let path = `${this.data_dir}/icons/${name}.svg`;
289+
get_click_icon(mode, click_type, color) {
290+
let name = `${mode}_${click_type}_${color}.svg`;
291+
let path = `${this.data_dir}/icons/${name}`;
203292
return this.get_icon_cached(path);
204293
}
205294

206295
get_icon_cached(path) {
207296
if (this.colored_icon_store[path])
208297
return this.colored_icon_store[path];
209298

210-
if (GLib.file_test(path, GLib.FileTest.IS_REGULAR)) {
299+
if (GLib.file_test(path, GLib.FileTest.IS_REGULAR)) {
211300
this.colored_icon_store[path] = Gio.icon_new_for_string(path);
212301
return this.colored_icon_store[path];
213302
}
214303

215-
return null;
304+
return null;
216305
}
217306

218307
disable() {
@@ -232,37 +321,72 @@ class MouseClickEffects {
232321
this.create_icon_data(ClickType.LEFT, this.left_click_color);
233322
this.create_icon_data(ClickType.MIDDLE, this.middle_click_color);
234323
this.create_icon_data(ClickType.RIGHT, this.right_click_color);
324+
this.create_icon_data(ClickType.MOUSE_IDLE, this.mouse_idle_watcher_color);
325+
this.create_icon_data(ClickType.MOUSE_MOV, this.mouse_movement_color);
235326
}
236327

237328
set_active(enabled) {
238329
this.enabled = enabled;
330+
239331
this.listener.deregister('mouse');
332+
if (this.mouse_movement_tracker) {
333+
this.mouse_movement_tracker.finalize();
334+
this.mouse_movement_tracker = null;
335+
}
336+
if (this.idleMonitor) {
337+
this.idleMonitor.finalize();
338+
this.idleMonitor = null;
339+
}
240340

241341
if (enabled) {
242342
this.listener.register('mouse');
243-
global.log(UUID, "Click effects activated");
343+
344+
if (this.mouse_movement_tracker_enabled) {
345+
const icon = this.get_click_icon(this.icon_mode, ClickType.MOUSE_MOV, this.mouse_movement_color);
346+
this.mouse_movement_tracker = new MouseMovementTracker(
347+
icon, this.size, this.general_opacity,
348+
this.mouse_movement_tracker_persist_on_stopped_enabled);
349+
this.mouse_movement_tracker.start();
350+
}
351+
352+
// TODO
353+
// if (this.mouse_idle_watcher_enabled) {
354+
// // XXX: only enable according w respective settings
355+
// this.idleMonitor = new IdleMonitor({
356+
// idle_delay: IDLE_TIME,
357+
// on_idle: this.on_idle_handler,
358+
// on_active: this.on_active_handler,
359+
// on_finish: this.on_finish_handler,
360+
// });
361+
// this.idleMonitor.start();
362+
// }
363+
364+
global.log(UUID, "activated");
244365
} else {
245-
global.log(UUID, "Click effects deactivated");
366+
global.log(UUID, "deactivated");
246367
}
247368
}
248369

249370
create_icon_data(click_type, color) {
250371
if (this.get_click_icon(this.icon_mode, click_type, color))
251372
return true;
252373

253-
let source = Gio.File.new_for_path(`${this.app_icons_dir}/${this.icon_mode}.svg`);
374+
let source = Gio.File.new_for_path(`${this.app_icons_dir}/${this.icon_mode}.svg`);
254375
let [l_success, contents] = source.load_contents(null);
255376

256377
contents = ByteArray.toString(contents);
257378
contents = contents.replace('fill="#000000"', `fill="${color}"`);
258379

259-
let name = `${this.icon_mode}_${click_type}_${color}`;
260-
let dest = Gio.File.new_for_path(`${this.data_dir}/icons/${name}.svg`);
380+
let name = `${this.icon_mode}_${click_type}_${color}.svg`;
381+
let path = `${this.data_dir}/icons/${name}`;
382+
let dest = Gio.File.new_for_path(path);
261383

262384
if (!dest.query_exists(null))
263385
dest.create(Gio.FileCreateFlags.NONE, null);
264386

265387
let [r_success, tag] = dest.replace_contents(contents, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
388+
389+
if (r_success) global.log(UUID, `created colored icon cache for ${name}`);
266390
return r_success;
267391
}
268392

@@ -315,16 +439,16 @@ class MouseClickEffects {
315439
let extension = null;
316440

317441
function enable() {
318-
extension.enable();
442+
extension.enable();
319443
}
320444

321445
function disable() {
322-
extension.disable();
323-
extension = null;
446+
extension.disable();
447+
extension = null;
324448
}
325449

326450
function init(metadata) {
327-
if (!extension) {
451+
if (!extension) {
328452
Atspi.init();
329453
extension = new MouseClickEffects(metadata);
330454
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const { Meta } = imports.gi;
2+
const { IDLE_TIME } = require('./constants.js');
3+
4+
5+
var IdleMonitor = class IdleMonitor {
6+
constructor(params = {idle_delay: IDLE_TIME, on_idle: null, on_active: null, on_finish: null}) {
7+
this.idle_monitor = Meta.IdleMonitor.get_core();
8+
this.idle_delay = idle_delay;
9+
this._on_idle = params.on_idle;
10+
this._on_active = params.on_active;
11+
this._on_finish = params.on_finish;
12+
this._idle_watch_id = null;
13+
this.idle = false;
14+
}
15+
16+
start() {
17+
this.idle_monitor.add_idle_watch(this.idle_delay, this.idle_handler.bind(this));
18+
this.idle = this.idle_monitor.get_idletime() > this.idle_delay;
19+
}
20+
21+
finalize() {
22+
if (this._on_finish) this._on_finish();
23+
if (this._idle_watch_id) this.idle_monitor.remove_watch(this._idle_watch_id);
24+
}
25+
26+
idle_handler() {
27+
this._idle_watch_id = this.idle_monitor.add_user_active_watch(this.active_handler.bind(this));
28+
this.idle = true;
29+
if (this._on_idle) this._on_idle();
30+
}
31+
32+
active_handler() {
33+
if (this._on_active) this._on_active();
34+
}
35+
}

0 commit comments

Comments
 (0)