33 Commits
v0.7 ... v0.7.2

Author SHA1 Message Date
Peter Fajdiga
5004417285 bump package to 0.7.2 2024-10-27 13:51:52 +01:00
Peter Fajdiga
a110aee7ce ClientWrapper: implement workaround for Qt5 JS bug 2024-09-10 23:33:56 +02:00
Peter Fajdiga
817ea64171 DesktopManager: fix getDesktopForClient 2024-08-30 12:14:40 +02:00
Peter Fajdiga
af930a9b2f Tiled: check if user is resizing any window in the grid 2024-03-18 19:28:58 +01:00
Peter Fajdiga
489a1447e7 ClientWrapper: create a workaround for the problem with stuck off-screen windows on Wayland 2024-03-18 19:28:58 +01:00
Peter Fajdiga
b984f025ec Column: avoid setting preferredWidth of a window when another window's height in the same column is being resized by the user 2024-03-10 21:29:47 +01:00
Peter Fajdiga
4e1204f1bd DesktopManager: refactor getDesktopForClient 2024-03-10 21:02:24 +01:00
Peter Fajdiga
bbcf51783d World: fix grammar in comment 2024-03-10 15:44:17 +01:00
Peter Fajdiga
019da3766e kwin.d.ts: use unknown for Tile 2024-03-10 15:44:17 +01:00
Peter Fajdiga
1535c994b8 use === everywhere 2024-03-10 15:44:16 +01:00
Peter Fajdiga
296d0deca9 remove void keyword in QSignal params 2024-03-09 21:55:15 +01:00
Peter Fajdiga
17e7d5b46e kwin.d.ts: remove unneeded QSignal params 2024-03-09 21:50:27 +01:00
Peter Fajdiga
840a50d14d move ClientAreaOption to kwin.d.ts 2024-03-09 21:44:23 +01:00
Peter Fajdiga
4f99c4dd45 Actions: remove superfluous return values 2024-03-09 19:53:10 +01:00
Peter Fajdiga
030eddaf34 Floating: add missing ; 2024-03-09 19:34:54 +01:00
Peter Fajdiga
7246a7660e kwin.d.ts: remove superfluous comments 2024-03-09 19:19:39 +01:00
Peter Fajdiga
687256d1dd src/config: unjsonify 2024-03-09 19:19:38 +01:00
Peter Fajdiga
12bb7506cc src/keyBindings: unjsonify 2024-03-09 19:19:13 +01:00
Peter Fajdiga
1808ee0025 mark all signal properties as readonly 2024-03-09 19:07:12 +01:00
Peter Fajdiga
3021f61933 metadata.json: change description 2024-03-01 15:54:06 +01:00
Peter Fajdiga
e908138478 fix bug where resizing used manualScrollStep setting instead of manualResizeStep setting 2024-02-22 20:24:12 +01:00
Peter Fajdiga
99ad115370 rename scrollers 2024-02-18 22:11:34 +01:00
Peter Fajdiga
c5a4238f5f move scroller src files to src/behavior/scroller 2024-02-18 22:07:28 +01:00
Peter Fajdiga
0670d9c265 separate clampScrollX logic into different Clamper implementations 2024-02-18 22:06:14 +01:00
Peter Fajdiga
845874b0d0 separate increaseColumnWidth and columnWidthDecrease logic into different ColumnResizer implementations 2024-02-18 22:06:13 +01:00
Peter Fajdiga
a422a077f6 Makefile: add prerequisites to package target 2024-02-18 21:28:17 +01:00
Peter Fajdiga
2fe1be99cb Desktop.scrollCenterVisible: stop prioritizing visible columns (this is now done by ColumnRange.addNeighbors) 2024-02-18 20:21:34 +01:00
Peter Fajdiga
1a449c238d ColumnRange: prioritize nearer columns 2024-02-18 20:15:58 +01:00
Peter Fajdiga
9bda7d1a09 bump version to 0.7.1 2024-02-12 21:27:34 +01:00
Peter Fajdiga
2ce72bcee8 qt.d.ts: add console.trace 2024-02-12 21:17:37 +01:00
Peter Fajdiga
ff3f6c5d6b Desktop: fix ColumnRange when a column's width is still 0 2024-02-12 21:10:12 +01:00
Peter Fajdiga
3ab230b498 Makefile: append version number to package file name 2024-02-11 20:37:28 +01:00
Peter Fajdiga
ba9f362a1c .gitignore: ignore suffixed packages 2024-02-11 20:37:27 +01:00
31 changed files with 463 additions and 427 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
/package/contents/code/main.js /package/contents/code/main.js
/package/contents/config/main.xml /package/contents/config/main.xml
/karousel.tar.gz /karousel*.tar.gz
/.idea /.idea

View File

@@ -1,6 +1,7 @@
.PHONY: * .PHONY: *
TSC_SCRIPT_FLAGS = --lib es2020 ./src/extern/qt.d.ts TSC_SCRIPT_FLAGS = --lib es2020 ./src/extern/qt.d.ts
VERSION = $(shell grep '"Version":' ./package/metadata.json | grep -o '[0-9\.]*')
config: config:
mkdir -p ./package/contents/config mkdir -p ./package/contents/config
@@ -15,8 +16,8 @@ install: build config
uninstall: uninstall:
kpackagetool5 --type=KWin/Script -r ./package kpackagetool5 --type=KWin/Script -r ./package
package: package: build config
tar -czf ./karousel.tar.gz ./package tar -czf ./karousel_${subst .,_,${VERSION}}.tar.gz ./package
logs: logs:
journalctl -t kwin_x11 -g '^qml:|^file://.*karousel' -f journalctl -t kwin_x11 -g '^qml:|^file://.*karousel' -f

View File

@@ -21,7 +21,7 @@ function formatComment(comment: string | undefined) {
function printCols(...columns: (string[] | string)[]) { function printCols(...columns: (string[] | string)[]) {
const nCols = columns.length; const nCols = columns.length;
if (nCols == 0) { if (nCols === 0) {
return; return;
} }
@@ -30,7 +30,7 @@ function printCols(...columns: (string[] | string)[]) {
).map( ).map(
(column: string[] | string) => column.length (column: string[] | string) => column.length
)); ));
if (nRows == Infinity) { if (nRows === Infinity) {
// we only have single string columns // we only have single string columns
nRows = 1; nRows = 1;
} }

View File

@@ -1,7 +1,7 @@
{ {
"KPlugin": { "KPlugin": {
"Name": "Karousel", "Name": "Karousel",
"Description": "Manual columnar tiling extension for KWin", "Description": "Scrollable tiling extension for KWin",
"Icon": "preferences-system-windows", "Icon": "preferences-system-windows",
"Authors": [{ "Authors": [{
"Email": "peter.fajdiga@gmail.com", "Email": "peter.fajdiga@gmail.com",
@@ -9,7 +9,7 @@
}], }],
"Id": "karousel", "Id": "karousel",
"ServiceTypes": ["KWin/Script"], "ServiceTypes": ["KWin/Script"],
"Version": "0.7", "Version": "0.7.2",
"License": "GPLv3", "License": "GPLv3",
"Website": "https://github.com/peterfajdiga/karousel", "Website": "https://github.com/peterfajdiga/karousel",
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues" "BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"

View File

@@ -166,89 +166,13 @@ namespace Actions {
columnWidthIncrease: () => { columnWidthIncrease: () => {
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => { world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
const desktop = grid.desktop; config.columnResizer.increaseWidth(column, config.manualResizeStep);
const visibleRange = desktop.getCurrentVisibleRange();
if(!column.isVisible(visibleRange, true) || column.getWidth() >= column.getMaxWidth()) {
return;
}
let leftVisibleColumn = grid.getLeftmostVisibleColumn(visibleRange, true);
let rightVisibleColumn = grid.getRightmostVisibleColumn(visibleRange, true);
if (leftVisibleColumn === null || rightVisibleColumn === null) {
console.assert(false); // should at least see self
return;
}
const leftSpace = leftVisibleColumn.getLeft() - visibleRange.getLeft();
const rightSpace = visibleRange.getRight() - rightVisibleColumn.getRight();
const newWidth = findNextStep(
[
visibleRange.getWidth(),
column.getWidth() + config.manualResizeStep,
column.getWidth() + leftSpace + rightSpace,
column.getWidth() + leftSpace + rightSpace + leftVisibleColumn.getWidth() + grid.config.gapsInnerHorizontal,
column.getWidth() + leftSpace + rightSpace + rightVisibleColumn.getWidth() + grid.config.gapsInnerHorizontal,
],
width => width - column.getWidth(),
)
if (newWidth === undefined) {
return;
}
column.setWidth(newWidth, true);
desktop.scrollCenterVisible(column, false);
desktop.onLayoutChanged();
desktop.autoAdjustScroll();
}); });
}, },
columnWidthDecrease: () => { columnWidthDecrease: () => {
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => { world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
const desktop = grid.desktop; config.columnResizer.decreaseWidth(column, config.manualResizeStep);
const visibleRange = desktop.getCurrentVisibleRange();
if(!column.isVisible(visibleRange, true) || column.getWidth() <= column.getMinWidth()) {
return;
}
const leftVisibleColumn = grid.getLeftmostVisibleColumn(visibleRange, true);
const rightVisibleColumn = grid.getRightmostVisibleColumn(visibleRange, true);
if (leftVisibleColumn === null || rightVisibleColumn === null) {
console.assert(false); // should at least see self
return;
}
let leftOffScreenColumn = grid.getPrevColumn(leftVisibleColumn);
if (leftOffScreenColumn === column) {
leftOffScreenColumn = null;
}
let rightOffScreenColumn = grid.getNextColumn(rightVisibleColumn);
if (rightOffScreenColumn === column) {
rightOffScreenColumn = null;
}
const visibleColumnsWidth = rightVisibleColumn.getRight() - leftVisibleColumn.getLeft();
const unusedWidth = visibleRange.getWidth() - visibleColumnsWidth;
const leftOffScreen = leftOffScreenColumn === null ? 0 : leftOffScreenColumn.getWidth() + grid.config.gapsInnerHorizontal - unusedWidth;
const rightOffScreen = rightOffScreenColumn === null ? 0 : rightOffScreenColumn.getWidth() + grid.config.gapsInnerHorizontal - unusedWidth;
const newWidth = findNextStep(
[
visibleRange.getWidth(),
column.getWidth() - config.manualResizeStep,
column.getWidth() - leftOffScreen,
column.getWidth() - rightOffScreen,
],
width => column.getWidth() - width,
)
if (newWidth === undefined) {
return;
}
column.setWidth(newWidth, true);
desktop.scrollCenterVisible(column, true);
desktop.onLayoutChanged();
desktop.autoAdjustScroll();
}); });
}, },
@@ -337,7 +261,7 @@ namespace Actions {
const grid = desktopManager.getCurrentDesktop().grid; const grid = desktopManager.getCurrentDesktop().grid;
const targetColumn = grid.getColumnAtIndex(columnIndex); const targetColumn = grid.getColumnAtIndex(columnIndex);
if (targetColumn === null) { if (targetColumn === null) {
return null; return;
} }
targetColumn.focus(); targetColumn.focus();
}); });
@@ -347,7 +271,7 @@ namespace Actions {
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => { world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
const targetColumn = grid.getColumnAtIndex(columnIndex); const targetColumn = grid.getColumnAtIndex(columnIndex);
if (targetColumn === null) { if (targetColumn === null) {
return null; return;
} }
window.moveToColumn(targetColumn); window.moveToColumn(targetColumn);
grid.desktop.autoAdjustScroll(); grid.desktop.autoAdjustScroll();
@@ -358,7 +282,7 @@ namespace Actions {
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => { world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
const targetColumn = grid.getColumnAtIndex(columnIndex); const targetColumn = grid.getColumnAtIndex(columnIndex);
if (targetColumn === null || targetColumn === column) { if (targetColumn === null || targetColumn === column) {
return null; return;
} }
if (targetColumn.isAfter(column)) { if (targetColumn.isAfter(column)) {
column.moveAfter(targetColumn); column.moveAfter(targetColumn);
@@ -399,21 +323,14 @@ namespace Actions {
}); });
} }
function findNextStep(steps: number[], evaluate: (step: number) => number) {
let bestScore = Infinity;
let bestStep = undefined;
for (const step of steps) {
const score = evaluate(step);
if (score > 0 && score < bestScore) {
bestScore = score;
bestStep = step;
}
}
return bestStep;
}
export type Config = { export type Config = {
manualScrollStep: number, manualScrollStep: number,
manualResizeStep: number, manualResizeStep: number,
columnResizer: ColumnResizer,
}; };
export type ColumnResizer = {
increaseWidth(column: Column, step: number): void,
decreaseWidth(column: Column, step: number): void,
}
} }

View File

@@ -1,10 +0,0 @@
enum ClientAreaOption {
PlacementArea,
MovementArea,
MaximizeArea,
MaximizeFullArea,
FullScreenArea,
WorkArea,
FullArea,
ScreenArea,
}

View File

@@ -0,0 +1,96 @@
class ContextualResizer {
public increaseWidth(column: Column, step: number) {
const grid = column.grid;
const desktop = grid.desktop;
const visibleRange = desktop.getCurrentVisibleRange();
if(!column.isVisible(visibleRange, true) || column.getWidth() >= column.getMaxWidth()) {
return;
}
let leftVisibleColumn = grid.getLeftmostVisibleColumn(visibleRange, true);
let rightVisibleColumn = grid.getRightmostVisibleColumn(visibleRange, true);
if (leftVisibleColumn === null || rightVisibleColumn === null) {
console.assert(false); // should at least see self
return;
}
const leftSpace = leftVisibleColumn.getLeft() - visibleRange.getLeft();
const rightSpace = visibleRange.getRight() - rightVisibleColumn.getRight();
const newWidth = ContextualResizer.findNextStep(
[
visibleRange.getWidth(),
column.getWidth() + step,
column.getWidth() + leftSpace + rightSpace,
column.getWidth() + leftSpace + rightSpace + leftVisibleColumn.getWidth() + grid.config.gapsInnerHorizontal,
column.getWidth() + leftSpace + rightSpace + rightVisibleColumn.getWidth() + grid.config.gapsInnerHorizontal,
],
width => width - column.getWidth(),
)
if (newWidth === undefined) {
return;
}
column.setWidth(newWidth, true);
desktop.scrollCenterVisible(column);
}
public decreaseWidth(column: Column, step: number) {
const grid = column.grid;
const desktop = grid.desktop;
const visibleRange = desktop.getCurrentVisibleRange();
if(!column.isVisible(visibleRange, true) || column.getWidth() <= column.getMinWidth()) {
return;
}
const leftVisibleColumn = grid.getLeftmostVisibleColumn(visibleRange, true);
const rightVisibleColumn = grid.getRightmostVisibleColumn(visibleRange, true);
if (leftVisibleColumn === null || rightVisibleColumn === null) {
console.assert(false); // should at least see self
return;
}
let leftOffScreenColumn = grid.getPrevColumn(leftVisibleColumn);
if (leftOffScreenColumn === column) {
leftOffScreenColumn = null;
}
let rightOffScreenColumn = grid.getNextColumn(rightVisibleColumn);
if (rightOffScreenColumn === column) {
rightOffScreenColumn = null;
}
const visibleColumnsWidth = rightVisibleColumn.getRight() - leftVisibleColumn.getLeft();
const unusedWidth = visibleRange.getWidth() - visibleColumnsWidth;
const leftOffScreen = leftOffScreenColumn === null ? 0 : leftOffScreenColumn.getWidth() + grid.config.gapsInnerHorizontal - unusedWidth;
const rightOffScreen = rightOffScreenColumn === null ? 0 : rightOffScreenColumn.getWidth() + grid.config.gapsInnerHorizontal - unusedWidth;
const newWidth = ContextualResizer.findNextStep(
[
visibleRange.getWidth(),
column.getWidth() - step,
column.getWidth() - leftOffScreen,
column.getWidth() - rightOffScreen,
],
width => column.getWidth() - width,
)
if (newWidth === undefined) {
return;
}
column.setWidth(newWidth, true);
desktop.scrollCenterVisible(column);
}
private static findNextStep(steps: number[], evaluate: (step: number) => number) {
let bestScore = Infinity;
let bestStep = undefined;
for (const step of steps) {
const score = evaluate(step);
if (score > 0 && score < bestScore) {
bestScore = score;
bestStep = step;
}
}
return bestStep;
}
}

View File

@@ -0,0 +1,9 @@
class RawResizer {
public increaseWidth(column: Column, step: number) {
column.adjustWidth(step, true);
}
public decreaseWidth(column: Column, step: number) {
column.adjustWidth(-step, true);
}
}

View File

@@ -1,13 +1,5 @@
class ScrollerCentered { class CenterClamper {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollCenterRange(column);
}
public clampScrollX(desktop: Desktop, x: number) { public clampScrollX(desktop: Desktop, x: number) {
return ScrollerCentered.clampScrollX(desktop, x);
}
public static clampScrollX(desktop: Desktop, x: number) {
const firstColumn = desktop.grid.getFirstColumn(); const firstColumn = desktop.grid.getFirstColumn();
if (firstColumn === null) { if (firstColumn === null) {
return 0; return 0;

View File

@@ -1,8 +1,4 @@
class ScrollerLazy { class EdgeClamper {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollIntoView(column);
}
public clampScrollX(desktop: Desktop, x: number) { public clampScrollX(desktop: Desktop, x: number) {
let minScroll = 0; let minScroll = 0;
let maxScroll = desktop.grid.getWidth() - desktop.tilingArea.width; let maxScroll = desktop.grid.getWidth() - desktop.tilingArea.width;

View File

@@ -0,0 +1,5 @@
class CenteredScroller {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollCenterRange(column);
}
}

View File

@@ -0,0 +1,5 @@
class GroupedScroller {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollCenterVisible(column);
}
}

View File

@@ -0,0 +1,5 @@
class LazyScroller {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollIntoView(column);
}
}

View File

@@ -72,103 +72,103 @@ const defaultWindowRules = `[
const configDef = [ const configDef = [
{ {
"name": "gapsOuterTop", name: "gapsOuterTop",
"type": "UInt", type: "UInt",
"default": 18 default: 18,
}, },
{ {
"name": "gapsOuterBottom", name: "gapsOuterBottom",
"type": "UInt", type: "UInt",
"default": 18 default: 18,
}, },
{ {
"name": "gapsOuterLeft", name: "gapsOuterLeft",
"type": "UInt", type: "UInt",
"default": 18 default: 18,
}, },
{ {
"name": "gapsOuterRight", name: "gapsOuterRight",
"type": "UInt", type: "UInt",
"default": 18 default: 18,
}, },
{ {
"name": "gapsInnerHorizontal", name: "gapsInnerHorizontal",
"type": "UInt", type: "UInt",
"default": 18 default: 18,
}, },
{ {
"name": "gapsInnerVertical", name: "gapsInnerVertical",
"type": "UInt", type: "UInt",
"default": 18 default: 18,
}, },
{ {
"name": "manualScrollStep", name: "manualScrollStep",
"type": "UInt", type: "UInt",
"default": 200 default: 200,
}, },
{ {
"name": "manualResizeStep", name: "manualResizeStep",
"type": "UInt", type: "UInt",
"default": 600 default: 600,
}, },
{ {
"name": "offScreenOpacity", name: "offScreenOpacity",
"type": "UInt", type: "UInt",
"default": 100 default: 100,
}, },
{ {
"name": "untileOnDrag", name: "untileOnDrag",
"type": "Bool", type: "Bool",
"default": true default: true,
}, },
{ {
"name": "stackColumnsByDefault", name: "stackColumnsByDefault",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "resizeNeighborColumn", name: "resizeNeighborColumn",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "reMaximize", name: "reMaximize",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "skipSwitcher", name: "skipSwitcher",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "scrollingLazy", name: "scrollingLazy",
"type": "Bool", type: "Bool",
"default": true default: true,
}, },
{ {
"name": "scrollingCentered", name: "scrollingCentered",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "scrollingGrouped", name: "scrollingGrouped",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "tiledKeepBelow", name: "tiledKeepBelow",
"type": "Bool", type: "Bool",
"default": true default: true,
}, },
{ {
"name": "floatingKeepAbove", name: "floatingKeepAbove",
"type": "Bool", type: "Bool",
"default": false default: false,
}, },
{ {
"name": "windowRules", name: "windowRules",
"type": "String", type: "String",
"default": defaultWindowRules default: defaultWindowRules,
} }
]; ];

62
src/extern/kwin.d.ts vendored
View File

@@ -1,39 +1,44 @@
declare const KWin: { declare const KWin: {
// Functions
readConfig(key: string, defaultValue: any): any; readConfig(key: string, defaultValue: any): any;
registerShortcut(name: string, description: string, keySequence: string, callback: () => void): void; registerShortcut(name: string, description: string, keySequence: string, callback: () => void): void;
}; };
declare const workspace: { declare const workspace: {
// Read-write Properties
readonly desktops: number; readonly desktops: number;
readonly currentDesktop: number; readonly currentDesktop: number;
readonly currentActivity: string; readonly currentActivity: string;
// Read-write Properties
activeClient: KwinClient; activeClient: KwinClient;
// Signals readonly currentDesktopChanged: QSignal<[]>
currentDesktopChanged: QSignal<[oldDesktopNumber: number]> readonly clientAdded: QSignal<[KwinClient]>;
clientAdded: QSignal<[KwinClient]>; readonly clientRemoved: QSignal<[KwinClient]>;
clientRemoved: QSignal<[KwinClient]>; readonly clientMinimized: QSignal<[KwinClient]>;
clientMinimized: QSignal<[KwinClient]>; readonly clientUnminimized: QSignal<[KwinClient]>;
clientUnminimized: QSignal<[KwinClient]>; readonly clientMaximizeSet: QSignal<[KwinClient, horizontally: boolean, vertically: boolean]>;
clientMaximizeSet: QSignal<[KwinClient, horizontally: boolean, vertically: boolean]>; readonly clientActivated: QSignal<[KwinClient]>;
clientActivated: QSignal<[KwinClient]>; readonly numberDesktopsChanged: QSignal<[]>;
numberDesktopsChanged: QSignal<[oldNumberOfVirtualDesktops: number]>; readonly currentActivityChanged: QSignal<[]>;
currentActivityChanged: QSignal<[newActivity: string]>; readonly virtualScreenSizeChanged: QSignal<[]>;
virtualScreenSizeChanged: QSignal<[void]>;
// Functions
clientArea(option: ClientAreaOption, screenNumber: number, desktopNumber: number); clientArea(option: ClientAreaOption, screenNumber: number, desktopNumber: number);
clientList(): KwinClient[]; clientList(): KwinClient[];
}; };
type Tile = any; const enum ClientAreaOption {
PlacementArea,
MovementArea,
MaximizeArea,
MaximizeFullArea,
FullScreenArea,
WorkArea,
FullArea,
ScreenArea,
}
type Tile = unknown;
interface KwinClient { interface KwinClient {
// Read-only Properties
readonly shadeable: boolean; readonly shadeable: boolean;
readonly caption: string; readonly caption: string;
readonly minSize: QmlSize; readonly minSize: QmlSize;
@@ -47,9 +52,8 @@ interface KwinClient {
readonly dock: boolean; readonly dock: boolean;
readonly normalWindow: boolean; readonly normalWindow: boolean;
readonly managed: boolean; readonly managed: boolean;
opacity: number;
// Read-write Properties opacity: number;
fullScreen: boolean; fullScreen: boolean;
activities: string[]; // empty array means all activities activities: string[]; // empty array means all activities
skipSwitcher: boolean; skipSwitcher: boolean;
@@ -61,17 +65,15 @@ interface KwinClient {
desktop: number; // -1 means all desktops desktop: number; // -1 means all desktops
tile: Tile; tile: Tile;
// Signals readonly fullScreenChanged: QSignal<[]>;
fullScreenChanged: QSignal<[void]>; readonly desktopChanged: QSignal<[]>;
desktopChanged: QSignal<[void]>; readonly activitiesChanged: QSignal<[]>;
activitiesChanged: QSignal<[KwinClient]>; readonly captionChanged: QSignal<[]>;
captionChanged: QSignal<[void]>; readonly tileChanged: QSignal<[]>;
tileChanged: QSignal<[Tile]>; readonly moveResizedChanged: QSignal<[]>;
moveResizedChanged: QSignal<[void]>; readonly moveResizeCursorChanged: QSignal<[]>;
moveResizeCursorChanged: QSignal<[void]>; readonly clientStartUserMovedResized: QSignal<[]>;
clientStartUserMovedResized: QSignal<[void]>; readonly frameGeometryChanged: QSignal<[KwinClient, oldGeometry: QmlRect]>;
frameGeometryChanged: QSignal<[KwinClient, oldGeometry: QmlRect]>;
// Functions
setMaximize(vertically: boolean, horizontally: boolean): void; setMaximize(vertically: boolean, horizontally: boolean): void;
} }

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

@@ -1,5 +1,6 @@
declare const console: { declare const console: {
log(...args: any[]); log(...args: any[]);
trace();
assert(boolean); assert(boolean);
}; };
@@ -35,7 +36,7 @@ type QSignal<T extends unknown[]> = {
type QmlTimer = { type QmlTimer = {
interval: number; interval: number;
triggered: QSignal<[void]>; readonly triggered: QSignal<[]>;
restart(): void; restart(): void;
destroy(): void; destroy(): void;
}; };

View File

@@ -1,218 +1,218 @@
const keyBindings: KeyBinding[] = [ const keyBindings: KeyBinding[] = [
{ {
"name": "window-toggle-floating", name: "window-toggle-floating",
"description": "Toggle floating", description: "Toggle floating",
"defaultKeySequence": "Meta+Space", defaultKeySequence: "Meta+Space",
"action": "windowToggleFloating", action: "windowToggleFloating",
}, },
{ {
"name": "focus-left", name: "focus-left",
"description": "Move focus left", description: "Move focus left",
"defaultKeySequence": "Meta+A", defaultKeySequence: "Meta+A",
"action": "focusLeft", action: "focusLeft",
}, },
{ {
"name": "focus-right", name: "focus-right",
"description": "Move focus right", description: "Move focus right",
"comment": "Clashes with default KDE shortcuts, may require manual remapping", comment: "Clashes with default KDE shortcuts, may require manual remapping",
"defaultKeySequence": "Meta+D", defaultKeySequence: "Meta+D",
"action": "focusRight", action: "focusRight",
}, },
{ {
"name": "focus-up", name: "focus-up",
"description": "Move focus up", description: "Move focus up",
"comment": "Clashes with default KDE shortcuts, may require manual remapping", comment: "Clashes with default KDE shortcuts, may require manual remapping",
"defaultKeySequence": "Meta+W", defaultKeySequence: "Meta+W",
"action": "focusUp", action: "focusUp",
}, },
{ {
"name": "focus-down", name: "focus-down",
"description": "Move focus down", description: "Move focus down",
"comment": "Clashes with default KDE shortcuts, may require manual remapping", comment: "Clashes with default KDE shortcuts, may require manual remapping",
"defaultKeySequence": "Meta+S", defaultKeySequence: "Meta+S",
"action": "focusDown", action: "focusDown",
}, },
{ {
"name": "focus-start", name: "focus-start",
"description": "Move focus to start", description: "Move focus to start",
"defaultKeySequence": "Meta+Home", defaultKeySequence: "Meta+Home",
"action": "focusStart", action: "focusStart",
}, },
{ {
"name": "focus-end", name: "focus-end",
"description": "Move focus to end", description: "Move focus to end",
"defaultKeySequence": "Meta+End", defaultKeySequence: "Meta+End",
"action": "focusEnd", action: "focusEnd",
}, },
{ {
"name": "window-move-left", name: "window-move-left",
"description": "Move window left", description: "Move window left",
"comment": "Moves window out of and into columns", comment: "Moves window out of and into columns",
"defaultKeySequence": "Meta+Shift+A", defaultKeySequence: "Meta+Shift+A",
"action": "windowMoveLeft", action: "windowMoveLeft",
}, },
{ {
"name": "window-move-right", name: "window-move-right",
"description": "Move window right", description: "Move window right",
"comment": "Moves window out of and into columns", comment: "Moves window out of and into columns",
"defaultKeySequence": "Meta+Shift+D", defaultKeySequence: "Meta+Shift+D",
"action": "windowMoveRight", action: "windowMoveRight",
}, },
{ {
"name": "window-move-up", name: "window-move-up",
"description": "Move window up", description: "Move window up",
"defaultKeySequence": "Meta+Shift+W", defaultKeySequence: "Meta+Shift+W",
"action": "windowMoveUp", action: "windowMoveUp",
}, },
{ {
"name": "window-move-down", name: "window-move-down",
"description": "Move window down", description: "Move window down",
"defaultKeySequence": "Meta+Shift+S", defaultKeySequence: "Meta+Shift+S",
"action": "windowMoveDown", action: "windowMoveDown",
}, },
{ {
"name": "window-move-start", name: "window-move-start",
"description": "Move window to start", description: "Move window to start",
"defaultKeySequence": "Meta+Shift+Home", defaultKeySequence: "Meta+Shift+Home",
"action": "windowMoveStart", action: "windowMoveStart",
}, },
{ {
"name": "window-move-end", name: "window-move-end",
"description": "Move window to end", description: "Move window to end",
"defaultKeySequence": "Meta+Shift+End", defaultKeySequence: "Meta+Shift+End",
"action": "windowMoveEnd", action: "windowMoveEnd",
}, },
{ {
"name": "column-toggle-stacked", name: "column-toggle-stacked",
"description": "Toggle stacked layout for focused column", description: "Toggle stacked layout for focused column",
"comment": "One window in the column visible, others shaded; not supported on Wayland", comment: "One window in the column visible, others shaded; not supported on Wayland",
"defaultKeySequence": "Meta+X", defaultKeySequence: "Meta+X",
"action": "columnToggleStacked", action: "columnToggleStacked",
}, },
{ {
"name": "column-move-left", name: "column-move-left",
"description": "Move column left", description: "Move column left",
"defaultKeySequence": "Meta+Ctrl+Shift+A", defaultKeySequence: "Meta+Ctrl+Shift+A",
"action": "columnMoveLeft", action: "columnMoveLeft",
}, },
{ {
"name": "column-move-right", name: "column-move-right",
"description": "Move column right", description: "Move column right",
"defaultKeySequence": "Meta+Ctrl+Shift+D", defaultKeySequence: "Meta+Ctrl+Shift+D",
"action": "columnMoveRight", action: "columnMoveRight",
}, },
{ {
"name": "column-move-start", name: "column-move-start",
"description": "Move column to start", description: "Move column to start",
"defaultKeySequence": "Meta+Ctrl+Shift+Home", defaultKeySequence: "Meta+Ctrl+Shift+Home",
"action": "columnMoveStart", action: "columnMoveStart",
}, },
{ {
"name": "column-move-end", name: "column-move-end",
"description": "Move column to end", description: "Move column to end",
"defaultKeySequence": "Meta+Ctrl+Shift+End", defaultKeySequence: "Meta+Ctrl+Shift+End",
"action": "columnMoveEnd", action: "columnMoveEnd",
}, },
{ {
"name": "column-width-increase", name: "column-width-increase",
"description": "Increase column width", description: "Increase column width",
"defaultKeySequence": "Meta+Ctrl++", defaultKeySequence: "Meta+Ctrl++",
"action": "columnWidthIncrease", action: "columnWidthIncrease",
}, },
{ {
"name": "column-width-decrease", name: "column-width-decrease",
"description": "Decrease column width", description: "Decrease column width",
"defaultKeySequence": "Meta+Ctrl+-", defaultKeySequence: "Meta+Ctrl+-",
"action": "columnWidthDecrease", action: "columnWidthDecrease",
}, },
{ {
"name": "columns-width-equalize", name: "columns-width-equalize",
"description": "Equalize widths of visible columns", description: "Equalize widths of visible columns",
"defaultKeySequence": "Meta+Ctrl+X", defaultKeySequence: "Meta+Ctrl+X",
"action": "columnsWidthEqualize", action: "columnsWidthEqualize",
}, },
{ {
"name": "grid-scroll-focused", name: "grid-scroll-focused",
"description": "Center focused window", description: "Center focused window",
"comment": "Scrolls so that the focused window is centered in the screen", comment: "Scrolls so that the focused window is centered in the screen",
"defaultKeySequence": "Meta+Alt+Return", defaultKeySequence: "Meta+Alt+Return",
"action": "gridScrollFocused", action: "gridScrollFocused",
}, },
{ {
"name": "grid-scroll-left-column", name: "grid-scroll-left-column",
"description": "Scroll one column to the left", description: "Scroll one column to the left",
"defaultKeySequence": "Meta+Alt+A", defaultKeySequence: "Meta+Alt+A",
"action": "gridScrollLeftColumn", action: "gridScrollLeftColumn",
}, },
{ {
"name": "grid-scroll-right-column", name: "grid-scroll-right-column",
"description": "Scroll one column to the right", description: "Scroll one column to the right",
"defaultKeySequence": "Meta+Alt+D", defaultKeySequence: "Meta+Alt+D",
"action": "gridScrollRightColumn", action: "gridScrollRightColumn",
}, },
{ {
"name": "grid-scroll-left", name: "grid-scroll-left",
"description": "Scroll left", description: "Scroll left",
"defaultKeySequence": "Meta+Alt+PgUp", defaultKeySequence: "Meta+Alt+PgUp",
"action": "gridScrollLeft", action: "gridScrollLeft",
}, },
{ {
"name": "grid-scroll-right", name: "grid-scroll-right",
"description": "Scroll right", description: "Scroll right",
"defaultKeySequence": "Meta+Alt+PgDown", defaultKeySequence: "Meta+Alt+PgDown",
"action": "gridScrollRight", action: "gridScrollRight",
}, },
{ {
"name": "grid-scroll-start", name: "grid-scroll-start",
"description": "Scroll to start", description: "Scroll to start",
"defaultKeySequence": "Meta+Alt+Home", defaultKeySequence: "Meta+Alt+Home",
"action": "gridScrollStart", action: "gridScrollStart",
}, },
{ {
"name": "grid-scroll-end", name: "grid-scroll-end",
"description": "Scroll to end", description: "Scroll to end",
"defaultKeySequence": "Meta+Alt+End", defaultKeySequence: "Meta+Alt+End",
"action": "gridScrollEnd", action: "gridScrollEnd",
}, },
]; ];
const numKeyBindings: NumKeyBinding[] = [ const numKeyBindings: NumKeyBinding[] = [
{ {
"name": "focus-", name: "focus-",
"description": "Move focus to column ", description: "Move focus to column ",
"comment": "Clashes with default KDE shortcuts, may require manual remapping", comment: "Clashes with default KDE shortcuts, may require manual remapping",
"defaultModifiers": "Meta", defaultModifiers: "Meta",
"fKeys": false, fKeys: false,
"action": "focusColumn", action: "focusColumn",
}, },
{ {
"name": "window-move-to-column-", name: "window-move-to-column-",
"description": "Move window to column ", description: "Move window to column ",
"comment": "Requires manual remapping according to your keyboard layout, e.g. Meta+Shift+1 -> Meta+!", comment: "Requires manual remapping according to your keyboard layout, e.g. Meta+Shift+1 -> Meta+!",
"defaultModifiers": "Meta+Shift", defaultModifiers: "Meta+Shift",
"fKeys": false, fKeys: false,
"action": "windowMoveToColumn", action: "windowMoveToColumn",
}, },
{ {
"name": "column-move-to-column-", name: "column-move-to-column-",
"description": "Move column to position ", description: "Move column to position ",
"comment": "Requires manual remapping according to your keyboard layout, e.g. Meta+Ctrl+Shift+1 -> Meta+Ctrl+!", comment: "Requires manual remapping according to your keyboard layout, e.g. Meta+Ctrl+Shift+1 -> Meta+Ctrl+!",
"defaultModifiers": "Meta+Ctrl+Shift", defaultModifiers: "Meta+Ctrl+Shift",
"fKeys": false, fKeys: false,
"action": "columnMoveToColumn", action: "columnMoveToColumn",
}, },
{ {
"name": "column-move-to-desktop-", name: "column-move-to-desktop-",
"description": "Move column to desktop ", description: "Move column to desktop ",
"defaultModifiers": "Meta+Ctrl+Shift", defaultModifiers: "Meta+Ctrl+Shift",
"fKeys": true, fKeys: true,
"action": "columnMoveToDesktop", action: "columnMoveToDesktop",
}, },
{ {
"name": "tail-move-to-desktop-", name: "tail-move-to-desktop-",
"description": "Move this and all following columns to desktop ", description: "Move this and all following columns to desktop ",
"defaultModifiers": "Meta+Ctrl+Shift+Alt", defaultModifiers: "Meta+Ctrl+Shift+Alt",
"fKeys": true, fKeys: true,
"action": "tailMoveToDesktop", action: "tailMoveToDesktop",
}, },
]; ];

View File

@@ -53,7 +53,12 @@ function registerNumKeyBindings(name: string, description: string, modifiers: st
} }
function registerKeyBindings(world: World, config: Config) { function registerKeyBindings(world: World, config: Config) {
const actions = Actions.init(world, config); const actions = Actions.init(world, {
manualScrollStep: config.manualScrollStep,
manualResizeStep: config.manualResizeStep,
columnResizer: config.scrollingCentered ? new RawResizer() : new ContextualResizer(),
});
for (const binding of keyBindings) { for (const binding of keyBindings) {
registerKeyBinding(binding.name, binding.description, binding.defaultKeySequence, actions[binding.action]); registerKeyBinding(binding.name, binding.description, binding.defaultKeySequence, actions[binding.action]);
} }

View File

@@ -92,16 +92,17 @@ class Column {
public setWidth(width: number, setPreferred: boolean) { public setWidth(width: number, setPreferred: boolean) {
width = clamp(width, this.getMinWidth(), this.getMaxWidth()); width = clamp(width, this.getMinWidth(), this.getMaxWidth());
const oldWidth = this.width; if (width === this.width) {
return;
}
this.width = width; this.width = width;
if (setPreferred) { if (setPreferred) {
for (const window of this.windows.iterator()) { for (const window of this.windows.iterator()) {
window.client.preferredWidth = width; window.client.preferredWidth = width;
} }
} }
if (width !== oldWidth) { this.grid.onColumnWidthChanged(this);
this.grid.onColumnWidthChanged(this, oldWidth, width);
}
} }
public adjustWidth(widthDelta: number, setPreferred: boolean) { public adjustWidth(widthDelta: number, setPreferred: boolean) {

View File

@@ -78,13 +78,10 @@ class Desktop {
this.adjustScroll(Math.round(windowCenter - screenCenter), false); this.adjustScroll(Math.round(windowCenter - screenCenter), false);
} }
public scrollCenterVisible(focusedColumn: Column, prioritiseVisible: boolean) { public scrollCenterVisible(focusedColumn: Column) {
const columnRange = new Desktop.ColumnRange(focusedColumn); const columnRange = new Desktop.ColumnRange(focusedColumn);
const visibleRange = this.getCurrentVisibleRange(); const visibleRange = this.getCurrentVisibleRange();
if (prioritiseVisible) { columnRange.addNeighbors(visibleRange, this.grid.config.gapsInnerHorizontal);
columnRange.addNeighbors(visibleRange, this.grid.config.gapsInnerHorizontal, column => column.isVisible(visibleRange, true));
}
columnRange.addNeighbors(visibleRange, this.grid.config.gapsInnerHorizontal, () => true);
this.scrollCenterRange(columnRange); this.scrollCenterRange(columnRange);
} }
@@ -112,7 +109,7 @@ class Desktop {
} }
private clampScrollX(x: number) { private clampScrollX(x: number) {
return this.config.scroller.clampScrollX(this, x); return this.config.clamper.clampScrollX(this, x);
} }
public setScroll(x: number, force: boolean) { public setScroll(x: number, force: boolean) {
@@ -195,6 +192,7 @@ namespace Desktop {
marginLeft: number, marginLeft: number,
marginRight: number, marginRight: number,
scroller: Desktop.Scroller, scroller: Desktop.Scroller,
clamper: Desktop.Clamper,
}; };
export type Range = { export type Range = {
@@ -242,17 +240,15 @@ namespace Desktop {
this.width = initialColumn.getWidth(); this.width = initialColumn.getWidth();
} }
public addNeighbors(visibleRange: Desktop.Range, gap: number, condition: (column: Column) => boolean) { public addNeighbors(visibleRange: Desktop.Range, gap: number) {
const grid = this.left.grid; const grid = this.left.grid;
const columnRange = this; const columnRange = this;
function canFit(column: Column) { function canFit(column: Column) {
return columnRange.width + gap + column.getWidth() <= visibleRange.getWidth() return columnRange.width + gap + column.getWidth() <= visibleRange.getWidth();
} }
function isUsable(column: Column|null) { function isUsable(column: Column|null) {
return column !== null && return column !== null && canFit(column);
canFit(column) &&
condition(column)
} }
let leftColumn = grid.getPrevColumn(this.left); let leftColumn = grid.getPrevColumn(this.left);
@@ -267,10 +263,11 @@ namespace Desktop {
} }
checkColumns(); checkColumns();
const visibleCenter = visibleRange.getLeft() + visibleRange.getWidth() / 2;
while (leftColumn !== null || rightColumn !== null) { while (leftColumn !== null || rightColumn !== null) {
const leftWidth = leftColumn === null ? 0 : leftColumn.getWidth(); const leftToCenter = leftColumn === null ? Infinity : Math.abs(leftColumn.getLeft() - visibleCenter);
const rightWidth = rightColumn === null ? 0 : rightColumn.getWidth(); const rightToCenter = rightColumn === null ? Infinity : Math.abs(rightColumn.getRight() - visibleCenter);
if (leftWidth > rightWidth) { if (leftToCenter < rightToCenter) {
this.addLeft(leftColumn!, gap); this.addLeft(leftColumn!, gap);
leftColumn = grid.getPrevColumn(leftColumn!); leftColumn = grid.getPrevColumn(leftColumn!);
} else { } else {
@@ -306,6 +303,9 @@ namespace Desktop {
export type Scroller = { export type Scroller = {
scrollToColumn(desktop: Desktop, column: Column): void; scrollToColumn(desktop: Desktop, column: Column): void;
}
export type Clamper = {
clampScrollX(desktop: Desktop, x: number): number; clampScrollX(desktop: Desktop, x: number): number;
} }
} }

View File

@@ -43,6 +43,10 @@ class Grid {
return this.width; return this.width;
} }
public isUserResizing() {
return this.userResize;
}
public getPrevColumn(column: Column) { public getPrevColumn(column: Column) {
return this.columns.getPrev(column); return this.columns.getPrev(column);
} }
@@ -185,7 +189,7 @@ class Grid {
this.desktop.autoAdjustScroll(); this.desktop.autoAdjustScroll();
} }
public onColumnWidthChanged(column: Column, oldWidth: number, width: number) { public onColumnWidthChanged(column: Column) {
const nextColumn = this.columns.getNext(column); const nextColumn = this.columns.getNext(column);
this.columnsSetX(nextColumn); this.columnsSetX(nextColumn);
this.desktop.onLayoutChanged(); this.desktop.onLayoutChanged();

View File

@@ -1,9 +0,0 @@
class ScrollerGrouped {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollCenterVisible(column, true);
}
public clampScrollX(desktop: Desktop, x: number) {
return ScrollerCentered.clampScrollX(desktop, x);
}
}

View File

@@ -31,8 +31,9 @@ class WindowRuleEnforcer {
manager.connect(kwinClient.captionChanged, () => { manager.connect(kwinClient.captionChanged, () => {
const shouldTile = enforcer.shouldTile(kwinClient); const shouldTile = enforcer.shouldTile(kwinClient);
world.do((clientManager, desktopManager) => { world.do((clientManager, desktopManager) => {
if (shouldTile) { const desktop = desktopManager.getDesktopForClient(kwinClient);
clientManager.tileClient(kwinClient); if (shouldTile && desktop !== undefined) {
clientManager.tileClient(kwinClient, desktop.grid);
} else { } else {
clientManager.untileClient(kwinClient); clientManager.untileClient(kwinClient);
} }
@@ -71,11 +72,11 @@ class WindowRuleEnforcer {
} }
private static joinRegexes(regexes: string[]) { private static joinRegexes(regexes: string[]) {
if (regexes.length == 0) { if (regexes.length === 0) {
return new RegExp(""); return new RegExp("");
} }
if (regexes.length == 1) { if (regexes.length === 1) {
return new RegExp("^" + regexes[0] + "$"); return new RegExp("^" + regexes[0] + "$");
} }

View File

@@ -60,7 +60,7 @@ function initWorkspaceSignalHandlers(world: World) {
world.do(() => {}); // re-arrange desktop world.do(() => {}); // re-arrange desktop
}); });
manager.connect(workspace.numberDesktopsChanged, (oldNumberOfVirtualDesktops: number) => { manager.connect(workspace.numberDesktopsChanged, () => {
world.updateDesktops(); world.updateDesktops();
}); });

View File

@@ -27,13 +27,13 @@ class ClientManager {
public addClient(kwinClient: KwinClient) { public addClient(kwinClient: KwinClient) {
console.assert(!this.hasClient(kwinClient)); console.assert(!this.hasClient(kwinClient));
const desktop = this.desktopManager.getDesktopForClient(kwinClient);
let constructState: (client: ClientWrapper) => ClientState.State; let constructState: (client: ClientWrapper) => ClientState.State;
if (kwinClient.dock) { if (kwinClient.dock) {
constructState = () => new ClientState.Docked(this.world, kwinClient); constructState = () => new ClientState.Docked(this.world, kwinClient);
} else if (this.windowRuleEnforcer.shouldTile(kwinClient)) { } else if (this.windowRuleEnforcer.shouldTile(kwinClient) && desktop !== undefined) {
const grid = this.desktopManager.getDesktopForClient(kwinClient).grid; constructState = (client: ClientWrapper) => new ClientState.Tiled(this.world, client, desktop.grid);
constructState = (client: ClientWrapper) => new ClientState.Tiled(this.world, client, grid);
} else { } else {
constructState = (client: ClientWrapper) => new ClientState.Floating(this.world, client, this.config, false); constructState = (client: ClientWrapper) => new ClientState.Floating(this.world, client, this.config, false);
} }
@@ -86,12 +86,16 @@ class ClientManager {
return; return;
} }
if (client.stateManager.getState() instanceof ClientState.TiledMinimized) { if (client.stateManager.getState() instanceof ClientState.TiledMinimized) {
const grid = this.desktopManager.getDesktopForClient(kwinClient).grid; const desktop = this.desktopManager.getDesktopForClient(kwinClient);
client.stateManager.setState(() => new ClientState.Tiled(this.world, client, grid), false); 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) { public tileClient(kwinClient: KwinClient, grid: Grid) {
const client = this.clientMap.get(kwinClient); const client = this.clientMap.get(kwinClient);
if (client === undefined) { if (client === undefined) {
return; return;
@@ -99,7 +103,6 @@ class ClientManager {
if (client.stateManager.getState() instanceof ClientState.Tiled) { if (client.stateManager.getState() instanceof ClientState.Tiled) {
return; return;
} }
const grid = this.desktopManager.getDesktopForClient(kwinClient).grid;
client.stateManager.setState(() => new ClientState.Tiled(this.world, client, grid), false); client.stateManager.setState(() => new ClientState.Tiled(this.world, client, grid), false);
} }
@@ -147,8 +150,11 @@ class ClientManager {
const clientState = client.stateManager.getState(); const clientState = client.stateManager.getState();
if ((clientState instanceof ClientState.Floating || clientState instanceof ClientState.Pinned) && Clients.canTileEver(kwinClient)) { if ((clientState instanceof ClientState.Floating || clientState instanceof ClientState.Pinned) && Clients.canTileEver(kwinClient)) {
Clients.makeTileable(kwinClient); Clients.makeTileable(kwinClient);
const grid = this.desktopManager.getDesktopForClient(kwinClient).grid; const desktop = this.desktopManager.getDesktopForClient(kwinClient);
client.stateManager.setState(() => new ClientState.Tiled(this.world, client, grid), false); if (desktop === undefined) {
return;
}
client.stateManager.setState(() => new ClientState.Tiled(this.world, client, desktop.grid), false);
} else if (clientState instanceof ClientState.Tiled) { } else if (clientState instanceof ClientState.Tiled) {
client.stateManager.setState(() => new ClientState.Floating(this.world, client, this.config, true), false); client.stateManager.setState(() => new ClientState.Floating(this.world, client, this.config, true), false);
} }

View File

@@ -33,8 +33,15 @@ class ClientWrapper {
// window is being manually resized, prevent fighting with the user // window is being manually resized, prevent fighting with the user
return; return;
} }
this.lastPlacement = Qt.rect(x, y, width, height); const clientWrapper = this; // workaround for bug in Qt5's JS engine
this.kwinClient.frameGeometry = this.lastPlacement; clientWrapper.lastPlacement = Qt.rect(x, y, width, height);
clientWrapper.kwinClient.frameGeometry = clientWrapper.lastPlacement;
if (clientWrapper.kwinClient.frameGeometry !== clientWrapper.lastPlacement) {
// frameGeometry assignment failed. This sometimes happens on Wayland
// when a window is off-screen, effectively making it stuck there.
clientWrapper.kwinClient.frameGeometry.x = x; // This makes it unstuck.
clientWrapper.kwinClient.frameGeometry = clientWrapper.lastPlacement;
}
}); });
} }

View File

@@ -39,7 +39,9 @@ class DesktopManager {
} }
public getDesktopForClient(kwinClient: KwinClient) { public getDesktopForClient(kwinClient: KwinClient) {
console.assert(kwinClient.activities.length === 1 && kwinClient.desktop > 0); if (kwinClient.activities.length !== 1 || kwinClient.desktop <= 0) {
return undefined;
}
return this.getDesktop(kwinClient.activities[0], kwinClient.desktop); return this.getDesktop(kwinClient.activities[0], kwinClient.desktop);
} }

View File

@@ -11,7 +11,7 @@ class World {
this.workspaceSignalManager = initWorkspaceSignalHandlers(this); this.workspaceSignalManager = initWorkspaceSignalHandlers(this);
this.screenResizedDelayer = new Delayer(1000, () => { this.screenResizedDelayer = new Delayer(1000, () => {
// this delay ensures that docks get taken into account by `workspace.clientArea` // this delay ensures that docks are taken into account by `workspace.clientArea`
const desktopManager = this.desktopManager; // workaround for bug in Qt5's JS engine const desktopManager = this.desktopManager; // workaround for bug in Qt5's JS engine
for (const desktop of desktopManager.desktops()) { for (const desktop of desktopManager.desktops()) {
desktop.onLayoutChanged(); desktop.onLayoutChanged();
@@ -40,10 +40,11 @@ class World {
marginBottom: config.gapsOuterBottom, marginBottom: config.gapsOuterBottom,
marginLeft: config.gapsOuterLeft, marginLeft: config.gapsOuterLeft,
marginRight: config.gapsOuterRight, marginRight: config.gapsOuterRight,
scroller: config.scrollingLazy ? new ScrollerLazy() : scroller: config.scrollingLazy ? new LazyScroller() :
config.scrollingCentered ? new ScrollerCentered() : config.scrollingCentered ? new CenteredScroller() :
config.scrollingGrouped ? new ScrollerGrouped() : config.scrollingGrouped ? new GroupedScroller() :
console.assert(false), console.assert(false),
clamper: config.scrollingLazy ? new EdgeClamper() : new CenterClamper(),
}, },
layoutConfig, layoutConfig,
workspace.currentActivity, workspace.currentActivity,

View File

@@ -54,7 +54,7 @@ namespace ClientState {
clientManager.pinClient(kwinClient); clientManager.pinClient(kwinClient);
}); });
} }
}) });
return manager; return manager;
} }

View File

@@ -68,7 +68,7 @@ namespace ClientState {
oldDesktopNumber = kwinClient.desktop; oldDesktopNumber = kwinClient.desktop;
}); });
manager.connect(kwinClient.activitiesChanged, (kwinClient: KwinClient) => { manager.connect(kwinClient.activitiesChanged, () => {
const desktops = kwinClient.desktop === -1 ? [] : [kwinClient.desktop]; const desktops = kwinClient.desktop === -1 ? [] : [kwinClient.desktop];
const changedActivities = oldActivities.length === 0 || kwinClient.activities.length === 0 ? const changedActivities = oldActivities.length === 0 || kwinClient.activities.length === 0 ?
[] : [] :

View File

@@ -33,23 +33,25 @@ namespace ClientState {
manager.connect(kwinClient.desktopChanged, () => { manager.connect(kwinClient.desktopChanged, () => {
world.do((clientManager, desktopManager) => { world.do((clientManager, desktopManager) => {
if (kwinClient.desktop === -1) { const desktop = desktopManager.getDesktopForClient(kwinClient);
if (desktop === undefined) {
// windows on all desktops are not supported // windows on all desktops are not supported
clientManager.untileClient(kwinClient); clientManager.untileClient(kwinClient);
return; return;
} }
Tiled.moveWindowToCorrectGrid(desktopManager, window); Tiled.moveWindowToGrid(window, desktop.grid);
}); });
}); });
manager.connect(kwinClient.activitiesChanged, () => { manager.connect(kwinClient.activitiesChanged, () => {
world.do((clientManager, desktopManager) => { world.do((clientManager, desktopManager) => {
if (kwinClient.activities.length !== 1) { const desktop = desktopManager.getDesktopForClient(kwinClient);
if (desktop === undefined) {
// windows on multiple activities are not supported // windows on multiple activities are not supported
clientManager.untileClient(kwinClient); clientManager.untileClient(kwinClient);
return; return;
} }
Tiled.moveWindowToCorrectGrid(desktopManager, window); Tiled.moveWindowToGrid(window, desktop.grid);
}); });
}) })
@@ -106,6 +108,7 @@ namespace ClientState {
if (kwinClient.resize) { if (kwinClient.resize) {
world.do(() => window.onUserResize(oldGeometry, !cursorChangedAfterResizeStart)); world.do(() => window.onUserResize(oldGeometry, !cursorChangedAfterResizeStart));
} else if ( } else if (
!window.column.grid.isUserResizing() &&
!client.isManipulatingGeometry(newGeometry) && !client.isManipulatingGeometry(newGeometry) &&
!Clients.isMaximizedGeometry(kwinClient) && !Clients.isMaximizedGeometry(kwinClient) &&
!Clients.isFullScreenGeometry(kwinClient) // not using `kwinClient.fullScreen` because it may not be set yet at this point !Clients.isFullScreenGeometry(kwinClient) // not using `kwinClient.fullScreen` because it may not be set yet at this point
@@ -130,17 +133,13 @@ namespace ClientState {
return manager; return manager;
} }
private static moveWindowToCorrectGrid(desktopManager: DesktopManager, window: Window) { private static moveWindowToGrid(window: Window, grid: Grid) {
const kwinClient = window.client.kwinClient; if (grid === window.column.grid) {
// window already on the given grid
const oldGrid = window.column.grid;
const newGrid = desktopManager.getDesktopForClient(kwinClient).grid;
if (oldGrid === newGrid) {
// window already on the correct grid
return; return;
} }
const newColumn = new Column(newGrid, newGrid.getLastFocusedColumn() ?? newGrid.getLastColumn()); const newColumn = new Column(grid, grid.getLastFocusedColumn() ?? grid.getLastColumn());
window.moveToColumn(newColumn); window.moveToColumn(newColumn);
} }