Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57c4643098 | ||
|
|
e92563b424 | ||
|
|
671326bdd7 | ||
|
|
5e9db7d2cd | ||
|
|
b447eacdfd | ||
|
|
94f6e6f33b | ||
|
|
85b0221220 | ||
|
|
1894b055f7 | ||
|
|
05f7550a3b | ||
|
|
a04f629de0 | ||
|
|
4bda4d0d7c | ||
|
|
8bf076948a | ||
|
|
04bd85a287 |
11
README.md
11
README.md
@@ -22,6 +22,8 @@ Similar window managers include [PaperWM](https://github.com/paperwm/PaperWM) an
|
||||
- Doesn't support windows on multiple activities
|
||||
|
||||
## Key bindings
|
||||
The key bindings can be configured in KDE System Settings among KWin's own keyboard shortcuts.
|
||||
Here's the default ones:
|
||||
| Shortcut | Action |
|
||||
| --- | --- |
|
||||
| Meta+Space | Toggle floating |
|
||||
@@ -37,19 +39,16 @@ Similar window managers include [PaperWM](https://github.com/paperwm/PaperWM) an
|
||||
| Meta+Shift+S | Move window down |
|
||||
| Meta+Shift+Home | Move window to start |
|
||||
| Meta+Shift+End | Move window to end |
|
||||
| Meta+X | Expand window (Expands focused window vertically; toggles stacked layout for focused column) |
|
||||
| Meta+X | Toggle stacked layout for focused column |
|
||||
| Meta+Ctrl+Shift+A | Move column left |
|
||||
| Meta+Ctrl+Shift+D | Move column right |
|
||||
| Meta+Ctrl+Shift+Home | Move column to start |
|
||||
| Meta+Ctrl+Shift+End | Move column to end |
|
||||
| Meta+Ctrl+X | Expand column (Expands focused column horizontally to fill the screen) |
|
||||
| Meta+Alt++ | Expand fully visible columns (Expands fully visible columns to fill the screen) |
|
||||
| Meta+Alt+- | Shrink visible columns (Shrinks fully and partially visible columns, making them fully visible and filling the screen) |
|
||||
| Meta+Ctrl++ | Increase column width |
|
||||
| Meta+Ctrl+- | Decrease column width |
|
||||
| Meta+Alt+Return | Center focused window (Scrolls so that the focused window is centered in the screen) |
|
||||
| Meta+Alt+A | Scroll one column to the left |
|
||||
| Meta+Alt+D | Scroll one column to the right |
|
||||
| Meta+Alt+PgUp | Scroll left |
|
||||
| Meta+Alt+PgDown | Scroll right |
|
||||
| Meta+Alt+Home | Scroll to start |
|
||||
| Meta+Alt+End | Scroll to end |
|
||||
| Meta+[N] | Move focus to column N |
|
||||
|
||||
@@ -184,7 +184,31 @@
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_manualScrollStep">
|
||||
<property name="text">
|
||||
<string>Manual scroll step size:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QSpinBox" name="kcfg_manualScrollStep">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_untileOnDrag">
|
||||
<property name="text">
|
||||
<string>Un-tile windows by dragging them</string>
|
||||
@@ -192,7 +216,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_stackColumnsByDefault">
|
||||
<property name="text">
|
||||
<string>Stack columns by default</string>
|
||||
@@ -200,7 +224,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="9" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_resizeNeighborColumn">
|
||||
<property name="text">
|
||||
<string>Resize neighbor column on edge resize</string>
|
||||
@@ -208,7 +232,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="10" column="0" colspan="2">
|
||||
<item row="11" column="0" colspan="2">
|
||||
<spacer name="bottomSpacer_tab_general">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
}],
|
||||
"Id": "karousel",
|
||||
"ServiceTypes": ["KWin/Script"],
|
||||
"Version": "0.3",
|
||||
"Version": "0.3.1",
|
||||
"License": "GPLv3",
|
||||
"Website": "https://github.com/peterfajdiga/karousel",
|
||||
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"
|
||||
|
||||
335
src/Actions.ts
Normal file
335
src/Actions.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
module Actions {
|
||||
export function init(world: World, config: Config) {
|
||||
return {
|
||||
focusLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
prevColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
nextColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusUp: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const prevWindow = column.getPrevWindow(window);
|
||||
if (prevWindow === null) {
|
||||
return;
|
||||
}
|
||||
prevWindow.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusDown: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const nextWindow = column.getNextWindow(window);
|
||||
if (nextWindow === null) {
|
||||
return;
|
||||
}
|
||||
nextWindow.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusStart: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
firstColumn.focus();
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
focusEnd: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
lastColumn.focus();
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
windowMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
window.moveToColumn(prevColumn);
|
||||
grid.container.onGridReordered();
|
||||
} 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) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
window.moveToColumn(nextColumn);
|
||||
grid.container.onGridReordered();
|
||||
} 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) => {
|
||||
column.moveWindowUp(window);
|
||||
grid.container.arrange(); // TODO (optimization): only arrange moved windows
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveDown: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
column.moveWindowDown(window);
|
||||
grid.container.arrange(); // TODO (optimization): only arrange moved windows
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const newColumn = new Column(grid, null);
|
||||
window.moveToColumn(newColumn);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const newColumn = new Column(grid, grid.getLastColumn());
|
||||
window.moveToColumn(newColumn);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowToggleFloating: () => {
|
||||
const kwinClient = workspace.activeClient;
|
||||
world.toggleFloatingClient(kwinClient);
|
||||
},
|
||||
|
||||
columnMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
grid.moveColumnLeft(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
grid.moveColumnRight(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
column.moveAfter(null);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
column.moveAfter(grid.getLastColumn());
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnToggleStacked: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
column.toggleStacked();
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthIncrease: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
grid.increaseColumnWidth(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthDecrease: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
grid.decreaseColumnWidth(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
gridScrollLeft: () => {
|
||||
gridScroll(world, -config.manualScrollStep);
|
||||
},
|
||||
|
||||
gridScrollRight: () => {
|
||||
gridScroll(world, config.manualScrollStep);
|
||||
},
|
||||
|
||||
gridScrollStart: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(firstColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollEnd: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(lastColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollFocused: () => {
|
||||
const focusedWindow = world.getFocusedWindow();
|
||||
if (focusedWindow === null) {
|
||||
return;
|
||||
}
|
||||
const column = focusedWindow.column;
|
||||
const grid = column.grid;
|
||||
grid.container.scrollCenterColumn(column);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollLeftColumn: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const column = grid.getLeftmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid.container.scrollToColumn(prevColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollRightColumn: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const column = grid.getRightmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid.container.scrollToColumn(nextColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
},
|
||||
|
||||
windowMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
}
|
||||
window.moveToColumn(targetColumn);
|
||||
grid.container.onGridReordered();
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null || targetColumn === column) {
|
||||
return null;
|
||||
}
|
||||
if (targetColumn.isAfter(column)) {
|
||||
column.moveAfter(targetColumn);
|
||||
} else {
|
||||
column.moveAfter(grid.getPrevColumn(targetColumn));
|
||||
}
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveToDesktop: (desktopIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, oldGrid) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = world.getGridInCurrentActivity(desktopNumber);
|
||||
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) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = world.getGridInCurrentActivity(desktopNumber);
|
||||
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();
|
||||
}
|
||||
|
||||
export type Config = {
|
||||
manualScrollStep: number,
|
||||
}
|
||||
}
|
||||
341
src/actions.ts
341
src/actions.ts
@@ -1,341 +0,0 @@
|
||||
function initActions(world: World) {
|
||||
return {
|
||||
focusLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
prevColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
nextColumn.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusUp: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const prevWindow = column.getPrevWindow(window);
|
||||
if (prevWindow === null) {
|
||||
return;
|
||||
}
|
||||
prevWindow.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusDown: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const nextWindow = column.getNextWindow(window);
|
||||
if (nextWindow === null) {
|
||||
return;
|
||||
}
|
||||
nextWindow.focus();
|
||||
});
|
||||
},
|
||||
|
||||
focusStart: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
firstColumn.focus();
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
focusEnd: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
lastColumn.focus();
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
windowMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
window.moveToColumn(prevColumn);
|
||||
grid.container.onGridReordered();
|
||||
} 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) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
window.moveToColumn(nextColumn);
|
||||
grid.container.onGridReordered();
|
||||
} 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) => {
|
||||
column.moveWindowUp(window);
|
||||
grid.container.arrange(); // TODO (optimization): only arrange moved windows
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveDown: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
column.moveWindowDown(window);
|
||||
grid.container.arrange(); // TODO (optimization): only arrange moved windows
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const newColumn = new Column(grid, null);
|
||||
window.moveToColumn(newColumn);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const newColumn = new Column(grid, grid.getLastColumn());
|
||||
window.moveToColumn(newColumn);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
windowToggleFloating: () => {
|
||||
const kwinClient = workspace.activeClient;
|
||||
world.toggleFloatingClient(kwinClient);
|
||||
},
|
||||
|
||||
columnMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
grid.moveColumnLeft(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveRight: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
grid.moveColumnRight(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
column.moveAfter(null);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
column.moveAfter(grid.getLastColumn());
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnToggleStacked: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
column.toggleStacked();
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthIncrease: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
grid.increaseColumnWidth(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthDecrease: () => {
|
||||
world.doIfTiledFocused(false, (window, column, grid) => {
|
||||
grid.decreaseColumnWidth(column);
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
gridScrollStart: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const firstColumn = grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(firstColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollEnd: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const lastColumn = grid.getLastColumn();
|
||||
if (lastColumn === null) {
|
||||
return;
|
||||
}
|
||||
grid.container.scrollToColumn(lastColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollFocused: () => {
|
||||
const focusedWindow = world.getFocusedWindow();
|
||||
if (focusedWindow === null) {
|
||||
return;
|
||||
}
|
||||
const column = focusedWindow.column;
|
||||
const grid = column.grid;
|
||||
grid.container.scrollCenterColumn(column);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollLeftColumn: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const column = grid.getLeftmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid.container.scrollToColumn(prevColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
|
||||
gridScrollRightColumn: () => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const column = grid.getRightmostVisibleColumn(grid.container.getCurrentScrollPos(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
grid.container.scrollToColumn(nextColumn);
|
||||
grid.container.arrange();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function initNumActions(world: World) {
|
||||
return {
|
||||
focusColumn: (columnIndex: number) => {
|
||||
const grid = world.getCurrentGrid();
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
}
|
||||
targetColumn.focus();
|
||||
},
|
||||
|
||||
windowMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
}
|
||||
window.moveToColumn(targetColumn);
|
||||
grid.container.onGridReordered();
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null || targetColumn === column) {
|
||||
return null;
|
||||
}
|
||||
if (targetColumn.isAfter(column)) {
|
||||
column.moveAfter(targetColumn);
|
||||
} else {
|
||||
column.moveAfter(grid.getPrevColumn(targetColumn));
|
||||
}
|
||||
grid.container.arrange();
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveToDesktop: (desktopIndex: number) => {
|
||||
world.doIfTiledFocused(true, (window, column, oldGrid) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = world.getGridInCurrentActivity(desktopNumber);
|
||||
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) => {
|
||||
const desktopNumber = desktopIndex + 1;
|
||||
const newGrid = world.getGridInCurrentActivity(desktopNumber);
|
||||
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();
|
||||
}
|
||||
|
||||
function canTileEver(kwinClient: AbstractClient) {
|
||||
return kwinClient.resizeable;
|
||||
}
|
||||
|
||||
function canTileNow(kwinClient: AbstractClient) {
|
||||
return canTileEver(kwinClient) && !kwinClient.minimized && kwinClient.desktop > 0 && kwinClient.activities.length === 1;
|
||||
}
|
||||
|
||||
function makeTileable(kwinClient: AbstractClient) {
|
||||
if (kwinClient.minimized) {
|
||||
kwinClient.minimized = false;
|
||||
}
|
||||
if (kwinClient.desktop <= 0) {
|
||||
kwinClient.desktop = workspace.currentDesktop;
|
||||
}
|
||||
if (kwinClient.activities.length !== 1) {
|
||||
kwinClient.activities = [workspace.currentActivity];
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ type Config = {
|
||||
gapsInnerHorizontal: number,
|
||||
gapsInnerVertical: number,
|
||||
overscroll: number,
|
||||
manualScrollStep: number,
|
||||
untileOnDrag: boolean,
|
||||
stackColumnsByDefault: boolean,
|
||||
resizeNeighborColumn: boolean,
|
||||
|
||||
@@ -83,6 +83,11 @@ const configDef = [
|
||||
"type": "UInt",
|
||||
"default": 18
|
||||
},
|
||||
{
|
||||
"name": "manualScrollStep",
|
||||
"type": "UInt",
|
||||
"default": 200
|
||||
},
|
||||
{
|
||||
"name": "untileOnDrag",
|
||||
"type": "Bool",
|
||||
|
||||
@@ -140,6 +140,18 @@ const keyBindings: KeyBinding[] = [
|
||||
"defaultKeySequence": "Meta+Alt+D",
|
||||
"action": "gridScrollRightColumn",
|
||||
},
|
||||
{
|
||||
"name": "grid-scroll-left",
|
||||
"description": "Scroll left",
|
||||
"defaultKeySequence": "Meta+Alt+PgUp",
|
||||
"action": "gridScrollLeft",
|
||||
},
|
||||
{
|
||||
"name": "grid-scroll-right",
|
||||
"description": "Scroll right",
|
||||
"defaultKeySequence": "Meta+Alt+PgDown",
|
||||
"action": "gridScrollRight",
|
||||
},
|
||||
{
|
||||
"name": "grid-scroll-start",
|
||||
"description": "Scroll to start",
|
||||
|
||||
@@ -3,7 +3,7 @@ interface KeyBinding {
|
||||
description: string;
|
||||
comment?: string;
|
||||
defaultKeySequence: string;
|
||||
action: keyof ReturnType<typeof initActions>;
|
||||
action: keyof ReturnType<typeof Actions.init>;
|
||||
}
|
||||
|
||||
interface NumKeyBinding {
|
||||
@@ -12,7 +12,7 @@ interface NumKeyBinding {
|
||||
comment?: string;
|
||||
defaultModifiers: string;
|
||||
fKeys: boolean;
|
||||
action: keyof ReturnType<typeof initNumActions>;
|
||||
action: keyof ReturnType<typeof Actions.initNum>;
|
||||
}
|
||||
|
||||
function catchWrap(f: () => void) {
|
||||
@@ -49,13 +49,13 @@ function registerNumKeyBindings(name: string, description: string, modifiers: st
|
||||
}
|
||||
}
|
||||
|
||||
function registerKeyBindings(world: World) {
|
||||
const actions = initActions(world);
|
||||
function registerKeyBindings(world: World, config: Config) {
|
||||
const actions = Actions.init(world, config);
|
||||
for (const binding of keyBindings) {
|
||||
registerKeyBinding(binding.name, binding.description, binding.defaultKeySequence, actions[binding.action]);
|
||||
}
|
||||
|
||||
const numActions = initNumActions(world);
|
||||
const numActions = Actions.initNum(world);
|
||||
for (const binding of numKeyBindings) {
|
||||
registerNumKeyBindings(binding.name, binding.description, binding.defaultModifiers, binding.fKeys, numActions[binding.action]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class Column {
|
||||
public grid: Grid;
|
||||
public gridX: number;
|
||||
public width: number; // TODO: increase column width to contain transients
|
||||
private width: number; // TODO: increase column width to contain transients
|
||||
private readonly windows: LinkedList<Window>;
|
||||
private stacked: boolean;
|
||||
private focusTaker: Window|null;
|
||||
|
||||
@@ -72,7 +72,7 @@ class Grid {
|
||||
if (firstMovedColumn !== null) {
|
||||
for (const column of this.columns.iteratorFrom(firstMovedColumn)) {
|
||||
column.gridX = x;
|
||||
x += column.width + this.config.gapsInnerHorizontal;
|
||||
x += column.getWidth() + this.config.gapsInnerHorizontal;
|
||||
}
|
||||
}
|
||||
this.width = x - this.config.gapsInnerHorizontal;
|
||||
@@ -108,7 +108,7 @@ class Grid {
|
||||
let nVisible = 0;
|
||||
for (const column of this.columns.iterator()) {
|
||||
if (column.isVisible(scrollPos, fullyVisible)) {
|
||||
width += column.width;
|
||||
width += column.getWidth();
|
||||
nVisible++;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,6 @@ class Grid {
|
||||
const scrollPos = this.container.getScrollPosForColumn(column);
|
||||
if (this.width < scrollPos.width) {
|
||||
column.adjustWidth(scrollPos.width - this.width, false);
|
||||
this.container.arrange();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,7 +169,6 @@ class Grid {
|
||||
const scrollPos = this.container.getScrollPosForColumn(column);
|
||||
if (this.width <= scrollPos.width) {
|
||||
column.setWidth(Math.round(column.getWidth() / 2), false);
|
||||
this.container.arrange();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,7 +189,8 @@ class Grid {
|
||||
const shrinkLeft = leftInvisibleWidth < rightInvisibleWidth;
|
||||
const widthDelta = (shrinkLeft ? leftInvisibleWidth : rightInvisibleWidth);
|
||||
if (shrinkLeft) {
|
||||
this.container.adjustScroll(-widthDelta, false);
|
||||
const maxDelta = column.getWidth() - column.getMinWidth();
|
||||
this.container.adjustScroll(-Math.min(widthDelta, maxDelta), false);
|
||||
}
|
||||
column.adjustWidth(-widthDelta, true);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class ScrollView {
|
||||
}
|
||||
|
||||
scrollCenterColumn(column: Column) {
|
||||
const windowCenter = column.getLeft() + column.width / 2;
|
||||
const windowCenter = column.getLeft() + column.getWidth() / 2;
|
||||
const screenCenter = this.scrollX + this.tilingArea.width / 2;
|
||||
this.adjustScroll(Math.round(windowCenter - screenCenter), false);
|
||||
}
|
||||
@@ -127,6 +127,7 @@ class ScrollView {
|
||||
// TODO (optimization): only arrange visible windows
|
||||
this.updateArea();
|
||||
this.grid.arrange(this.tilingArea.x - this.scrollX);
|
||||
this.world.ensureFocusedTransientsVisible(); // TODO: refactor - call from elsewhere
|
||||
}
|
||||
|
||||
public onGridWidthChanged() {
|
||||
|
||||
@@ -29,7 +29,7 @@ class Window {
|
||||
|
||||
arrange(x: number, y: number, width: number, height: number) {
|
||||
if (this.skipArrange) {
|
||||
// window is being manually resized, prevent fighting with the user
|
||||
// window is maximized, fullscreen, or being manually resized, prevent fighting with the user
|
||||
return;
|
||||
}
|
||||
this.client.place(x, y, width, height);
|
||||
@@ -94,10 +94,10 @@ class Window {
|
||||
if (resizeNeighborColumn && this.column.grid.config.resizeNeighborColumn) {
|
||||
const neighborColumn = resizingLeftSide ? this.column.grid.getPrevColumn(this.column) : this.column.grid.getNextColumn(this.column);
|
||||
if (neighborColumn !== null) {
|
||||
const oldNeighborWidth = neighborColumn.width;
|
||||
const oldNeighborWidth = neighborColumn.getWidth();
|
||||
neighborColumn.adjustWidth(-widthDelta, true);
|
||||
if (resizingLeftSide) {
|
||||
leftEdgeDelta -= neighborColumn.width - oldNeighborWidth;
|
||||
leftEdgeDelta -= neighborColumn.getWidth() - oldNeighborWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
function init() {
|
||||
const config = loadConfig();
|
||||
const world = new World(config);
|
||||
registerKeyBindings(world);
|
||||
registerKeyBindings(world, config);
|
||||
return world;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class WindowRuleEnforcer {
|
||||
}
|
||||
|
||||
shouldTile(kwinClient: AbstractClient) {
|
||||
return canTileNow(kwinClient) && (
|
||||
return Clients.canTileNow(kwinClient) && (
|
||||
this.preferTiling.matches(kwinClient) ||
|
||||
kwinClient.normalWindow && kwinClient.managed && !this.preferFloating.matches(kwinClient)
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
|
||||
manager.connect(workspace.clientAdded, (kwinClient: AbstractClient) => {
|
||||
console.assert(!world.hasClient(kwinClient));
|
||||
if (canTileEver(kwinClient)) {
|
||||
if (Clients.canTileEver(kwinClient)) {
|
||||
// never open new tileable clients on all desktops or activities
|
||||
if (kwinClient.desktop <= 0) {
|
||||
kwinClient.desktop = workspace.currentDesktop;
|
||||
|
||||
@@ -119,6 +119,25 @@ class ClientWrapper {
|
||||
this.transients.splice(i, 1);
|
||||
}
|
||||
|
||||
public ensureTransientsVisible(screenSize: QRect) {
|
||||
for (const transient of this.transients) {
|
||||
if (transient.stateManager.getState() instanceof ClientStateFloating) {
|
||||
transient.ensureVisible(screenSize);
|
||||
transient.ensureTransientsVisible(screenSize);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ensureVisible(screenSize: QRect) {
|
||||
const frame = this.kwinClient.frameGeometry;
|
||||
if (frame.left < 0) {
|
||||
frame.x = 0;
|
||||
} else if (frame.right > screenSize.width) {
|
||||
frame.x = screenSize.width - frame.width;
|
||||
}
|
||||
}
|
||||
|
||||
destroy(passFocus: boolean) {
|
||||
this.stateManager.destroy(passFocus);
|
||||
this.signalManager.destroy();
|
||||
|
||||
21
src/world/Clients.ts
Normal file
21
src/world/Clients.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
module Clients {
|
||||
export function canTileEver(kwinClient: AbstractClient) {
|
||||
return kwinClient.resizeable;
|
||||
}
|
||||
|
||||
export function canTileNow(kwinClient: AbstractClient) {
|
||||
return canTileEver(kwinClient) && !kwinClient.minimized && kwinClient.desktop > 0 && kwinClient.activities.length === 1;
|
||||
}
|
||||
|
||||
export function makeTileable(kwinClient: AbstractClient) {
|
||||
if (kwinClient.minimized) {
|
||||
kwinClient.minimized = false;
|
||||
}
|
||||
if (kwinClient.desktop <= 0) {
|
||||
kwinClient.desktop = workspace.currentDesktop;
|
||||
}
|
||||
if (kwinClient.activities.length !== 1) {
|
||||
kwinClient.activities = [workspace.currentActivity];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,12 +38,7 @@ class World {
|
||||
marginRight: config.gapsOuterRight,
|
||||
overscroll: config.overscroll,
|
||||
},
|
||||
{
|
||||
gapsInnerHorizontal: config.gapsInnerHorizontal,
|
||||
gapsInnerVertical: config.gapsInnerVertical,
|
||||
stackColumnsByDefault: config.stackColumnsByDefault,
|
||||
resizeNeighborColumn: config.resizeNeighborColumn,
|
||||
},
|
||||
config,
|
||||
workspace.currentActivity,
|
||||
workspace.desktops,
|
||||
);
|
||||
@@ -118,6 +113,12 @@ class World {
|
||||
return transientFor;
|
||||
}
|
||||
|
||||
public ensureFocusedTransientsVisible() {
|
||||
this.doIfTiledFocused(true, (window, column, grid) => {
|
||||
window.client.ensureTransientsVisible(grid.container.clientArea);
|
||||
});
|
||||
}
|
||||
|
||||
minimizeClient(kwinClient: AbstractClient) {
|
||||
const client = this.clientMap.get(kwinClient);
|
||||
if (client === undefined) {
|
||||
@@ -166,8 +167,8 @@ class World {
|
||||
}
|
||||
|
||||
const clientState = client.stateManager.getState();
|
||||
if (clientState instanceof ClientStateFloating && canTileEver(kwinClient)) {
|
||||
makeTileable(kwinClient);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user