Fix critical bugs and improve laptop compatibility
Major fixes: - Fix keybinding closure bug causing all zones to map to zone 9 - Change keybindings from Super+Numpad to Super+Ctrl+1-9 for laptop compatibility - Fix shift-drag detection by implementing continuous modifier polling during window drag - Fix window object reference by using global.display.focus_window - Fix window API compatibility (use property checks instead of get_maximized()) - Correct grab-op-begin/end signal handler parameters Technical improvements: - Add modifier polling (50ms interval) during window drag to detect Shift key press/release - Use Mainloop.idle_add to defer window resize operations - Remove debug logging for production use - Improve error handling and cleanup in destroy() method The extension now fully supports: - Shift-drag window snapping with visual zone overlay - Keyboard shortcuts for direct zone snapping (Super+Ctrl+1-9) - Zone overlay toggle (Super+Z) - Layout cycling (Super+Shift+Z) - Graphical zone editor (Super+Shift+E) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ A window tiling manager extension for Linux Mint's Cinnamon Desktop, inspired by
|
|||||||
- **Super + Z**: Toggle zone overlay (show/hide zones)
|
- **Super + Z**: Toggle zone overlay (show/hide zones)
|
||||||
- **Super + Shift + Z**: Cycle through different layouts
|
- **Super + Shift + Z**: Cycle through different layouts
|
||||||
- **Super + Shift + E**: Open graphical zone editor
|
- **Super + Shift + E**: Open graphical zone editor
|
||||||
- **Super + Numpad 1-9**: Snap focused window to zone 1-9
|
- **Super + Ctrl + 1-9**: Snap focused window to zone 1-9
|
||||||
|
|
||||||
### Mouse Usage
|
### Mouse Usage
|
||||||
|
|
||||||
|
|||||||
123
extension.js
123
extension.js
@@ -333,6 +333,7 @@ ZoneManager.prototype = {
|
|||||||
this.layouts = DEFAULT_LAYOUTS;
|
this.layouts = DEFAULT_LAYOUTS;
|
||||||
this.isShowing = false;
|
this.isShowing = false;
|
||||||
this.dragInProgress = false;
|
this.dragInProgress = false;
|
||||||
|
this.draggedWindow = null;
|
||||||
this.editor = new ZoneEditor();
|
this.editor = new ZoneEditor();
|
||||||
|
|
||||||
// Connect to window grab events
|
// Connect to window grab events
|
||||||
@@ -346,6 +347,9 @@ ZoneManager.prototype = {
|
|||||||
Lang.bind(this, this._onGrabBegin));
|
Lang.bind(this, this._onGrabBegin));
|
||||||
this.grabOpEndId = global.display.connect('grab-op-end',
|
this.grabOpEndId = global.display.connect('grab-op-end',
|
||||||
Lang.bind(this, this._onGrabEnd));
|
Lang.bind(this, this._onGrabEnd));
|
||||||
|
|
||||||
|
// Monitor for modifier key changes
|
||||||
|
this.modifierPollTimeoutId = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupKeybindings: function() {
|
_setupKeybindings: function() {
|
||||||
@@ -363,38 +367,68 @@ ZoneManager.prototype = {
|
|||||||
Lang.bind(this, function() { this.editor.startEditor(); })
|
Lang.bind(this, function() { this.editor.startEditor(); })
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add keybindings for quick snap to zones (Super+Numpad)
|
// Add keybindings for quick snap to zones (Super+Ctrl+1-9)
|
||||||
for (let i = 1; i <= 9; i++) {
|
for (let i = 1; i <= 9; i++) {
|
||||||
|
const zoneIndex = i - 1; // Capture the value for this iteration
|
||||||
Main.keybindingManager.addHotKey(
|
Main.keybindingManager.addHotKey(
|
||||||
'snap-to-zone-' + i,
|
'snap-to-zone-' + i,
|
||||||
'<Super>KP_' + i,
|
'<Super><Ctrl>' + i,
|
||||||
Lang.bind(this, function() { this._snapToZone(i - 1); })
|
Lang.bind(this, function() {
|
||||||
|
this._snapToZone(zoneIndex);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onGrabBegin: function(display, window, op) {
|
_checkModifiersDuringDrag: function() {
|
||||||
// Check if this is a window move operation
|
if (!this.dragInProgress) {
|
||||||
if (op === Meta.GrabOp.MOVING) {
|
return false; // Stop polling
|
||||||
this.dragInProgress = true;
|
|
||||||
|
|
||||||
// Check if Shift is held - show overlay
|
|
||||||
let mods = global.get_pointer()[2];
|
|
||||||
if (mods & Clutter.ModifierType.SHIFT_MASK) {
|
|
||||||
this._showZonesOverlay();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pointerInfo = global.get_pointer();
|
||||||
|
let mods = pointerInfo[2];
|
||||||
|
let shiftPressed = !!(mods & Clutter.ModifierType.SHIFT_MASK);
|
||||||
|
|
||||||
|
if (shiftPressed && !this.isShowing) {
|
||||||
|
this._showZonesOverlay();
|
||||||
|
} else if (!shiftPressed && this.isShowing) {
|
||||||
|
this._hideZonesOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Continue polling
|
||||||
},
|
},
|
||||||
|
|
||||||
_onGrabEnd: function(display, window, op) {
|
_onGrabBegin: function(display, window) {
|
||||||
if (op === Meta.GrabOp.MOVING && this.dragInProgress) {
|
// Window drag has begun
|
||||||
|
this.dragInProgress = true;
|
||||||
|
|
||||||
|
// Get the actual window being dragged - use focus_window as it's the dragged window
|
||||||
|
this.draggedWindow = global.display.focus_window;
|
||||||
|
|
||||||
|
// Start polling for modifier key changes during drag
|
||||||
|
if (this.modifierPollTimeoutId) {
|
||||||
|
Mainloop.source_remove(this.modifierPollTimeoutId);
|
||||||
|
}
|
||||||
|
this.modifierPollTimeoutId = Mainloop.timeout_add(50, Lang.bind(this, this._checkModifiersDuringDrag));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onGrabEnd: function(display, window) {
|
||||||
|
if (this.dragInProgress) {
|
||||||
this.dragInProgress = false;
|
this.dragInProgress = false;
|
||||||
|
|
||||||
|
// Stop polling for modifiers
|
||||||
|
if (this.modifierPollTimeoutId) {
|
||||||
|
Mainloop.source_remove(this.modifierPollTimeoutId);
|
||||||
|
this.modifierPollTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isShowing) {
|
if (this.isShowing) {
|
||||||
// Snap to zone if cursor is over one
|
// Snap to zone if cursor is over one
|
||||||
this._snapWindowToZone(window);
|
this._snapWindowToZone(this.draggedWindow);
|
||||||
this._hideZonesOverlay();
|
this._hideZonesOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.draggedWindow = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -527,8 +561,10 @@ ZoneManager.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_moveWindowToZone: function(window, zone, monitor) {
|
_moveWindowToZone: function(window, zone, monitor) {
|
||||||
|
if (!window || !zone || !monitor) return;
|
||||||
|
|
||||||
// Unmaximize if maximized
|
// Unmaximize if maximized
|
||||||
if (window.get_maximized()) {
|
if (window.maximized_horizontally || window.maximized_vertically) {
|
||||||
window.unmaximize(Meta.MaximizeFlags.BOTH);
|
window.unmaximize(Meta.MaximizeFlags.BOTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,8 +574,13 @@ ZoneManager.prototype = {
|
|||||||
let width = Math.round(monitor.width * zone.width);
|
let width = Math.round(monitor.width * zone.width);
|
||||||
let height = Math.round(monitor.height * zone.height);
|
let height = Math.round(monitor.height * zone.height);
|
||||||
|
|
||||||
// Move and resize window
|
// Use Mainloop.idle_add to defer the operation
|
||||||
window.move_resize_frame(true, x, y, width, height);
|
// This ensures the unmaximize completes before move/resize
|
||||||
|
Mainloop.idle_add(Lang.bind(this, function() {
|
||||||
|
// Move and resize window (false = programmatic operation, not user-initiated)
|
||||||
|
window.move_resize_frame(false, x, y, width, height);
|
||||||
|
return false; // Don't repeat
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
cycleLayout: function() {
|
cycleLayout: function() {
|
||||||
@@ -558,28 +599,40 @@ ZoneManager.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
// Disconnect signals
|
// Clean up overlay FIRST to prevent corrupt display
|
||||||
if (this.grabOpBeginId) {
|
|
||||||
global.display.disconnect(this.grabOpBeginId);
|
|
||||||
}
|
|
||||||
if (this.grabOpEndId) {
|
|
||||||
global.display.disconnect(this.grabOpEndId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove keybindings
|
|
||||||
Main.keybindingManager.removeHotKey('show-zones');
|
|
||||||
Main.keybindingManager.removeHotKey('open-zone-editor');
|
|
||||||
for (let i = 1; i <= 9; i++) {
|
|
||||||
Main.keybindingManager.removeHotKey('snap-to-zone-' + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up overlay
|
|
||||||
this._hideZonesOverlay();
|
this._hideZonesOverlay();
|
||||||
|
|
||||||
|
// Stop modifier polling if active
|
||||||
|
if (this.modifierPollTimeoutId) {
|
||||||
|
Mainloop.source_remove(this.modifierPollTimeoutId);
|
||||||
|
this.modifierPollTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up editor
|
// Clean up editor
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor._cancelEditor();
|
this.editor._cancelEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disconnect signals
|
||||||
|
if (this.grabOpBeginId) {
|
||||||
|
global.display.disconnect(this.grabOpBeginId);
|
||||||
|
this.grabOpBeginId = null;
|
||||||
|
}
|
||||||
|
if (this.grabOpEndId) {
|
||||||
|
global.display.disconnect(this.grabOpEndId);
|
||||||
|
this.grabOpEndId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove keybindings
|
||||||
|
try {
|
||||||
|
Main.keybindingManager.removeHotKey('show-zones');
|
||||||
|
Main.keybindingManager.removeHotKey('open-zone-editor');
|
||||||
|
for (let i = 1; i <= 9; i++) {
|
||||||
|
Main.keybindingManager.removeHotKey('snap-to-zone-' + i);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
global.logError('GridSnap: Error removing keybindings - ' + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,9 @@ echo "2. Open System Settings → Extensions"
|
|||||||
echo "3. Find 'GridSnap' and toggle it on"
|
echo "3. Find 'GridSnap' and toggle it on"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Keyboard shortcuts:"
|
echo "Keyboard shortcuts:"
|
||||||
echo " Super+Z : Show/hide zones"
|
echo " Super+Z : Show/hide zones"
|
||||||
echo " Super+Shift+Z : Cycle layouts"
|
echo " Super+Shift+Z : Cycle layouts"
|
||||||
echo " Super+Numpad1-9 : Snap to zone"
|
echo " Super+Ctrl+1-9 : Snap to zone"
|
||||||
|
echo " Super+Shift+E : Open zone editor"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Enjoy your new window manager!"
|
echo "Enjoy your new window manager!"
|
||||||
|
|||||||
Reference in New Issue
Block a user