Added a toggle follow key to the magnifying glass.

This commit is contained in:
Æmotion
2025-05-15 05:28:23 -07:00
parent a96d0535ee
commit c7ff755fde
2 changed files with 97 additions and 49 deletions
+7 -4
View File
@@ -35,6 +35,7 @@ ComfyUI-MagnifyGlass adds an intuitive magnifying glass to your ComfyUI canvas,
- **⚙️ Settings Integration**: All options are configurable through the ComfyUI settings dialog.
- **💾 Persistent Offsets**: Manually adjusted offsets are saved and loaded across sessions.
- **🖼️ Texture Filtering**: Choose between Linear (smooth) and Nearest (pixelated) texture filtering for the magnified view.
- **🔒 Always Active Mode**: Toggle the magnifier to stay on/off without holding keys.
![Magnify Glass Settings PNG](images/magnify_glass_settings.png)
@@ -60,7 +61,7 @@ Restart ComfyUI after installation. No additional `pip install` steps are typica
## 🚀 Usage
1. **Activation**:
- By default, press and hold `Alt + X` to activate the magnifying glass.
- By default, press and hold `Alt + X` to activate the magnifying glass. If "Always Active Mode" is enabled, this combination toggles the magnifier on or off.
- The activation key and whether `Alt/Option` is required can be changed in the settings.
2. **Using the Magnifier**:
- While active, move your mouse over the ComfyUI canvas to see the magnified view.
@@ -68,7 +69,8 @@ Restart ComfyUI after installation. No additional `pip install` steps are typica
3. **Controls (while active)**:
- **Arrow Keys**: Nudge the magnified view (adjusts `offsetX`/`offsetY`).
- **Shift + Arrow Keys**: Nudge the magnified view by a larger step.
- **Reset Key (default 'R')**: Resets `offsetX` and `offsetY` to zero. (Configurable, may require Alt/Option).
- **Reset Key (default 'R')**: Resets `offsetX` and `offsetY` to zero. (Configurable, respects the global "Require Alt/Option Key" setting).
- **Toggle Follow Key (default 'H')**: Toggles the `Follow Cursor` behavior on/off. (Configurable, respects the global "Require Alt/Option Key" setting).
4. **Configuration**:
- Access all settings by clicking the ⚙️ (Settings) icon in ComfyUI, then find the "🔍 Magnify Glass" section.
@@ -86,16 +88,17 @@ All options are available in the ComfyUI settings dialog under the "🔍 Magnify
| **Border Width (px)** | Width of the border around the magnifying glass. | `2` |
| **Border Color** | Color of the border around the magnifying glass. | `#ffffff` |
| **Activation Key** | The key (case-insensitive) to hold down to activate the magnifier. | `x` |
| **Require Alt/Option Key** | If Yes, Alt (Windows/Linux) or Option (Mac) must be held along with the activation key. | `Yes (true)` |
| **Require Alt/Option Key** | If Yes, Alt (Windows/Linux) or Option (Mac) must be held for activation and for reset. | `Yes (true)` |
| **Follow Cursor Position** | If Yes, the magnifier window moves with the cursor. If No, it stays where activated. | `Yes (true)` |
| **Offset Adjust Step (Graph Units)** | How many graph units the view shifts when pressing arrow keys (Shift+Arrow = 5x). | `5` |
| **Reset Offset Key** | The key to press to reset the view offset while active. | `r` |
| **Require Alt/Option for Reset** | If Yes, Alt/Option must be held with the reset key. | `Yes (true)` |
| **Debug Mode** | Show detailed logging and the debug visualization overlay. | `Disabled (false)`|
| **Glass Position** | Position of the magnifying glass relative to the cursor. | `Bottom` |
| **Shape** | Shape of the magnifying glass (Circle, Square, Rounded Square). | `Circle` |
| **Show Border** | Enable or disable the border around the magnifying glass. | `Yes (true)` |
| **Texture Filtering** | Controls how the magnified image is scaled. Linear is smoother, Nearest is sharper/pixelated. | `Linear` |
| **Always Active Mode** | If Yes, activating the magnifier keeps it on until activated again. If No, it deactivates on key release. | `No (false)` |
| **Toggle Follow Key** | The key to toggle 'Follow Cursor' behavior. Works with Alt/Option if 'Require Alt/Option Key' is Yes. | `h` |
## ❓ Troubleshooting
+90 -45
View File
@@ -17,16 +17,17 @@ app.registerExtension({
"🔍MagnifyGlass.BorderColor": "#ffffff",
"🔍MagnifyGlass.BorderWidth": 2,
"🔍MagnifyGlass.ActivationKey": "x",
"🔍MagnifyGlass.AltRequired": true,
"🔍MagnifyGlass.AltRequired": false,
"🔍MagnifyGlass.FollowCursor": true,
"🔍MagnifyGlass.DebugMode": false,
"🔍MagnifyGlass.OffsetStep": 5,
"🔍MagnifyGlass.GlassPosition": "Bottom",
"🔍MagnifyGlass.ResetKey": "r",
"🔍MagnifyGlass.ResetAltRequired": true,
"🔍MagnifyGlass.GlassShape": "Circle",
"🔍MagnifyGlass.ResetKey": "o",
"🔍MagnifyGlass.GlassShape": "Rounded Square",
"🔍MagnifyGlass.BorderEnabled": true,
"🔍MagnifyGlass.TextureFiltering": "Linear",
"🔍MagnifyGlass.AlwaysActiveMode": false,
"🔍MagnifyGlass.ToggleFollowCursorKey": "h",
};
// Function to safely get settings values
@@ -355,10 +356,11 @@ app.registerExtension({
this.config.offsetStep = getSettingValue("🔍MagnifyGlass.OffsetStep", DEFAULT_SETTINGS["🔍MagnifyGlass.OffsetStep"]);
this.config.glassPosition = getSettingValue("🔍MagnifyGlass.GlassPosition", DEFAULT_SETTINGS["🔍MagnifyGlass.GlassPosition"]);
this.config.resetKey = getSettingValue("🔍MagnifyGlass.ResetKey", DEFAULT_SETTINGS["🔍MagnifyGlass.ResetKey"]);
this.config.resetAltRequired = getSettingValue("🔍MagnifyGlass.ResetAltRequired", DEFAULT_SETTINGS["🔍MagnifyGlass.ResetAltRequired"]);
this.config.glassShape = getSettingValue("🔍MagnifyGlass.GlassShape", DEFAULT_SETTINGS["🔍MagnifyGlass.GlassShape"]);
this.config.borderEnabled = getSettingValue("🔍MagnifyGlass.BorderEnabled", DEFAULT_SETTINGS["🔍MagnifyGlass.BorderEnabled"]);
this.config.textureFiltering = getSettingValue("🔍MagnifyGlass.TextureFiltering", DEFAULT_SETTINGS["🔍MagnifyGlass.TextureFiltering"]);
this.config.alwaysActiveMode = getSettingValue("🔍MagnifyGlass.AlwaysActiveMode", DEFAULT_SETTINGS["🔍MagnifyGlass.AlwaysActiveMode"]);
this.config.toggleFollowCursorKey = getSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", DEFAULT_SETTINGS["🔍MagnifyGlass.ToggleFollowCursorKey"]);
// Offsets X/Y are managed separately via load/save/arrow keys
}
@@ -432,10 +434,11 @@ app.registerExtension({
this.offsetStep = DEFAULT_SETTINGS["🔍MagnifyGlass.OffsetStep"];
this.glassPosition = DEFAULT_SETTINGS["🔍MagnifyGlass.GlassPosition"];
this.resetKey = DEFAULT_SETTINGS["🔍MagnifyGlass.ResetKey"];
this.resetAltRequired = DEFAULT_SETTINGS["🔍MagnifyGlass.ResetAltRequired"];
this.glassShape = DEFAULT_SETTINGS["🔍MagnifyGlass.GlassShape"];
this.borderEnabled = DEFAULT_SETTINGS["🔍MagnifyGlass.BorderEnabled"];
this.textureFiltering = DEFAULT_SETTINGS["🔍MagnifyGlass.TextureFiltering"];
this.alwaysActiveMode = DEFAULT_SETTINGS["🔍MagnifyGlass.AlwaysActiveMode"];
this.toggleFollowCursorKey = DEFAULT_SETTINGS["🔍MagnifyGlass.ToggleFollowCursorKey"];
// Alignment adjustment parameters - managed separately
this.offsetX = 0; // Default before loading saved
@@ -455,10 +458,11 @@ app.registerExtension({
this.offsetStep = getSettingValue("🔍MagnifyGlass.OffsetStep", this.offsetStep);
this.glassPosition = getSettingValue("🔍MagnifyGlass.GlassPosition", this.glassPosition);
this.resetKey = getSettingValue("🔍MagnifyGlass.ResetKey", this.resetKey);
this.resetAltRequired = getSettingValue("🔍MagnifyGlass.ResetAltRequired", this.resetAltRequired);
this.glassShape = getSettingValue("🔍MagnifyGlass.GlassShape", this.glassShape);
this.borderEnabled = getSettingValue("🔍MagnifyGlass.BorderEnabled", this.borderEnabled);
this.textureFiltering = getSettingValue("🔍MagnifyGlass.TextureFiltering", this.textureFiltering);
this.alwaysActiveMode = getSettingValue("🔍MagnifyGlass.AlwaysActiveMode", this.alwaysActiveMode);
this.toggleFollowCursorKey = getSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", this.toggleFollowCursorKey);
}
loadSavedOffsets() {
@@ -933,12 +937,21 @@ app.registerExtension({
// Magnifier activation
if (e.key.toLowerCase() === config.activationKey &&
(!config.altRequired || e.altKey)) {
if (!state.active) {
state.active = true;
this.magnifyGlass.ui.show();
// On activation, immediately update cursor position from current mouse position
this.updateInitialPosition();
if (config.alwaysActiveMode) {
if (state.active) {
state.active = false;
this.magnifyGlass.ui.hide();
} else {
state.active = true;
this.magnifyGlass.ui.show();
this.updateInitialPosition(); // Update position when activating
}
} else {
if (!state.active) {
state.active = true;
this.magnifyGlass.ui.show();
this.updateInitialPosition(); // Update position when activating
}
}
}
@@ -967,7 +980,7 @@ app.registerExtension({
offsetChanged = true;
e.preventDefault();
} else if (e.key.toLowerCase() === config.resetKey.toLowerCase() &&
(!config.resetAltRequired || e.altKey)) { // Use configured reset key & check Alt
(!config.altRequired || e.altKey)) { // Use configured reset key & check Alt
// Reset offsets to zero
config.offsetX = 0;
config.offsetY = 0;
@@ -975,6 +988,19 @@ app.registerExtension({
e.preventDefault();
}
// Toggle Follow Cursor Key
if (e.key.toLowerCase() === config.toggleFollowCursorKey &&
(!config.altRequired || e.altKey)) {
config.followCursor = !config.followCursor;
this.magnifyGlass.debugger.log(`Follow Cursor Toggled: ${config.followCursor ? 'ON' : 'OFF'}`);
if (state.active && config.followCursor) {
// If toggled ON and active, update glass position to current cursor
this.magnifyGlass.ui.positionGlass(this.magnifyGlass.lastKnownMousePosition.x, this.magnifyGlass.lastKnownMousePosition.y);
}
// We might also want to trigger an update of the settings UI if it shows this value directly, but that's more complex.
e.preventDefault();
}
// If offset was changed, update the view
if (offsetChanged) {
// Save the changes to localStorage
@@ -989,14 +1015,12 @@ app.registerExtension({
const state = this.magnifyGlass.state;
// Check if the released key is the activation key OR if Alt was required and Alt was released.
if (e.key.toLowerCase() === config.activationKey ||
(config.altRequired && e.key === "Alt")) {
if (!config.alwaysActiveMode &&
(e.key.toLowerCase() === config.activationKey ||
(config.altRequired && e.key === "Alt"))) {
// Only deactivate if the *other* key required for activation is NOT still pressed.
// This handles cases like: Alt+X, release X (deactivate), or Alt+X, release Alt (deactivate).
// But if Alt+X, release X, but Alt is still held for another purpose, it shouldn't deactivate if activationKey is still held (though less common).
// The primary goal is to ensure releasing *either* key (if both were used) deactivates.
let shouldDeactivate = false;
if (config.altRequired) {
// If Alt is required, releasing either Alt or the activation key deactivates.
@@ -1056,21 +1080,24 @@ app.registerExtension({
updateInitialPosition() {
if (!this.magnifyGlass.litegraphCanvas) return;
const rect = this.magnifyGlass.litegraphCanvas.getBoundingClientRect();
const clientX = this.magnifyGlass.lastKnownMousePosition.x;
const clientY = this.magnifyGlass.lastKnownMousePosition.y;
// Only use the position if it's over the canvas
if (clientX >= rect.left && clientX <= rect.right &&
clientY >= rect.top && clientY <= rect.bottom) {
// If alwaysActiveMode is true, we want to set the initial position
// even if the cursor is not strictly over the canvas at the moment of activation.
// However, we still need a valid canvas context for calculations.
// The current logic in handleMouseMove which sets state.x/y only when over canvas remains crucial
// for continuous updates. This initial positioning is a one-shot for activation.
const isOverCanvas = clientX >= rect.left && clientX <= rect.right &&
clientY >= rect.top && clientY <= rect.bottom;
if (this.magnifyGlass.config.alwaysActiveMode || isOverCanvas) {
const cssMouseXOnCanvas = clientX - rect.left;
const cssMouseYOnCanvas = clientY - rect.top;
// Convert CSS coordinates on canvas to actual canvas pixel coordinates
const canvasElement = this.magnifyGlass.litegraphCanvas;
// Ensure rect.width and rect.height are not zero to prevent division by zero
const scaleX = rect.width > 0 ? canvasElement.width / rect.width : 1;
const scaleY = rect.height > 0 ? canvasElement.height / rect.height : 1;
@@ -1079,7 +1106,7 @@ app.registerExtension({
this.magnifyGlass.state.x = pixelX;
this.magnifyGlass.state.y = pixelY;
// Position the glass immediately based on current cursor
this.magnifyGlass.ui.positionGlass(clientX, clientY);
this.magnifyGlass.updateMagnifiedView();
@@ -1324,11 +1351,11 @@ app.registerExtension({
type: "combo",
options: [ { value: true, text: "Yes" }, { value: false, text: "No" } ],
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.AltRequired"],
tooltip: "If Yes, Alt (Windows/Linux) or Option (Mac) must be held along with the activation key.",
tooltip: "If Yes, Alt (Windows/Linux) or Option (Mac) must be held for activation and for reset.",
onChange: (value) => {
if (magnifyGlass && magnifyGlass.config) {
magnifyGlass.config.altRequired = value;
magnifyGlass.debugger.log(`Activation key set to ${magnifyGlass.config.altRequired ? 'Alt/Option+' : ''}${magnifyGlass.config.activationKey.toUpperCase()}`);
magnifyGlass.debugger.log(`Require Alt/Option set to ${value}. Activation: ${magnifyGlass.config.altRequired ? 'Alt/Option+' : ''}${magnifyGlass.config.activationKey.toUpperCase()}, Reset: ${magnifyGlass.config.altRequired ? 'Alt/Option+' : ''}${magnifyGlass.config.resetKey.toUpperCase()}`);
}
}
});
@@ -1378,21 +1405,6 @@ app.registerExtension({
}
});
app.ui.settings.addSetting({
id: "🔍MagnifyGlass.ResetAltRequired",
name: "⌨️ Magnify Glass: Require Alt/Option for Reset",
type: "combo",
options: [ { value: true, text: "Yes" }, { value: false, text: "No" } ],
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.ResetAltRequired"],
tooltip: "If Yes, Alt (Windows/Linux) or Option (Mac) must be held along with the reset key.",
onChange: (value) => {
if (magnifyGlass && magnifyGlass.config) {
magnifyGlass.config.resetAltRequired = value;
magnifyGlass.debugger.log(`Require Alt/Option for Reset set to ${value}. Reset key combination is now ${magnifyGlass.config.resetAltRequired ? 'Alt/Option+' : ''}${magnifyGlass.config.resetKey.toUpperCase()}`);
}
}
});
app.ui.settings.addSetting({
id: "🔍MagnifyGlass.DebugMode",
name: "🐞 Magnify Glass: Debug Mode",
@@ -1497,6 +1509,39 @@ app.registerExtension({
}
});
app.ui.settings.addSetting({
id: "🔍MagnifyGlass.AlwaysActiveMode",
name: "🔒 Magnify Glass: Always Active Mode",
type: "combo",
options: [
{ value: true, text: "Yes" },
{ value: false, text: "No" }
],
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.AlwaysActiveMode"],
tooltip: "If Yes, activating the magnifier keeps it on until activated again. If No, it deactivates on key release.",
onChange: (value) => {
if (magnifyGlass && magnifyGlass.config) {
magnifyGlass.config.alwaysActiveMode = value;
// No immediate UI change needed other than behavior change
}
}
});
app.ui.settings.addSetting({
id: "🔍MagnifyGlass.ToggleFollowCursorKey",
name: "⌨️ Magnify Glass: Toggle Follow Key",
type: "combo",
options: ["f", "g", "h", "j", "k"], // Example keys, can be expanded
defaultValue: DEFAULT_SETTINGS["🔍MagnifyGlass.ToggleFollowCursorKey"],
tooltip: "The key (case-insensitive) to toggle the 'Follow Cursor' behavior. Works with Alt/Option if 'Require Alt/Option Key' is Yes.",
onChange: (value) => {
if (magnifyGlass && magnifyGlass.config) {
magnifyGlass.config.toggleFollowCursorKey = value.toLowerCase();
magnifyGlass.debugger.log(`Toggle Follow Cursor key set to ${magnifyGlass.config.altRequired ? 'Alt/Option+' : ''}${magnifyGlass.config.toggleFollowCursorKey.toUpperCase()}`);
}
}
});
// --- Initialization ---
// Load settings into the config object *after* settings are registered