Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions application.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,17 @@ class Application {
#connectWindowGrabs() {
// start snapping when the user starts moving a window
this.#signals.connect(global.display, 'grab-op-begin', (display, screen, window, op) => {
if (op === Meta.GrabOp.MOVING && window.window_type === Meta.WindowType.NORMAL) {
if (op === Meta.GrabOp.MOVING && window.window_type === Meta.WindowType.NORMAL) {
// reload styling
this.#loadThemeColors();
const enableSnappingModifiers = mapModifierSettingToModifierType(this.#settings.settingsData.enableSnappingModifiers.value);

const enableMultiSnappingModifiers = mapModifierSettingToModifierType(this.#settings.settingsData.enableMultiSnappingModifiers.value);

// Create WindowSnapper for each monitor
const nMonitors = global.display.get_n_monitors();
for (let i = 0; i < nMonitors; i++) {
const layout = this.#readOrCreateLayoutForDisplay(i, LayoutOf2x2);
const snapper = new WindowSnapper(i, layout, window, enableSnappingModifiers);
const snapper = new WindowSnapper(i, layout, window, enableSnappingModifiers, enableMultiSnappingModifiers);
this.#windowSnappers.push(snapper);
}
}
Expand All @@ -300,4 +301,4 @@ class Application {
}
}

module.exports = { Application, LayoutOf2x2 };
module.exports = { Application, LayoutOf2x2 };
64 changes: 55 additions & 9 deletions node_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,19 @@ class LayoutNode {
return this.children.reduce((found, child) => found || child.findNode(predicate), null);
}

// find all node in the tree that matches the given predicate and return them in flat list
findAllNodes(predicate) {
let result = [];

this.forSelfAndDescendants((n) => {
if(predicate(n)) {
result.push(n);
}
})

return result;
}

// delete the given node in the tree if found
delete(node) {
let index = this.children.indexOf(node);
Expand Down Expand Up @@ -738,10 +751,12 @@ class PreviewSplitOperation extends LayoutOperation {
class SnappingOperation extends LayoutOperation {
showRegions = false;
#enableSnappingModifiers;
#enableMultiSnappingModifiers;

constructor(tree, enableSnappingModifiers) {
constructor(tree, enableSnappingModifiers, enableMultiSnappingModifiers) {
super(tree);
this.#enableSnappingModifiers = enableSnappingModifiers;
this.#enableMultiSnappingModifiers = enableMultiSnappingModifiers;
}

onMotion(x, y, state) {
Expand All @@ -751,31 +766,62 @@ class SnappingOperation extends LayoutOperation {
return this.cancel();
}

const multiSnapEnabled = this.#enableMultiSnappingModifiers.some((e) => (state & e));

// Find node at mouse position
let node = this.tree.findNodeAtPosition(x, y);
if (!node) {
return this.cancel();
if(!multiSnapEnabled){
return this.cancel();
}

return OperationResult.notHandled();
}

// activate the region to snap into
this.showRegions = true;

this.tree.forSelfAndDescendants(n => {
n.isSnappingDestination = false;
n.isHighlighted = false;
});
if(!multiSnapEnabled) {
this.tree.forSelfAndDescendants(n => {
n.isSnappingDestination = false;
n.isHighlighted = false;
});
}

node.isSnappingDestination = true;
node.isHighlighted = true;

return OperationResult.handledAndRedraw();
}

currentSnapToRect() {
var snapToNode = this.tree.findNode(n => n.isSnappingDestination);
if (!snapToNode) {
let snapToNodes = this.tree.findAllNodes(n => n.isSnappingDestination);

if (snapToNodes.length == 0) {
return null;
}
return snapToNode.snapRect();

return snapToNodes
.map((n) => n.snapRect())
.reduce((rect_a, rect_b) => {
let min_x = Math.min(rect_a.x, rect_b.x);
let min_y = Math.min(rect_a.y, rect_b.y);
let max_x = Math.max(
rect_a.x + rect_a.width,
rect_b.x + rect_b.width,
);
let max_y = Math.max(
rect_a.y + rect_a.height,
rect_b.y + rect_b.height,
);

return {
x: min_x,
y: min_y,
width: max_x - min_x,
height: max_y - min_y,
};
});
}

cancel() {
Expand Down
14 changes: 13 additions & 1 deletion settings-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,17 @@
"SUPER": "SUPER",
"SHIFT": "SHIFT"
}
},
"enableMultiSnappingModifiers": {
"type": "combobox",
"description": "Key modifier required to activate snapping to multiple arias",
"default": "",
"options": {
"(disabled)": "",
"CTRL": "CTRL",
"ALT": "ALT",
"SUPER": "SUPER",
"SHIFT": "SHIFT"
}
}
}
}
12 changes: 9 additions & 3 deletions window-snapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ class WindowSnapper {
// the modifier key to enable snapping
#enableSnappingModifiers;

// the modifier key to enable snapping to multiple areas
#enableMultiSnappingModifiers;

#signals = new SignalManager.SignalManager(null);

constructor(displayIdx, layout, window, enableSnappingModifiers) {
constructor(displayIdx, layout, window, enableSnappingModifiers, enableMultiSnappingModifiers) {
// the layout to use for the snapping operation
this.#layout = layout;

Expand All @@ -42,6 +45,9 @@ class WindowSnapper {
// the modifier key to enable snapping
this.#enableSnappingModifiers = enableSnappingModifiers;

// the modifier key to enable snapping to multiple areas
this.#enableMultiSnappingModifiers = enableMultiSnappingModifiers;

// get the size of the display
let workArea = getUsableScreenArea(displayIdx);

Expand All @@ -65,7 +71,7 @@ class WindowSnapper {

// ensure the layout is correct for the snap area
this.#layout.calculateRects(workArea.x, workArea.y, workArea.width, workArea.height);
this.#snappingOperation = new SnappingOperation(this.#layout, this.#enableSnappingModifiers);
this.#snappingOperation = new SnappingOperation(this.#layout, this.#enableSnappingModifiers, this.#enableMultiSnappingModifiers);

this.#signals.connect(this.#window, 'position-changed', this.#onWindowMoved.bind(this));
}
Expand Down Expand Up @@ -134,4 +140,4 @@ class WindowSnapper {
}
}

module.exports = { WindowSnapper };
module.exports = { WindowSnapper };