refactor arrange
This commit is contained in:
216
src/Actions.ts
216
src/Actions.ts
@@ -2,7 +2,7 @@ module Actions {
|
||||
export function init(world: World, config: Config) {
|
||||
return {
|
||||
focusLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
@@ -12,7 +12,7 @@ module Actions {
|
||||
},
|
||||
|
||||
focusRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
@@ -22,7 +22,7 @@ module Actions {
|
||||
},
|
||||
|
||||
focusUp: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const prevWindow = column.getPrevWindow(window);
|
||||
if (prevWindow === null) {
|
||||
return;
|
||||
@@ -32,7 +32,7 @@ module Actions {
|
||||
},
|
||||
|
||||
focusDown: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const nextWindow = column.getNextWindow(window);
|
||||
if (nextWindow === null) {
|
||||
return;
|
||||
@@ -42,27 +42,29 @@ module Actions {
|
||||
},
|
||||
|
||||
focusStart: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
firstColumn.focus();
|
||||
grid.container.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
firstColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusEnd: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
lastColumn.focus();
|
||||
grid.container.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
lastColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
@@ -70,18 +72,17 @@ module Actions {
|
||||
return;
|
||||
}
|
||||
window.moveToColumn(prevColumn);
|
||||
grid.container.onGridReordered();
|
||||
grid.container.autoAdjustScroll();
|
||||
} else {
|
||||
// move from shared column into own column
|
||||
const newColumn = new Column(grid, grid.getPrevColumn(column));
|
||||
window.moveToColumn(newColumn);
|
||||
}
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
@@ -89,97 +90,89 @@ module Actions {
|
||||
return;
|
||||
}
|
||||
window.moveToColumn(nextColumn);
|
||||
grid.container.onGridReordered();
|
||||
grid.container.autoAdjustScroll();
|
||||
} else {
|
||||
// move from shared column into own column
|
||||
const newColumn = new Column(grid, column);
|
||||
window.moveToColumn(newColumn);
|
||||
}
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveUp: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
// TODO (optimization): only arrange moved windows
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
column.moveWindowUp(window);
|
||||
grid.container.arrange(); // TODO (optimization): only arrange moved windows
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveDown: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
// TODO (optimization): only arrange moved windows
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
column.moveWindowDown(window);
|
||||
grid.container.arrange(); // TODO (optimization): only arrange moved windows
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const newColumn = new Column(grid, null);
|
||||
window.moveToColumn(newColumn);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const newColumn = new Column(grid, grid.getLastColumn());
|
||||
window.moveToColumn(newColumn);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowToggleFloating: () => {
|
||||
const kwinClient = workspace.activeClient;
|
||||
world.toggleFloatingClient(kwinClient);
|
||||
world.do((clientManager, svm) => {
|
||||
clientManager.toggleFloatingClient(kwinClient);
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
grid.moveColumnLeft(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
grid.moveColumnRight(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
column.moveAfter(null);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
column.moveAfter(grid.getLastColumn());
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnToggleStacked: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
world.doIfTiledFocused(false, (world, svm, window, column, grid) => {
|
||||
column.toggleStacked();
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthIncrease: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
world.doIfTiledFocused(false, (world, svm, window, column, grid) => {
|
||||
grid.increaseColumnWidth(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthDecrease: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
world.doIfTiledFocused(false, (world, svm, window, column, grid) => {
|
||||
grid.decreaseColumnWidth(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -192,66 +185,65 @@ module Actions {
|
||||
},
|
||||
|
||||
gridScrollStart: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(firstColumn);
|
||||
grid.container.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(firstColumn);
|
||||
});
|
||||
},
|
||||
|
||||
gridScrollEnd: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(lastColumn);
|
||||
grid.container.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(lastColumn);
|
||||
});
|
||||
},
|
||||
|
||||
gridScrollFocused: () => {
|
||||
const focusedWindow = world.getFocusedWindow(true);
|
||||
if (focusedWindow === null) {
|
||||
return;
|
||||
}
|
||||
const column = focusedWindow.column;
|
||||
const grid = column.grid;
|
||||
grid.container.scrollCenterColumn(column);
|
||||
grid.container.arrange();
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
grid.container.scrollCenterColumn(column);
|
||||
})
|
||||
},
|
||||
|
||||
gridScrollLeftColumn: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const column = grid.getLeftmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const column = grid.getLeftmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid.container.scrollToColumn(prevColumn);
|
||||
grid.container.arrange();
|
||||
grid.container.scrollToColumn(prevColumn);
|
||||
});
|
||||
},
|
||||
|
||||
gridScrollRightColumn: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const column = grid.getRightmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const column = grid.getRightmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid.container.scrollToColumn(nextColumn);
|
||||
grid.container.arrange();
|
||||
grid.container.scrollToColumn(nextColumn);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -259,28 +251,29 @@ module Actions {
|
||||
export function initNum(world: World) {
|
||||
return {
|
||||
focusColumn: (columnIndex: number) => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
}
|
||||
targetColumn.focus();
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
}
|
||||
targetColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
}
|
||||
window.moveToColumn(targetColumn);
|
||||
grid.container.onGridReordered();
|
||||
grid.container.arrange();
|
||||
grid.container.autoAdjustScroll();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (world, svm, window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null || targetColumn === column) {
|
||||
return null;
|
||||
@@ -290,43 +283,38 @@ module Actions {
|
||||
} else {
|
||||
column.moveAfter(grid.getPrevColumn(targetColumn));
|
||||
}
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveToDesktop: (desktopIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, oldGrid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, svm, window, column, oldGrid) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = world.getGridInCurrentActivity(desktopNumber);
|
||||
const newGrid = svm.getInCurrentActivity(desktopNumber).grid;
|
||||
if (newGrid === null || newGrid === oldGrid) {
|
||||
return;
|
||||
}
|
||||
column.moveToGrid(newGrid, newGrid.getLastColumn());
|
||||
oldGrid.container.arrange();
|
||||
newGrid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
tailMoveToDesktop: (desktopIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, oldGrid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, svm, window, column, oldGrid) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = world.getGridInCurrentActivity(desktopNumber);
|
||||
const newGrid = svm.getInCurrentActivity(desktopNumber).grid;
|
||||
if (newGrid === null || newGrid === oldGrid) {
|
||||
return;
|
||||
}
|
||||
oldGrid.evacuateTail(newGrid, column);
|
||||
oldGrid.container.arrange();
|
||||
newGrid.container.arrange();
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function gridScroll(world: World, amount: number) {
|
||||
const scrollAmount = amount;
|
||||
const grid = world.getCurrentGrid();
|
||||
grid.container.adjustScroll(scrollAmount, false);
|
||||
grid.container.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
const grid = svm.getCurrent().grid;
|
||||
grid.container.adjustScroll(amount, false);
|
||||
});
|
||||
}
|
||||
|
||||
export type Config = {
|
||||
|
||||
@@ -47,10 +47,12 @@ class Column {
|
||||
|
||||
public moveWindowUp(window: Window) {
|
||||
this.windows.moveBack(window);
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public moveWindowDown(window: Window) {
|
||||
this.windows.moveForward(window);
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public getWindowCount() {
|
||||
@@ -124,6 +126,8 @@ class Column {
|
||||
|
||||
window.height += heightDelta;
|
||||
otherWindow.height -= heightDelta;
|
||||
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public resizeWindows() {
|
||||
@@ -144,9 +148,11 @@ class Column {
|
||||
remainingWindows--;
|
||||
}
|
||||
// TODO: respect min height
|
||||
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
private getFocusTaker() {
|
||||
public getFocusTaker() {
|
||||
if (this.focusTaker === null || !this.windows.contains(this.focusTaker)) {
|
||||
return null;
|
||||
}
|
||||
@@ -206,6 +212,7 @@ class Column {
|
||||
return;
|
||||
}
|
||||
this.stacked = !this.stacked;
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public isVisible(scrollPos: ScrollView.Pos, fullyVisible: boolean) {
|
||||
@@ -230,6 +237,8 @@ class Column {
|
||||
if (window.isFocused()) {
|
||||
this.onWindowFocused(window);
|
||||
}
|
||||
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onWindowRemoved(window: Window, passFocus: boolean) {
|
||||
@@ -251,6 +260,8 @@ class Column {
|
||||
windowToFocus.focus();
|
||||
}
|
||||
}
|
||||
|
||||
this.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onWindowFocused(window: Window) {
|
||||
|
||||
@@ -16,7 +16,8 @@ class Grid {
|
||||
this.userResize = false;
|
||||
this.userResizeFinishedDelayer = new Delayer(50, () => {
|
||||
// this delay prevents windows' contents from freezing after resizing
|
||||
this.container.onGridWidthChanged();
|
||||
this.container.onLayoutChanged();
|
||||
this.container.autoAdjustScroll();
|
||||
this.container.arrange();
|
||||
});
|
||||
}
|
||||
@@ -24,7 +25,8 @@ class Grid {
|
||||
public moveColumnLeft(column: Column) {
|
||||
this.columns.moveBack(column);
|
||||
this.columnsSetX(column);
|
||||
this.container.onGridReordered();
|
||||
this.container.onLayoutChanged();
|
||||
this.container.autoAdjustScroll();
|
||||
}
|
||||
|
||||
public moveColumnRight(column: Column) {
|
||||
@@ -66,6 +68,14 @@ class Grid {
|
||||
return this.lastFocusedColumn;
|
||||
}
|
||||
|
||||
public getLastFocusedWindow() {
|
||||
const lastFocusedColumn = this.getLastFocusedColumn();
|
||||
if (lastFocusedColumn === null) {
|
||||
return null;
|
||||
}
|
||||
return lastFocusedColumn.getFocusTaker();
|
||||
}
|
||||
|
||||
private columnsSetX(firstMovedColumn: Column|null) {
|
||||
const lastUnmovedColumn = firstMovedColumn === null ? this.columns.getLast() : this.columns.getPrev(firstMovedColumn);
|
||||
let x = lastUnmovedColumn === null ? 0 : lastUnmovedColumn.getRight() + this.config.gapsInnerHorizontal;
|
||||
@@ -200,6 +210,11 @@ class Grid {
|
||||
column.arrange(x);
|
||||
x += column.getWidth() + this.config.gapsInnerHorizontal;
|
||||
}
|
||||
|
||||
const focusedWindow = this.getLastFocusedWindow();
|
||||
if (focusedWindow !== null) {
|
||||
focusedWindow.client.ensureTransientsVisible(this.container.clientArea);
|
||||
}
|
||||
}
|
||||
|
||||
public onColumnAdded(column: Column, prevColumn: Column|null) {
|
||||
@@ -209,7 +224,8 @@ class Grid {
|
||||
this.columns.insertAfter(column, prevColumn);
|
||||
}
|
||||
this.columnsSetX(column);
|
||||
this.container.onGridWidthChanged();
|
||||
this.container.onLayoutChanged();
|
||||
this.container.autoAdjustScroll();
|
||||
}
|
||||
|
||||
public onColumnRemoved(column: Column, passFocus: boolean) {
|
||||
@@ -226,8 +242,9 @@ class Grid {
|
||||
if (passFocus && columnToFocus !== null) {
|
||||
columnToFocus.focus();
|
||||
} else {
|
||||
this.container.onGridWidthChanged();
|
||||
this.container.autoAdjustScroll();
|
||||
}
|
||||
this.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onColumnMoved(column: Column, prevColumn: Column|null) {
|
||||
@@ -235,15 +252,17 @@ class Grid {
|
||||
const firstMovedColumn = movedLeft ? column : this.getNextColumn(column);
|
||||
this.columns.move(column, prevColumn);
|
||||
this.columnsSetX(firstMovedColumn);
|
||||
this.container.onGridReordered();
|
||||
this.container.onLayoutChanged();
|
||||
this.container.autoAdjustScroll();
|
||||
}
|
||||
|
||||
public onColumnWidthChanged(column: Column, oldWidth: number, width: number) {
|
||||
const nextColumn = this.columns.getNext(column);
|
||||
this.columnsSetX(nextColumn);
|
||||
if (!this.userResize) {
|
||||
this.container.onGridWidthChanged();
|
||||
this.container.autoAdjustScroll();
|
||||
}
|
||||
this.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onColumnFocused(column: Column) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
class ScrollView {
|
||||
public readonly world: World;
|
||||
public readonly grid: Grid;
|
||||
public readonly desktop: number;
|
||||
private readonly config: ScrollView.Config;
|
||||
private scrollX: number;
|
||||
private dirty: boolean;
|
||||
public clientArea: QRect;
|
||||
public tilingArea: QRect;
|
||||
|
||||
constructor(world: World, desktop: number, config: ScrollView.Config, layoutConfig: LayoutConfig) {
|
||||
constructor(desktop: number, config: ScrollView.Config, layoutConfig: LayoutConfig) {
|
||||
this.config = config;
|
||||
this.world = world;
|
||||
this.scrollX = 0;
|
||||
this.dirty = false;
|
||||
this.desktop = desktop;
|
||||
this.grid = new Grid(this, layoutConfig);
|
||||
this.updateArea();
|
||||
@@ -65,7 +65,7 @@ class ScrollView {
|
||||
}
|
||||
|
||||
public scrollToColumn(column: Column) {
|
||||
this.scrollX = this.getScrollPosForColumn(column).x;
|
||||
this.setScroll(this.getScrollPosForColumn(column).x, true);
|
||||
}
|
||||
|
||||
public scrollCenterColumn(column: Column) {
|
||||
@@ -74,18 +74,18 @@ class ScrollView {
|
||||
this.adjustScroll(Math.round(windowCenter - screenCenter), false);
|
||||
}
|
||||
|
||||
private autoAdjustScroll() {
|
||||
const focusedWindow = this.world.getFocusedWindow(true);
|
||||
if (focusedWindow === null) {
|
||||
public autoAdjustScroll() {
|
||||
const focusedColumn = this.grid.getLastFocusedColumn();
|
||||
if (focusedColumn === null) {
|
||||
this.removeOverscroll();
|
||||
return;
|
||||
}
|
||||
|
||||
const column = focusedWindow.column;
|
||||
if (column.grid !== this.grid) {
|
||||
if (focusedColumn.grid !== this.grid) {
|
||||
return;
|
||||
}
|
||||
this.scrollToColumn(column);
|
||||
|
||||
this.scrollToColumn(focusedColumn);
|
||||
}
|
||||
|
||||
private getScrollPos(scrollX: number) {
|
||||
@@ -108,11 +108,15 @@ class ScrollView {
|
||||
}
|
||||
|
||||
private setScroll(x: number, force: boolean) {
|
||||
const oldScrollX = this.scrollX;
|
||||
this.scrollX = force ? x : this.clampScrollX(x);
|
||||
if (this.scrollX !== oldScrollX) {
|
||||
this.onLayoutChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private applyScrollPos(scrollPos: ScrollView.Pos) {
|
||||
this.scrollX = scrollPos.x;
|
||||
this.setScroll(scrollPos.x, true);
|
||||
}
|
||||
|
||||
public adjustScroll(dx: number, force: boolean) {
|
||||
@@ -126,16 +130,15 @@ class ScrollView {
|
||||
public arrange() {
|
||||
// TODO (optimization): only arrange visible windows
|
||||
this.updateArea();
|
||||
if (!this.dirty) {
|
||||
return;
|
||||
}
|
||||
this.grid.arrange(this.tilingArea.x - this.scrollX);
|
||||
this.world.ensureFocusedTransientsVisible(); // TODO: refactor - call from elsewhere
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
public onGridWidthChanged() {
|
||||
this.autoAdjustScroll();
|
||||
}
|
||||
|
||||
public onGridReordered() {
|
||||
this.autoAdjustScroll();
|
||||
public onLayoutChanged() {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
|
||||
@@ -63,6 +63,7 @@ class Window {
|
||||
}
|
||||
this.client.setMaximize(false, false);
|
||||
this.client.setFullScreen(false);
|
||||
this.column.grid.container.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onMaximizedChanged(horizontally: boolean, vertically: boolean) {
|
||||
|
||||
@@ -3,7 +3,7 @@ class WindowRuleEnforcer {
|
||||
private readonly preferTiling: ClientMatcher;
|
||||
private readonly followCaption: Set<string>;
|
||||
|
||||
constructor(world: World, windowRules: WindowRule[]) {
|
||||
constructor(windowRules: WindowRule[]) {
|
||||
const [mapFloat, mapTile] = createWindowRuleMaps(windowRules);
|
||||
this.preferFloating = new ClientMatcher(mapFloat);
|
||||
this.preferTiling = new ClientMatcher(mapTile);
|
||||
@@ -26,11 +26,13 @@ class WindowRuleEnforcer {
|
||||
const manager = new SignalManager();
|
||||
manager.connect(kwinClient.captionChanged, () => {
|
||||
const shouldTile = enforcer.shouldTile(kwinClient);
|
||||
if (shouldTile) {
|
||||
world.tileClient(kwinClient);
|
||||
} else {
|
||||
world.untileClient(kwinClient);
|
||||
}
|
||||
world.do((clientManager, svm) => {
|
||||
if (shouldTile) {
|
||||
clientManager.tileClient(kwinClient);
|
||||
} else {
|
||||
clientManager.untileClient(kwinClient);
|
||||
}
|
||||
});
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(workspace.clientAdded, (kwinClient: AbstractClient) => {
|
||||
console.assert(!world.hasClient(kwinClient));
|
||||
if (Clients.canTileEver(kwinClient)) {
|
||||
// never open new tileable clients on all desktops or activities
|
||||
if (kwinClient.desktop <= 0) {
|
||||
@@ -12,26 +11,32 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
kwinClient.activities = [workspace.currentActivity];
|
||||
}
|
||||
}
|
||||
world.addClient(kwinClient);
|
||||
world.do((clientManager, svm) => {
|
||||
clientManager.addClient(kwinClient)
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientRemoved, (kwinClient: AbstractClient) => {
|
||||
console.assert(world.hasClient(kwinClient));
|
||||
world.removeClient(kwinClient, true);
|
||||
world.do((clientManager, svm) => {
|
||||
clientManager.removeClient(kwinClient, true);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientMinimized, (kwinClient: AbstractClient) => {
|
||||
world.minimizeClient(kwinClient);
|
||||
world.do((clientManager, svm) => {
|
||||
clientManager.minimizeClient(kwinClient);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientUnminimized, (kwinClient: AbstractClient) => {
|
||||
world.unminimizeClient(kwinClient);
|
||||
world.do((clientManager, svm) => {
|
||||
clientManager.unminimizeClient(kwinClient);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientMaximizeSet, (kwinClient: AbstractClient, horizontally: boolean, vertically: boolean) => {
|
||||
world.doIfTiled(kwinClient, false, (window, column, grid) => {
|
||||
world.doIfTiled(kwinClient, false, (world, svm, window, column, grid) => {
|
||||
window.onMaximizedChanged(horizontally, vertically);
|
||||
grid.container.arrange();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,17 +44,14 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
if (kwinClient === null) {
|
||||
return;
|
||||
}
|
||||
world.onClientFocused(kwinClient);
|
||||
world.doIfTiled(kwinClient, true, (window, column, grid) => {
|
||||
window.onFocused();
|
||||
grid.container.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
clientManager.onClientFocused(kwinClient);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(workspace.clientFullScreenSet, (kwinClient: X11Client, fullScreen: boolean, user: boolean) => {
|
||||
world.doIfTiled(kwinClient, false, (window, column, grid) => {
|
||||
world.doIfTiled(kwinClient, false, (clientManager, svm, window, column, grid) => {
|
||||
window.onFullScreenChanged(fullScreen);
|
||||
grid.container.arrange();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
164
src/world/ClientManager.ts
Normal file
164
src/world/ClientManager.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
class ClientManager {
|
||||
private readonly world: World;
|
||||
private readonly scrollViewManager: ScrollViewManager;
|
||||
private readonly clientMap: Map<AbstractClient, ClientWrapper>;
|
||||
private lastFocusedClient: AbstractClient|null;
|
||||
private readonly windowRuleEnforcer: WindowRuleEnforcer;
|
||||
|
||||
constructor(config: Config, world: World, svm: ScrollViewManager) {
|
||||
this.world = world;
|
||||
this.scrollViewManager = svm;
|
||||
this.clientMap = new Map();
|
||||
this.lastFocusedClient = null;
|
||||
|
||||
let parsedWindowRules: WindowRule[] = [];
|
||||
try {
|
||||
parsedWindowRules = JSON.parse(config.windowRules);
|
||||
} catch (error: any) {
|
||||
console.log("failed to parse windowRules:", error);
|
||||
}
|
||||
this.windowRuleEnforcer = new WindowRuleEnforcer(parsedWindowRules);
|
||||
}
|
||||
|
||||
public addClient(kwinClient: AbstractClient) {
|
||||
console.assert(!this.hasClient(kwinClient));
|
||||
const client = new ClientWrapper(
|
||||
kwinClient,
|
||||
new ClientStateFloating(),
|
||||
this.findTransientFor(kwinClient),
|
||||
this.windowRuleEnforcer.initClientSignalManager(this.world, kwinClient),
|
||||
);
|
||||
this.clientMap.set(kwinClient, client);
|
||||
|
||||
if (kwinClient.dock) {
|
||||
client.stateManager.setState(new ClientStateDocked(this.world, kwinClient), false);
|
||||
} else if (this.windowRuleEnforcer.shouldTile(kwinClient)) {
|
||||
const grid = this.scrollViewManager.getForClient(client.kwinClient).grid;
|
||||
client.stateManager.setState(new ClientStateTiled(this.world, client, grid), false);
|
||||
}
|
||||
}
|
||||
|
||||
public removeClient(kwinClient: AbstractClient, passFocus: boolean) {
|
||||
console.assert(this.hasClient(kwinClient));
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
client.destroy(passFocus && kwinClient === this.lastFocusedClient);
|
||||
this.clientMap.delete(kwinClient);
|
||||
}
|
||||
|
||||
private findTransientFor(kwinClient: AbstractClient) {
|
||||
if (!kwinClient.transient) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const transientFor = this.clientMap.get(kwinClient.transientFor);
|
||||
if (transientFor === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return transientFor;
|
||||
}
|
||||
|
||||
public minimizeClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiled) {
|
||||
client.stateManager.setState(new ClientStateTiledMinimized(), kwinClient === this.lastFocusedClient);
|
||||
}
|
||||
}
|
||||
|
||||
public unminimizeClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiledMinimized) {
|
||||
const grid = this.scrollViewManager.getForClient(client.kwinClient).grid;
|
||||
client.stateManager.setState(new ClientStateTiled(this.world, client, grid), false);
|
||||
}
|
||||
}
|
||||
|
||||
public tileClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiled) {
|
||||
return;
|
||||
}
|
||||
const grid = this.scrollViewManager.getForClient(client.kwinClient).grid;
|
||||
client.stateManager.setState(new ClientStateTiled(this.world, client, grid), false);
|
||||
}
|
||||
|
||||
public untileClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiled) {
|
||||
client.stateManager.setState(new ClientStateFloating(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleFloatingClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientState = client.stateManager.getState();
|
||||
if (clientState instanceof ClientStateFloating && Clients.canTileEver(kwinClient)) {
|
||||
Clients.makeTileable(kwinClient);
|
||||
const grid = this.scrollViewManager.getForClient(client.kwinClient).grid;
|
||||
client.stateManager.setState(new ClientStateTiled(this.world, client, grid), false);
|
||||
} else if (clientState instanceof ClientStateTiled) {
|
||||
client.stateManager.setState(new ClientStateFloating(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public hasClient(kwinClient: AbstractClient) {
|
||||
return this.clientMap.has(kwinClient);
|
||||
}
|
||||
|
||||
public onClientFocused(kwinClient: AbstractClient) {
|
||||
this.lastFocusedClient = kwinClient;
|
||||
const window = this.findTiledWindow(kwinClient, true);
|
||||
if (window !== null) {
|
||||
window.onFocused();
|
||||
}
|
||||
}
|
||||
|
||||
public findTiledWindow(kwinClient: AbstractClient, followTransient: boolean) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.findTiledWindowOfClient(client, followTransient);
|
||||
}
|
||||
|
||||
private findTiledWindowOfClient(client: ClientWrapper, followTransient: boolean): Window|null {
|
||||
const clientState = client.stateManager.getState();
|
||||
if (clientState instanceof ClientStateTiled) {
|
||||
return clientState.window;
|
||||
} else if (followTransient && client.transientFor !== null) {
|
||||
return this.findTiledWindowOfClient(client.transientFor, true);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private removeAllClients() {
|
||||
for (const kwinClient of Array.from(this.clientMap.keys())) {
|
||||
this.removeClient(kwinClient, false);
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.removeAllClients();
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,11 @@ class ClientStateTiled {
|
||||
readonly window: Window;
|
||||
private readonly signalManager: SignalManager;
|
||||
|
||||
constructor(world: World, client: ClientWrapper) {
|
||||
constructor(world: World, client: ClientWrapper, grid: Grid) {
|
||||
client.prepareForTiling();
|
||||
|
||||
const grid = world.getClientGrid(client.kwinClient);
|
||||
const column = new Column(grid, grid.getLastFocusedColumn() ?? grid.getLastColumn());
|
||||
const window = new Window(client, column);
|
||||
grid.container.arrange();
|
||||
|
||||
this.window = window;
|
||||
this.signalManager = ClientStateTiled.initSignalManager(world, window);
|
||||
@@ -21,7 +19,6 @@ class ClientStateTiled {
|
||||
const grid = window.column.grid;
|
||||
const clientWrapper = window.client;
|
||||
window.destroy(passFocus);
|
||||
grid.container.arrange();
|
||||
|
||||
clientWrapper.prepareForFloating(grid.container.clientArea);
|
||||
}
|
||||
@@ -32,39 +29,45 @@ class ClientStateTiled {
|
||||
const manager = new SignalManager();
|
||||
|
||||
manager.connect(kwinClient.desktopChanged, () => {
|
||||
if (kwinClient.desktop === -1) {
|
||||
// windows on all desktops are not supported
|
||||
world.untileClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
ClientStateTiled.moveWindowToCorrectGrid(world, window);
|
||||
world.do((clientManager, svm) => {
|
||||
if (kwinClient.desktop === -1) {
|
||||
// windows on all desktops are not supported
|
||||
clientManager.untileClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
ClientStateTiled.moveWindowToCorrectGrid(svm, window);
|
||||
});
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.activitiesChanged, (kwinClient: AbstractClient) => {
|
||||
if (kwinClient.activities.length !== 1) {
|
||||
// windows on multiple activities are not supported
|
||||
world.untileClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
ClientStateTiled.moveWindowToCorrectGrid(world, window);
|
||||
world.do((clientManager, svm) => {
|
||||
if (kwinClient.activities.length !== 1) {
|
||||
// windows on multiple activities are not supported
|
||||
clientManager.untileClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
ClientStateTiled.moveWindowToCorrectGrid(svm, window);
|
||||
});
|
||||
})
|
||||
|
||||
let lastResize = false;
|
||||
manager.connect(kwinClient.moveResizedChanged, () => {
|
||||
if (world.untileOnDrag && kwinClient.move) {
|
||||
world.untileClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
world.do((clientManager, svm) => {
|
||||
if (world.untileOnDrag && kwinClient.move) {
|
||||
clientManager.untileClient(kwinClient);
|
||||
return;
|
||||
}
|
||||
|
||||
const grid = window.column.grid;
|
||||
const resize = kwinClient.resize;
|
||||
if (!lastResize && resize) {
|
||||
grid.onUserResizeStarted();
|
||||
}
|
||||
if (lastResize && !resize) {
|
||||
grid.onUserResizeFinished();
|
||||
}
|
||||
lastResize = resize;
|
||||
const grid = window.column.grid;
|
||||
const resize = kwinClient.resize;
|
||||
if (!lastResize && resize) {
|
||||
grid.onUserResizeStarted();
|
||||
}
|
||||
if (lastResize && !resize) {
|
||||
grid.onUserResizeFinished();
|
||||
}
|
||||
lastResize = resize;
|
||||
});
|
||||
});
|
||||
|
||||
let cursorChangedAfterResizeStart = false;
|
||||
@@ -76,27 +79,27 @@ class ClientStateTiled {
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: TopLevel, oldGeometry: QRect) => {
|
||||
const scrollView = window.column.grid.container;
|
||||
if (kwinClient.resize) {
|
||||
window.onUserResize(oldGeometry, !cursorChangedAfterResizeStart);
|
||||
scrollView.arrange();
|
||||
} else {
|
||||
const maximized = rectEqual(kwinClient.frameGeometry, scrollView.clientArea);
|
||||
if (!client.isManipulatingGeometry() && !kwinClient.fullScreen && !maximized) {
|
||||
window.onProgrammaticResize(oldGeometry);
|
||||
scrollView.arrange();
|
||||
world.do((clientManager, svm) => {
|
||||
const scrollView = window.column.grid.container;
|
||||
if (kwinClient.resize) {
|
||||
window.onUserResize(oldGeometry, !cursorChangedAfterResizeStart);
|
||||
} else {
|
||||
const maximized = rectEqual(kwinClient.frameGeometry, scrollView.clientArea);
|
||||
if (!client.isManipulatingGeometry() && !kwinClient.fullScreen && !maximized) {
|
||||
window.onProgrammaticResize(oldGeometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
private static moveWindowToCorrectGrid(world: World, window: Window) {
|
||||
private static moveWindowToCorrectGrid(svm: ScrollViewManager, window: Window) {
|
||||
const kwinClient = window.client.kwinClient;
|
||||
|
||||
const oldGrid = window.column.grid;
|
||||
const newGrid = world.getClientGrid(kwinClient);
|
||||
const newGrid = svm.getForClient(kwinClient).grid;
|
||||
if (oldGrid === newGrid) {
|
||||
// window already on the correct grid
|
||||
return;
|
||||
@@ -104,7 +107,5 @@ class ClientStateTiled {
|
||||
|
||||
const newColumn = new Column(newGrid, newGrid.getLastFocusedColumn() ?? newGrid.getLastColumn());
|
||||
window.moveToColumn(newColumn);
|
||||
oldGrid.container.arrange();
|
||||
newGrid.container.arrange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
class ScrollViewManager {
|
||||
private readonly world: World;
|
||||
private readonly config: ScrollView.Config;
|
||||
public readonly layoutConfig: LayoutConfig;
|
||||
private readonly scrollViewsPerActivity: Map<string, ScrollView[]>;
|
||||
private nDesktops: number;
|
||||
|
||||
constructor(world: World, config: ScrollView.Config, layoutConfig: LayoutConfig, currentActivity: string, nDesktops: number) {
|
||||
constructor(config: ScrollView.Config, layoutConfig: LayoutConfig, currentActivity: string) {
|
||||
this.config = config;
|
||||
this.layoutConfig = layoutConfig;
|
||||
this.world = world;
|
||||
this.scrollViewsPerActivity = new Map();
|
||||
this.nDesktops = 0;
|
||||
this.setNDesktops(nDesktops);
|
||||
this.update()
|
||||
this.addActivity(currentActivity);
|
||||
}
|
||||
|
||||
get(activity: string, desktopNumber: number) {
|
||||
public update() {
|
||||
this.setNDesktops(workspace.desktops);
|
||||
}
|
||||
|
||||
public get(activity: string, desktopNumber: number) {
|
||||
const desktopIndex = desktopNumber - 1;
|
||||
if (desktopIndex >= this.nDesktops || this.nDesktops < 0) {
|
||||
throw new Error("invalid desktop number: " + String(desktopNumber));
|
||||
@@ -26,7 +28,20 @@ class ScrollViewManager {
|
||||
return this.scrollViewsPerActivity.get(activity)![desktopIndex];
|
||||
}
|
||||
|
||||
setNDesktops(nDesktops: number) {
|
||||
public getCurrent() {
|
||||
return this.get(workspace.currentActivity, workspace.currentDesktop);
|
||||
}
|
||||
|
||||
public getInCurrentActivity(desktopNumber: number) {
|
||||
return this.get(workspace.currentActivity, desktopNumber);
|
||||
}
|
||||
|
||||
public getForClient(kwinClient: AbstractClient) {
|
||||
console.assert(kwinClient.activities.length === 1);
|
||||
return this.get(kwinClient.activities[0], kwinClient.desktop);
|
||||
}
|
||||
|
||||
private setNDesktops(nDesktops: number) {
|
||||
if (nDesktops > this.nDesktops) {
|
||||
this.addDesktopsToActivities(nDesktops - this.nDesktops);
|
||||
} else if (nDesktops < this.nDesktops) {
|
||||
@@ -45,7 +60,7 @@ class ScrollViewManager {
|
||||
const nStart = scrollViews.length;
|
||||
for (let i = 0; i < n; i++) {
|
||||
const desktopNumber = nStart + i + 1;
|
||||
scrollViews.push(new ScrollView(this.world, desktopNumber, this.config, this.layoutConfig));
|
||||
scrollViews.push(new ScrollView(desktopNumber, this.config, this.layoutConfig));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +75,13 @@ class ScrollViewManager {
|
||||
}
|
||||
}
|
||||
|
||||
addActivity(activity: string) {
|
||||
private addActivity(activity: string) {
|
||||
const scrollViews: ScrollView[] = [];
|
||||
this.addDesktops(scrollViews, this.nDesktops);
|
||||
this.scrollViewsPerActivity.set(activity, scrollViews);
|
||||
}
|
||||
|
||||
removeActivity(activity: string) {
|
||||
private removeActivity(activity: string) {
|
||||
const removedScrollViews = this.scrollViewsPerActivity.get(activity)!;
|
||||
this.scrollViewsPerActivity.delete(activity);
|
||||
const targetActivityScrollViews = this.scrollViewsPerActivity.values().next().value;
|
||||
@@ -75,7 +90,13 @@ class ScrollViewManager {
|
||||
}
|
||||
}
|
||||
|
||||
*scrollViews() {
|
||||
public destroy() {
|
||||
for (const scrollView of this.scrollViews()) {
|
||||
scrollView.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public *scrollViews() {
|
||||
for (const scrollViews of this.scrollViewsPerActivity.values()) {
|
||||
for (const scrollView of scrollViews) {
|
||||
yield scrollView;
|
||||
|
||||
@@ -1,36 +1,24 @@
|
||||
class World {
|
||||
public readonly untileOnDrag: boolean;
|
||||
private readonly scrollViewManager: ScrollViewManager;
|
||||
private readonly clientMap: Map<AbstractClient, ClientWrapper>;
|
||||
private lastFocusedClient: AbstractClient|null;
|
||||
public readonly clientManager: ClientManager;
|
||||
private readonly workspaceSignalManager: SignalManager;
|
||||
private readonly windowRuleEnforcer: WindowRuleEnforcer;
|
||||
private readonly screenResizedDelayer: Delayer;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.untileOnDrag = config.untileOnDrag;
|
||||
this.clientMap = new Map();
|
||||
this.lastFocusedClient = null;
|
||||
this.workspaceSignalManager = initWorkspaceSignalHandlers(this);
|
||||
|
||||
let parsedWindowRules: WindowRule[] = [];
|
||||
try {
|
||||
parsedWindowRules = JSON.parse(config.windowRules);
|
||||
} catch (error: any) {
|
||||
console.log("failed to parse windowRules:", error);
|
||||
}
|
||||
this.windowRuleEnforcer = new WindowRuleEnforcer(this, parsedWindowRules);
|
||||
|
||||
this.screenResizedDelayer = new Delayer(1000, () => {
|
||||
// this delay ensures that docks get taken into account by `workspace.clientArea`
|
||||
const gridManager = this.scrollViewManager; // workaround for bug in Qt5's JS engine
|
||||
for (const scrollView of gridManager.scrollViews()) {
|
||||
scrollView.arrange();
|
||||
scrollView.onLayoutChanged();
|
||||
}
|
||||
this.update();
|
||||
});
|
||||
|
||||
this.scrollViewManager = new ScrollViewManager(
|
||||
this,
|
||||
{
|
||||
marginTop: config.gapsOuterTop,
|
||||
marginBottom: config.gapsOuterBottom,
|
||||
@@ -40,206 +28,59 @@ class World {
|
||||
},
|
||||
config,
|
||||
workspace.currentActivity,
|
||||
workspace.desktops,
|
||||
);
|
||||
this.clientManager = new ClientManager(config, this, this.scrollViewManager);
|
||||
this.addExistingClients();
|
||||
}
|
||||
|
||||
public updateDesktops() {
|
||||
this.scrollViewManager.setNDesktops(workspace.desktops);
|
||||
this.update();
|
||||
}
|
||||
|
||||
private addExistingClients() {
|
||||
const kwinClients = workspace.clientList();
|
||||
for (let i = 0; i < kwinClients.length; i++) {
|
||||
const kwinClient = kwinClients[i];
|
||||
this.addClient(kwinClient);
|
||||
this.clientManager.addClient(kwinClient);
|
||||
}
|
||||
}
|
||||
|
||||
private getGrid(activity: string, desktopNumber: number) {
|
||||
console.assert(desktopNumber > 0 && desktopNumber <= workspace.desktops);
|
||||
return this.scrollViewManager.get(activity, desktopNumber).grid;
|
||||
public updateDesktops() {
|
||||
this.scrollViewManager.update();
|
||||
}
|
||||
|
||||
public getGridInCurrentActivity(desktopNumber: number) {
|
||||
return this.getGrid(workspace.currentActivity, desktopNumber);
|
||||
public update() {
|
||||
this.scrollViewManager.getCurrent().arrange();
|
||||
}
|
||||
|
||||
public getCurrentGrid() {
|
||||
return this.getGrid(workspace.currentActivity, workspace.currentDesktop);
|
||||
public do(f: (clientManager: ClientManager, svm: ScrollViewManager) => void) {
|
||||
f(this.clientManager, this.scrollViewManager);
|
||||
this.update();
|
||||
}
|
||||
|
||||
public getClientGrid(kwinClient: AbstractClient) {
|
||||
console.assert(kwinClient.activities.length === 1);
|
||||
return this.getGrid(kwinClient.activities[0], kwinClient.desktop);
|
||||
}
|
||||
|
||||
public addClient(kwinClient: AbstractClient) {
|
||||
const client = new ClientWrapper(
|
||||
kwinClient,
|
||||
new ClientStateFloating(),
|
||||
this.findTransientFor(kwinClient),
|
||||
this.windowRuleEnforcer.initClientSignalManager(this, kwinClient),
|
||||
);
|
||||
this.clientMap.set(kwinClient, client);
|
||||
|
||||
if (kwinClient.dock) {
|
||||
client.stateManager.setState(new ClientStateDocked(this, kwinClient), false);
|
||||
} else if (this.windowRuleEnforcer.shouldTile(kwinClient)) {
|
||||
client.stateManager.setState(new ClientStateTiled(this, client), false);
|
||||
}
|
||||
}
|
||||
|
||||
public removeClient(kwinClient: AbstractClient, passFocus: boolean) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
client.destroy(passFocus && kwinClient === this.lastFocusedClient);
|
||||
this.clientMap.delete(kwinClient);
|
||||
}
|
||||
|
||||
private findTransientFor(kwinClient: AbstractClient) {
|
||||
if (!kwinClient.transient) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const transientFor = this.clientMap.get(kwinClient.transientFor);
|
||||
if (transientFor === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return transientFor;
|
||||
}
|
||||
|
||||
public ensureFocusedTransientsVisible() {
|
||||
this.doIfTiledFocused(true, (window, column, grid) => {
|
||||
window.client.ensureTransientsVisible(grid.container.clientArea);
|
||||
});
|
||||
}
|
||||
|
||||
public minimizeClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiled) {
|
||||
client.stateManager.setState(new ClientStateTiledMinimized(), kwinClient === this.lastFocusedClient);
|
||||
}
|
||||
}
|
||||
|
||||
public unminimizeClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiledMinimized) {
|
||||
client.stateManager.setState(new ClientStateTiled(this, client), false);
|
||||
}
|
||||
}
|
||||
|
||||
public tileClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiled) {
|
||||
return;
|
||||
}
|
||||
client.stateManager.setState(new ClientStateTiled(this, client), false);
|
||||
}
|
||||
|
||||
public untileClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
if (client.stateManager.getState() instanceof ClientStateTiled) {
|
||||
client.stateManager.setState(new ClientStateFloating(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleFloatingClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientState = client.stateManager.getState();
|
||||
if (clientState instanceof ClientStateFloating && Clients.canTileEver(kwinClient)) {
|
||||
Clients.makeTileable(kwinClient);
|
||||
client.stateManager.setState(new ClientStateTiled(this, client), false);
|
||||
} else if (clientState instanceof ClientStateTiled) {
|
||||
client.stateManager.setState(new ClientStateFloating(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public hasClient(kwinClient: AbstractClient) {
|
||||
return this.clientMap.has(kwinClient);
|
||||
}
|
||||
|
||||
public onClientFocused(kwinClient: AbstractClient) {
|
||||
this.lastFocusedClient = kwinClient;
|
||||
}
|
||||
|
||||
private findTiledWindow(client: ClientWrapper, followTransient: boolean): Window|null {
|
||||
const clientState = client.stateManager.getState();
|
||||
if (clientState instanceof ClientStateTiled) {
|
||||
return clientState.window;
|
||||
} else if (followTransient && client.transientFor !== null) {
|
||||
return this.findTiledWindow(client.transientFor, true);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public doIfTiled(kwinClient: AbstractClient, followTransient: boolean, f: (window: Window, column: Column, grid: Grid) => void) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const window = this.findTiledWindow(client, followTransient);
|
||||
public doIfTiled(
|
||||
kwinClient: AbstractClient,
|
||||
followTransient: boolean,
|
||||
f: (clientManager: ClientManager, svm: ScrollViewManager, window: Window, column: Column, grid: Grid) => void,
|
||||
) {
|
||||
const window = this.clientManager.findTiledWindow(kwinClient, followTransient);
|
||||
if (window === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const column = window.column;
|
||||
const grid = column.grid;
|
||||
f(window, column, grid);
|
||||
f(this.clientManager, this.scrollViewManager, window, column, grid);
|
||||
this.update();
|
||||
}
|
||||
|
||||
public doIfTiledFocused(followTransient: boolean, f: (window: Window, column: Column, grid: Grid) => void) {
|
||||
public doIfTiledFocused(
|
||||
followTransient: boolean,
|
||||
f: (clientManager: ClientManager, svm: ScrollViewManager, window: Window, column: Column, grid: Grid) => void,
|
||||
) {
|
||||
this.doIfTiled(workspace.activeClient, followTransient, f);
|
||||
}
|
||||
|
||||
public getFocusedWindow(followTransient: boolean) {
|
||||
const activeClient = workspace.activeClient;
|
||||
if (activeClient === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const client = this.clientMap.get(activeClient);
|
||||
if (client === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.findTiledWindow(client, followTransient);
|
||||
}
|
||||
|
||||
private removeAllClients() {
|
||||
for (const kwinClient of Array.from(this.clientMap.keys())) {
|
||||
this.removeClient(kwinClient, false);
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.workspaceSignalManager.destroy();
|
||||
this.removeAllClients();
|
||||
for (const scrollView of this.scrollViewManager.scrollViews()) {
|
||||
scrollView.destroy();
|
||||
}
|
||||
this.clientManager.destroy();
|
||||
this.scrollViewManager.destroy();
|
||||
}
|
||||
|
||||
public onScreenResized() {
|
||||
|
||||
Reference in New Issue
Block a user