17 Commits
v0.8 ... v0.9.1

Author SHA1 Message Date
Peter Fajdiga
20a3ece4b5 bump version to 0.9.1 2024-04-06 08:36:51 +02:00
Peter Fajdiga
3cad8102ee qt.d.ts: remove QByteArray type 2024-04-05 14:49:36 +02:00
Peter Fajdiga
7fd45eed8f kwin.d.ts: mark cursorPos and minSize as immutable 2024-04-05 14:48:56 +02:00
Peter Fajdiga
7299341608 Tiled: use clientGeometry to determine border resize 2024-04-05 14:46:19 +02:00
Peter Fajdiga
842ec1ac63 WindowRuleEnforcer: fix bug in joinRegexes 2024-04-05 13:58:50 +02:00
Peter Fajdiga
0523465b84 WindowRuleEnforcer: remove debug logs 2024-04-01 19:26:50 +02:00
Peter Fajdiga
c7cfa261b9 bump version to 0.9 2024-03-30 12:46:59 +01:00
Peter Fajdiga
56955e4df3 src/config: don't tile kded windows 2024-03-30 12:45:42 +01:00
Peter Fajdiga
bb308cfbfb config: merge X11 and Wayland class regexes in window rules 2024-03-30 12:45:40 +01:00
Peter Fajdiga
6c00245943 config: escape . in window rules 2024-03-30 12:45:38 +01:00
Peter Fajdiga
2efdbe5a7b support regex for class selector in window rules (resolves #41) 2024-03-30 12:45:36 +01:00
Peter Fajdiga
092cbf3ff1 kwin.d.ts: remove unused signal maximizedChanged 2024-03-30 12:45:33 +01:00
Peter Fajdiga
f9ae299ce8 Tiled: restore opacity after un-tiling 2024-03-30 12:45:28 +01:00
Peter Fajdiga
695f5edf6a config: add option to disable layering (resolves #41) 2024-03-30 12:45:25 +01:00
Peter Fajdiga
9b80b535a1 readme: update QML dependencies 2024-03-19 10:58:51 +01:00
Peter Fajdiga
752df86db5 bump version to 0.8.1 2024-03-19 10:46:33 +01:00
Peter Fajdiga
f05eefe19b config: add plasmashell to window rules (fixes #38) 2024-03-19 10:17:37 +01:00
10 changed files with 75 additions and 69 deletions

View File

@@ -19,7 +19,7 @@ Similar window managers include [PaperWM](https://github.com/paperwm/PaperWM),
## Dependencies
Karousel requires the following QML modules:
- QtQuick 2.15
- QtQuick 6.0
- org.kde.kwin 3.0
- org.kde.notification 1.0

View File

@@ -121,6 +121,13 @@
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="kcfg_noLayering">
<property name="text">
<string>No layering</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -9,7 +9,7 @@
"Name": "Peter Fajdiga"
}],
"Id": "karousel",
"Version": "0.8",
"Version": "0.9.1",
"License": "GPLv3",
"Website": "https://github.com/peterfajdiga/karousel",
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"

View File

@@ -4,43 +4,31 @@ const defaultWindowRules = `[
"tile": false
},
{
"class": "kcalc",
"class": "(org\\\\.kde\\\\.)?plasmashell",
"tile": false
},
{
"class": "org.kde.kcalc",
"class": "(org\\\\.kde\\\\.)?kded6",
"tile": false
},
{
"class": "kfind",
"class": "(org\\\\.kde\\\\.)?kcalc",
"tile": false
},
{
"class": "(org\\\\.kde\\\\.)?kfind",
"tile": true
},
{
"class": "org.kde.kfind",
"tile": true
},
{
"class": "kruler",
"class": "(org\\\\.kde\\\\.)?kruler",
"tile": false
},
{
"class": "org.kde.kruler",
"class": "(org\\\\.kde\\\\.)?krunner",
"tile": false
},
{
"class": "krunner",
"tile": false
},
{
"class": "org.kde.krunner",
"tile": false
},
{
"class": "yakuake",
"tile": false
},
{
"class": "org.kde.yakuake",
"class": "(org\\\\.kde\\\\.)?yakuake",
"tile": false
},
{
@@ -166,6 +154,11 @@ const configDef = [
type: "Bool",
default: false,
},
{
name: "noLayering",
type: "Bool",
default: false,
},
{
name: "windowRules",
type: "String",

View File

@@ -9,7 +9,7 @@ declare const Workspace: {
readonly currentActivity: string;
readonly activeScreen: Output;
readonly windows: KwinClient[];
readonly cursorPos: QmlPoint;
readonly cursorPos: Readonly<QmlPoint>;
activeWindow: KwinClient;
@@ -49,9 +49,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,7 +60,7 @@ 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;
@@ -81,7 +82,6 @@ interface KwinClient {
readonly desktopsChanged: QSignal<[]>;
readonly activitiesChanged: QSignal<[]>;
readonly minimizedChanged: QSignal<[]>;
readonly maximizedChanged: QSignal<[]>
readonly maximizedAboutToChange: QSignal<[MaximizedMode]>
readonly captionChanged: QSignal<[]>;
readonly tileChanged: QSignal<[]>;

2
src/extern/qt.d.ts vendored
View File

@@ -11,8 +11,6 @@ declare const Qt: {
type QmlObject = unknown;
type QByteArray = string;
type QmlPoint = {
x: number;
y: number;

View File

@@ -1,15 +1,19 @@
class ClientMatcher {
private readonly rules: Map<string, RegExp>;
private readonly regex: RegExp;
constructor(rules: Map<string, RegExp>) {
this.rules = rules;
constructor(regex: RegExp) {
this.regex = regex;
}
public matches(kwinClient: KwinClient) {
const rule = this.rules.get(kwinClient.resourceClass);
if (rule === undefined) {
return false;
}
return rule.test(kwinClient.caption);
return this.regex.test(ClientMatcher.getClientString(kwinClient));
}
public static getClientString(kwinClient: KwinClient) {
return ClientMatcher.getRuleString(kwinClient.resourceClass, kwinClient.caption);
}
public static getRuleString(ruleClass: string, ruleCaption: string) {
return ruleClass + "\0" + ruleCaption;
}
}

View File

@@ -1,5 +1,5 @@
type WindowRule = {
class: string,
caption: string,
class: string | undefined,
caption: string | undefined,
tile: boolean,
};

View File

@@ -1,13 +1,13 @@
class WindowRuleEnforcer {
private readonly preferFloating: ClientMatcher;
private readonly preferTiling: ClientMatcher;
private readonly followCaption: Set<string>;
private readonly followCaption: RegExp;
constructor(windowRules: WindowRule[]) {
const [mapFloat, mapTile] = WindowRuleEnforcer.createWindowRuleMaps(windowRules);
this.preferFloating = new ClientMatcher(mapFloat);
this.preferTiling = new ClientMatcher(mapTile);
this.followCaption = new Set([...mapFloat.keys(), ...mapTile.keys()]);
const [floatRegex, tileRegex, followCaptionRegex] = WindowRuleEnforcer.createWindowRuleRegexes(windowRules);
this.preferFloating = new ClientMatcher(floatRegex);
this.preferTiling = new ClientMatcher(tileRegex);
this.followCaption = followCaptionRegex;
}
public shouldTile(kwinClient: KwinClient) {
@@ -22,7 +22,7 @@ class WindowRuleEnforcer {
}
public initClientSignalManager(world: World, kwinClient: KwinClient) {
if (!this.followCaption.has(kwinClient.resourceClass)) {
if (!this.followCaption.test(kwinClient.resourceClass)) {
return null;
}
@@ -42,46 +42,47 @@ class WindowRuleEnforcer {
return manager;
}
private static createWindowRuleMaps(windowRules: WindowRule[]) {
const mapFloat = new Map<string, string[]>();
const mapTile = new Map<string, string[]>();
private static createWindowRuleRegexes(windowRules: WindowRule[]) {
const floatRegexes: string[] = [];
const tileRegexes: string[] = [];
const followCaptionRegexes: string[] = [];
for (const windowRule of windowRules) {
const map = windowRule.tile ? mapTile : mapFloat;
let captions = map.get(windowRule.class);
if (captions === undefined) {
captions = [];
map.set(windowRule.class, captions);
}
if (windowRule.caption !== undefined) {
captions.push(windowRule.caption);
const ruleClass = WindowRuleEnforcer.parseRegex(windowRule.class);
const ruleCaption = WindowRuleEnforcer.parseRegex(windowRule.caption);
const ruleString = ClientMatcher.getRuleString(ruleClass, ruleCaption);
(windowRule.tile ? tileRegexes : floatRegexes).push(ruleString);
if (ruleCaption !== ".*") {
followCaptionRegexes.push(ruleClass);
}
}
return [
WindowRuleEnforcer.createWindowRuleRegexMap(mapFloat),
WindowRuleEnforcer.createWindowRuleRegexMap(mapTile),
WindowRuleEnforcer.joinRegexes(floatRegexes),
WindowRuleEnforcer.joinRegexes(tileRegexes),
WindowRuleEnforcer.joinRegexes(followCaptionRegexes),
];
}
private static createWindowRuleRegexMap(windowRuleMap: Map<string, string[]>) {
const regexMap = new Map<string, RegExp>;
for (const [k, v] of windowRuleMap) {
regexMap.set(k, WindowRuleEnforcer.joinRegexes(v));
private static parseRegex(rawRule: string | undefined) {
if (rawRule === undefined || rawRule === "" || rawRule === ".*") {
return ".*";
} else {
return rawRule;
}
return regexMap;
}
private static joinRegexes(regexes: string[]) {
if (regexes.length === 0) {
return new RegExp("");
return new RegExp("a^"); // match nothing
}
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) {

View File

@@ -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();
}
});
@@ -174,6 +174,9 @@ namespace ClientState {
if (config.tiledKeepBelow) {
client.kwinClient.keepBelow = false;
}
if (config.offScreenOpacity < 1.0) {
client.kwinClient.opacity = 1.0;
}
client.setShade(false);
client.setFullScreen(false);
if (client.kwinClient.tile === null) {