18 Commits
v0.1 ... v0.2

Author SHA1 Message Date
Peter Fajdiga
414bfc8518 bump version to 0.2 2023-06-23 13:25:50 +02:00
Peter Fajdiga
81a82cbfde add comments to key bindings 2023-06-23 13:25:05 +02:00
Peter Fajdiga
9318799a82 add action shrinkVisibleColumns 2023-06-23 12:49:03 +02:00
Peter Fajdiga
2f93e3aa8f rename action expandVisibleColumns 2023-06-23 09:37:08 +02:00
Peter Fajdiga
e1263dd544 make grid-scroll-focused center the focused column 2023-06-17 10:32:24 +02:00
Peter Fajdiga
b614fd4481 keyBindings: extend description for window-expand 2023-06-17 09:57:37 +02:00
Peter Fajdiga
13d6f39bf2 generate key binding documentation 2023-06-17 08:26:58 +02:00
Peter Fajdiga
3b103841c2 keyBindings: remove duplicate binding 2023-06-16 18:06:14 +02:00
Peter Fajdiga
9ec9e8e62d keyBindings: move interface definitions to loader.ts 2023-06-16 14:20:49 +02:00
Peter Fajdiga
208ef7d9fb move generators/config/kcfg.ts 2023-06-16 13:51:55 +02:00
Peter Fajdiga
b7610be193 split keyBindings and numKeyBindings 2023-06-16 13:38:57 +02:00
Peter Fajdiga
ed9d4320ae separate key bindings definition from implementation 2023-06-16 13:16:00 +02:00
Peter Fajdiga
e3a6b1ad91 move keyBindings/loader.ts 2023-06-16 13:13:20 +02:00
Peter Fajdiga
31a56b8f24 rename shortcut -> key binding 2023-06-16 11:58:37 +02:00
Peter Fajdiga
a65a62f396 prepend "karousel" to shortcuts in registerShortcut 2023-06-16 11:54:20 +02:00
Peter Fajdiga
88b976b252 add shortcut and action karousel-column-expand-visible 2023-06-08 10:09:07 +02:00
Peter Fajdiga
4bdc031d7b Grid: make internal methods private 2023-06-08 09:29:46 +02:00
Peter Fajdiga
0cf395d2e1 when un-tiling and re-tiling a window, put it in the same place 2023-06-03 13:52:35 +02:00
14 changed files with 463 additions and 83 deletions

View File

@@ -1,8 +1,10 @@
.PHONY: *
TSC_SCRIPT_FLAGS = --lib es2020 ./src/extern.d.ts
config:
mkdir -p ./package/contents/config
tsc ./src/config/definition.ts ./configgen/kcfg.ts --outFile /dev/stdout | node - > ./package/contents/config/main.xml
tsc ${TSC_SCRIPT_FLAGS} ./src/config/definition.ts ./generators/config/kcfg.ts --outFile /dev/stdout | node - > ./package/contents/config/main.xml
build:
tsc --outFile ./package/contents/code/main.js
@@ -18,3 +20,12 @@ package:
logs:
journalctl -t kwin_x11 -g '^qml:|^file://.*karousel' -f
docs-key-bindings-plain:
@tsc ${TSC_SCRIPT_FLAGS} ./src/keyBindings/definition.ts ./generators/docs/keyBindings.ts ./generators/docs/keyBindingsPlain.ts --outFile /dev/stdout | node -
docs-key-bindings-table:
@tsc ${TSC_SCRIPT_FLAGS} ./src/keyBindings/definition.ts ./generators/docs/keyBindings.ts ./generators/docs/keyBindingsTable.ts --outFile /dev/stdout | node -
docs-key-bindings-fmt:
@tsc ${TSC_SCRIPT_FLAGS} ./src/keyBindings/definition.ts ./generators/docs/keyBindings.ts ./generators/docs/keyBindingsFmt.ts --outFile /dev/stdout | node -

View File

@@ -0,0 +1,67 @@
interface KeyBinding {
name: string;
description: string;
comment?: string;
defaultKeySequence: string;
action: string;
}
interface NumKeyBinding {
name: string;
description: string;
comment?: string;
defaultModifiers: string;
fKeys: boolean;
action: string;
}
function formatComment(comment: string | undefined) {
return comment === undefined ? "" : ` (${comment})`;
}
function printCols(...columns: (string[] | string)[]) {
const nCols = columns.length;
if (nCols == 0) {
return;
}
let nRows = Math.min(...columns.filter(
(column: string[] | string) => column instanceof Array
).map(
(column: string[] | string) => column.length
));
if (nRows == Infinity) {
// we only have single string columns
nRows = 1;
}
const colWidths = columns.map(
(column: string[] | string) => {
if (column instanceof Array) {
return Math.max(...column.map(
(cell: string) => cell.length
))
} else {
return column.length;
}
}
);
function getCell(col: number, row: number) {
const column = columns[col];
const cell = column instanceof Array ? column[row] : column;
if (col < nCols-1) {
return cell.padEnd(colWidths[col]);
} else {
return cell;
}
}
for (let row = 0; row < nRows; row++) {
let line = "";
for (let col = 0; col < nCols; col++) {
line += getCell(col, row);
}
console.log(line);
}
}

View File

@@ -0,0 +1,14 @@
const colLeft = [
...keyBindings.map((binding: KeyBinding) => binding.defaultKeySequence),
...numKeyBindings.map((binding: NumKeyBinding) => {
const numPrefix = binding.fKeys ? "F" : "";
return `${binding.defaultModifiers}+${numPrefix}[N]`;
}),
];
const colRight = [
...keyBindings.map((binding: KeyBinding) => `${binding.description}${formatComment(binding.comment)}`),
...numKeyBindings.map((binding: NumKeyBinding) => `${binding.description}N${formatComment(binding.comment)}`),
];
printCols(colLeft, " ", colRight);

View File

@@ -0,0 +1,8 @@
for (const binding of keyBindings) {
console.log(`${binding.defaultKeySequence} - ${binding.description}${formatComment(binding.comment)}`);
}
for (const binding of numKeyBindings) {
const numPrefix = binding.fKeys ? "F" : "";
console.log(`${binding.defaultModifiers}+${numPrefix}[N] - ${binding.description}N${formatComment(binding.comment)}`);
}

View File

@@ -0,0 +1,18 @@
const colLeft = [
"Shortcut",
"---",
...keyBindings.map((binding: KeyBinding) => binding.defaultKeySequence),
...numKeyBindings.map((binding: NumKeyBinding) => {
const numPrefix = binding.fKeys ? "F" : "";
return `${binding.defaultModifiers}+${numPrefix}[N]`;
}),
];
const colRight = [
"Action",
"---",
...keyBindings.map((binding: KeyBinding) => `${binding.description}${formatComment(binding.comment)}`),
...numKeyBindings.map((binding: NumKeyBinding) => `${binding.description}N${formatComment(binding.comment)}`),
];
printCols("| ", colLeft, " | ", colRight, " |");

View File

@@ -9,7 +9,7 @@
}],
"Id": "karousel",
"ServiceTypes": ["KWin/Script"],
"Version": "0.1",
"Version": "0.2",
"License": "GPLv3",
"Website": "https://github.com/peterfajdiga/karousel",
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"

View File

@@ -175,6 +175,18 @@ function initActions(world: World) {
});
},
expandVisibleColumns: () => {
const grid = world.getCurrentGrid();
grid.rescaleVisibleColumns(true);
grid.arrange();
},
shrinkVisibleColumns: () => {
const grid = world.getCurrentGrid();
grid.rescaleVisibleColumns(false);
grid.arrange();
},
gridScrollLeft: () => {
gridScroll(world, -world.config.manualScrollStep);
},
@@ -210,7 +222,7 @@ function initActions(world: World) {
}
const column = focusedWindow.column;
const grid = column.grid;
grid.scrollToColumn(column);
grid.scrollCenterColumn(column);
grid.arrange();
},
@@ -245,7 +257,11 @@ function initActions(world: World) {
grid.scrollToColumn(nextColumn);
grid.arrange();
},
};
}
function initNumActions(world: World) {
return {
focusColumn: (columnIndex: number) => {
const grid = world.getCurrentGrid();
const targetColumn = grid.getColumnAtIndex(columnIndex);

12
src/extern.d.ts vendored
View File

@@ -1,9 +1,9 @@
const qmlBase;
const console;
const KWin;
const Qt;
const workspace;
const options;
declare const qmlBase;
declare const console;
declare const KWin;
declare const Qt;
declare const workspace;
declare const options;
type AbstractClient = any;
type TopLevel = any;

View File

@@ -0,0 +1,217 @@
const keyBindings: KeyBinding[] = [
{
"name": "window-toggle-floating",
"description": "Toggle floating",
"defaultKeySequence": "Meta+Space",
"action": "windowToggleFloating",
},
{
"name": "focus-left",
"description": "Move focus left",
"defaultKeySequence": "Meta+A",
"action": "focusLeft",
},
{
"name": "focus-right",
"description": "Move focus right",
"defaultKeySequence": "Meta+D",
"action": "focusRight",
},
{
"name": "focus-up",
"description": "Move focus up",
"defaultKeySequence": "Meta+W",
"action": "focusUp",
},
{
"name": "focus-down",
"description": "Move focus down",
"defaultKeySequence": "Meta+S",
"action": "focusDown",
},
{
"name": "focus-start",
"description": "Move focus to start",
"defaultKeySequence": "Meta+Home",
"action": "focusStart",
},
{
"name": "focus-end",
"description": "Move focus to end",
"defaultKeySequence": "Meta+End",
"action": "focusEnd",
},
{
"name": "window-move-left",
"description": "Move window left",
"comment": "Moves window out of and into columns",
"defaultKeySequence": "Meta+Shift+A",
"action": "windowMoveLeft",
},
{
"name": "window-move-right",
"description": "Move window right",
"comment": "Moves window out of and into columns",
"defaultKeySequence": "Meta+Shift+D",
"action": "windowMoveRight",
},
{
"name": "window-move-up",
"description": "Move window up",
"defaultKeySequence": "Meta+Shift+W",
"action": "windowMoveUp",
},
{
"name": "window-move-down",
"description": "Move window down",
"defaultKeySequence": "Meta+Shift+S",
"action": "windowMoveDown",
},
{
"name": "window-move-start",
"description": "Move window to start",
"defaultKeySequence": "Meta+Shift+Home",
"action": "windowMoveStart",
},
{
"name": "window-move-end",
"description": "Move window to end",
"defaultKeySequence": "Meta+Shift+End",
"action": "windowMoveEnd",
},
{
"name": "window-expand",
"description": "Expand window",
"comment": "Expands focused window vertically; toggles stacked layout for focused column",
"defaultKeySequence": "Meta+X",
"action": "windowExpand",
},
{
"name": "column-move-left",
"description": "Move column left",
"defaultKeySequence": "Meta+Ctrl+Shift+A",
"action": "columnMoveLeft",
},
{
"name": "column-move-right",
"description": "Move column right",
"defaultKeySequence": "Meta+Ctrl+Shift+D",
"action": "columnMoveRight",
},
{
"name": "column-move-start",
"description": "Move column to start",
"defaultKeySequence": "Meta+Ctrl+Shift+Home",
"action": "columnMoveStart",
},
{
"name": "column-move-end",
"description": "Move column to end",
"defaultKeySequence": "Meta+Ctrl+Shift+End",
"action": "columnMoveEnd",
},
{
"name": "column-expand",
"description": "Expand column",
"comment": "Expands focused column horizontally to fill the screen",
"defaultKeySequence": "Meta+Ctrl+X",
"action": "columnExpand",
},
{
"name": "expand-visible-columns",
"description": "Expand fully visible columns",
"comment": "Expands fully visible columns to fill the screen",
"defaultKeySequence": "Meta+Alt++",
"action": "expandVisibleColumns",
},
{
"name": "shrink-visible-columns",
"description": "Shrink visible columns",
"comment": "Shrinks fully and partially visible columns, making them fully visible and filling the screen",
"defaultKeySequence": "Meta+Alt+-",
"action": "shrinkVisibleColumns",
},
{
"name": "grid-scroll-focused",
"description": "Center focused window",
"comment": "Scrolls so that the focused window is centered in the screen",
"defaultKeySequence": "Meta+Alt+Return",
"action": "gridScrollFocused",
},
{
"name": "grid-scroll-left-column",
"description": "Scroll one column to the left",
"defaultKeySequence": "Meta+Alt+A",
"action": "gridScrollLeftColumn",
},
{
"name": "grid-scroll-right-column",
"description": "Scroll one column to the right",
"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",
"defaultKeySequence": "Meta+Alt+Home",
"action": "gridScrollStart",
},
{
"name": "grid-scroll-end",
"description": "Scroll to end",
"defaultKeySequence": "Meta+Alt+End",
"action": "gridScrollEnd",
},
];
const numKeyBindings: NumKeyBinding[] = [
{
"name": "focus-",
"description": "Move focus to column ",
"defaultModifiers": "Meta",
"fKeys": false,
"action": "focusColumn",
},
{
"name": "window-move-to-column-",
"description": "Move window to column ",
"comment": "Requires manual remapping according to your keyboard layout, e.g. Meta+Shift+1 -> Meta+!",
"defaultModifiers": "Meta+Shift",
"fKeys": false,
"action": "windowMoveToColumn",
},
{
"name": "column-move-to-column-",
"description": "Move column to position ",
"comment": "Requires manual remapping according to your keyboard layout, e.g. Meta+Ctrl+Shift+1 -> Meta+Ctrl+!",
"defaultModifiers": "Meta+Ctrl+Shift",
"fKeys": false,
"action": "columnMoveToColumn",
},
{
"name": "column-move-to-desktop-",
"description": "Move column to desktop ",
"defaultModifiers": "Meta+Ctrl+Shift",
"fKeys": true,
"action": "columnMoveToDesktop",
},
{
"name": "tail-move-to-desktop-",
"description": "Move this and all following columns to desktop ",
"defaultModifiers": "Meta+Ctrl+Shift+Alt",
"fKeys": true,
"action": "tailMoveToDesktop",
},
];

62
src/keyBindings/loader.ts Normal file
View File

@@ -0,0 +1,62 @@
interface KeyBinding {
name: string;
description: string;
comment?: string;
defaultKeySequence: string;
action: keyof ReturnType<typeof initActions>;
}
interface NumKeyBinding {
name: string;
description: string;
comment?: string;
defaultModifiers: string;
fKeys: boolean;
action: keyof ReturnType<typeof initNumActions>;
}
function catchWrap(f: () => void) {
return () => {
try {
f();
} catch (error: any) {
console.log(error);
console.log(error.stack);
}
};
}
function registerKeyBinding(name: string, description: string, keySequence: string, callback: () => void) {
KWin.registerShortcut(
"karousel-" + name,
"Karousel: " + description,
keySequence,
catchWrap(callback),
);
}
function registerNumKeyBindings(name: string, description: string, modifiers: string, fKeys: boolean, callback: (i: number) => void) {
const numPrefix = fKeys ? "F" : "";
const n = fKeys ? 12 : 9;
for (let i = 0; i < n; i++) {
const numKey = String(i + 1);
registerKeyBinding(
name + numKey,
description + numKey,
modifiers + "+" + numPrefix + numKey,
() => callback(i),
);
}
}
function registerKeyBindings(world: World) {
const actions = initActions(world);
for (const binding of keyBindings) {
registerKeyBinding(binding.name, binding.description, binding.defaultKeySequence, actions[binding.action]);
}
const numActions = initNumActions(world);
for (const binding of numKeyBindings) {
registerNumKeyBindings(binding.name, binding.description, binding.defaultModifiers, binding.fKeys, numActions[binding.action]);
}
}

View File

@@ -114,6 +114,31 @@ class Grid {
return last;
}
rescaleVisibleColumns(fullyVisible: boolean) {
const startColumn = this.getLeftmostVisibleColumn(fullyVisible);
const endColumn = this.getRightmostVisibleColumn(fullyVisible);
if (startColumn === null || endColumn === null) {
return;
}
const startX = startColumn.gridX;
const endX = endColumn.gridX + endColumn.width;
const width = endX - startX;
const scaleRatio = this.tilingArea.width / width;
let remainingWidth = this.tilingArea.width;
for (const column of this.columns.iteratorFrom(startColumn)) {
if (column !== endColumn) {
const newWidth = Math.round(column.width * scaleRatio);
column.setWidth(newWidth, true);
remainingWidth -= newWidth + this.world.config.gapsInnerHorizontal;
} else {
column.setWidth(remainingWidth, true);
break;
}
}
}
scrollToColumn(column: Column) {
const left = column.gridX - this.scrollX; // in screen space
const right = left + column.width; // in screen space
@@ -128,6 +153,12 @@ class Grid {
}
}
scrollCenterColumn(column: Column) {
const windowCenter = column.gridX + column.width / 2 + this.world.config.gapsInnerHorizontal - this.scrollX; // in screen space
const screenCenter = this.tilingArea.x + this.tilingArea.width / 2;
this.adjustScroll(Math.round(windowCenter - screenCenter), false);
}
autoAdjustScroll() {
const focusedWindow = this.world.getFocusedWindow();
if (focusedWindow === null) {
@@ -142,7 +173,7 @@ class Grid {
this.scrollToColumn(column);
}
setScroll(x: number, force: boolean) {
private setScroll(x: number, force: boolean) {
if (!force) {
let minScroll = 0;
let maxScroll = this.width - this.tilingArea.width;
@@ -160,11 +191,11 @@ class Grid {
this.setScroll(this.scrollX + dx, force);
}
removeOverscroll() {
private removeOverscroll() {
this.setScroll(this.scrollX, false);
}
columnsSetX(firstMovedColumn: Column|null) {
private columnsSetX(firstMovedColumn: Column|null) {
const lastUnmovedColumn = firstMovedColumn === null ? this.columns.getLast() : this.columns.getPrev(firstMovedColumn);
let x = lastUnmovedColumn === null ? 0 : lastUnmovedColumn.gridX + lastUnmovedColumn.width + this.world.config.gapsInnerHorizontal;
if (firstMovedColumn !== null) {
@@ -197,18 +228,17 @@ class Grid {
}
onColumnRemoved(column: Column, passFocus: boolean) {
const isLastColumn = this.columns.length() === 1;
const nextColumn = this.getNextColumn(column);
const columnToFocus = isLastColumn ? null : this.getPrevColumn(column) ?? nextColumn;
if (column === this.lastFocusedColumn) {
this.lastFocusedColumn = null;
this.lastFocusedColumn = columnToFocus;
}
const lastColumn = this.columns.length() === 1;
const columnToFocus = lastColumn || !passFocus ? null : this.getPrevColumn(column) ?? this.getNextColumn(column);
const nextColumn = this.columns.getNext(column);
this.columns.remove(column);
this.columnsSetX(nextColumn);
if (columnToFocus !== null) {
if (passFocus && columnToFocus !== null) {
columnToFocus.focus();
} else {
this.removeOverscroll();

View File

@@ -1,6 +1,6 @@
function init() {
const config = loadConfig();
const world = new World(config);
registerShortcuts(world);
registerKeyBindings(world);
return world;
}

View File

@@ -1,63 +0,0 @@
function catchWrap(f: () => void) {
return () => {
try {
f();
} catch (error: any) {
console.log(error);
console.log(error.stack);
}
};
}
function registerShortcutDbg(title: string, text: string, keySequence: string, callback: () => void) {
KWin.registerShortcut(title, text, keySequence, catchWrap(callback));
}
function registerNumShortcuts(title: string, text: string, keySequence: string, callback: (i: number) => void, n: number) {
for (let i = 0; i < n; i++) {
const numKey = String(i + 1);
registerShortcutDbg(title+numKey, text+numKey, keySequence+numKey, () => callback(i));
}
}
function registerShortcuts(world: World) {
const actions = initActions(world);
registerShortcutDbg("karousel-window-toggle-floating", "Karousel: Toggle floating", "Meta+Space", actions.windowToggleFloating);
registerShortcutDbg("karousel-focus-left", "Karousel: Move focus left", "Meta+A", actions.focusLeft);
registerShortcutDbg("karousel-focus-right", "Karousel: Move focus right", "Meta+D", actions.focusRight);
registerShortcutDbg("karousel-focus-up", "Karousel: Move focus up", "Meta+W", actions.focusUp);
registerShortcutDbg("karousel-focus-down", "Karousel: Move focus down", "Meta+S", actions.focusDown);
registerShortcutDbg("karousel-focus-start", "Karousel: Move focus to start", "Meta+Home", actions.focusStart);
registerShortcutDbg("karousel-focus-end", "Karousel: Move focus to end", "Meta+End", actions.focusEnd);
registerShortcutDbg("karousel-window-move-left", "Karousel: Move window left", "Meta+Shift+A", actions.windowMoveLeft);
registerShortcutDbg("karousel-window-move-right", "Karousel: Move window right", "Meta+Shift+D", actions.windowMoveRight);
registerShortcutDbg("karousel-window-move-up", "Karousel: Move window up", "Meta+Shift+W", actions.windowMoveUp);
registerShortcutDbg("karousel-window-move-down", "Karousel: Move window down", "Meta+Shift+S", actions.windowMoveDown);
registerShortcutDbg("karousel-window-move-start", "Karousel: Move window to start", "Meta+Shift+Home", actions.windowMoveStart);
registerShortcutDbg("karousel-window-move-end", "Karousel: Move window to end", "Meta+Shift+End", actions.windowMoveEnd);
registerShortcutDbg("karousel-window-expand", "Karousel: Expand window", "Meta+X", actions.windowExpand);
registerShortcutDbg("karousel-column-move-left", "Karousel: Move column left", "Meta+Ctrl+Shift+A", actions.columnMoveLeft);
registerShortcutDbg("karousel-column-move-right", "Karousel: Move column right", "Meta+Ctrl+Shift+D", actions.columnMoveRight);
registerShortcutDbg("karousel-column-move-start", "Karousel: Move column to start", "Meta+Ctrl+Shift+Home", actions.columnMoveStart);
registerShortcutDbg("karousel-column-move-end", "Karousel: Move column to end", "Meta+Ctrl+Shift+End", actions.columnMoveEnd);
registerShortcutDbg("karousel-column-expand", "Karousel: Expand column", "Meta+Ctrl+X", actions.columnExpand);
registerShortcutDbg("karousel-grid-scroll-focused", "Karousel: Scroll to focused window", "Meta+Alt+Return", actions.gridScrollFocused);
registerShortcutDbg("karousel-grid-scroll-left-column", "Karousel: Scroll one column to the left", "Meta+Alt+A", actions.gridScrollLeftColumn);
registerShortcutDbg("karousel-grid-scroll-left-column", "Karousel: Scroll one column to the left", "Meta+Alt+A", actions.gridScrollLeftColumn);
registerShortcutDbg("karousel-grid-scroll-right-column", "Karousel: Scroll one column to the right", "Meta+Alt+D", actions.gridScrollRightColumn);
registerShortcutDbg("karousel-grid-scroll-left", "Karousel: Scroll left", "Meta+Alt+PgUp", actions.gridScrollLeft);
registerShortcutDbg("karousel-grid-scroll-right", "Karousel: Scroll right", "Meta+Alt+PgDown", actions.gridScrollRight);
registerShortcutDbg("karousel-grid-scroll-start", "Karousel: Scroll to start", "Meta+Alt+Home", actions.gridScrollStart);
registerShortcutDbg("karousel-grid-scroll-end", "Karousel: Scroll to end", "Meta+Alt+End", actions.gridScrollEnd);
registerNumShortcuts("karousel-focus-", "Karousel: Move focus to column ", "Meta+", actions.focusColumn, 9);
registerNumShortcuts("karousel-window-move-to-column-", "Karousel: Move window to column ", "Meta+Shift+", actions.windowMoveToColumn, 9);
registerNumShortcuts("karousel-column-move-to-column-", "Karousel: Move column to position ", "Meta+Ctrl+Shift+", actions.columnMoveToColumn, 9);
registerNumShortcuts("karousel-column-move-to-desktop-", "Karousel: Move column to desktop ", "Meta+Ctrl+Shift+F", actions.columnMoveToDesktop, 12);
registerNumShortcuts("karousel-tail-move-to-desktop-", "Karousel: Move this and all following columns to desktop ", "Meta+Ctrl+Shift+Alt+F", actions.tailMoveToDesktop, 12);
}