From f58c8411aa710780e9fb6c95db071c3f2dba5e01 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 14 Dec 2025 15:27:45 -0700 Subject: [PATCH 1/4] Adds a keybind into the module that allows GMs to temporarily hide their cursor from everyone else (including other GMs) --- langs/en-ca.json | 12 +++ module/hooks/init.mjs | 2 + module/settings/toggleMouseBroadcast.mjs | 130 +++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 module/settings/toggleMouseBroadcast.mjs diff --git a/langs/en-ca.json b/langs/en-ca.json index 02cd095..777e3c9 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -65,6 +65,12 @@ "label": "Configure Hotbar" } }, + "keybindings": { + "toggleMouseBroadcast": { + "name": "Toggle Mouse Position", + "hint": "(v13+) Temporarily turns off or on the mouse cursor position that players see." + } + }, "apps": { "no-settings-to-display": "No settings to display", "make-global-reference": "Make Global Reference" @@ -77,6 +83,12 @@ "ignoreAndDontShowAgain": "", "disableEntirely": "" } + }, + "notifs": { + "toggleMouseBroadcast": { + "hidingCursor": "Hiding your cursor from others!", + "showingCursor": "Showing your cursor to others!" + } } } } diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index 60cde1b..d8bae1f 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -1,5 +1,6 @@ // Settings import { preventMovementHistory } from "../settings/preventMovementHistory.mjs"; +import { toggleMouseBroadcast } from "../settings/toggleMouseBroadcast.mjs"; // Utils import { Logger } from "../utils/Logger.mjs"; @@ -14,4 +15,5 @@ Hooks.on(`init`, () => { Logger.log(`Initializing`); preventMovementHistory(); + toggleMouseBroadcast(); }); diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/settings/toggleMouseBroadcast.mjs new file mode 100644 index 0000000..6f70692 --- /dev/null +++ b/module/settings/toggleMouseBroadcast.mjs @@ -0,0 +1,130 @@ +import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; +import { __ID__ } from "../consts.mjs"; +import { Logger } from "../utils/Logger.mjs"; + +const key = `toggleMouseBroadcast`; + +/** @type {number | null} */ +let notifID = null; + +export function toggleMouseBroadcast() { + status[key] = SettingStatusEnum.Unknown; + + // #region Registration + const prevented = Hooks.call(`${__ID__}.preventSetting`, key); + if (!prevented) { + Logger.log(`Preventing setting "${key}" from being registered`); + status[key] = SettingStatusEnum.Blocked; + return; + }; + Logger.log(`Registering setting: ${key}`); + + // MARK: setting + game.settings.register(__ID__, key, { + scope: `client`, + config: false, + default: true, + }); + + // MARK: keybind + game.keybindings.register(__ID__, key, { + name: `OFT.keybindings.${key}.name`, + hint: `OFT.keybindings.${key}.hint`, + precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL, + restricted: true, + editable: [ + { key: `KeyH` }, + ], + onDown: (event) => { + if (!game.user.hasPermission(`SHOW_CURSOR`)) { return }; + event.preventDefault?.(); + const current = game.settings.get(__ID__, key); + + if (current) { + notifID = ui.notifications.warn( + `OFT.notifs.${key}.hidingCursor`, + { console: false, localize: true, permanent: true }, + ).id; + } else { + if (notifID != null) { + ui.notifications.remove(notifID); + }; + ui.notifications.success( + `OFT.notifs.${key}.showingCursor`, + { console: false, localize: true }, + ); + }; + + game.settings.set(__ID__, key, !current); + + // Hide the existing cursor + game.user.broadcastActivity({ cursor: null }); + return true; + }, + }); + // #endregion Registration + + // #region Implementation + Hooks.on(`renderControlsConfig`, renderControlsConfigHandler); + Hooks.once(`ready`, readyHandler); + + class OFTControlsLayer extends CONFIG.Canvas.layers.controls.layerClass { + _onMouseMove(currentPos) { + if (!game.settings.get(__ID__, key)) { + game.user.broadcastActivity({}); + }; + super._onMouseMove(currentPos); + }; + }; + CONFIG.Canvas.layers.controls.layerClass = OFTControlsLayer; + + // #endregion Implementation + + status[key] = SettingStatusEnum.Registered; +}; + +// #region Helpers +const tabGroup = `categories`; + +/** + * Handle showing the "hiding your cursor" notification when the user + * connects to the server initially, allowing the current state to be + * correctly described to the user. + */ +function readyHandler() { + const hideCursor = !game.settings.get(__ID__, key); + const canShowCursor = game.user.hasPermission(`SHOW_CURSOR`); + if (hideCursor && canShowCursor) { + notifID = ui.notifications.warn( + `OFT.notifs.${key}.hidingCursor`, + { console: false, localize: true, permanent: true }, + ).id; + }; +}; + +/** + * Handles hiding the keybinding from the configuration if and when the user + * does not have the SHOW_CURSOR permission since this keybinding is really + * useless if they can't even broadcast their cursor in the first place. + */ +function renderControlsConfigHandler(_app, element) { + + if (game.user.hasPermission(`SHOW_CURSOR`)) { return }; + + const keybindingList = element.querySelector(`section[data-group="${tabGroup}"][data-tab="${__ID__}"]`); + const tabButton = element.querySelector(`button[data-group="${tabGroup}"][data-tab="${__ID__}"]`); + + if (keybindingList.childElementCount === 0) { + tabButton?.remove(); + return; + }; + + const keybind = keybindingList.querySelector(`.form-group[data-action-id="${__ID__}.${key}"]`); + keybind?.remove(); + + if (keybindingList.childElementCount === 0) { + keybindingList.remove(); + tabButton.remove(); + }; +}; +// #endregion Helpers From e6e02301ab66259c4d4c4d9e96e9c66b0a4c7506 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 20:02:55 -0700 Subject: [PATCH 2/4] Improve wording of keybinding name and description --- langs/en-ca.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/langs/en-ca.json b/langs/en-ca.json index 777e3c9..d2e091a 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -67,8 +67,8 @@ }, "keybindings": { "toggleMouseBroadcast": { - "name": "Toggle Mouse Position", - "hint": "(v13+) Temporarily turns off or on the mouse cursor position that players see." + "name": "Toggle Show Cursor", + "hint": "(v13+) Temporarily turns off the mouse cursor position that other players can see. Hides the cursor until you activate this keybind again." } }, "apps": { From 8c12c60815fed61ce57e721c7a07ad3623e0cab4 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 20:03:29 -0700 Subject: [PATCH 3/4] Unset the notification ID when we remove it --- module/settings/toggleMouseBroadcast.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/settings/toggleMouseBroadcast.mjs index 6f70692..189f6db 100644 --- a/module/settings/toggleMouseBroadcast.mjs +++ b/module/settings/toggleMouseBroadcast.mjs @@ -48,6 +48,7 @@ export function toggleMouseBroadcast() { } else { if (notifID != null) { ui.notifications.remove(notifID); + notifID = null; }; ui.notifications.success( `OFT.notifs.${key}.showingCursor`, From f088cc474e953e58f9fb6d19eacc10e162b85d7d Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 20:04:32 -0700 Subject: [PATCH 4/4] Remove excess DOM manipulation --- module/settings/toggleMouseBroadcast.mjs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/settings/toggleMouseBroadcast.mjs index 189f6db..3761f47 100644 --- a/module/settings/toggleMouseBroadcast.mjs +++ b/module/settings/toggleMouseBroadcast.mjs @@ -115,11 +115,6 @@ function renderControlsConfigHandler(_app, element) { const keybindingList = element.querySelector(`section[data-group="${tabGroup}"][data-tab="${__ID__}"]`); const tabButton = element.querySelector(`button[data-group="${tabGroup}"][data-tab="${__ID__}"]`); - if (keybindingList.childElementCount === 0) { - tabButton?.remove(); - return; - }; - const keybind = keybindingList.querySelector(`.form-group[data-action-id="${__ID__}.${key}"]`); keybind?.remove();