Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc78f11bc | ||
|
|
f1206b18b1 | ||
|
|
c6066c354d | ||
|
|
914202f091 | ||
|
|
b85c86e7db | ||
|
|
fdb4b88333 | ||
|
|
4e3d924366 | ||
|
|
3d3e8cff17 | ||
|
|
a79229da75 | ||
|
|
53d04c1d33 | ||
|
|
a18ff61d9e | ||
|
|
99ffad9223 | ||
|
|
e776df509b | ||
|
|
63de2d4cae | ||
|
|
85d361f16f | ||
|
|
f3b75807be | ||
|
|
88bce26456 | ||
|
|
3a75ddab0f | ||
|
|
ee0aa93308 | ||
|
|
33ca138420 | ||
|
|
79f4aaeef8 | ||
|
|
0aca6e1146 | ||
|
|
28e54434aa | ||
|
|
ee14509228 | ||
|
|
1596edc43f | ||
|
|
b897ab5b9f | ||
|
|
5e6dad8459 | ||
|
|
beeba74442 | ||
|
|
3e14440180 | ||
|
|
36836ad258 | ||
|
|
bebc009cc6 | ||
|
|
a4ba8516dc | ||
|
|
675a70d907 | ||
|
|
3ba66f1c89 | ||
|
|
4556198b2e | ||
|
|
7b8de5955d | ||
|
|
68a687b7d4 | ||
|
|
20a3ece4b5 | ||
|
|
3cad8102ee | ||
|
|
7fd45eed8f | ||
|
|
7299341608 | ||
|
|
842ec1ac63 | ||
|
|
0523465b84 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/package/contents/code/main.js
|
||||
/package/contents/config/main.xml
|
||||
/karousel*.tar.gz
|
||||
run-ts-tmp.js
|
||||
/.idea
|
||||
|
||||
23
Makefile
23
Makefile
@@ -1,32 +1,29 @@
|
||||
.PHONY: *
|
||||
|
||||
TSC_SCRIPT_FLAGS = --lib es2020 ./src/extern/qt.d.ts
|
||||
VERSION = $(shell grep '"Version":' ./package/metadata.json | grep -o '[0-9\.]*')
|
||||
|
||||
config:
|
||||
build: tests
|
||||
tsc -p ./src/main --outFile ./package/contents/code/main.js
|
||||
mkdir -p ./package/contents/config
|
||||
tsc ${TSC_SCRIPT_FLAGS} ./src/config/definition.ts ./generators/config/kcfg.ts --outFile /dev/stdout | node - > ./package/contents/config/main.xml
|
||||
./run-ts.sh ./src/generators/config > ./package/contents/config/main.xml
|
||||
|
||||
build:
|
||||
tsc --outFile ./package/contents/code/main.js
|
||||
tests:
|
||||
./run-ts.sh ./src/tests
|
||||
|
||||
install: build config
|
||||
install: build
|
||||
kpackagetool6 --type=KWin/Script -i ./package || kpackagetool6 --type=KWin/Script -u ./package
|
||||
|
||||
uninstall:
|
||||
kpackagetool6 --type=KWin/Script -r karousel
|
||||
|
||||
package: build config
|
||||
package: build
|
||||
tar -czf ./karousel_${subst .,_,${VERSION}}.tar.gz ./package
|
||||
|
||||
logs:
|
||||
journalctl -t kwin_x11 -g '^qml:|^file://.*karousel' -f
|
||||
|
||||
docs-key-bindings-bbcode:
|
||||
@tsc ${TSC_SCRIPT_FLAGS} ./src/keyBindings/definition.ts ./generators/docs/keyBindings.ts ./generators/docs/keyBindingsBbcode.ts --outFile /dev/stdout | node -
|
||||
@./run-ts.sh ./src/generators/docs/keyBindingsBbcode
|
||||
|
||||
docs-key-bindings-table:
|
||||
@tsc ${TSC_SCRIPT_FLAGS} ./src/keyBindings/definition.ts ./generators/docs/keyBindings.ts ./generators/docs/keyBindingsTable.ts --outFile /dev/stdout | node -
|
||||
@./run-ts.sh ./src/generators/docs/keyBindingsTable
|
||||
|
||||
docs-key-bindings-fmt:
|
||||
@tsc ${TSC_SCRIPT_FLAGS} ./src/keyBindings/definition.ts ./generators/docs/keyBindings.ts ./generators/docs/keyBindingsFmt.ts --outFile /dev/stdout | node -
|
||||
@./run-ts.sh ./src/generators/docs/keyBindingsFmt
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"Name": "Peter Fajdiga"
|
||||
}],
|
||||
"Id": "karousel",
|
||||
"Version": "0.9",
|
||||
"Version": "0.9.4",
|
||||
"License": "GPLv3",
|
||||
"Website": "https://github.com/peterfajdiga/karousel",
|
||||
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"
|
||||
|
||||
2
run-ts.sh
Executable file
2
run-ts.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
tsc -p "$1" --outFile ./run-ts-tmp.js && node ./run-ts-tmp.js && rm ./run-ts-tmp.js
|
||||
6
src/extern/global.d.ts
vendored
6
src/extern/global.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
declare const qmlBase: QmlObject;
|
||||
declare const notificationInvalidWindowRules: Notification;
|
||||
|
||||
type Notification = {
|
||||
sendEvent(): void;
|
||||
};
|
||||
4
src/generators/config/tsconfig.json
Normal file
4
src/generators/config/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["../../lib/**/*", "./**/*"]
|
||||
}
|
||||
@@ -1,18 +1,3 @@
|
||||
type KeyBinding = {
|
||||
name: string;
|
||||
description: string;
|
||||
comment?: string;
|
||||
defaultKeySequence: string;
|
||||
}
|
||||
|
||||
type NumKeyBinding = {
|
||||
name: string;
|
||||
description: string;
|
||||
comment?: string;
|
||||
defaultModifiers: string;
|
||||
fKeys: boolean;
|
||||
}
|
||||
|
||||
function formatComment(comment: string | undefined) {
|
||||
return comment === undefined ? "" : ` (${comment})`;
|
||||
}
|
||||
8
src/generators/docs/keyBindingsBbcode/tsconfig.json
Normal file
8
src/generators/docs/keyBindingsBbcode/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"include": [
|
||||
"../../../lib/**/*",
|
||||
"../keyBindings.ts",
|
||||
"./**/*"
|
||||
]
|
||||
}
|
||||
8
src/generators/docs/keyBindingsFmt/tsconfig.json
Normal file
8
src/generators/docs/keyBindingsFmt/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"include": [
|
||||
"../../../lib/**/*",
|
||||
"../keyBindings.ts",
|
||||
"./**/*"
|
||||
]
|
||||
}
|
||||
8
src/generators/docs/keyBindingsTable/tsconfig.json
Normal file
8
src/generators/docs/keyBindingsTable/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"include": [
|
||||
"../../../lib/**/*",
|
||||
"../keyBindings.ts",
|
||||
"./**/*"
|
||||
]
|
||||
}
|
||||
@@ -148,13 +148,13 @@ namespace Actions {
|
||||
|
||||
case "column-move-start": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveAfter(null);
|
||||
grid.moveColumn(column, null);
|
||||
});
|
||||
};
|
||||
|
||||
case "column-move-end": return () => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveAfter(grid.getLastColumn());
|
||||
grid.moveColumn(column, grid.getLastColumn());
|
||||
});
|
||||
};
|
||||
|
||||
@@ -252,6 +252,12 @@ namespace Actions {
|
||||
});
|
||||
};
|
||||
|
||||
case "screen-switch": return () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.selectScreen(Workspace.activeScreen);
|
||||
});
|
||||
};
|
||||
|
||||
default: throw new Error("unknown action: " + name);
|
||||
}
|
||||
}
|
||||
@@ -286,10 +292,10 @@ namespace Actions {
|
||||
if (targetColumn === null || targetColumn === column) {
|
||||
return;
|
||||
}
|
||||
if (targetColumn.isAfter(column)) {
|
||||
column.moveAfter(targetColumn);
|
||||
if (targetColumn.isToTheRightOf(column)) {
|
||||
grid.moveColumn(column, targetColumn);
|
||||
} else {
|
||||
column.moveAfter(grid.getPrevColumn(targetColumn));
|
||||
grid.moveColumn(column, grid.getPrevColumn(targetColumn));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -3,6 +3,10 @@ const defaultWindowRules = `[
|
||||
"class": "ksmserver-logout-greeter",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "xwaylandvideobridge",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "(org\\\\.kde\\\\.)?plasmashell",
|
||||
"tile": false
|
||||
@@ -62,32 +66,32 @@ const configDef = [
|
||||
{
|
||||
name: "gapsOuterTop",
|
||||
type: "UInt",
|
||||
default: 18,
|
||||
default: 16,
|
||||
},
|
||||
{
|
||||
name: "gapsOuterBottom",
|
||||
type: "UInt",
|
||||
default: 18,
|
||||
default: 16,
|
||||
},
|
||||
{
|
||||
name: "gapsOuterLeft",
|
||||
type: "UInt",
|
||||
default: 18,
|
||||
default: 16,
|
||||
},
|
||||
{
|
||||
name: "gapsOuterRight",
|
||||
type: "UInt",
|
||||
default: 18,
|
||||
default: 16,
|
||||
},
|
||||
{
|
||||
name: "gapsInnerHorizontal",
|
||||
type: "UInt",
|
||||
default: 18,
|
||||
default: 8,
|
||||
},
|
||||
{
|
||||
name: "gapsInnerVertical",
|
||||
type: "UInt",
|
||||
default: 18,
|
||||
default: 8,
|
||||
},
|
||||
{
|
||||
name: "manualScrollStep",
|
||||
6
src/lib/extern/global.d.ts
vendored
Normal file
6
src/lib/extern/global.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
declare const console: Console;
|
||||
declare const Qt: Qt;
|
||||
declare const KWin: KWin;
|
||||
declare const Workspace: Workspace;
|
||||
declare const qmlBase: QmlObject;
|
||||
declare const notificationInvalidWindowRules: Notification;
|
||||
17
src/extern/kwin.d.ts → src/lib/extern/kwin.ts
vendored
17
src/extern/kwin.d.ts → src/lib/extern/kwin.ts
vendored
@@ -1,15 +1,15 @@
|
||||
declare const KWin: {
|
||||
type KWin = {
|
||||
readConfig(key: string, defaultValue: any): any;
|
||||
};
|
||||
|
||||
declare const Workspace: {
|
||||
type Workspace = {
|
||||
readonly activities: string[];
|
||||
readonly desktops: KwinDesktop[];
|
||||
readonly currentDesktop: KwinDesktop;
|
||||
readonly currentActivity: string;
|
||||
readonly activeScreen: Output;
|
||||
readonly windows: KwinClient[];
|
||||
readonly cursorPos: QmlPoint;
|
||||
readonly cursorPos: Readonly<QmlPoint>;
|
||||
|
||||
activeWindow: KwinClient;
|
||||
|
||||
@@ -17,12 +17,13 @@ declare const Workspace: {
|
||||
readonly windowAdded: QSignal<[KwinClient]>;
|
||||
readonly windowRemoved: QSignal<[KwinClient]>;
|
||||
readonly windowActivated: QSignal<[KwinClient]>;
|
||||
readonly desktopsChanged: QSignal<[]>;
|
||||
readonly screensChanged: QSignal<[]>;
|
||||
readonly activitiesChanged: QSignal<[]>;
|
||||
readonly desktopsChanged: QSignal<[]>;
|
||||
readonly currentActivityChanged: QSignal<[]>;
|
||||
readonly virtualScreenSizeChanged: QSignal<[]>;
|
||||
|
||||
clientArea(option: ClientAreaOption, output: Output, kwinDesktop: KwinDesktop);
|
||||
clientArea(option: ClientAreaOption, output: Output, kwinDesktop: KwinDesktop): QmlRect;
|
||||
};
|
||||
|
||||
const enum ClientAreaOption {
|
||||
@@ -49,9 +50,10 @@ type Output = unknown;
|
||||
interface KwinClient {
|
||||
readonly shadeable: boolean;
|
||||
readonly caption: string;
|
||||
readonly minSize: QmlSize;
|
||||
readonly minSize: Readonly<QmlSize>;
|
||||
readonly transient: boolean;
|
||||
readonly transientFor: KwinClient;
|
||||
readonly clientGeometry: Readonly<QmlRect>;
|
||||
readonly move: boolean;
|
||||
readonly resize: boolean;
|
||||
readonly moveable: boolean;
|
||||
@@ -59,11 +61,12 @@ interface KwinClient {
|
||||
readonly fullScreenable: boolean;
|
||||
readonly maximizable: boolean;
|
||||
readonly output: Output;
|
||||
readonly resourceClass: QByteArray;
|
||||
readonly resourceClass: string;
|
||||
readonly dock: boolean;
|
||||
readonly normalWindow: boolean;
|
||||
readonly managed: boolean;
|
||||
readonly popupWindow: boolean;
|
||||
readonly pid: number;
|
||||
|
||||
fullScreen: boolean;
|
||||
activities: string[]; // empty array means all activities
|
||||
3
src/lib/extern/notification.ts
vendored
Normal file
3
src/lib/extern/notification.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
type Notification = {
|
||||
sendEvent(): void;
|
||||
};
|
||||
18
src/extern/qt.d.ts → src/lib/extern/qt.ts
vendored
18
src/extern/qt.d.ts → src/lib/extern/qt.ts
vendored
@@ -1,18 +1,16 @@
|
||||
declare const console: {
|
||||
log(...args: any[]);
|
||||
trace();
|
||||
assert(boolean);
|
||||
};
|
||||
type Console = {
|
||||
log(...args: any[]): void;
|
||||
trace(): void;
|
||||
assert(assertion: boolean, message?: string): void;
|
||||
}
|
||||
|
||||
declare const Qt: {
|
||||
type Qt = {
|
||||
rect(x: number, y: number, width: number, height: number): QmlRect;
|
||||
createQmlObject(qml: string, parent: QmlObject);
|
||||
};
|
||||
createQmlObject(qml: string, parent: QmlObject): QmlObject;
|
||||
}
|
||||
|
||||
type QmlObject = unknown;
|
||||
|
||||
type QByteArray = string;
|
||||
|
||||
type QmlPoint = {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -146,6 +146,11 @@ const keyBindings: KeyBinding[] = [
|
||||
description: "Scroll to end",
|
||||
defaultKeySequence: "Meta+Alt+End",
|
||||
},
|
||||
{
|
||||
name: "screen-switch",
|
||||
description: "Move Karousel grid to the current screen",
|
||||
defaultKeySequence: "Meta+Ctrl+Return",
|
||||
}
|
||||
];
|
||||
|
||||
const numKeyBindings: NumKeyBinding[] = [
|
||||
@@ -19,7 +19,7 @@ class Column {
|
||||
|
||||
public moveToGrid(targetGrid: Grid, prevColumn: Column|null) {
|
||||
if (targetGrid === this.grid) {
|
||||
this.grid.onColumnMoved(this, prevColumn);
|
||||
this.grid.moveColumn(this, prevColumn);
|
||||
} else {
|
||||
this.grid.onColumnRemoved(this, false);
|
||||
this.grid = targetGrid;
|
||||
@@ -30,21 +30,14 @@ class Column {
|
||||
}
|
||||
}
|
||||
|
||||
public moveAfter(prevColumn: Column|null) {
|
||||
if (prevColumn === this) {
|
||||
return;
|
||||
}
|
||||
this.grid.onColumnMoved(this, prevColumn);
|
||||
}
|
||||
|
||||
public isAfter(other: Column) {
|
||||
return this.gridX > other.gridX;
|
||||
}
|
||||
|
||||
public isBefore(other: Column) {
|
||||
public isToTheLeftOf(other: Column) {
|
||||
return this.gridX < other.gridX;
|
||||
}
|
||||
|
||||
public isToTheRightOf(other: Column) {
|
||||
return this.gridX > other.gridX;
|
||||
}
|
||||
|
||||
public moveWindowUp(window: Window) {
|
||||
this.windows.moveBack(window);
|
||||
this.grid.desktop.onLayoutChanged();
|
||||
@@ -1,8 +1,5 @@
|
||||
class Desktop {
|
||||
public readonly grid: Grid;
|
||||
public readonly kwinDesktop: KwinDesktop;
|
||||
private readonly pinManager: PinManager;
|
||||
private readonly config: Desktop.Config;
|
||||
private scrollX: number;
|
||||
private dirty: boolean;
|
||||
private dirtyScroll: boolean;
|
||||
@@ -10,21 +7,24 @@ class Desktop {
|
||||
public clientArea: QmlRect;
|
||||
public tilingArea: QmlRect;
|
||||
|
||||
constructor(kwinDesktop: KwinDesktop, pinManager: PinManager, config: Desktop.Config, layoutConfig: LayoutConfig) {
|
||||
this.pinManager = pinManager;
|
||||
this.config = config;
|
||||
constructor(
|
||||
public readonly kwinDesktop: KwinDesktop,
|
||||
private readonly pinManager: PinManager,
|
||||
private readonly config: Desktop.Config,
|
||||
private readonly getScreen: () => Output,
|
||||
layoutConfig: LayoutConfig,
|
||||
) {
|
||||
this.scrollX = 0;
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
this.dirtyPins = true;
|
||||
this.kwinDesktop = kwinDesktop;
|
||||
this.grid = new Grid(this, layoutConfig);
|
||||
this.clientArea = Desktop.getClientArea(kwinDesktop);
|
||||
this.clientArea = Desktop.getClientArea(this.getScreen(), kwinDesktop);
|
||||
this.tilingArea = Desktop.getTilingArea(this.clientArea, kwinDesktop, pinManager, config);
|
||||
}
|
||||
|
||||
private updateArea() {
|
||||
const newClientArea = Desktop.getClientArea(this.kwinDesktop);
|
||||
const newClientArea = Desktop.getClientArea(this.getScreen(), this.kwinDesktop);
|
||||
if (newClientArea === this.clientArea && !this.dirtyPins) {
|
||||
return;
|
||||
}
|
||||
@@ -37,8 +37,8 @@ class Desktop {
|
||||
this.autoAdjustScroll();
|
||||
}
|
||||
|
||||
private static getClientArea(kwinDesktop: KwinDesktop) {
|
||||
return Workspace.clientArea(ClientAreaOption.PlacementArea, Workspace.activeScreen, kwinDesktop);
|
||||
private static getClientArea(screen: Output, kwinDesktop: KwinDesktop) {
|
||||
return Workspace.clientArea(ClientAreaOption.PlacementArea, screen, kwinDesktop);
|
||||
}
|
||||
|
||||
private static getTilingArea(clientArea: QmlRect, kwinDesktop: KwinDesktop, pinManager: PinManager, config: Desktop.Config) {
|
||||
@@ -24,6 +24,18 @@ class Grid {
|
||||
});
|
||||
}
|
||||
|
||||
public moveColumn(column: Column, prevColumn: Column|null) {
|
||||
if (column === prevColumn) {
|
||||
return;
|
||||
}
|
||||
const movedLeft = prevColumn === null ? true : column.isToTheRightOf(prevColumn);
|
||||
const firstMovedColumn = movedLeft ? column : this.getNextColumn(column);
|
||||
this.columns.move(column, prevColumn);
|
||||
this.columnsSetX(firstMovedColumn);
|
||||
this.desktop.onLayoutChanged();
|
||||
this.desktop.autoAdjustScroll();
|
||||
}
|
||||
|
||||
public moveColumnLeft(column: Column) {
|
||||
this.columns.moveBack(column);
|
||||
this.columnsSetX(column);
|
||||
@@ -180,15 +192,6 @@ class Grid {
|
||||
}
|
||||
}
|
||||
|
||||
public onColumnMoved(column: Column, prevColumn: Column|null) {
|
||||
const movedLeft = prevColumn === null ? true : column.isAfter(prevColumn);
|
||||
const firstMovedColumn = movedLeft ? column : this.getNextColumn(column);
|
||||
this.columns.move(column, prevColumn);
|
||||
this.columnsSetX(firstMovedColumn);
|
||||
this.desktop.onLayoutChanged();
|
||||
this.desktop.autoAdjustScroll();
|
||||
}
|
||||
|
||||
public onColumnWidthChanged(column: Column) {
|
||||
const nextColumn = this.columns.getNext(column);
|
||||
this.columnsSetX(nextColumn);
|
||||
@@ -36,7 +36,7 @@ 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.maximizedMode > MaximizedMode.Unmaximized) {
|
||||
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,
|
||||
@@ -79,7 +79,7 @@ class Window {
|
||||
}
|
||||
|
||||
public onMaximizedChanged(maximizedMode: MaximizedMode) {
|
||||
const maximized = maximizedMode > MaximizedMode.Unmaximized;
|
||||
const maximized = maximizedMode !== MaximizedMode.Unmaximized;
|
||||
this.skipArrange = maximized;
|
||||
if (this.column.grid.config.tiledKeepBelow) {
|
||||
this.client.kwinClient.keepBelow = !maximized;
|
||||
@@ -5,9 +5,6 @@ class WindowRuleEnforcer {
|
||||
|
||||
constructor(windowRules: WindowRule[]) {
|
||||
const [floatRegex, tileRegex, followCaptionRegex] = WindowRuleEnforcer.createWindowRuleRegexes(windowRules);
|
||||
log("floatRegex", floatRegex);
|
||||
log("tileRegex", tileRegex);
|
||||
log("followCaptionRegex", followCaptionRegex);
|
||||
this.preferFloating = new ClientMatcher(floatRegex);
|
||||
this.preferTiling = new ClientMatcher(tileRegex);
|
||||
this.followCaption = followCaptionRegex;
|
||||
@@ -19,6 +16,7 @@ class WindowRuleEnforcer {
|
||||
kwinClient.normalWindow &&
|
||||
!kwinClient.transient &&
|
||||
kwinClient.managed &&
|
||||
kwinClient.pid > -1 &&
|
||||
!this.preferFloating.matches(kwinClient)
|
||||
)
|
||||
);
|
||||
@@ -52,7 +50,10 @@ class WindowRuleEnforcer {
|
||||
for (const windowRule of windowRules) {
|
||||
const ruleClass = WindowRuleEnforcer.parseRegex(windowRule.class);
|
||||
const ruleCaption = WindowRuleEnforcer.parseRegex(windowRule.caption);
|
||||
const ruleString = ClientMatcher.getRuleString(ruleClass, ruleCaption);
|
||||
const ruleString = ClientMatcher.getRuleString(
|
||||
WindowRuleEnforcer.wrapParens(ruleClass),
|
||||
WindowRuleEnforcer.wrapParens(ruleCaption)
|
||||
);
|
||||
|
||||
(windowRule.tile ? tileRegexes : floatRegexes).push(ruleString);
|
||||
if (ruleCaption !== ".*") {
|
||||
@@ -81,11 +82,11 @@ class WindowRuleEnforcer {
|
||||
}
|
||||
|
||||
if (regexes.length === 1) {
|
||||
return new RegExp("^" + regexes[0] + "$");
|
||||
return new RegExp("^(" + regexes[0] + ")$");
|
||||
}
|
||||
|
||||
const joinedRegexes = regexes.map(WindowRuleEnforcer.wrapParens).join("|");
|
||||
return new RegExp("^" + joinedRegexes + "$");
|
||||
return new RegExp("^(" + joinedRegexes + ")$");
|
||||
}
|
||||
|
||||
private static wrapParens(str: string) {
|
||||
@@ -17,7 +17,7 @@ class Delayer {
|
||||
}
|
||||
|
||||
function initQmlTimer() {
|
||||
return Qt.createQmlObject(
|
||||
return <QmlTimer>Qt.createQmlObject(
|
||||
`import QtQuick 6.0
|
||||
Timer {}`,
|
||||
qmlBase
|
||||
@@ -11,7 +11,7 @@ class ShortcutAction {
|
||||
}
|
||||
|
||||
private static initShortcutHandler(keyBinding: KeyBinding) {
|
||||
return Qt.createQmlObject(
|
||||
return <ShortcutHandler>Qt.createQmlObject(
|
||||
`import QtQuick 6.0
|
||||
import org.kde.kwin 3.0
|
||||
ShortcutHandler {
|
||||
@@ -34,16 +34,22 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
world.do(() => {}); // re-arrange desktop
|
||||
});
|
||||
|
||||
manager.connect(Workspace.desktopsChanged, () => {
|
||||
manager.connect(Workspace.screensChanged, () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.updateDesktops();
|
||||
})
|
||||
desktopManager.selectScreen(Workspace.activeScreen);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(Workspace.activitiesChanged, () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.updateActivities();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(Workspace.desktopsChanged, () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.updateDesktops();
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(Workspace.virtualScreenSizeChanged, () => {
|
||||
@@ -118,6 +118,11 @@ class ClientManager {
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.getMaximizedMode() !== MaximizedMode.Unmaximized) {
|
||||
// the client is not really kwin-tiled, just maximized
|
||||
kwinClient.tile = null;
|
||||
return;
|
||||
}
|
||||
client.stateManager.setState(() => new ClientState.Pinned(this.world, this.pinManager, this.desktopManager, kwinClient, this.config), false);
|
||||
this.pinManager.addClient(kwinClient);
|
||||
for (const desktop of this.desktopManager.getDesktopsForClient(kwinClient)) {
|
||||
@@ -180,7 +180,7 @@ class ClientWrapper {
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(client.kwinClient.maximizedAboutToChange, (maximizedMode: MaximizedMode) => {
|
||||
if (maximizedMode > MaximizedMode.Unmaximized && client.kwinClient.tile !== null) {
|
||||
if (maximizedMode !== MaximizedMode.Unmaximized && client.kwinClient.tile !== null) {
|
||||
client.kwinClient.tile = null;
|
||||
}
|
||||
client.maximizedMode = maximizedMode;
|
||||
@@ -3,6 +3,7 @@ class DesktopManager {
|
||||
private readonly config: Desktop.Config;
|
||||
public readonly layoutConfig: LayoutConfig;
|
||||
private readonly desktops: Map<string, Desktop>; // key is activityId|desktopId
|
||||
private selectedScreen: Output;
|
||||
private kwinActivities: Set<string>;
|
||||
private kwinDesktops: Set<KwinDesktop>;
|
||||
|
||||
@@ -11,6 +12,7 @@ class DesktopManager {
|
||||
this.config = config;
|
||||
this.layoutConfig = layoutConfig;
|
||||
this.desktops = new Map();
|
||||
this.selectedScreen = Workspace.activeScreen;
|
||||
this.kwinActivities = new Set(Workspace.activities);
|
||||
this.kwinDesktops = new Set(Workspace.desktops);
|
||||
this.addDesktop(currentActivity, currentDesktop);
|
||||
@@ -43,7 +45,13 @@ class DesktopManager {
|
||||
|
||||
private addDesktop(activity: string, kwinDesktop: KwinDesktop) {
|
||||
const desktopKey = DesktopManager.getDesktopKey(activity, kwinDesktop);
|
||||
const desktop = new Desktop(kwinDesktop, this.pinManager, this.config, this.layoutConfig);
|
||||
const desktop = new Desktop(
|
||||
kwinDesktop,
|
||||
this.pinManager,
|
||||
this.config,
|
||||
() => this.selectedScreen,
|
||||
this.layoutConfig,
|
||||
);
|
||||
this.desktops.set(desktopKey, desktop);
|
||||
return desktop;
|
||||
}
|
||||
@@ -72,6 +80,10 @@ class DesktopManager {
|
||||
this.kwinDesktops = newDesktops;
|
||||
}
|
||||
|
||||
public selectScreen(screen: Output) {
|
||||
this.selectedScreen = screen;
|
||||
}
|
||||
|
||||
private removeActivity(activity: string) {
|
||||
for (const kwinDesktop of this.kwinDesktops) {
|
||||
this.destroyDesktop(activity, kwinDesktop);
|
||||
@@ -45,10 +45,7 @@ class World {
|
||||
marginBottom: config.gapsOuterBottom,
|
||||
marginLeft: config.gapsOuterLeft,
|
||||
marginRight: config.gapsOuterRight,
|
||||
scroller: config.scrollingLazy ? new LazyScroller() :
|
||||
config.scrollingCentered ? new CenteredScroller() :
|
||||
config.scrollingGrouped ? new GroupedScroller() :
|
||||
console.assert(false),
|
||||
scroller: World.createScroller(config),
|
||||
clamper: config.scrollingLazy ? new EdgeClamper() : new CenterClamper(),
|
||||
},
|
||||
layoutConfig,
|
||||
@@ -60,6 +57,19 @@ class World {
|
||||
this.update();
|
||||
}
|
||||
|
||||
private static createScroller(config: Config) {
|
||||
if (config.scrollingLazy) {
|
||||
return new LazyScroller();
|
||||
} else if (config.scrollingCentered) {
|
||||
return new CenteredScroller();
|
||||
} else if (config.scrollingGrouped) {
|
||||
return new GroupedScroller();
|
||||
} else {
|
||||
log("No scrolling mode selected, using default");
|
||||
return new LazyScroller();
|
||||
}
|
||||
}
|
||||
|
||||
private addExistingClients() {
|
||||
const kwinClients = Workspace.windows;
|
||||
for (let i = 0; i < kwinClients.length; i++) {
|
||||
@@ -82,8 +82,8 @@ namespace ClientState {
|
||||
|
||||
if (kwinClient.resize) {
|
||||
resizing = true;
|
||||
resizingBorder = Workspace.cursorPos.x > kwinClient.frameGeometry.right ||
|
||||
Workspace.cursorPos.x < kwinClient.frameGeometry.left;
|
||||
resizingBorder = Workspace.cursorPos.x > kwinClient.clientGeometry.right ||
|
||||
Workspace.cursorPos.x < kwinClient.clientGeometry.left;
|
||||
window.column.grid.onUserResizeStarted();
|
||||
}
|
||||
});
|
||||
4
src/main/tsconfig.json
Normal file
4
src/main/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["../lib/**/*", "./**/*"]
|
||||
}
|
||||
36
src/tests/rules/WindowRuleEnforcer.ts
Normal file
36
src/tests/rules/WindowRuleEnforcer.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
const testCases = [
|
||||
{tiledByDefault: true, resourceClass: "unknown", caption: "anything", shouldTile: true},
|
||||
{tiledByDefault: false, resourceClass: "unknown", caption: "anything", shouldTile: false},
|
||||
{tiledByDefault: true, resourceClass: "ksmserver-logout-greeter", caption: "anything", shouldTile: false},
|
||||
{tiledByDefault: true, resourceClass: "org.kde.plasmashell", caption: "something", shouldTile: false},
|
||||
{tiledByDefault: true, resourceClass: "plasmashell", caption: "something", shouldTile: false},
|
||||
{tiledByDefault: false, resourceClass: "org.kde.kfind", caption: "something", shouldTile: true},
|
||||
{tiledByDefault: false, resourceClass: "kfind", caption: "something", shouldTile: true},
|
||||
{tiledByDefault: true, resourceClass: "zoom", caption: "something", shouldTile: true},
|
||||
{tiledByDefault: true, resourceClass: "zoom", caption: "zoom", shouldTile: false},
|
||||
];
|
||||
|
||||
const enforcer = new WindowRuleEnforcer(JSON.parse(defaultWindowRules));
|
||||
for (const testCase of testCases) {
|
||||
const kwinClient: any = createKwinClient(testCase.tiledByDefault, testCase.resourceClass, testCase.caption);
|
||||
assert(enforcer.shouldTile(kwinClient) === testCase.shouldTile, "failed case: " + JSON.stringify(testCase));
|
||||
}
|
||||
|
||||
function createKwinClient(normalWindow: boolean, resoureClass: string, caption: string) {
|
||||
return {
|
||||
normalWindow: normalWindow,
|
||||
transient: false,
|
||||
managed: true,
|
||||
pid: 100,
|
||||
moveable: true,
|
||||
resizeable: true,
|
||||
popupWindow: false,
|
||||
minimized: false,
|
||||
desktops: [1],
|
||||
activities: [1],
|
||||
resourceClass: resoureClass,
|
||||
caption: caption,
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/tests/tests.ts
Normal file
17
src/tests/tests.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
declare const process: {
|
||||
exit(code?: number): void,
|
||||
};
|
||||
|
||||
function assert(assertion: boolean, message?: string) {
|
||||
if (assertion) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message != undefined) {
|
||||
console.assert(assertion, message);
|
||||
} else {
|
||||
console.assert(assertion);
|
||||
}
|
||||
console.trace();
|
||||
process.exit(1);
|
||||
}
|
||||
4
src/tests/tsconfig.json
Normal file
4
src/tests/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["../lib/**/*", "./**/*"]
|
||||
}
|
||||
12
src/tsconfig.json
Normal file
12
src/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"lib": ["es2020"],
|
||||
"module": "none",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
108
tsconfig.json
108
tsconfig.json
@@ -1,108 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": [
|
||||
"es2020",
|
||||
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "none", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
"allowJs": false, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./ts_built.js", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": false, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user