Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f550285778 | ||
|
|
5247a6a0d3 | ||
|
|
2b114a63dc | ||
|
|
63e4015f3a | ||
|
|
02db31266b | ||
|
|
67d4d89700 | ||
|
|
755cf90b1a | ||
|
|
e6a01217a5 | ||
|
|
21d7bbd6c4 | ||
|
|
605215acdc | ||
|
|
4b6808dba1 | ||
|
|
f9749c6f56 | ||
|
|
9b40b2f777 | ||
|
|
33470b4d7b | ||
|
|
8947719621 | ||
|
|
4bf4f8e8a1 | ||
|
|
080de7cf97 | ||
|
|
c29902dc15 | ||
|
|
1736b0a398 | ||
|
|
a1c44647ca | ||
|
|
0ea75d6348 | ||
|
|
12901e45ce | ||
|
|
29b4ccd1dd | ||
|
|
7b547bc5b8 | ||
|
|
78a127111b | ||
|
|
333b7601b2 | ||
|
|
1927ae445d | ||
|
|
1f563dae01 | ||
|
|
6b82eedbfe | ||
|
|
b479735130 | ||
|
|
c8f022d66f | ||
|
|
7f71750a8e | ||
|
|
13ebf24732 | ||
|
|
ec6b3247b7 | ||
|
|
50681d3a07 |
4
Makefile
4
Makefile
@@ -11,10 +11,10 @@ build:
|
||||
tsc --outFile ./package/contents/code/main.js
|
||||
|
||||
install: build config
|
||||
kpackagetool5 --type=KWin/Script -i ./package || kpackagetool5 --type=KWin/Script -u ./package
|
||||
kpackagetool6 --type=KWin/Script -i ./package || kpackagetool6 --type=KWin/Script -u ./package
|
||||
|
||||
uninstall:
|
||||
kpackagetool5 --type=KWin/Script -r ./package
|
||||
kpackagetool6 --type=KWin/Script -r karousel
|
||||
|
||||
package: build config
|
||||
tar -czf ./karousel_${subst .,_,${VERSION}}.tar.gz ./package
|
||||
|
||||
@@ -3,7 +3,6 @@ type KeyBinding = {
|
||||
description: string;
|
||||
comment?: string;
|
||||
defaultKeySequence: string;
|
||||
action: string;
|
||||
}
|
||||
|
||||
type NumKeyBinding = {
|
||||
@@ -12,7 +11,6 @@ type NumKeyBinding = {
|
||||
comment?: string;
|
||||
defaultModifiers: string;
|
||||
fKeys: boolean;
|
||||
action: string;
|
||||
}
|
||||
|
||||
function formatComment(comment: string | undefined) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick 6.0
|
||||
import org.kde.kwin 3.0
|
||||
import org.kde.notification 1.0
|
||||
import "./main.js" as Karousel
|
||||
import "../code/main.js" as Karousel
|
||||
|
||||
Item {
|
||||
id: qmlBase
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"KPackageStructure": "KWin/Script",
|
||||
"KPlugin": {
|
||||
"Name": "Karousel",
|
||||
"Description": "Scrollable tiling extension for KWin",
|
||||
@@ -8,13 +9,13 @@
|
||||
"Name": "Peter Fajdiga"
|
||||
}],
|
||||
"Id": "karousel",
|
||||
"ServiceTypes": ["KWin/Script"],
|
||||
"Version": "0.7.1",
|
||||
"Version": "0.8",
|
||||
"License": "GPLv3",
|
||||
"Website": "https://github.com/peterfajdiga/karousel",
|
||||
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"
|
||||
},
|
||||
"X-Plasma-API": "declarativescript",
|
||||
"X-Plasma-MainScript": "code/main.qml",
|
||||
"X-Plasma-API-Minimum-Version": "6.0",
|
||||
"X-Plasma-MainScript": "ui/main.qml",
|
||||
"X-KDE-ConfigModule": "kwin/effects/configs/kcm_kwin4_genericscripted"
|
||||
}
|
||||
|
||||
164
src/Actions.ts
164
src/Actions.ts
@@ -1,7 +1,7 @@
|
||||
namespace Actions {
|
||||
export function init(world: World, config: Config) {
|
||||
return {
|
||||
focusLeft: () => {
|
||||
export function getAction(world: World, config: Config, name: string) {
|
||||
switch (name) {
|
||||
case "focus-left": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
@@ -9,9 +9,9 @@ namespace Actions {
|
||||
}
|
||||
prevColumn.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
focusRight: () => {
|
||||
case "focus-right": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
@@ -19,9 +19,9 @@ namespace Actions {
|
||||
}
|
||||
nextColumn.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
focusUp: () => {
|
||||
case "focus-up": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const prevWindow = column.getPrevWindow(window);
|
||||
if (prevWindow === null) {
|
||||
@@ -29,9 +29,9 @@ namespace Actions {
|
||||
}
|
||||
prevWindow.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
focusDown: () => {
|
||||
case "focus-down": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const nextWindow = column.getNextWindow(window);
|
||||
if (nextWindow === null) {
|
||||
@@ -39,9 +39,9 @@ namespace Actions {
|
||||
}
|
||||
nextWindow.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
focusStart: () => {
|
||||
case "focus-start": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
@@ -50,9 +50,9 @@ namespace Actions {
|
||||
}
|
||||
firstColumn.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
focusEnd: () => {
|
||||
case "focus-end": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const lastColumn = grid.getLastColumn();
|
||||
@@ -61,9 +61,9 @@ namespace Actions {
|
||||
}
|
||||
lastColumn.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveLeft: () => {
|
||||
case "window-move-left": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
@@ -79,9 +79,9 @@ namespace Actions {
|
||||
window.moveToColumn(newColumn);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveRight: () => {
|
||||
case "window-move-right": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
@@ -97,100 +97,100 @@ namespace Actions {
|
||||
window.moveToColumn(newColumn);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveUp: () => {
|
||||
case "window-move-up": return () => {
|
||||
// TODO (optimization): only arrange moved windows
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveWindowUp(window);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveDown: () => {
|
||||
case "window-move-down": return () => {
|
||||
// TODO (optimization): only arrange moved windows
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveWindowDown(window);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveStart: () => {
|
||||
case "window-move-start": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const newColumn = new Column(grid, null);
|
||||
window.moveToColumn(newColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveEnd: () => {
|
||||
case "window-move-end": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const newColumn = new Column(grid, grid.getLastColumn());
|
||||
window.moveToColumn(newColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowToggleFloating: () => {
|
||||
const kwinClient = workspace.activeClient;
|
||||
case "window-toggle-floating": return () => {
|
||||
const kwinClient = Workspace.activeWindow;
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.toggleFloatingClient(kwinClient);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnMoveLeft: () => {
|
||||
case "column-move-left": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.moveColumnLeft(column);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnMoveRight: () => {
|
||||
case "column-move-right": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.moveColumnRight(column);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnMoveStart: () => {
|
||||
case "column-move-start": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveAfter(null);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnMoveEnd: () => {
|
||||
case "column-move-end": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveAfter(grid.getLastColumn());
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnToggleStacked: () => {
|
||||
case "column-toggle-stacked": return () => {
|
||||
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.toggleStacked();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnWidthIncrease: () => {
|
||||
case "column-width-increase": return () => {
|
||||
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
|
||||
config.columnResizer.increaseWidth(column, config.manualResizeStep);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnWidthDecrease: () => {
|
||||
case "column-width-decrease": return () => {
|
||||
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
|
||||
config.columnResizer.decreaseWidth(column, config.manualResizeStep);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnsWidthEqualize: () => {
|
||||
case "columns-width-equalize": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.getCurrentDesktop().equalizeVisibleColumnsWidths();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollLeft: () => {
|
||||
case "grid-scroll-left": return () => {
|
||||
gridScroll(world, -config.manualScrollStep);
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollRight: () => {
|
||||
case "grid-scroll-right": return () => {
|
||||
gridScroll(world, config.manualScrollStep);
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollStart: () => {
|
||||
case "grid-scroll-start": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
@@ -199,9 +199,9 @@ namespace Actions {
|
||||
}
|
||||
grid.desktop.scrollToColumn(firstColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollEnd: () => {
|
||||
case "grid-scroll-end": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const lastColumn = grid.getLastColumn();
|
||||
@@ -210,15 +210,15 @@ namespace Actions {
|
||||
}
|
||||
grid.desktop.scrollToColumn(lastColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollFocused: () => {
|
||||
case "grid-scroll-focused": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.desktop.scrollCenterRange(column);
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollLeftColumn: () => {
|
||||
case "grid-scroll-left-column": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const column = grid.getLeftmostVisibleColumn(grid.desktop.getCurrentVisibleRange(), true);
|
||||
@@ -233,9 +233,9 @@ namespace Actions {
|
||||
|
||||
grid.desktop.scrollToColumn(prevColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
gridScrollRightColumn: () => {
|
||||
case "grid-scroll-right-column": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const column = grid.getRightmostVisibleColumn(grid.desktop.getCurrentVisibleRange(), true);
|
||||
@@ -250,13 +250,15 @@ namespace Actions {
|
||||
|
||||
grid.desktop.scrollToColumn(nextColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
default: throw new Error("unknown action: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
export function initNum(world: World) {
|
||||
return {
|
||||
focusColumn: (columnIndex: number) => {
|
||||
export function getNumAction(world: World, name: string) {
|
||||
switch (name) {
|
||||
case "focus-": return (columnIndex: number) => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
@@ -265,9 +267,9 @@ namespace Actions {
|
||||
}
|
||||
targetColumn.focus();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
windowMoveToColumn: (columnIndex: number) => {
|
||||
case "window-move-to-column-": return (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
@@ -276,9 +278,9 @@ namespace Actions {
|
||||
window.moveToColumn(targetColumn);
|
||||
grid.desktop.autoAdjustScroll();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnMoveToColumn: (columnIndex: number) => {
|
||||
case "column-move-to-column-": return (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null || targetColumn === column) {
|
||||
@@ -290,30 +292,38 @@ namespace Actions {
|
||||
column.moveAfter(grid.getPrevColumn(targetColumn));
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
columnMoveToDesktop: (desktopIndex: number) => {
|
||||
case "column-move-to-desktop-": return (desktopIndex: number) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, oldGrid) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = desktopManager.getDesktopInCurrentActivity(desktopNumber).grid;
|
||||
const kwinDesktop = Workspace.desktops[desktopIndex];
|
||||
if (kwinDesktop === undefined) {
|
||||
return;
|
||||
}
|
||||
const newGrid = desktopManager.getDesktopInCurrentActivity(kwinDesktop).grid;
|
||||
if (newGrid === null || newGrid === oldGrid) {
|
||||
return;
|
||||
}
|
||||
column.moveToGrid(newGrid, newGrid.getLastColumn());
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
tailMoveToDesktop: (desktopIndex: number) => {
|
||||
case "tail-move-to-desktop-": return (desktopIndex: number) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, oldGrid) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = desktopManager.getDesktopInCurrentActivity(desktopNumber).grid;
|
||||
const kwinDesktop = Workspace.desktops[desktopIndex];
|
||||
if (kwinDesktop === undefined) {
|
||||
return;
|
||||
}
|
||||
const newGrid = desktopManager.getDesktopInCurrentActivity(kwinDesktop).grid;
|
||||
if (newGrid === null || newGrid === oldGrid) {
|
||||
return;
|
||||
}
|
||||
oldGrid.evacuateTail(newGrid, column);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
default: throw new Error("unknown num action: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
function gridScroll(world: World, amount: number) {
|
||||
|
||||
67
src/extern/kwin.d.ts
vendored
67
src/extern/kwin.d.ts
vendored
@@ -1,28 +1,28 @@
|
||||
declare const KWin: {
|
||||
readConfig(key: string, defaultValue: any): any;
|
||||
registerShortcut(name: string, description: string, keySequence: string, callback: () => void): void;
|
||||
};
|
||||
|
||||
declare const workspace: {
|
||||
readonly desktops: number;
|
||||
readonly currentDesktop: number;
|
||||
declare const Workspace: {
|
||||
readonly activities: string[];
|
||||
readonly desktops: KwinDesktop[];
|
||||
readonly currentDesktop: KwinDesktop;
|
||||
readonly currentActivity: string;
|
||||
readonly activeScreen: Output;
|
||||
readonly windows: KwinClient[];
|
||||
readonly cursorPos: QmlPoint;
|
||||
|
||||
activeClient: KwinClient;
|
||||
activeWindow: KwinClient;
|
||||
|
||||
readonly currentDesktopChanged: QSignal<[]>
|
||||
readonly clientAdded: QSignal<[KwinClient]>;
|
||||
readonly clientRemoved: QSignal<[KwinClient]>;
|
||||
readonly clientMinimized: QSignal<[KwinClient]>;
|
||||
readonly clientUnminimized: QSignal<[KwinClient]>;
|
||||
readonly clientMaximizeSet: QSignal<[KwinClient, horizontally: boolean, vertically: boolean]>;
|
||||
readonly clientActivated: QSignal<[KwinClient]>;
|
||||
readonly numberDesktopsChanged: QSignal<[]>;
|
||||
readonly windowAdded: QSignal<[KwinClient]>;
|
||||
readonly windowRemoved: QSignal<[KwinClient]>;
|
||||
readonly windowActivated: QSignal<[KwinClient]>;
|
||||
readonly desktopsChanged: QSignal<[]>;
|
||||
readonly activitiesChanged: QSignal<[]>;
|
||||
readonly currentActivityChanged: QSignal<[]>;
|
||||
readonly virtualScreenSizeChanged: QSignal<[]>;
|
||||
|
||||
clientArea(option: ClientAreaOption, screenNumber: number, desktopNumber: number);
|
||||
clientList(): KwinClient[];
|
||||
clientArea(option: ClientAreaOption, output: Output, kwinDesktop: KwinDesktop);
|
||||
};
|
||||
|
||||
const enum ClientAreaOption {
|
||||
@@ -36,7 +36,15 @@ const enum ClientAreaOption {
|
||||
ScreenArea,
|
||||
}
|
||||
|
||||
const enum MaximizedMode {
|
||||
Unmaximized,
|
||||
Vertically,
|
||||
Horizontally,
|
||||
Maximized,
|
||||
}
|
||||
|
||||
type Tile = unknown;
|
||||
type Output = unknown;
|
||||
|
||||
interface KwinClient {
|
||||
readonly shadeable: boolean;
|
||||
@@ -46,14 +54,17 @@ interface KwinClient {
|
||||
readonly transientFor: KwinClient;
|
||||
readonly move: boolean;
|
||||
readonly resize: boolean;
|
||||
readonly moveable: boolean;
|
||||
readonly resizeable: boolean;
|
||||
readonly screen: number;
|
||||
readonly fullScreenable: boolean;
|
||||
readonly maximizable: boolean;
|
||||
readonly output: Output;
|
||||
readonly resourceClass: QByteArray;
|
||||
readonly dock: boolean;
|
||||
readonly normalWindow: boolean;
|
||||
readonly managed: boolean;
|
||||
readonly popupWindow: boolean;
|
||||
|
||||
opacity: number;
|
||||
fullScreen: boolean;
|
||||
activities: string[]; // empty array means all activities
|
||||
skipSwitcher: boolean;
|
||||
@@ -62,18 +73,30 @@ interface KwinClient {
|
||||
shade: boolean;
|
||||
minimized: boolean;
|
||||
frameGeometry: QmlRect;
|
||||
desktop: number; // -1 means all desktops
|
||||
desktops: KwinDesktop[]; // empty array means all desktops
|
||||
tile: Tile;
|
||||
opacity: number;
|
||||
|
||||
readonly fullScreenChanged: QSignal<[]>;
|
||||
readonly desktopChanged: QSignal<[]>;
|
||||
readonly desktopsChanged: QSignal<[]>;
|
||||
readonly activitiesChanged: QSignal<[]>;
|
||||
readonly minimizedChanged: QSignal<[]>;
|
||||
readonly maximizedChanged: QSignal<[]>
|
||||
readonly maximizedAboutToChange: QSignal<[MaximizedMode]>
|
||||
readonly captionChanged: QSignal<[]>;
|
||||
readonly tileChanged: QSignal<[]>;
|
||||
readonly moveResizedChanged: QSignal<[]>;
|
||||
readonly moveResizeCursorChanged: QSignal<[]>;
|
||||
readonly clientStartUserMovedResized: QSignal<[]>;
|
||||
readonly frameGeometryChanged: QSignal<[KwinClient, oldGeometry: QmlRect]>;
|
||||
readonly interactiveMoveResizeStarted: QSignal<[]>;
|
||||
readonly interactiveMoveResizeFinished: QSignal<[]>;
|
||||
readonly frameGeometryChanged: QSignal<[oldGeometry: QmlRect]>;
|
||||
|
||||
setMaximize(vertically: boolean, horizontally: boolean): void;
|
||||
}
|
||||
|
||||
interface KwinDesktop {
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
type ShortcutHandler = {
|
||||
readonly activated: QSignal<[]>;
|
||||
destroy(): void;
|
||||
};
|
||||
|
||||
5
src/extern/qt.d.ts
vendored
5
src/extern/qt.d.ts
vendored
@@ -13,6 +13,11 @@ type QmlObject = unknown;
|
||||
|
||||
type QByteArray = string;
|
||||
|
||||
type QmlPoint = {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
type QmlRect = {
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
@@ -3,176 +3,148 @@ const keyBindings: KeyBinding[] = [
|
||||
name: "window-toggle-floating",
|
||||
description: "Toggle floating",
|
||||
defaultKeySequence: "Meta+Space",
|
||||
action: "windowToggleFloating",
|
||||
},
|
||||
{
|
||||
name: "focus-left",
|
||||
description: "Move focus left",
|
||||
defaultKeySequence: "Meta+A",
|
||||
action: "focusLeft",
|
||||
},
|
||||
{
|
||||
name: "focus-right",
|
||||
description: "Move focus right",
|
||||
comment: "Clashes with default KDE shortcuts, may require manual remapping",
|
||||
defaultKeySequence: "Meta+D",
|
||||
action: "focusRight",
|
||||
},
|
||||
{
|
||||
name: "focus-up",
|
||||
description: "Move focus up",
|
||||
comment: "Clashes with default KDE shortcuts, may require manual remapping",
|
||||
defaultKeySequence: "Meta+W",
|
||||
action: "focusUp",
|
||||
},
|
||||
{
|
||||
name: "focus-down",
|
||||
description: "Move focus down",
|
||||
comment: "Clashes with default KDE shortcuts, may require manual remapping",
|
||||
defaultKeySequence: "Meta+S",
|
||||
action: "focusDown",
|
||||
},
|
||||
{
|
||||
name: "focus-start",
|
||||
description: "Move focus to start",
|
||||
defaultKeySequence: "Meta+Home",
|
||||
action: "focusStart",
|
||||
},
|
||||
{
|
||||
name: "focus-end",
|
||||
description: "Move focus to end",
|
||||
defaultKeySequence: "Meta+End",
|
||||
action: "focusEnd",
|
||||
},
|
||||
{
|
||||
name: "window-move-left",
|
||||
description: "Move window left",
|
||||
comment: "Moves window out of and into columns",
|
||||
defaultKeySequence: "Meta+Shift+A",
|
||||
action: "windowMoveLeft",
|
||||
},
|
||||
{
|
||||
name: "window-move-right",
|
||||
description: "Move window right",
|
||||
comment: "Moves window out of and into columns",
|
||||
defaultKeySequence: "Meta+Shift+D",
|
||||
action: "windowMoveRight",
|
||||
},
|
||||
{
|
||||
name: "window-move-up",
|
||||
description: "Move window up",
|
||||
defaultKeySequence: "Meta+Shift+W",
|
||||
action: "windowMoveUp",
|
||||
},
|
||||
{
|
||||
name: "window-move-down",
|
||||
description: "Move window down",
|
||||
defaultKeySequence: "Meta+Shift+S",
|
||||
action: "windowMoveDown",
|
||||
},
|
||||
{
|
||||
name: "window-move-start",
|
||||
description: "Move window to start",
|
||||
defaultKeySequence: "Meta+Shift+Home",
|
||||
action: "windowMoveStart",
|
||||
},
|
||||
{
|
||||
name: "window-move-end",
|
||||
description: "Move window to end",
|
||||
defaultKeySequence: "Meta+Shift+End",
|
||||
action: "windowMoveEnd",
|
||||
},
|
||||
{
|
||||
name: "column-toggle-stacked",
|
||||
description: "Toggle stacked layout for focused column",
|
||||
comment: "One window in the column visible, others shaded; not supported on Wayland",
|
||||
defaultKeySequence: "Meta+X",
|
||||
action: "columnToggleStacked",
|
||||
},
|
||||
{
|
||||
name: "column-move-left",
|
||||
description: "Move column left",
|
||||
defaultKeySequence: "Meta+Ctrl+Shift+A",
|
||||
action: "columnMoveLeft",
|
||||
},
|
||||
{
|
||||
name: "column-move-right",
|
||||
description: "Move column right",
|
||||
defaultKeySequence: "Meta+Ctrl+Shift+D",
|
||||
action: "columnMoveRight",
|
||||
},
|
||||
{
|
||||
name: "column-move-start",
|
||||
description: "Move column to start",
|
||||
defaultKeySequence: "Meta+Ctrl+Shift+Home",
|
||||
action: "columnMoveStart",
|
||||
},
|
||||
{
|
||||
name: "column-move-end",
|
||||
description: "Move column to end",
|
||||
defaultKeySequence: "Meta+Ctrl+Shift+End",
|
||||
action: "columnMoveEnd",
|
||||
},
|
||||
{
|
||||
name: "column-width-increase",
|
||||
description: "Increase column width",
|
||||
defaultKeySequence: "Meta+Ctrl++",
|
||||
action: "columnWidthIncrease",
|
||||
},
|
||||
{
|
||||
name: "column-width-decrease",
|
||||
description: "Decrease column width",
|
||||
defaultKeySequence: "Meta+Ctrl+-",
|
||||
action: "columnWidthDecrease",
|
||||
},
|
||||
{
|
||||
name: "columns-width-equalize",
|
||||
description: "Equalize widths of visible columns",
|
||||
defaultKeySequence: "Meta+Ctrl+X",
|
||||
action: "columnsWidthEqualize",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-focused",
|
||||
description: "Center focused window",
|
||||
comment: "Scrolls so that the focused window is centered in the screen",
|
||||
defaultKeySequence: "Meta+Alt+Return",
|
||||
action: "gridScrollFocused",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-left-column",
|
||||
description: "Scroll one column to the left",
|
||||
defaultKeySequence: "Meta+Alt+A",
|
||||
action: "gridScrollLeftColumn",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-right-column",
|
||||
description: "Scroll one column to the right",
|
||||
defaultKeySequence: "Meta+Alt+D",
|
||||
action: "gridScrollRightColumn",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-left",
|
||||
description: "Scroll left",
|
||||
defaultKeySequence: "Meta+Alt+PgUp",
|
||||
action: "gridScrollLeft",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-right",
|
||||
description: "Scroll right",
|
||||
defaultKeySequence: "Meta+Alt+PgDown",
|
||||
action: "gridScrollRight",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-start",
|
||||
description: "Scroll to start",
|
||||
defaultKeySequence: "Meta+Alt+Home",
|
||||
action: "gridScrollStart",
|
||||
},
|
||||
{
|
||||
name: "grid-scroll-end",
|
||||
description: "Scroll to end",
|
||||
defaultKeySequence: "Meta+Alt+End",
|
||||
action: "gridScrollEnd",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -183,7 +155,6 @@ const numKeyBindings: NumKeyBinding[] = [
|
||||
comment: "Clashes with default KDE shortcuts, may require manual remapping",
|
||||
defaultModifiers: "Meta",
|
||||
fKeys: false,
|
||||
action: "focusColumn",
|
||||
},
|
||||
{
|
||||
name: "window-move-to-column-",
|
||||
@@ -191,7 +162,6 @@ const numKeyBindings: NumKeyBinding[] = [
|
||||
comment: "Requires manual remapping according to your keyboard layout, e.g. Meta+Shift+1 -> Meta+!",
|
||||
defaultModifiers: "Meta+Shift",
|
||||
fKeys: false,
|
||||
action: "windowMoveToColumn",
|
||||
},
|
||||
{
|
||||
name: "column-move-to-column-",
|
||||
@@ -199,20 +169,17 @@ const numKeyBindings: NumKeyBinding[] = [
|
||||
comment: "Requires manual remapping according to your keyboard layout, e.g. Meta+Ctrl+Shift+1 -> Meta+Ctrl+!",
|
||||
defaultModifiers: "Meta+Ctrl+Shift",
|
||||
fKeys: false,
|
||||
action: "columnMoveToColumn",
|
||||
},
|
||||
{
|
||||
name: "column-move-to-desktop-",
|
||||
description: "Move column to desktop ",
|
||||
defaultModifiers: "Meta+Ctrl+Shift",
|
||||
fKeys: true,
|
||||
action: "columnMoveToDesktop",
|
||||
},
|
||||
{
|
||||
name: "tail-move-to-desktop-",
|
||||
description: "Move this and all following columns to desktop ",
|
||||
defaultModifiers: "Meta+Ctrl+Shift+Alt",
|
||||
fKeys: true,
|
||||
action: "tailMoveToDesktop",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -3,7 +3,6 @@ type KeyBinding = {
|
||||
description: string;
|
||||
comment?: string;
|
||||
defaultKeySequence: string;
|
||||
action: keyof ReturnType<typeof Actions.init>;
|
||||
};
|
||||
|
||||
type NumKeyBinding = {
|
||||
@@ -12,7 +11,6 @@ type NumKeyBinding = {
|
||||
comment?: string;
|
||||
defaultModifiers: string;
|
||||
fKeys: boolean;
|
||||
action: keyof ReturnType<typeof Actions.initNum>;
|
||||
};
|
||||
|
||||
function catchWrap(f: () => void) {
|
||||
@@ -26,45 +24,44 @@ function catchWrap(f: () => void) {
|
||||
};
|
||||
}
|
||||
|
||||
function registerKeyBinding(name: string, description: string, keySequence: string, callback: () => void) {
|
||||
KWin.registerShortcut(
|
||||
"karousel-" + name,
|
||||
"Karousel: " + description,
|
||||
keySequence,
|
||||
catchWrap(callback),
|
||||
);
|
||||
function registerKeyBinding(world: World, config: Actions.Config, shortcutActions: ShortcutAction[], keyBinding: KeyBinding) {
|
||||
shortcutActions.push(new ShortcutAction(
|
||||
keyBinding,
|
||||
catchWrap(Actions.getAction(world, config, keyBinding.name)),
|
||||
));
|
||||
}
|
||||
|
||||
function registerNumKeyBindings(name: string, description: string, modifiers: string, fKeys: boolean, callback: (i: number) => void) {
|
||||
const numPrefix = fKeys ? "F" : "";
|
||||
const n = fKeys ? 12 : 9;
|
||||
function registerNumKeyBindings(world: World, shortcutActions: ShortcutAction[], numKeyBinding: NumKeyBinding) {
|
||||
const numPrefix = numKeyBinding.fKeys ? "F" : "";
|
||||
const n = numKeyBinding.fKeys ? 12 : 9;
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const numKey = String(i + 1);
|
||||
const keySequence = i < n ?
|
||||
modifiers + "+" + numPrefix + numKey :
|
||||
numKeyBinding.defaultModifiers + "+" + numPrefix + numKey :
|
||||
"";
|
||||
registerKeyBinding(
|
||||
name + numKey,
|
||||
description + numKey,
|
||||
keySequence,
|
||||
() => callback(i),
|
||||
);
|
||||
const action = Actions.getNumAction(world, numKeyBinding.name);
|
||||
shortcutActions.push(new ShortcutAction(
|
||||
{
|
||||
name: numKeyBinding.name + numKey,
|
||||
description: numKeyBinding.description + numKey,
|
||||
defaultKeySequence: keySequence,
|
||||
},
|
||||
catchWrap(() => action(i)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function registerKeyBindings(world: World, config: Config) {
|
||||
const actions = Actions.init(world, {
|
||||
manualScrollStep: config.manualScrollStep,
|
||||
manualResizeStep: config.manualResizeStep,
|
||||
columnResizer: config.scrollingCentered ? new RawResizer() : new ContextualResizer(),
|
||||
});
|
||||
// TODO: refactor
|
||||
function registerKeyBindings(world: World, config: Actions.Config) {
|
||||
const shortcutActions: ShortcutAction[] = [];
|
||||
|
||||
for (const binding of keyBindings) {
|
||||
registerKeyBinding(binding.name, binding.description, binding.defaultKeySequence, actions[binding.action]);
|
||||
for (const keyBinding of keyBindings) {
|
||||
registerKeyBinding(world, config, shortcutActions, keyBinding);
|
||||
}
|
||||
|
||||
const numActions = Actions.initNum(world);
|
||||
for (const binding of numKeyBindings) {
|
||||
registerNumKeyBindings(binding.name, binding.description, binding.defaultModifiers, binding.fKeys, numActions[binding.action]);
|
||||
for (const numKeyBinding of numKeyBindings) {
|
||||
registerNumKeyBindings(world, shortcutActions, numKeyBinding);
|
||||
}
|
||||
|
||||
return shortcutActions;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class Column {
|
||||
this.grid = targetGrid;
|
||||
targetGrid.onColumnAdded(this, prevColumn);
|
||||
for (const window of this.windows.iterator()) {
|
||||
window.client.kwinClient.desktop = targetGrid.desktop.desktopNumber;
|
||||
window.client.kwinClient.desktops = [targetGrid.desktop.kwinDesktop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Desktop {
|
||||
public readonly grid: Grid;
|
||||
public readonly desktopNumber: number;
|
||||
public readonly kwinDesktop: KwinDesktop;
|
||||
private readonly pinManager: PinManager;
|
||||
private readonly config: Desktop.Config;
|
||||
private scrollX: number;
|
||||
@@ -10,26 +10,26 @@ class Desktop {
|
||||
public clientArea: QmlRect;
|
||||
public tilingArea: QmlRect;
|
||||
|
||||
constructor(desktopNumber: number, pinManager: PinManager, config: Desktop.Config, layoutConfig: LayoutConfig) {
|
||||
constructor(kwinDesktop: KwinDesktop, pinManager: PinManager, config: Desktop.Config, layoutConfig: LayoutConfig) {
|
||||
this.pinManager = pinManager;
|
||||
this.config = config;
|
||||
this.scrollX = 0;
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
this.dirtyPins = true;
|
||||
this.desktopNumber = desktopNumber;
|
||||
this.kwinDesktop = kwinDesktop;
|
||||
this.grid = new Grid(this, layoutConfig);
|
||||
this.clientArea = Desktop.getClientArea(desktopNumber);
|
||||
this.tilingArea = Desktop.getTilingArea(this.clientArea, desktopNumber, pinManager, config);
|
||||
this.clientArea = Desktop.getClientArea(kwinDesktop);
|
||||
this.tilingArea = Desktop.getTilingArea(this.clientArea, kwinDesktop, pinManager, config);
|
||||
}
|
||||
|
||||
private updateArea() {
|
||||
const newClientArea = Desktop.getClientArea(this.desktopNumber);
|
||||
const newClientArea = Desktop.getClientArea(this.kwinDesktop);
|
||||
if (newClientArea === this.clientArea && !this.dirtyPins) {
|
||||
return;
|
||||
}
|
||||
this.clientArea = newClientArea;
|
||||
this.tilingArea = Desktop.getTilingArea(newClientArea, this.desktopNumber, this.pinManager, this.config);
|
||||
this.tilingArea = Desktop.getTilingArea(newClientArea, this.kwinDesktop, this.pinManager, this.config);
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
this.dirtyPins = false;
|
||||
@@ -37,12 +37,12 @@ class Desktop {
|
||||
this.autoAdjustScroll();
|
||||
}
|
||||
|
||||
private static getClientArea(desktopNumber: number) {
|
||||
return workspace.clientArea(ClientAreaOption.PlacementArea, 0, desktopNumber);
|
||||
private static getClientArea(kwinDesktop: KwinDesktop) {
|
||||
return Workspace.clientArea(ClientAreaOption.PlacementArea, Workspace.activeScreen, kwinDesktop);
|
||||
}
|
||||
|
||||
private static getTilingArea(clientArea: QmlRect, desktopNumber: number, pinManager: PinManager, config: Desktop.Config) {
|
||||
const availableSpace = pinManager.getAvailableSpace(desktopNumber, clientArea);
|
||||
private static getTilingArea(clientArea: QmlRect, kwinDesktop: KwinDesktop, pinManager: PinManager, config: Desktop.Config) {
|
||||
const availableSpace = pinManager.getAvailableSpace(kwinDesktop, clientArea);
|
||||
const top = availableSpace.top + config.marginTop;
|
||||
const bottom = availableSpace.bottom - config.marginBottom;
|
||||
const left = availableSpace.left + config.marginLeft;
|
||||
|
||||
@@ -10,8 +10,7 @@ class Window {
|
||||
this.height = client.kwinClient.frameGeometry.height;
|
||||
this.focusedState = {
|
||||
fullScreen: false,
|
||||
maximizedHorizontally: false,
|
||||
maximizedVertically: false,
|
||||
maximizedMode: MaximizedMode.Unmaximized,
|
||||
};
|
||||
this.skipArrange = false;
|
||||
this.column = column;
|
||||
@@ -37,8 +36,11 @@ class Window {
|
||||
if (this.column.grid.config.reMaximize && this.isFocused()) {
|
||||
// do this here rather than in `onFocused` to ensure it happens after placement
|
||||
// (otherwise placement may not happen at all)
|
||||
if (this.focusedState.maximizedVertically || this.focusedState.maximizedHorizontally) {
|
||||
this.client.setMaximize(this.focusedState.maximizedVertically, this.focusedState.maximizedHorizontally);
|
||||
if (this.focusedState.maximizedMode > MaximizedMode.Unmaximized) {
|
||||
this.client.setMaximize(
|
||||
this.focusedState.maximizedMode === MaximizedMode.Horizontally || this.focusedState.maximizedMode === MaximizedMode.Maximized,
|
||||
this.focusedState.maximizedMode === MaximizedMode.Vertically || this.focusedState.maximizedMode === MaximizedMode.Maximized,
|
||||
);
|
||||
maximized = true;
|
||||
}
|
||||
if (this.focusedState.fullScreen) {
|
||||
@@ -76,8 +78,8 @@ class Window {
|
||||
this.column.grid.desktop.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onMaximizedChanged(horizontally: boolean, vertically: boolean) {
|
||||
const maximized = horizontally || vertically;
|
||||
public onMaximizedChanged(maximizedMode: MaximizedMode) {
|
||||
const maximized = maximizedMode > MaximizedMode.Unmaximized;
|
||||
this.skipArrange = maximized;
|
||||
if (this.column.grid.config.tiledKeepBelow) {
|
||||
this.client.kwinClient.keepBelow = !maximized;
|
||||
@@ -86,8 +88,7 @@ class Window {
|
||||
this.client.kwinClient.keepAbove = maximized;
|
||||
}
|
||||
if (this.isFocused()) {
|
||||
this.focusedState.maximizedHorizontally = horizontally;
|
||||
this.focusedState.maximizedVertically = vertically;
|
||||
this.focusedState.maximizedMode = maximizedMode;
|
||||
}
|
||||
this.column.grid.desktop.onLayoutChanged();
|
||||
}
|
||||
@@ -145,7 +146,6 @@ class Window {
|
||||
namespace Window {
|
||||
export type State = {
|
||||
fullScreen: boolean,
|
||||
maximizedHorizontally: boolean,
|
||||
maximizedVertically: boolean,
|
||||
maximizedMode: MaximizedMode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
function init() {
|
||||
const config = loadConfig();
|
||||
const world = new World(config);
|
||||
registerKeyBindings(world, config);
|
||||
return world;
|
||||
return new World(loadConfig());
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ class WindowRuleEnforcer {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const desktop = desktopManager.getDesktopForClient(kwinClient);
|
||||
if (shouldTile && desktop !== undefined) {
|
||||
clientManager.tileClient(kwinClient, desktop.grid);
|
||||
clientManager.tileKwinClient(kwinClient, desktop.grid);
|
||||
} else {
|
||||
clientManager.untileClient(kwinClient);
|
||||
clientManager.floatKwinClient(kwinClient);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ class Delayer {
|
||||
|
||||
function initQmlTimer() {
|
||||
return Qt.createQmlObject(
|
||||
`import QtQuick 2.15
|
||||
`import QtQuick 6.0
|
||||
Timer {}`,
|
||||
qmlBase
|
||||
);
|
||||
|
||||
25
src/utils/ShortcutAction.ts
Normal file
25
src/utils/ShortcutAction.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
class ShortcutAction {
|
||||
private readonly shortcutHandler: ShortcutHandler;
|
||||
|
||||
constructor(keyBinding: KeyBinding, f: () => void) {
|
||||
this.shortcutHandler = ShortcutAction.initShortcutHandler(keyBinding);
|
||||
this.shortcutHandler.activated.connect(f);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.shortcutHandler.destroy();
|
||||
}
|
||||
|
||||
private static initShortcutHandler(keyBinding: KeyBinding) {
|
||||
return Qt.createQmlObject(
|
||||
`import QtQuick 6.0
|
||||
import org.kde.kwin 3.0
|
||||
ShortcutHandler {
|
||||
name: "karousel-${keyBinding.name}";
|
||||
text: "Karousel: ${keyBinding.description}";
|
||||
sequence: "${keyBinding.defaultKeySequence}";
|
||||
}`,
|
||||
qmlBase,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,23 @@
|
||||
function initWorkspaceSignalHandlers(world: World) {
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(workspace.clientAdded, (kwinClient: KwinClient) => {
|
||||
manager.connect(Workspace.windowAdded, (kwinClient: KwinClient) => {
|
||||
if (Clients.canTileEver(kwinClient)) {
|
||||
// never open new tileable clients on all desktops or activities
|
||||
if (kwinClient.desktop <= 0) {
|
||||
kwinClient.desktop = workspace.currentDesktop;
|
||||
}
|
||||
if (kwinClient.activities.length !== 1) {
|
||||
kwinClient.activities = [workspace.currentActivity];
|
||||
}
|
||||
Clients.makeTileable(kwinClient);
|
||||
}
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.addClient(kwinClient)
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientRemoved, (kwinClient: KwinClient) => {
|
||||
manager.connect(Workspace.windowRemoved, (kwinClient: KwinClient) => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.removeClient(kwinClient, true);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientMinimized, (kwinClient: KwinClient) => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.minimizeClient(kwinClient);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientUnminimized, (kwinClient: KwinClient) => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.unminimizeClient(kwinClient);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientMaximizeSet, (kwinClient: KwinClient, horizontally: boolean, vertically: boolean) => {
|
||||
if ((horizontally || vertically) && kwinClient.tile !== null) {
|
||||
kwinClient.tile = null;
|
||||
}
|
||||
world.doIfTiled(kwinClient, false, (clientManager, desktopManager, window, column, grid) => {
|
||||
window.onMaximizedChanged(horizontally, vertically);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientActivated, (kwinClient: KwinClient) => {
|
||||
manager.connect(Workspace.windowActivated, (kwinClient: KwinClient) => {
|
||||
if (kwinClient === null) {
|
||||
return;
|
||||
}
|
||||
@@ -52,19 +26,27 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.currentDesktopChanged, () => {
|
||||
manager.connect(Workspace.currentDesktopChanged, () => {
|
||||
world.do(() => {}); // re-arrange desktop
|
||||
});
|
||||
|
||||
manager.connect(workspace.currentActivityChanged, () => {
|
||||
manager.connect(Workspace.currentActivityChanged, () => {
|
||||
world.do(() => {}); // re-arrange desktop
|
||||
});
|
||||
|
||||
manager.connect(workspace.numberDesktopsChanged, () => {
|
||||
world.updateDesktops();
|
||||
manager.connect(Workspace.desktopsChanged, () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.updateDesktops();
|
||||
})
|
||||
});
|
||||
|
||||
manager.connect(workspace.virtualScreenSizeChanged, () => {
|
||||
manager.connect(Workspace.activitiesChanged, () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.updateActivities();
|
||||
})
|
||||
});
|
||||
|
||||
manager.connect(Workspace.virtualScreenSizeChanged, () => {
|
||||
world.onScreenResized();
|
||||
});
|
||||
|
||||
|
||||
@@ -76,44 +76,41 @@ class ClientManager {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientState.Tiled) {
|
||||
client.stateManager.setState(() => new ClientState.TiledMinimized(), kwinClient === this.lastFocusedClient);
|
||||
client.stateManager.setState(
|
||||
() => new ClientState.TiledMinimized(this.world, client),
|
||||
kwinClient === this.lastFocusedClient,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public unminimizeClient(kwinClient: KwinClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientState.TiledMinimized) {
|
||||
const desktop = this.desktopManager.getDesktopForClient(kwinClient);
|
||||
if (desktop !== undefined) {
|
||||
client.stateManager.setState(() => new ClientState.Tiled(this.world, client, desktop.grid), false);
|
||||
} else {
|
||||
client.stateManager.setState(() => new ClientState.Floating(this.world, client, this.config, false), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public tileClient(kwinClient: KwinClient, grid: Grid) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
public tileClient(client: ClientWrapper, grid: Grid) {
|
||||
if (client.stateManager.getState() instanceof ClientState.Tiled) {
|
||||
return;
|
||||
}
|
||||
client.stateManager.setState(() => new ClientState.Tiled(this.world, client, grid), false);
|
||||
}
|
||||
|
||||
public untileClient(kwinClient: KwinClient) {
|
||||
public floatClient(client: ClientWrapper) {
|
||||
if (client.stateManager.getState() instanceof ClientState.Floating) {
|
||||
return;
|
||||
}
|
||||
client.stateManager.setState(() => new ClientState.Floating(this.world, client, this.config, true), false);
|
||||
}
|
||||
|
||||
public tileKwinClient(kwinClient: KwinClient, grid: Grid) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientState.Tiled) {
|
||||
client.stateManager.setState(() => new ClientState.Floating(this.world, client, this.config, true), false);
|
||||
this.tileClient(client, grid);
|
||||
}
|
||||
|
||||
public floatKwinClient(kwinClient: KwinClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
this.floatClient(client);
|
||||
}
|
||||
|
||||
public pinClient(kwinClient: KwinClient) {
|
||||
|
||||
@@ -3,8 +3,10 @@ class ClientWrapper {
|
||||
public readonly stateManager: ClientState.Manager;
|
||||
public transientFor: ClientWrapper | null;
|
||||
private readonly transients: ClientWrapper[];
|
||||
private readonly signalManager: SignalManager;
|
||||
private readonly rulesSignalManager: SignalManager | null;
|
||||
public preferredWidth: number;
|
||||
private maximizedMode: MaximizedMode | undefined;
|
||||
private readonly manipulatingGeometry: Doer;
|
||||
private lastPlacement: QmlRect | null; // workaround for issue #19
|
||||
|
||||
@@ -20,6 +22,7 @@ class ClientWrapper {
|
||||
if (transientFor !== null) {
|
||||
transientFor.addTransient(this);
|
||||
}
|
||||
this.signalManager = ClientWrapper.initSignalManager(this);
|
||||
this.rulesSignalManager = rulesSignalManager;
|
||||
this.preferredWidth = kwinClient.frameGeometry.width;
|
||||
this.manipulatingGeometry = new Doer();
|
||||
@@ -44,9 +47,9 @@ class ClientWrapper {
|
||||
});
|
||||
}
|
||||
|
||||
private moveTransient(dx: number, dy: number, desktopNumber: number) {
|
||||
private moveTransient(dx: number, dy: number, kwinDesktops: KwinDesktop[]) {
|
||||
if (this.stateManager.getState() instanceof ClientState.Floating) {
|
||||
if (this.kwinClient.desktop === desktopNumber) {
|
||||
if (Clients.isOnOneOfVirtualDesktops(this.kwinClient, kwinDesktops)) {
|
||||
const frame = this.kwinClient.frameGeometry;
|
||||
this.kwinClient.frameGeometry = Qt.rect(
|
||||
frame.x + dx,
|
||||
@@ -57,32 +60,52 @@ class ClientWrapper {
|
||||
}
|
||||
|
||||
for (const transient of this.transients) {
|
||||
transient.moveTransient(dx, dy, desktopNumber);
|
||||
transient.moveTransient(dx, dy, kwinDesktops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public moveTransients(dx: number, dy: number) {
|
||||
for (const transient of this.transients) {
|
||||
transient.moveTransient(dx, dy, this.kwinClient.desktop);
|
||||
transient.moveTransient(dx, dy, this.kwinClient.desktops);
|
||||
}
|
||||
}
|
||||
|
||||
public focus() {
|
||||
workspace.activeClient = this.kwinClient;
|
||||
Workspace.activeWindow = this.kwinClient;
|
||||
}
|
||||
|
||||
public isFocused() {
|
||||
return workspace.activeClient === this.kwinClient;
|
||||
return Workspace.activeWindow === this.kwinClient;
|
||||
}
|
||||
|
||||
public setMaximize(horizontally: boolean, vertically: boolean) {
|
||||
if (!this.kwinClient.maximizable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.maximizedMode === undefined) {
|
||||
if (horizontally && vertically) {
|
||||
this.maximizedMode = MaximizedMode.Maximized;
|
||||
} else if (horizontally) {
|
||||
this.maximizedMode = MaximizedMode.Horizontally;
|
||||
} else if (vertically) {
|
||||
this.maximizedMode = MaximizedMode.Vertically;
|
||||
} else {
|
||||
this.maximizedMode = MaximizedMode.Unmaximized;
|
||||
}
|
||||
}
|
||||
|
||||
this.manipulatingGeometry.do(() => {
|
||||
this.kwinClient.setMaximize(vertically, horizontally);
|
||||
});
|
||||
}
|
||||
|
||||
public setFullScreen(fullScreen: boolean) {
|
||||
if (!this.kwinClient.fullScreenable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.manipulatingGeometry.do(() => {
|
||||
this.kwinClient.fullScreen = fullScreen;
|
||||
});
|
||||
@@ -98,6 +121,10 @@ class ClientWrapper {
|
||||
return this.kwinClient.shade;
|
||||
}
|
||||
|
||||
public getMaximizedMode() {
|
||||
return this.maximizedMode;
|
||||
}
|
||||
|
||||
public isManipulatingGeometry(newGeometry: QmlRect | null) {
|
||||
if (newGeometry !== null && newGeometry === this.lastPlacement) {
|
||||
return true;
|
||||
@@ -124,7 +151,7 @@ class ClientWrapper {
|
||||
}
|
||||
|
||||
public ensureVisible(screenSize: QmlRect) {
|
||||
if (this.kwinClient.desktop !== workspace.currentDesktop) {
|
||||
if (!Clients.isOnVirtualDesktop(this.kwinClient, Workspace.currentDesktop)) {
|
||||
return;
|
||||
}
|
||||
const frame = this.kwinClient.frameGeometry;
|
||||
@@ -137,6 +164,7 @@ class ClientWrapper {
|
||||
|
||||
public destroy(passFocus: boolean) {
|
||||
this.stateManager.destroy(passFocus);
|
||||
this.signalManager.destroy();
|
||||
if (this.rulesSignalManager !== null) {
|
||||
this.rulesSignalManager.destroy();
|
||||
}
|
||||
@@ -147,4 +175,17 @@ class ClientWrapper {
|
||||
transient.transientFor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static initSignalManager(client: ClientWrapper) {
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(client.kwinClient.maximizedAboutToChange, (maximizedMode: MaximizedMode) => {
|
||||
if (maximizedMode > MaximizedMode.Unmaximized && client.kwinClient.tile !== null) {
|
||||
client.kwinClient.tile = null;
|
||||
}
|
||||
client.maximizedMode = maximizedMode;
|
||||
});
|
||||
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
namespace Clients {
|
||||
export function canTileEver(kwinClient: KwinClient) {
|
||||
return kwinClient.resizeable;
|
||||
return kwinClient.moveable && kwinClient.resizeable && !kwinClient.popupWindow;
|
||||
}
|
||||
|
||||
export function canTileNow(kwinClient: KwinClient) {
|
||||
return canTileEver(kwinClient) && !kwinClient.minimized && kwinClient.desktop > 0 && kwinClient.activities.length === 1;
|
||||
return canTileEver(kwinClient) && !kwinClient.minimized && kwinClient.desktops.length === 1 && kwinClient.activities.length === 1;
|
||||
}
|
||||
|
||||
export function makeTileable(kwinClient: KwinClient) {
|
||||
if (kwinClient.minimized) {
|
||||
kwinClient.minimized = false;
|
||||
}
|
||||
if (kwinClient.desktop <= 0) {
|
||||
kwinClient.desktop = workspace.currentDesktop;
|
||||
if (kwinClient.desktops.length !== 1) {
|
||||
kwinClient.desktops = [Workspace.currentDesktop];
|
||||
}
|
||||
if (kwinClient.activities.length !== 1) {
|
||||
kwinClient.activities = [workspace.currentActivity];
|
||||
kwinClient.activities = [Workspace.currentActivity];
|
||||
}
|
||||
}
|
||||
|
||||
export function isMaximizedGeometry(kwinClient: KwinClient) {
|
||||
const maximizeArea = workspace.clientArea(ClientAreaOption.MaximizeArea, kwinClient.screen, kwinClient.desktop);
|
||||
return kwinClient.frameGeometry === maximizeArea;
|
||||
export function getKwinDesktopApprox(kwinClient: KwinClient) {
|
||||
switch (kwinClient.desktops.length) {
|
||||
case 0:
|
||||
return Workspace.currentDesktop;
|
||||
case 1:
|
||||
return kwinClient.desktops[0];
|
||||
default:
|
||||
if (kwinClient.desktops.includes(Workspace.currentDesktop)) {
|
||||
return Workspace.currentDesktop;
|
||||
} else {
|
||||
return kwinClient.desktops[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isFullScreenGeometry(kwinClient: KwinClient) {
|
||||
const fullScreenArea = workspace.clientArea(ClientAreaOption.FullScreenArea, kwinClient.screen, kwinClient.desktop);
|
||||
const fullScreenArea = Workspace.clientArea(ClientAreaOption.FullScreenArea, kwinClient.output, getKwinDesktopApprox(kwinClient));
|
||||
return kwinClient.frameGeometry === fullScreenArea;
|
||||
}
|
||||
|
||||
export function isOnVirtualDesktop(kwinClient: KwinClient, desktopNumber: number) {
|
||||
return kwinClient.desktop === desktopNumber || kwinClient.desktop === -1;
|
||||
export function isOnVirtualDesktop(kwinClient: KwinClient, kwinDesktop: KwinDesktop) {
|
||||
return kwinClient.desktops.length === 0 || kwinClient.desktops.includes(kwinDesktop);
|
||||
}
|
||||
|
||||
export function isOnOneOfVirtualDesktops(kwinClient: KwinClient, kwinDesktops: KwinDesktop[]) {
|
||||
return kwinClient.desktops.length === 0 || kwinClient.desktops.some(d => kwinDesktops.includes(d));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,147 +2,125 @@ class DesktopManager {
|
||||
private readonly pinManager: PinManager;
|
||||
private readonly config: Desktop.Config;
|
||||
public readonly layoutConfig: LayoutConfig;
|
||||
private readonly desktopsPerActivity: Map<string, Desktop[]>;
|
||||
private nVirtualDesktops: number;
|
||||
private readonly desktops: Map<string, Desktop>; // key is activityId|desktopId
|
||||
private kwinActivities: Set<string>;
|
||||
private kwinDesktops: Set<KwinDesktop>;
|
||||
|
||||
constructor(pinManager: PinManager, config: Desktop.Config, layoutConfig: LayoutConfig, currentActivity: string) {
|
||||
constructor(pinManager: PinManager, config: Desktop.Config, layoutConfig: LayoutConfig, currentActivity: string, currentDesktop: KwinDesktop) {
|
||||
this.pinManager = pinManager;
|
||||
this.config = config;
|
||||
this.layoutConfig = layoutConfig;
|
||||
this.desktopsPerActivity = new Map();
|
||||
this.nVirtualDesktops = 0;
|
||||
this.update()
|
||||
this.addActivity(currentActivity);
|
||||
this.desktops = new Map();
|
||||
this.kwinActivities = new Set(Workspace.activities);
|
||||
this.kwinDesktops = new Set(Workspace.desktops);
|
||||
this.addDesktop(currentActivity, currentDesktop);
|
||||
}
|
||||
|
||||
public update() {
|
||||
this.setNVirtualDesktops(workspace.desktops);
|
||||
}
|
||||
|
||||
public getDesktop(activity: string, desktopNumber: number) {
|
||||
const desktopIndex = desktopNumber - 1;
|
||||
if (desktopIndex >= this.nVirtualDesktops || desktopIndex < 0) {
|
||||
throw new Error("invalid desktop number: " + String(desktopNumber));
|
||||
public getDesktop(activity: string, kwinDesktop: KwinDesktop) {
|
||||
const desktopKey = DesktopManager.getDesktopKey(activity, kwinDesktop);
|
||||
const desktop = this.desktops.get(desktopKey);
|
||||
if (desktop !== undefined) {
|
||||
return desktop;
|
||||
} else {
|
||||
return this.addDesktop(activity, kwinDesktop);
|
||||
}
|
||||
if (!this.desktopsPerActivity.has(activity)) {
|
||||
this.addActivity(activity);
|
||||
}
|
||||
return this.desktopsPerActivity.get(activity)![desktopIndex];
|
||||
}
|
||||
|
||||
public getCurrentDesktop() {
|
||||
return this.getDesktop(workspace.currentActivity, workspace.currentDesktop);
|
||||
return this.getDesktop(Workspace.currentActivity, Workspace.currentDesktop);
|
||||
}
|
||||
|
||||
public getDesktopInCurrentActivity(desktopNumber: number) {
|
||||
return this.getDesktop(workspace.currentActivity, desktopNumber);
|
||||
public getDesktopInCurrentActivity(kwinDesktop: KwinDesktop) {
|
||||
return this.getDesktop(Workspace.currentActivity, kwinDesktop);
|
||||
}
|
||||
|
||||
public getDesktopForClient(kwinClient: KwinClient) {
|
||||
if (kwinClient.activities.length !== 1 && kwinClient.desktop === 0) {
|
||||
if (kwinClient.activities.length !== 1 || kwinClient.desktops.length !== 1) {
|
||||
return undefined;
|
||||
}
|
||||
return this.getDesktop(kwinClient.activities[0], kwinClient.desktop);
|
||||
return this.getDesktop(kwinClient.activities[0], kwinClient.desktops[0]);
|
||||
}
|
||||
|
||||
private setNVirtualDesktops(nVirtualDesktops: number) {
|
||||
if (nVirtualDesktops > this.nVirtualDesktops) {
|
||||
this.addDesktopsToActivities(nVirtualDesktops - this.nVirtualDesktops);
|
||||
} else if (nVirtualDesktops < this.nVirtualDesktops) {
|
||||
this.removeDesktopsFromActivities(this.nVirtualDesktops - nVirtualDesktops);
|
||||
}
|
||||
this.nVirtualDesktops = nVirtualDesktops;
|
||||
private addDesktop(activity: string, kwinDesktop: KwinDesktop) {
|
||||
const desktopKey = DesktopManager.getDesktopKey(activity, kwinDesktop);
|
||||
const desktop = new Desktop(kwinDesktop, this.pinManager, this.config, this.layoutConfig);
|
||||
this.desktops.set(desktopKey, desktop);
|
||||
return desktop;
|
||||
}
|
||||
|
||||
private addDesktopsToActivities(n: number) {
|
||||
for (const desktops of this.desktopsPerActivity.values()) {
|
||||
this.addDesktops(desktops, n);
|
||||
}
|
||||
private static getDesktopKey(activity: string, kwinDesktop: KwinDesktop) {
|
||||
return activity + "|" + kwinDesktop.id;
|
||||
}
|
||||
|
||||
private addDesktops(desktops: Desktop[], n: number) {
|
||||
const nStart = desktops.length;
|
||||
for (let i = 0; i < n; i++) {
|
||||
const desktopNumber = nStart + i + 1;
|
||||
desktops.push(new Desktop(desktopNumber, this.pinManager, this.config, this.layoutConfig));
|
||||
}
|
||||
}
|
||||
|
||||
private removeDesktopsFromActivities(n: number) {
|
||||
const lastRemainingDesktopIndex = this.nVirtualDesktops - n - 1;
|
||||
for (const desktops of this.desktopsPerActivity.values()) {
|
||||
const targetDesktop = desktops[lastRemainingDesktopIndex];
|
||||
for (let i = 0; i < n; i++) {
|
||||
const removedDesktop = desktops.pop()!;
|
||||
removedDesktop.grid.evacuate(targetDesktop.grid);
|
||||
public updateActivities() {
|
||||
const newActivities = new Set(Workspace.activities);
|
||||
for (const activity of this.kwinActivities) {
|
||||
if (!newActivities.has(activity)) {
|
||||
this.removeActivity(activity);
|
||||
}
|
||||
}
|
||||
this.kwinActivities = newActivities;
|
||||
}
|
||||
|
||||
private addActivity(activity: string) {
|
||||
const desktops: Desktop[] = [];
|
||||
this.addDesktops(desktops, this.nVirtualDesktops);
|
||||
this.desktopsPerActivity.set(activity, desktops);
|
||||
public updateDesktops() {
|
||||
const newDesktops = new Set(Workspace.desktops);
|
||||
for (const desktop of this.kwinDesktops) {
|
||||
if (!newDesktops.has(desktop)) {
|
||||
this.removeKwinDesktop(desktop);
|
||||
}
|
||||
}
|
||||
this.kwinDesktops = newDesktops;
|
||||
}
|
||||
|
||||
private removeActivity(activity: string) {
|
||||
const removedDesktops = this.desktopsPerActivity.get(activity)!;
|
||||
this.desktopsPerActivity.delete(activity);
|
||||
const targetActivityDesktops = this.desktopsPerActivity.values().next().value;
|
||||
for (let i = 0; i < removedDesktops.length; i++) {
|
||||
removedDesktops[i].grid.evacuate(targetActivityDesktops[i]);
|
||||
for (const kwinDesktop of this.kwinDesktops) {
|
||||
this.destroyDesktop(activity, kwinDesktop);
|
||||
}
|
||||
}
|
||||
|
||||
private removeKwinDesktop(kwinDesktop: KwinDesktop) {
|
||||
for (const activity of this.kwinActivities) {
|
||||
this.destroyDesktop(activity, kwinDesktop);
|
||||
}
|
||||
}
|
||||
|
||||
private destroyDesktop(activity: string, kwinDesktop: KwinDesktop) {
|
||||
const desktopKey = DesktopManager.getDesktopKey(activity, kwinDesktop);
|
||||
const desktop = this.desktops.get(desktopKey);
|
||||
if (desktop !== undefined) {
|
||||
desktop.destroy();
|
||||
this.desktops.delete(desktopKey);
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
for (const desktop of this.desktops()) {
|
||||
for (const desktop of this.desktops.values()) {
|
||||
desktop.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public *desktops() {
|
||||
for (const desktops of this.desktopsPerActivity.values()) {
|
||||
for (const desktop of desktops) {
|
||||
yield desktop;
|
||||
}
|
||||
public *getAllDesktops() {
|
||||
for (const desktop of this.desktops.values()) {
|
||||
yield desktop;
|
||||
}
|
||||
}
|
||||
|
||||
public *getDesktopsForClient(kwinClient: KwinClient) {
|
||||
const activities = kwinClient.activities.length > 0 ? kwinClient.activities : this.desktopsPerActivity.keys();
|
||||
for (const activity of activities) {
|
||||
if (!this.desktopsPerActivity.has(activity)) {
|
||||
this.addActivity(activity);
|
||||
}
|
||||
const activityDesktops = this.desktopsPerActivity.get(activity)!;
|
||||
if (kwinClient.desktop === -1) {
|
||||
for (const desktop of activityDesktops) {
|
||||
yield desktop;
|
||||
}
|
||||
} else {
|
||||
const desktopIndex = kwinClient.desktop - 1;
|
||||
yield activityDesktops[desktopIndex];
|
||||
}
|
||||
}
|
||||
public getDesktopsForClient(kwinClient: KwinClient) {
|
||||
const desktops = this.getDesktops(kwinClient.activities, kwinClient.desktops); // workaround for QTBUG-109880
|
||||
return desktops;
|
||||
}
|
||||
|
||||
// empty array means all
|
||||
public *getDesktops(desktopNumbers: number[], inputActivities: string[]) {
|
||||
const activities = inputActivities.length > 0 ? inputActivities : this.desktopsPerActivity.keys();
|
||||
for (const activity of activities) {
|
||||
if (!this.desktopsPerActivity.has(activity)) {
|
||||
this.addActivity(activity);
|
||||
}
|
||||
const activityDesktops = this.desktopsPerActivity.get(activity)!;
|
||||
if (desktopNumbers.length === 0) {
|
||||
for (const desktop of activityDesktops) {
|
||||
public *getDesktops(activities: string[], kwinDesktops: KwinDesktop[]) {
|
||||
const matchedActivities = activities.length > 0 ? activities : this.kwinActivities.keys();
|
||||
const matchedDesktops = kwinDesktops.length > 0 ? kwinDesktops : this.kwinDesktops.keys();
|
||||
for (const matchedActivity of matchedActivities) {
|
||||
for (const matchedDesktop of matchedDesktops) {
|
||||
const desktopKey = DesktopManager.getDesktopKey(matchedActivity, matchedDesktop);
|
||||
const desktop = this.desktops.get(desktopKey);
|
||||
if (desktop !== undefined) {
|
||||
yield desktop;
|
||||
}
|
||||
} else {
|
||||
for (const desktopNumber of desktopNumbers) {
|
||||
const desktopIndex = desktopNumber - 1;
|
||||
yield activityDesktops[desktopIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ class PinManager {
|
||||
this.pinnedClients.delete(kwinClient);
|
||||
}
|
||||
|
||||
public getAvailableSpace(desktopNumber: number, screen: QmlRect) {
|
||||
public getAvailableSpace(kwinDesktop: KwinDesktop, screen: QmlRect) {
|
||||
const baseLot = new PinManager.Lot(screen.top, screen.bottom, screen.left, screen.right);
|
||||
let lots = [baseLot];
|
||||
for (const client of this.pinnedClients) {
|
||||
if (!Clients.isOnVirtualDesktop(client, desktopNumber)) {
|
||||
if (!Clients.isOnVirtualDesktop(client, kwinDesktop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@ class World {
|
||||
public readonly clientManager: ClientManager;
|
||||
private readonly pinManager: PinManager;
|
||||
private readonly workspaceSignalManager: SignalManager;
|
||||
private readonly shortcutActions: ShortcutAction[];
|
||||
private readonly screenResizedDelayer: Delayer;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.untileOnDrag = config.untileOnDrag;
|
||||
this.workspaceSignalManager = initWorkspaceSignalHandlers(this);
|
||||
this.shortcutActions = registerKeyBindings(this, {
|
||||
manualScrollStep: config.manualScrollStep,
|
||||
manualResizeStep: config.manualResizeStep,
|
||||
columnResizer: config.scrollingCentered ? new RawResizer() : new ContextualResizer(),
|
||||
});
|
||||
|
||||
this.screenResizedDelayer = new Delayer(1000, () => {
|
||||
// this delay ensures that docks are taken into account by `workspace.clientArea`
|
||||
const desktopManager = this.desktopManager; // workaround for bug in Qt5's JS engine
|
||||
for (const desktop of desktopManager.desktops()) {
|
||||
// this delay ensures that docks are taken into account by `Workspace.clientArea`
|
||||
for (const desktop of this.desktopManager.getAllDesktops()) {
|
||||
desktop.onLayoutChanged();
|
||||
}
|
||||
this.update();
|
||||
@@ -47,7 +52,8 @@ class World {
|
||||
clamper: config.scrollingLazy ? new EdgeClamper() : new CenterClamper(),
|
||||
},
|
||||
layoutConfig,
|
||||
workspace.currentActivity,
|
||||
Workspace.currentActivity,
|
||||
Workspace.currentDesktop,
|
||||
);
|
||||
this.clientManager = new ClientManager(config, this, this.desktopManager, this.pinManager);
|
||||
this.addExistingClients();
|
||||
@@ -55,17 +61,13 @@ class World {
|
||||
}
|
||||
|
||||
private addExistingClients() {
|
||||
const kwinClients = workspace.clientList();
|
||||
const kwinClients = Workspace.windows;
|
||||
for (let i = 0; i < kwinClients.length; i++) {
|
||||
const kwinClient = kwinClients[i];
|
||||
this.clientManager.addClient(kwinClient);
|
||||
}
|
||||
}
|
||||
|
||||
public updateDesktops() {
|
||||
this.desktopManager.update();
|
||||
}
|
||||
|
||||
private update() {
|
||||
this.desktopManager.getCurrentDesktop().arrange();
|
||||
}
|
||||
@@ -94,11 +96,14 @@ class World {
|
||||
followTransient: boolean,
|
||||
f: (clientManager: ClientManager, desktopManager: DesktopManager, window: Window, column: Column, grid: Grid) => void,
|
||||
) {
|
||||
this.doIfTiled(workspace.activeClient, followTransient, f);
|
||||
this.doIfTiled(Workspace.activeWindow, followTransient, f);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.workspaceSignalManager.destroy();
|
||||
for (const shortcutAction of this.shortcutActions) {
|
||||
shortcutAction.destroy();
|
||||
}
|
||||
this.clientManager.destroy();
|
||||
this.desktopManager.destroy();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ClientState {
|
||||
|
||||
private static initSignalManager(world: World, kwinClient: KwinClient) {
|
||||
const manager = new SignalManager();
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QmlRect) => {
|
||||
manager.connect(kwinClient.frameGeometryChanged, () => {
|
||||
world.onScreenResized();
|
||||
});
|
||||
return manager;
|
||||
|
||||
@@ -24,7 +24,11 @@ namespace ClientState {
|
||||
}
|
||||
|
||||
private static limitHeight(client: ClientWrapper) {
|
||||
const placementArea = workspace.clientArea(ClientAreaOption.PlacementArea, client.kwinClient.screen, client.kwinClient.desktop);
|
||||
const placementArea = Workspace.clientArea(
|
||||
ClientAreaOption.PlacementArea,
|
||||
client.kwinClient.output,
|
||||
Clients.getKwinDesktopApprox(client.kwinClient),
|
||||
);
|
||||
const clientRect = client.kwinClient.frameGeometry;
|
||||
const width = client.preferredWidth;
|
||||
client.place(
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace ClientState {
|
||||
|
||||
private static initSignalManager(world: World, pinManager: PinManager, kwinClient: KwinClient) {
|
||||
const manager = new SignalManager();
|
||||
let oldDesktopNumber = kwinClient.desktop;
|
||||
let oldActivities = kwinClient.activities;
|
||||
let oldDesktops = kwinClient.desktops;
|
||||
|
||||
manager.connect(kwinClient.tileChanged, () => {
|
||||
if (kwinClient.tile === null) {
|
||||
@@ -41,7 +41,7 @@ namespace ClientState {
|
||||
}
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QmlRect) => {
|
||||
manager.connect(kwinClient.frameGeometryChanged, () => {
|
||||
if (kwinClient.tile === null) {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.unpinClient(kwinClient);
|
||||
@@ -56,25 +56,24 @@ namespace ClientState {
|
||||
})
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.desktopChanged, () => {
|
||||
const changedDesktops = oldDesktopNumber === -1 || kwinClient.desktop === -1 ?
|
||||
manager.connect(kwinClient.desktopsChanged, () => {
|
||||
const changedDesktops = oldDesktops.length === 0 || kwinClient.desktops.length === 0 ?
|
||||
[] :
|
||||
[oldDesktopNumber, kwinClient.desktop];
|
||||
union(oldDesktops, kwinClient.desktops);
|
||||
world.do((clientManager, desktopManager) => {
|
||||
for (const desktop of desktopManager.getDesktops(changedDesktops, kwinClient.activities)) {
|
||||
for (const desktop of desktopManager.getDesktops(kwinClient.activities, changedDesktops)) {
|
||||
desktop.onPinsChanged();
|
||||
}
|
||||
});
|
||||
oldDesktopNumber = kwinClient.desktop;
|
||||
oldDesktops = kwinClient.desktops;
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.activitiesChanged, () => {
|
||||
const desktops = kwinClient.desktop === -1 ? [] : [kwinClient.desktop];
|
||||
const changedActivities = oldActivities.length === 0 || kwinClient.activities.length === 0 ?
|
||||
[] :
|
||||
union(oldActivities, kwinClient.activities);
|
||||
world.do((clientManager, desktopManager) => {
|
||||
for (const desktop of desktopManager.getDesktops(desktops, changedActivities)) {
|
||||
for (const desktop of desktopManager.getDesktops(changedActivities, kwinClient.desktops)) {
|
||||
desktop.onPinsChanged();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,12 +31,12 @@ namespace ClientState {
|
||||
const kwinClient = client.kwinClient;
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(kwinClient.desktopChanged, () => {
|
||||
manager.connect(kwinClient.desktopsChanged, () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const desktop = desktopManager.getDesktopForClient(kwinClient);
|
||||
if (desktop === undefined) {
|
||||
// windows on all desktops are not supported
|
||||
clientManager.untileClient(kwinClient);
|
||||
// windows on multiple desktops are not supported
|
||||
clientManager.floatKwinClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
Tiled.moveWindowToGrid(window, desktop.grid);
|
||||
@@ -48,44 +48,54 @@ namespace ClientState {
|
||||
const desktop = desktopManager.getDesktopForClient(kwinClient);
|
||||
if (desktop === undefined) {
|
||||
// windows on multiple activities are not supported
|
||||
clientManager.untileClient(kwinClient);
|
||||
clientManager.floatKwinClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
Tiled.moveWindowToGrid(window, desktop.grid);
|
||||
});
|
||||
})
|
||||
|
||||
let lastResize = false;
|
||||
manager.connect(kwinClient.moveResizedChanged, () => {
|
||||
manager.connect(kwinClient.minimizedChanged, () => {
|
||||
console.assert(kwinClient.minimized);
|
||||
world.do((clientManager, desktopManager) => {
|
||||
if (kwinClient.move) {
|
||||
if (world.untileOnDrag) {
|
||||
clientManager.untileClient(kwinClient);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const grid = window.column.grid;
|
||||
const resize = kwinClient.resize;
|
||||
if (!lastResize && resize) {
|
||||
grid.onUserResizeStarted();
|
||||
}
|
||||
if (lastResize && !resize) {
|
||||
grid.onUserResizeFinished();
|
||||
}
|
||||
lastResize = resize;
|
||||
clientManager.minimizeClient(kwinClient);
|
||||
});
|
||||
});
|
||||
|
||||
let cursorChangedAfterResizeStart = false;
|
||||
manager.connect(kwinClient.moveResizeCursorChanged, () => {
|
||||
cursorChangedAfterResizeStart = true;
|
||||
});
|
||||
manager.connect(kwinClient.clientStartUserMovedResized, () => {
|
||||
cursorChangedAfterResizeStart = false;
|
||||
manager.connect(kwinClient.maximizedAboutToChange, (maximizedMode: MaximizedMode) => {
|
||||
world.do(() => {
|
||||
window.onMaximizedChanged(maximizedMode);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QmlRect) => {
|
||||
let resizing = false;
|
||||
let resizingBorder = false;
|
||||
manager.connect(kwinClient.interactiveMoveResizeStarted, () => {
|
||||
if (kwinClient.move) {
|
||||
if (world.untileOnDrag) {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.floatKwinClient(kwinClient);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (kwinClient.resize) {
|
||||
resizing = true;
|
||||
resizingBorder = Workspace.cursorPos.x > kwinClient.frameGeometry.right ||
|
||||
Workspace.cursorPos.x < kwinClient.frameGeometry.left;
|
||||
window.column.grid.onUserResizeStarted();
|
||||
}
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.interactiveMoveResizeFinished, () => {
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
window.column.grid.onUserResizeFinished();
|
||||
}
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.frameGeometryChanged, (oldGeometry: QmlRect) => {
|
||||
// on Wayland, this fires after `tileChanged`
|
||||
if (kwinClient.tile !== null) {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
@@ -106,11 +116,11 @@ namespace ClientState {
|
||||
}
|
||||
|
||||
if (kwinClient.resize) {
|
||||
world.do(() => window.onUserResize(oldGeometry, !cursorChangedAfterResizeStart));
|
||||
world.do(() => window.onUserResize(oldGeometry, resizingBorder));
|
||||
} else if (
|
||||
!window.column.grid.isUserResizing() &&
|
||||
!client.isManipulatingGeometry(newGeometry) &&
|
||||
!Clients.isMaximizedGeometry(kwinClient) &&
|
||||
client.getMaximizedMode() === MaximizedMode.Unmaximized &&
|
||||
!Clients.isFullScreenGeometry(kwinClient) // not using `kwinClient.fullScreen` because it may not be set yet at this point
|
||||
) {
|
||||
world.do(() => window.onFrameGeometryChanged());
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
namespace ClientState {
|
||||
export class TiledMinimized implements State {
|
||||
public destroy(passFocus: boolean) {}
|
||||
private readonly signalManager: SignalManager;
|
||||
|
||||
constructor(world: World, client: ClientWrapper) {
|
||||
this.signalManager = TiledMinimized.initSignalManager(world, client);
|
||||
}
|
||||
|
||||
public destroy(passFocus: boolean) {
|
||||
this.signalManager.destroy();
|
||||
}
|
||||
|
||||
private static initSignalManager(world: World, client: ClientWrapper) {
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(client.kwinClient.minimizedChanged, () => {
|
||||
console.assert(!client.kwinClient.minimized);
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const desktop = desktopManager.getDesktopForClient(client.kwinClient);
|
||||
if (desktop !== undefined) {
|
||||
clientManager.tileClient(client, desktop.grid);
|
||||
} else {
|
||||
clientManager.floatClient(client);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user