Adds a keybind into the module that allows GMs to temporarily hide their cursor from everyone else (including other GMs) #24

Merged
Oliver merged 4 commits from feature/hide-cursor-temporarily into main 2025-12-19 03:05:18 +00:00
3 changed files with 144 additions and 0 deletions
Showing only changes of commit f58c8411aa - Show all commits

View file

@ -65,6 +65,12 @@
"label": "Configure Hotbar" "label": "Configure Hotbar"
} }
}, },
"keybindings": {
"toggleMouseBroadcast": {
"name": "Toggle Mouse Position",
Oliver marked this conversation as resolved Outdated

Lets use "Toggle Show Cursor" to use the verbage that Foundry utilizes within the permission config

Lets use "Toggle Show Cursor" to use the verbage that Foundry utilizes within the permission config
"hint": "(v13+) Temporarily turns off or on the mouse cursor position that players see."
}
},
"apps": { "apps": {
"no-settings-to-display": "No settings to display", "no-settings-to-display": "No settings to display",
"make-global-reference": "Make Global Reference" "make-global-reference": "Make Global Reference"
@ -77,6 +83,12 @@
"ignoreAndDontShowAgain": "", "ignoreAndDontShowAgain": "",
"disableEntirely": "" "disableEntirely": ""
} }
},
"notifs": {
"toggleMouseBroadcast": {
"hidingCursor": "Hiding your cursor from others!",
"showingCursor": "Showing your cursor to others!"
}
} }
} }
} }

View file

@ -1,5 +1,6 @@
// Settings // Settings
import { preventMovementHistory } from "../settings/preventMovementHistory.mjs"; import { preventMovementHistory } from "../settings/preventMovementHistory.mjs";
import { toggleMouseBroadcast } from "../settings/toggleMouseBroadcast.mjs";
// Utils // Utils
import { Logger } from "../utils/Logger.mjs"; import { Logger } from "../utils/Logger.mjs";
@ -14,4 +15,5 @@ Hooks.on(`init`, () => {
Logger.log(`Initializing`); Logger.log(`Initializing`);
preventMovementHistory(); preventMovementHistory();
toggleMouseBroadcast();
}); });

View file

@ -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);
Oliver marked this conversation as resolved

Unset notifID back to null

Unset `notifID` back to null
};
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) {
Oliver marked this conversation as resolved

This can be removed as it's covered by the case at the end of the function

This can be removed as it's covered by the case at the end of the function
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