Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad6c3f1cae | ||
|
|
ba4dd2a9c1 | ||
|
|
bb61853009 | ||
|
|
0cfd9b9e36 | ||
|
|
43c4f7ef9a | ||
|
|
9cb3f33ecb | ||
|
|
31b9e61ae3 | ||
|
|
668e6696ab | ||
|
|
e63959cfbf | ||
|
|
ef2650beb8 | ||
|
|
750c47c040 | ||
|
|
88ca0d02e1 | ||
|
|
aba786b754 | ||
|
|
47aa625c99 | ||
|
|
03c7cc6503 | ||
|
|
9e9ff2b74f | ||
|
|
5674624e6f | ||
|
|
44dd88ef7c | ||
|
|
f800d6ecf0 | ||
|
|
3477e17bb3 | ||
|
|
755c781646 | ||
|
|
926345ba31 | ||
|
|
a2295ede43 | ||
|
|
ca80a7ca28 | ||
|
|
64474b1677 | ||
|
|
eca63cbc16 | ||
|
|
3a8baf4cd7 | ||
|
|
dc14171ae7 | ||
|
|
6dcf8979c2 | ||
|
|
fe5661c07f | ||
|
|
90b783b34b | ||
|
|
fb40bd9592 | ||
|
|
768d95450d | ||
|
|
e95a0e44c9 | ||
|
|
c1b8d05919 | ||
|
|
e98ce18105 | ||
|
|
25a9efc8e4 | ||
|
|
db48644944 | ||
|
|
950e0de076 | ||
|
|
05ffe0895e | ||
|
|
61db5ca69f | ||
|
|
f7b5dd0b9c | ||
|
|
f83f60c98f | ||
|
|
3e8734eefb | ||
|
|
bed0ea7ed8 | ||
|
|
92c99f0b87 | ||
|
|
b2024bc8aa | ||
|
|
58f358313b | ||
|
|
352a7061f6 | ||
|
|
7314c0ee24 | ||
|
|
c65361853c | ||
|
|
fa53e765b3 | ||
|
|
4d35681ee2 | ||
|
|
1824bcdf85 | ||
|
|
ce1b402bf2 | ||
|
|
2df6d5d8e6 | ||
|
|
e7d33030ba | ||
|
|
2bd000f0a6 | ||
|
|
6313d8f18e | ||
|
|
464ec3bcb1 | ||
|
|
fae793cb09 | ||
|
|
d7346a6fab | ||
|
|
0e5efd2be7 | ||
|
|
a1a315790e | ||
|
|
9d62499bf0 | ||
|
|
97cf61d1dd | ||
|
|
5d83c6dd2c | ||
|
|
8915e8a9da | ||
|
|
22e4c47189 | ||
|
|
552d2b851f | ||
|
|
c4ce795359 | ||
|
|
1ac1fc3c2b | ||
|
|
d0e041d16a | ||
|
|
8fc3fc976d | ||
|
|
e0eeace9dc | ||
|
|
84e2a06b35 | ||
|
|
3373e02658 |
12
README.md
12
README.md
@@ -13,9 +13,16 @@ unprompted reflow of window content.
|
||||
Windows are automatically centered when possible. And when running out of width, windows can be
|
||||
scrolled through horizontally.
|
||||
|
||||
Similar window managers include [PaperWM](https://github.com/paperwm/PaperWM) and
|
||||
Similar window managers include [PaperWM](https://github.com/paperwm/PaperWM),
|
||||
[Niri](https://github.com/YaLTeR/niri), and
|
||||
[Cardboard](https://gitlab.com/cardboardwm/cardboard).
|
||||
|
||||
## Dependencies
|
||||
Karousel requires the following QML modules:
|
||||
- QtQuick 2.15
|
||||
- org.kde.kwin 3.0
|
||||
- org.kde.notification 1.0
|
||||
|
||||
## Limitations
|
||||
- Doesn't support multiple screens
|
||||
- Doesn't support windows on all desktops
|
||||
@@ -39,13 +46,14 @@ Here's the default ones:
|
||||
| Meta+Shift+S | Move window down |
|
||||
| Meta+Shift+Home | Move window to start |
|
||||
| Meta+Shift+End | Move window to end |
|
||||
| Meta+X | Toggle stacked layout for focused column |
|
||||
| Meta+X | Toggle stacked layout for focused column (One window in the column visible, others shaded; not supported on Wayland) |
|
||||
| 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++ | Increase column width |
|
||||
| Meta+Ctrl+- | Decrease column width |
|
||||
| Meta+Ctrl+X | Equalize widths of visible columns |
|
||||
| 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 |
|
||||
|
||||
@@ -5,9 +5,20 @@ console.log(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
for (const entry of configDef) {
|
||||
console.log(` <entry name="${entry.name}" type="${entry.type}">
|
||||
<default>${entry.default}</default>
|
||||
<default>${escapeXml(entry.default)}</default>
|
||||
</entry>`);
|
||||
}
|
||||
|
||||
console.log(` </group>
|
||||
</kcfg>`);
|
||||
|
||||
function escapeXml(input: any) {
|
||||
if (typeof input === "string") {
|
||||
return input
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,141 @@
|
||||
<layout class="QVBoxLayout" name="layout_main">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabContainer">
|
||||
<widget class="QWidget" name="tab_general">
|
||||
<widget class="QWidget" name="tab_behavior">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
<string>Behavior</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="layout_tab_general">
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="kcfg_untileOnDrag">
|
||||
<property name="text">
|
||||
<string>Un-tile windows by dragging them</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="kcfg_stackColumnsByDefault">
|
||||
<property name="text">
|
||||
<string>Stack columns by default</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>New columns start in stacked mode (one window in the column visible, others shaded). Not supported on Wayland.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="kcfg_resizeNeighborColumn">
|
||||
<property name="text">
|
||||
<string>Resize neighbor column on edge resize</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When resizing a column by dragging its edge, also inversely resize the column on the other side of the edge</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="kcfg_reMaximize">
|
||||
<property name="text">
|
||||
<string>Re-maximize tiled windows</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Restore maximized and full-screen states of tiled windows on focus</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="kcfg_skipSwitcher">
|
||||
<property name="text">
|
||||
<string>Tiled windows skip switcher</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Scrolling mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="kcfg_scrollingLazy">
|
||||
<property name="text">
|
||||
<string>Only scroll as necessary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="kcfg_scrollingCentered">
|
||||
<property name="text">
|
||||
<string>Center focused column</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="kcfg_scrollingGrouped">
|
||||
<property name="text">
|
||||
<string>Center visible columns</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QGroupBox">
|
||||
<property name="title">
|
||||
<string>Layering mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="kcfg_tiledKeepBelow">
|
||||
<property name="text">
|
||||
<string>Keep tiled windows below</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="kcfg_floatingKeepAbove">
|
||||
<property name="text">
|
||||
<string>Keep floating windows above</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<spacer name="spacer_footer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
<widget class="QWidget" name="tab_parameters">
|
||||
<attribute name="title">
|
||||
<string>Parameters</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_gapsOuterTop">
|
||||
<property name="text">
|
||||
@@ -144,34 +273,13 @@
|
||||
</item>
|
||||
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_overscroll">
|
||||
<property name="text">
|
||||
<string>Overscroll amount:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QSpinBox" name="kcfg_overscroll">
|
||||
<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="7" column="0">
|
||||
<widget class="QLabel" name="label_manualScrollStep">
|
||||
<property name="text">
|
||||
<string>Manual scroll step size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QSpinBox" name="kcfg_manualScrollStep">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
@@ -185,112 +293,55 @@
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_manualResizeStep">
|
||||
<property name="text">
|
||||
<string>Manual resize step size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QSpinBox" name="kcfg_manualResizeStep">
|
||||
<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="0">
|
||||
<widget class="QLabel" name="label_offScreenOpacity">
|
||||
<property name="text">
|
||||
<string>Obscured window opacity:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<spacer name="separator_behavior">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<widget class="QSpinBox" name="kcfg_offScreenOpacity">
|
||||
<property name="suffix">
|
||||
<string> %</string>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_behavior">
|
||||
<property name="text">
|
||||
<string>Behavior:</string>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_untileOnDrag">
|
||||
<property name="text">
|
||||
<string>Un-tile windows by dragging them</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_stackColumnsByDefault">
|
||||
<property name="text">
|
||||
<string>Stack columns by default</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>New columns start in stacked mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_resizeNeighborColumn">
|
||||
<property name="text">
|
||||
<string>Resize neighbor column on edge resize</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When resizing a column by dragging its edge, also inversely resize the column on the other side of the edge</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="kcfg_reMaximize">
|
||||
<property name="text">
|
||||
<string>Re-maximize tiled windows</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Restore maximized and full-screen states of tiled windows on focus</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="13" column="1">
|
||||
<spacer name="separator_layering">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_layering">
|
||||
<property name="text">
|
||||
<string>Layering mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QRadioButton" name="kcfg_tiledKeepBelow">
|
||||
<property name="text">
|
||||
<string>Keep tiled windows below</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
<widget class="QRadioButton" name="kcfg_floatingKeepAbove">
|
||||
<property name="text">
|
||||
<string>Keep floating windows above</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
<widget class="QWidget" name="tab_windowRules">
|
||||
<attribute name="title">
|
||||
<string>Window Rules</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="layout_tab_windowRules">
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="kcfg_windowRules">
|
||||
<property name="tabChangesFocus">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
}],
|
||||
"Id": "karousel",
|
||||
"ServiceTypes": ["KWin/Script"],
|
||||
"Version": "0.5",
|
||||
"Version": "0.7",
|
||||
"License": "GPLv3",
|
||||
"Website": "https://github.com/peterfajdiga/karousel",
|
||||
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"
|
||||
|
||||
146
src/Actions.ts
146
src/Actions.ts
@@ -2,7 +2,7 @@ namespace Actions {
|
||||
export function init(world: World, config: Config) {
|
||||
return {
|
||||
focusLeft: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
if (prevColumn === null) {
|
||||
return;
|
||||
@@ -12,7 +12,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
focusRight: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
if (nextColumn === null) {
|
||||
return;
|
||||
@@ -22,7 +22,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
focusUp: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const prevWindow = column.getPrevWindow(window);
|
||||
if (prevWindow === null) {
|
||||
return;
|
||||
@@ -32,7 +32,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
focusDown: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const nextWindow = column.getNextWindow(window);
|
||||
if (nextWindow === null) {
|
||||
return;
|
||||
@@ -64,7 +64,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
windowMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const prevColumn = grid.getPrevColumn(column);
|
||||
@@ -82,7 +82,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
windowMoveRight: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
if (column.getWindowCount() === 1) {
|
||||
// move from own column into existing column
|
||||
const nextColumn = grid.getNextColumn(column);
|
||||
@@ -101,27 +101,27 @@ namespace Actions {
|
||||
|
||||
windowMoveUp: () => {
|
||||
// TODO (optimization): only arrange moved windows
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveWindowUp(window);
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveDown: () => {
|
||||
// TODO (optimization): only arrange moved windows
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveWindowDown(window);
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const newColumn = new Column(grid, null);
|
||||
window.moveToColumn(newColumn);
|
||||
});
|
||||
},
|
||||
|
||||
windowMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const newColumn = new Column(grid, grid.getLastColumn());
|
||||
window.moveToColumn(newColumn);
|
||||
});
|
||||
@@ -135,44 +135,126 @@ namespace Actions {
|
||||
},
|
||||
|
||||
columnMoveLeft: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.moveColumnLeft(column);
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveRight: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.moveColumnRight(column);
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveStart: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveAfter(null);
|
||||
});
|
||||
},
|
||||
|
||||
columnMoveEnd: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.moveAfter(grid.getLastColumn());
|
||||
});
|
||||
},
|
||||
|
||||
columnToggleStacked: () => {
|
||||
world.doIfTiledFocused(false, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
|
||||
column.toggleStacked();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthIncrease: () => {
|
||||
world.doIfTiledFocused(false, (world, desktopManager, window, column, grid) => {
|
||||
grid.increaseColumnWidth(column);
|
||||
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
|
||||
const desktop = grid.desktop;
|
||||
const visibleRange = desktop.getCurrentVisibleRange();
|
||||
if(!column.isVisible(visibleRange, true) || column.getWidth() >= column.getMaxWidth()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let leftVisibleColumn = grid.getLeftmostVisibleColumn(visibleRange, true);
|
||||
let rightVisibleColumn = grid.getRightmostVisibleColumn(visibleRange, true);
|
||||
if (leftVisibleColumn === null || rightVisibleColumn === null) {
|
||||
console.assert(false); // should at least see self
|
||||
return;
|
||||
}
|
||||
|
||||
const leftSpace = leftVisibleColumn.getLeft() - visibleRange.getLeft();
|
||||
const rightSpace = visibleRange.getRight() - rightVisibleColumn.getRight();
|
||||
|
||||
const newWidth = findNextStep(
|
||||
[
|
||||
visibleRange.getWidth(),
|
||||
column.getWidth() + config.manualResizeStep,
|
||||
column.getWidth() + leftSpace + rightSpace,
|
||||
column.getWidth() + leftSpace + rightSpace + leftVisibleColumn.getWidth() + grid.config.gapsInnerHorizontal,
|
||||
column.getWidth() + leftSpace + rightSpace + rightVisibleColumn.getWidth() + grid.config.gapsInnerHorizontal,
|
||||
],
|
||||
width => width - column.getWidth(),
|
||||
)
|
||||
if (newWidth === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
column.setWidth(newWidth, true);
|
||||
desktop.scrollCenterVisible(column, false);
|
||||
desktop.onLayoutChanged();
|
||||
desktop.autoAdjustScroll();
|
||||
});
|
||||
},
|
||||
|
||||
columnWidthDecrease: () => {
|
||||
world.doIfTiledFocused(false, (world, desktopManager, window, column, grid) => {
|
||||
grid.decreaseColumnWidth(column);
|
||||
world.doIfTiledFocused(false, (clientManager, desktopManager, window, column, grid) => {
|
||||
const desktop = grid.desktop;
|
||||
const visibleRange = desktop.getCurrentVisibleRange();
|
||||
if(!column.isVisible(visibleRange, true) || column.getWidth() <= column.getMinWidth()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const leftVisibleColumn = grid.getLeftmostVisibleColumn(visibleRange, true);
|
||||
const rightVisibleColumn = grid.getRightmostVisibleColumn(visibleRange, true);
|
||||
if (leftVisibleColumn === null || rightVisibleColumn === null) {
|
||||
console.assert(false); // should at least see self
|
||||
return;
|
||||
}
|
||||
|
||||
let leftOffScreenColumn = grid.getPrevColumn(leftVisibleColumn);
|
||||
if (leftOffScreenColumn === column) {
|
||||
leftOffScreenColumn = null;
|
||||
}
|
||||
let rightOffScreenColumn = grid.getNextColumn(rightVisibleColumn);
|
||||
if (rightOffScreenColumn === column) {
|
||||
rightOffScreenColumn = null;
|
||||
}
|
||||
|
||||
const visibleColumnsWidth = rightVisibleColumn.getRight() - leftVisibleColumn.getLeft();
|
||||
const unusedWidth = visibleRange.getWidth() - visibleColumnsWidth;
|
||||
const leftOffScreen = leftOffScreenColumn === null ? 0 : leftOffScreenColumn.getWidth() + grid.config.gapsInnerHorizontal - unusedWidth;
|
||||
const rightOffScreen = rightOffScreenColumn === null ? 0 : rightOffScreenColumn.getWidth() + grid.config.gapsInnerHorizontal - unusedWidth;
|
||||
|
||||
const newWidth = findNextStep(
|
||||
[
|
||||
visibleRange.getWidth(),
|
||||
column.getWidth() - config.manualResizeStep,
|
||||
column.getWidth() - leftOffScreen,
|
||||
column.getWidth() - rightOffScreen,
|
||||
],
|
||||
width => column.getWidth() - width,
|
||||
)
|
||||
if (newWidth === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
column.setWidth(newWidth, true);
|
||||
desktop.scrollCenterVisible(column, true);
|
||||
desktop.onLayoutChanged();
|
||||
desktop.autoAdjustScroll();
|
||||
});
|
||||
},
|
||||
|
||||
columnsWidthEqualize: () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
desktopManager.getCurrentDesktop().equalizeVisibleColumnsWidths();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -207,15 +289,15 @@ namespace Actions {
|
||||
},
|
||||
|
||||
gridScrollFocused: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
grid.desktop.scrollCenterColumn(column);
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.desktop.scrollCenterRange(column);
|
||||
})
|
||||
},
|
||||
|
||||
gridScrollLeftColumn: () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const column = grid.getLeftmostVisibleColumn(grid.desktop.getCurrentScrollPos(), true);
|
||||
const column = grid.getLeftmostVisibleColumn(grid.desktop.getCurrentVisibleRange(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
@@ -232,7 +314,7 @@ namespace Actions {
|
||||
gridScrollRightColumn: () => {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
const grid = desktopManager.getCurrentDesktop().grid;
|
||||
const column = grid.getRightmostVisibleColumn(grid.desktop.getCurrentScrollPos(), true);
|
||||
const column = grid.getRightmostVisibleColumn(grid.desktop.getCurrentVisibleRange(), true);
|
||||
if (column === null) {
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +344,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
windowMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null) {
|
||||
return null;
|
||||
@@ -273,7 +355,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
columnMoveToColumn: (columnIndex: number) => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
const targetColumn = grid.getColumnAtIndex(columnIndex);
|
||||
if (targetColumn === null || targetColumn === column) {
|
||||
return null;
|
||||
@@ -317,7 +399,21 @@ namespace Actions {
|
||||
});
|
||||
}
|
||||
|
||||
function findNextStep(steps: number[], evaluate: (step: number) => number) {
|
||||
let bestScore = Infinity;
|
||||
let bestStep = undefined;
|
||||
for (const step of steps) {
|
||||
const score = evaluate(step);
|
||||
if (score > 0 && score < bestScore) {
|
||||
bestScore = score;
|
||||
bestStep = step;
|
||||
}
|
||||
}
|
||||
return bestStep;
|
||||
}
|
||||
|
||||
export type Config = {
|
||||
manualScrollStep: number,
|
||||
manualResizeStep: number,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,12 +5,17 @@ type Config = {
|
||||
gapsOuterRight: number,
|
||||
gapsInnerHorizontal: number,
|
||||
gapsInnerVertical: number,
|
||||
overscroll: number,
|
||||
manualScrollStep: number,
|
||||
manualResizeStep: number,
|
||||
offScreenOpacity: number,
|
||||
untileOnDrag: boolean,
|
||||
stackColumnsByDefault: boolean,
|
||||
resizeNeighborColumn: boolean,
|
||||
reMaximize: boolean,
|
||||
skipSwitcher: boolean,
|
||||
scrollingLazy: boolean,
|
||||
scrollingCentered: boolean,
|
||||
scrollingGrouped: boolean,
|
||||
tiledKeepBelow: boolean,
|
||||
floatingKeepAbove: boolean,
|
||||
windowRules: string,
|
||||
|
||||
@@ -7,26 +7,45 @@ const defaultWindowRules = `[
|
||||
"class": "kcalc",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "org.kde.kcalc",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "kfind",
|
||||
"tile": true
|
||||
},
|
||||
{
|
||||
"class": "org.kde.kfind",
|
||||
"tile": true
|
||||
},
|
||||
{
|
||||
"class": "kruler",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "org.kde.kruler",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "krunner",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "zoom",
|
||||
"caption": "Zoom Cloud Meetings",
|
||||
"class": "org.kde.krunner",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "yakuake",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "org.kde.yakuake",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
"class": "zoom",
|
||||
"caption": "zoom",
|
||||
"caption": "Zoom Cloud Meetings|zoom|zoom <2>",
|
||||
"tile": false
|
||||
},
|
||||
{
|
||||
@@ -82,16 +101,21 @@ const configDef = [
|
||||
"type": "UInt",
|
||||
"default": 18
|
||||
},
|
||||
{
|
||||
"name": "overscroll",
|
||||
"type": "UInt",
|
||||
"default": 0
|
||||
},
|
||||
{
|
||||
"name": "manualScrollStep",
|
||||
"type": "UInt",
|
||||
"default": 200
|
||||
},
|
||||
{
|
||||
"name": "manualResizeStep",
|
||||
"type": "UInt",
|
||||
"default": 600
|
||||
},
|
||||
{
|
||||
"name": "offScreenOpacity",
|
||||
"type": "UInt",
|
||||
"default": 100
|
||||
},
|
||||
{
|
||||
"name": "untileOnDrag",
|
||||
"type": "Bool",
|
||||
@@ -112,6 +136,26 @@ const configDef = [
|
||||
"type": "Bool",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "skipSwitcher",
|
||||
"type": "Bool",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "scrollingLazy",
|
||||
"type": "Bool",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"name": "scrollingCentered",
|
||||
"type": "Bool",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "scrollingGrouped",
|
||||
"type": "Bool",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "tiledKeepBelow",
|
||||
"type": "Bool",
|
||||
|
||||
8
src/extern/kwin.d.ts
vendored
8
src/extern/kwin.d.ts
vendored
@@ -36,7 +36,7 @@ interface KwinClient {
|
||||
// Read-only Properties
|
||||
readonly shadeable: boolean;
|
||||
readonly caption: string;
|
||||
readonly minSize: QSize;
|
||||
readonly minSize: QmlSize;
|
||||
readonly transient: boolean;
|
||||
readonly transientFor: KwinClient;
|
||||
readonly move: boolean;
|
||||
@@ -47,15 +47,17 @@ interface KwinClient {
|
||||
readonly dock: boolean;
|
||||
readonly normalWindow: boolean;
|
||||
readonly managed: boolean;
|
||||
opacity: number;
|
||||
|
||||
// Read-write Properties
|
||||
fullScreen: boolean;
|
||||
activities: string[]; // empty array means all activities
|
||||
skipSwitcher: boolean;
|
||||
keepAbove: boolean;
|
||||
keepBelow: boolean;
|
||||
shade: boolean;
|
||||
minimized: boolean;
|
||||
frameGeometry: QRect;
|
||||
frameGeometry: QmlRect;
|
||||
desktop: number; // -1 means all desktops
|
||||
tile: Tile;
|
||||
|
||||
@@ -68,7 +70,7 @@ interface KwinClient {
|
||||
moveResizedChanged: QSignal<[void]>;
|
||||
moveResizeCursorChanged: QSignal<[void]>;
|
||||
clientStartUserMovedResized: QSignal<[void]>;
|
||||
frameGeometryChanged: QSignal<[KwinClient, oldGeometry: QRect]>;
|
||||
frameGeometryChanged: QSignal<[KwinClient, oldGeometry: QmlRect]>;
|
||||
|
||||
// Functions
|
||||
setMaximize(vertically: boolean, horizontally: boolean): void;
|
||||
|
||||
12
src/extern/qt.d.ts
vendored
12
src/extern/qt.d.ts
vendored
@@ -4,7 +4,7 @@ declare const console: {
|
||||
};
|
||||
|
||||
declare const Qt: {
|
||||
rect(x: number, y: number, width: number, height: number): QRect;
|
||||
rect(x: number, y: number, width: number, height: number): QmlRect;
|
||||
createQmlObject(qml: string, parent: QmlObject);
|
||||
};
|
||||
|
||||
@@ -12,18 +12,18 @@ type QmlObject = unknown;
|
||||
|
||||
type QByteArray = string;
|
||||
|
||||
type QRect = {
|
||||
type QmlRect = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
bottom: number; // top + height
|
||||
left: number;
|
||||
right: number;
|
||||
right: number; // left + width
|
||||
};
|
||||
|
||||
type QSize = {
|
||||
type QmlSize = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
@@ -33,7 +33,7 @@ type QSignal<T extends unknown[]> = {
|
||||
disconnect(handler: (...args: [...T]) => void): void;
|
||||
};
|
||||
|
||||
type QQmlTimer = {
|
||||
type QmlTimer = {
|
||||
interval: number;
|
||||
triggered: QSignal<[void]>;
|
||||
restart(): void;
|
||||
|
||||
@@ -85,6 +85,7 @@ const keyBindings: KeyBinding[] = [
|
||||
{
|
||||
"name": "column-toggle-stacked",
|
||||
"description": "Toggle stacked layout for focused column",
|
||||
"comment": "One window in the column visible, others shaded; not supported on Wayland",
|
||||
"defaultKeySequence": "Meta+X",
|
||||
"action": "columnToggleStacked",
|
||||
},
|
||||
@@ -124,6 +125,12 @@ const keyBindings: KeyBinding[] = [
|
||||
"defaultKeySequence": "Meta+Ctrl+-",
|
||||
"action": "columnWidthDecrease",
|
||||
},
|
||||
{
|
||||
"name": "columns-width-equalize",
|
||||
"description": "Equalize widths of visible columns",
|
||||
"defaultKeySequence": "Meta+Ctrl+X",
|
||||
"action": "columnsWidthEqualize",
|
||||
},
|
||||
{
|
||||
"name": "grid-scroll-focused",
|
||||
"description": "Center focused window",
|
||||
|
||||
@@ -180,7 +180,14 @@ class Column {
|
||||
window.focus();
|
||||
}
|
||||
|
||||
public arrange(x: number) {
|
||||
public arrange(x: number, visibleRange: Range, forceOpaque: boolean) {
|
||||
if (this.grid.config.offScreenOpacity < 1.0 && !forceOpaque) {
|
||||
const opacity = this.isVisible(visibleRange, true) ? 100 : this.grid.config.offScreenOpacity;
|
||||
for (const window of this.windows.iterator()) {
|
||||
window.client.kwinClient.opacity = opacity;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stacked && this.windows.length() >= 2 && this.canStack()) {
|
||||
this.arrangeStacked(x);
|
||||
return;
|
||||
@@ -237,13 +244,13 @@ class Column {
|
||||
return true;
|
||||
}
|
||||
|
||||
public isVisible(scrollPos: Desktop.ScrollPos, fullyVisible: boolean) {
|
||||
public isVisible(visibleRange: Desktop.Range, fullyVisible: boolean) {
|
||||
if (fullyVisible) {
|
||||
return this.getLeft() >= scrollPos.getLeft() &&
|
||||
this.getRight() <= scrollPos.getRight();
|
||||
return this.getLeft() >= visibleRange.getLeft() &&
|
||||
this.getRight() <= visibleRange.getRight();
|
||||
} else {
|
||||
return this.getRight() + this.grid.config.gapsInnerHorizontal > scrollPos.getLeft() &&
|
||||
this.getLeft() - this.grid.config.gapsInnerHorizontal < scrollPos.getRight();
|
||||
return this.getRight() + this.grid.config.gapsInnerHorizontal > visibleRange.getLeft() &&
|
||||
this.getLeft() - this.grid.config.gapsInnerHorizontal < visibleRange.getRight();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,17 @@ class Desktop {
|
||||
private readonly config: Desktop.Config;
|
||||
private scrollX: number;
|
||||
private dirty: boolean;
|
||||
private dirtyScroll: boolean;
|
||||
private dirtyPins: boolean;
|
||||
public clientArea: QRect;
|
||||
public tilingArea: QRect;
|
||||
public clientArea: QmlRect;
|
||||
public tilingArea: QmlRect;
|
||||
|
||||
constructor(desktopNumber: number, pinManager: PinManager, config: Desktop.Config, layoutConfig: LayoutConfig) {
|
||||
this.pinManager = pinManager;
|
||||
this.config = config;
|
||||
this.scrollX = 0;
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
this.dirtyPins = true;
|
||||
this.desktopNumber = desktopNumber;
|
||||
this.grid = new Grid(this, layoutConfig);
|
||||
@@ -29,6 +31,7 @@ class Desktop {
|
||||
this.clientArea = newClientArea;
|
||||
this.tilingArea = Desktop.getTilingArea(newClientArea, this.desktopNumber, this.pinManager, this.config);
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
this.dirtyPins = false;
|
||||
this.grid.onScreenSizeChanged();
|
||||
this.autoAdjustScroll();
|
||||
@@ -38,7 +41,7 @@ class Desktop {
|
||||
return workspace.clientArea(ClientAreaOption.PlacementArea, 0, desktopNumber);
|
||||
}
|
||||
|
||||
private static getTilingArea(clientArea: QRect, desktopNumber: number, pinManager: PinManager, config: Desktop.Config) {
|
||||
private static getTilingArea(clientArea: QmlRect, desktopNumber: number, pinManager: PinManager, config: Desktop.Config) {
|
||||
const availableSpace = pinManager.getAvailableSpace(desktopNumber, clientArea);
|
||||
const top = availableSpace.top + config.marginTop;
|
||||
const bottom = availableSpace.bottom - config.marginBottom;
|
||||
@@ -47,102 +50,116 @@ class Desktop {
|
||||
return Qt.rect(
|
||||
left,
|
||||
top,
|
||||
right - left + 1,
|
||||
bottom - top + 1,
|
||||
right - left,
|
||||
bottom - top,
|
||||
)
|
||||
}
|
||||
|
||||
// calculates Desktop.Pos that scrolls the column into view
|
||||
public getScrollPosForColumn(column: Column) {
|
||||
const left = column.getLeft();
|
||||
const right = column.getRight();
|
||||
const initialScrollPos = this.getCurrentScrollPos();
|
||||
public scrollIntoView(range: Desktop.Range) {
|
||||
const left = range.getLeft();
|
||||
const right = range.getRight();
|
||||
const initialVisibleRange = this.getCurrentVisibleRange();
|
||||
|
||||
let targetScrollX: number;
|
||||
if (left < initialScrollPos.getLeft()) {
|
||||
if (left < initialVisibleRange.getLeft()) {
|
||||
targetScrollX = left;
|
||||
} else if (right > initialScrollPos.getRight()) {
|
||||
} else if (right > initialVisibleRange.getRight()) {
|
||||
targetScrollX = right - this.tilingArea.width;
|
||||
} else {
|
||||
return this.getScrollPos(this.clampScrollX(this.scrollX));
|
||||
targetScrollX = initialVisibleRange.getLeft();
|
||||
}
|
||||
|
||||
const overscroll = this.getTargetOverscroll(targetScrollX, left < initialScrollPos.getLeft());
|
||||
return this.getScrollPos(this.clampScrollX(targetScrollX + overscroll));
|
||||
this.setScroll(targetScrollX, false);
|
||||
}
|
||||
|
||||
private getTargetOverscroll(targetScrollX: number, scrollLeft: boolean) {
|
||||
if (this.config.overscroll === 0) {
|
||||
return 0;
|
||||
}
|
||||
const visibleColumnsWidth = this.grid.getVisibleColumnsWidth(this.getScrollPos(targetScrollX), true);
|
||||
const remainingSpace = this.tilingArea.width - visibleColumnsWidth;
|
||||
const overscrollX = Math.min(this.config.overscroll, Math.round(remainingSpace / 2));
|
||||
const direction = scrollLeft ? -1 : 1;
|
||||
return overscrollX * direction;
|
||||
}
|
||||
|
||||
public scrollToColumn(column: Column) {
|
||||
this.setScroll(this.getScrollPosForColumn(column).x, true);
|
||||
}
|
||||
|
||||
public scrollCenterColumn(column: Column) {
|
||||
const windowCenter = column.getLeft() + column.getWidth() / 2;
|
||||
public scrollCenterRange(range: Desktop.Range) {
|
||||
const windowCenter = range.getLeft() + range.getWidth() / 2;
|
||||
const screenCenter = this.scrollX + this.tilingArea.width / 2;
|
||||
this.adjustScroll(Math.round(windowCenter - screenCenter), false);
|
||||
}
|
||||
|
||||
public scrollCenterVisible(focusedColumn: Column, prioritiseVisible: boolean) {
|
||||
const columnRange = new Desktop.ColumnRange(focusedColumn);
|
||||
const visibleRange = this.getCurrentVisibleRange();
|
||||
if (prioritiseVisible) {
|
||||
columnRange.addNeighbors(visibleRange, this.grid.config.gapsInnerHorizontal, column => column.isVisible(visibleRange, true));
|
||||
}
|
||||
columnRange.addNeighbors(visibleRange, this.grid.config.gapsInnerHorizontal, () => true);
|
||||
this.scrollCenterRange(columnRange);
|
||||
}
|
||||
|
||||
public autoAdjustScroll() {
|
||||
const focusedColumn = this.grid.getLastFocusedColumn();
|
||||
if (focusedColumn === null) {
|
||||
this.removeOverscroll();
|
||||
return;
|
||||
}
|
||||
|
||||
if (focusedColumn.grid !== this.grid) {
|
||||
if (focusedColumn === null || focusedColumn.grid !== this.grid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scrollToColumn(focusedColumn);
|
||||
}
|
||||
|
||||
private getScrollPos(scrollX: number) {
|
||||
return new Desktop.ScrollPos(scrollX, this.tilingArea.width);
|
||||
public scrollToColumn(column: Column) {
|
||||
if (this.dirtyScroll || !column.isVisible(this.getCurrentVisibleRange(), true)) {
|
||||
this.config.scroller.scrollToColumn(this, column);
|
||||
}
|
||||
}
|
||||
|
||||
public getCurrentScrollPos() {
|
||||
return this.getScrollPos(this.scrollX);
|
||||
private getVisibleRange(scrollX: number) {
|
||||
return new Desktop.RangeImpl(scrollX, this.tilingArea.width);
|
||||
}
|
||||
|
||||
public getCurrentVisibleRange() {
|
||||
return this.getVisibleRange(this.scrollX);
|
||||
}
|
||||
|
||||
private clampScrollX(x: number) {
|
||||
let minScroll = 0;
|
||||
let maxScroll = this.grid.getWidth() - this.tilingArea.width;
|
||||
if (maxScroll < 0) {
|
||||
const centerScroll = Math.round(maxScroll / 2);
|
||||
minScroll = centerScroll;
|
||||
maxScroll = centerScroll;
|
||||
}
|
||||
return clamp(x, minScroll, maxScroll);
|
||||
return this.config.scroller.clampScrollX(this, x);
|
||||
}
|
||||
|
||||
private setScroll(x: number, force: boolean) {
|
||||
public 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: Desktop.ScrollPos) {
|
||||
this.setScroll(scrollPos.x, true);
|
||||
this.dirtyScroll = false;
|
||||
}
|
||||
|
||||
public adjustScroll(dx: number, force: boolean) {
|
||||
this.setScroll(this.scrollX + dx, force);
|
||||
}
|
||||
|
||||
private removeOverscroll() {
|
||||
this.setScroll(this.scrollX, false);
|
||||
public equalizeVisibleColumnsWidths() {
|
||||
const visibleRange = this.getCurrentVisibleRange();
|
||||
const visibleColumns = Array.from(this.grid.getVisibleColumns(visibleRange, true));
|
||||
|
||||
let remainingWidth = this.tilingArea.width - (visibleColumns.length-1) * this.grid.config.gapsInnerHorizontal;
|
||||
let remainingColumns = visibleColumns.length;
|
||||
|
||||
const minWidths = visibleColumns.map(column => column.getMinWidth()).sort((a, b) => b - a);
|
||||
for (const minWidth of minWidths) {
|
||||
if (minWidth > remainingWidth / remainingColumns) {
|
||||
remainingWidth -= minWidth;
|
||||
remainingColumns--;
|
||||
}
|
||||
}
|
||||
|
||||
const avgWidth = remainingWidth / remainingColumns;
|
||||
for (const column of visibleColumns) {
|
||||
const minWidth = column.getMinWidth();
|
||||
if (minWidth > avgWidth) {
|
||||
column.setWidth(minWidth, true);
|
||||
} else {
|
||||
const columnWidth = Math.round(remainingWidth / remainingColumns);
|
||||
column.setWidth(columnWidth, true);
|
||||
remainingWidth -= column.getWidth();
|
||||
remainingColumns--;
|
||||
}
|
||||
}
|
||||
|
||||
this.scrollCenterRange(Desktop.RangeImpl.fromRanges(
|
||||
visibleColumns[0],
|
||||
visibleColumns[visibleColumns.length - 1],
|
||||
));
|
||||
}
|
||||
|
||||
public arrange() {
|
||||
@@ -151,16 +168,18 @@ class Desktop {
|
||||
if (!this.dirty) {
|
||||
return;
|
||||
}
|
||||
this.grid.arrange(this.tilingArea.x - this.scrollX);
|
||||
this.grid.arrange(this.tilingArea.x - this.scrollX, this.getCurrentVisibleRange());
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
public onLayoutChanged() {
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
}
|
||||
|
||||
public onPinsChanged() {
|
||||
this.dirty = true;
|
||||
this.dirtyScroll = true;
|
||||
this.dirtyPins = true;
|
||||
}
|
||||
|
||||
@@ -175,12 +194,18 @@ namespace Desktop {
|
||||
marginBottom: number,
|
||||
marginLeft: number,
|
||||
marginRight: number,
|
||||
overscroll: number,
|
||||
scroller: Desktop.Scroller,
|
||||
};
|
||||
|
||||
export class ScrollPos {
|
||||
public readonly x: number;
|
||||
public readonly width: number;
|
||||
export type Range = {
|
||||
getLeft(): number;
|
||||
getRight(): number;
|
||||
getWidth(): number;
|
||||
}
|
||||
|
||||
export class RangeImpl {
|
||||
private readonly x: number;
|
||||
private readonly width: number;
|
||||
|
||||
constructor(x: number, width: number) {
|
||||
this.x = x;
|
||||
@@ -194,5 +219,93 @@ namespace Desktop {
|
||||
public getRight() {
|
||||
return this.x + this.width;
|
||||
}
|
||||
|
||||
public getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
|
||||
public static fromRanges(leftRange: Range, rightRange: Range) {
|
||||
const left = leftRange.getLeft();
|
||||
const right = rightRange.getRight();
|
||||
return new RangeImpl(left, right - left);
|
||||
}
|
||||
}
|
||||
|
||||
export class ColumnRange {
|
||||
private left: Column;
|
||||
private right: Column;
|
||||
private width: number;
|
||||
|
||||
constructor(initialColumn: Column) {
|
||||
this.left = initialColumn;
|
||||
this.right = initialColumn;
|
||||
this.width = initialColumn.getWidth();
|
||||
}
|
||||
|
||||
public addNeighbors(visibleRange: Desktop.Range, gap: number, condition: (column: Column) => boolean) {
|
||||
const grid = this.left.grid;
|
||||
|
||||
const columnRange = this;
|
||||
function canFit(column: Column) {
|
||||
return columnRange.width + gap + column.getWidth() <= visibleRange.getWidth()
|
||||
}
|
||||
function isUsable(column: Column|null) {
|
||||
return column !== null &&
|
||||
canFit(column) &&
|
||||
condition(column)
|
||||
}
|
||||
|
||||
let leftColumn = grid.getPrevColumn(this.left);
|
||||
let rightColumn = grid.getNextColumn(this.right);
|
||||
function checkColumns() {
|
||||
if (!isUsable(leftColumn)) {
|
||||
leftColumn = null;
|
||||
}
|
||||
if (!isUsable(rightColumn)) {
|
||||
rightColumn = null;
|
||||
}
|
||||
}
|
||||
checkColumns();
|
||||
|
||||
while (leftColumn !== null || rightColumn !== null) {
|
||||
const leftWidth = leftColumn === null ? 0 : leftColumn.getWidth();
|
||||
const rightWidth = rightColumn === null ? 0 : rightColumn.getWidth();
|
||||
if (leftWidth > rightWidth) {
|
||||
this.addLeft(leftColumn!, gap);
|
||||
leftColumn = grid.getPrevColumn(leftColumn!);
|
||||
} else {
|
||||
this.addRight(rightColumn!, gap);
|
||||
rightColumn = grid.getNextColumn(rightColumn!);
|
||||
}
|
||||
checkColumns();
|
||||
}
|
||||
}
|
||||
|
||||
public addLeft(column: Column, gap: number) {
|
||||
this.left = column;
|
||||
this.width += column.getWidth() + gap;
|
||||
}
|
||||
|
||||
public addRight(column: Column, gap: number) {
|
||||
this.right = column;
|
||||
this.width += column.getWidth() + gap;
|
||||
}
|
||||
|
||||
public getLeft() {
|
||||
return this.left.getLeft();
|
||||
}
|
||||
|
||||
public getRight() {
|
||||
return this.right.getRight();
|
||||
}
|
||||
|
||||
public getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
}
|
||||
|
||||
export type Scroller = {
|
||||
scrollToColumn(desktop: Desktop, column: Column): void;
|
||||
clampScrollX(desktop: Desktop, x: number): number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import Range = Desktop.Range;
|
||||
|
||||
class Grid {
|
||||
public readonly desktop: Desktop;
|
||||
public readonly config: LayoutConfig;
|
||||
@@ -88,39 +90,41 @@ class Grid {
|
||||
this.width = x - this.config.gapsInnerHorizontal;
|
||||
}
|
||||
|
||||
public getLeftmostVisibleColumn(scrollPos: Desktop.ScrollPos, fullyVisible: boolean) {
|
||||
const scrollX = scrollPos.getLeft();
|
||||
public getLeftmostVisibleColumn(visibleRange: Desktop.Range, fullyVisible: boolean) {
|
||||
for (const column of this.columns.iterator()) {
|
||||
const x = fullyVisible ? column.getLeft() : column.getRight() + (this.config.gapsInnerHorizontal - 1);
|
||||
if (x >= scrollX) {
|
||||
if (column.isVisible(visibleRange, fullyVisible)) {
|
||||
return column;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getRightmostVisibleColumn(scrollPos: Desktop.ScrollPos, fullyVisible: boolean) {
|
||||
const scrollX = scrollPos.getRight();
|
||||
public getRightmostVisibleColumn(visibleRange: Desktop.Range, fullyVisible: boolean) {
|
||||
let last = null;
|
||||
for (const column of this.columns.iterator()) {
|
||||
const x = fullyVisible ? column.getRight() : column.getLeft() - (this.config.gapsInnerHorizontal - 1);
|
||||
if (x <= scrollX) {
|
||||
if (column.isVisible(visibleRange, fullyVisible)) {
|
||||
last = column;
|
||||
} else {
|
||||
} else if (last !== null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
public getVisibleColumnsWidth(scrollPos: Desktop.ScrollPos, fullyVisible: boolean) {
|
||||
public *getVisibleColumns(visibleRange: Desktop.Range, fullyVisible: boolean) {
|
||||
for (const column of this.columns.iterator()) {
|
||||
if (column.isVisible(visibleRange, fullyVisible)) {
|
||||
yield column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getVisibleColumnsWidth(visibleRange: Desktop.Range, fullyVisible: boolean) {
|
||||
let width = 0;
|
||||
let nVisible = 0;
|
||||
for (const column of this.columns.iterator()) {
|
||||
if (column.isVisible(scrollPos, fullyVisible)) {
|
||||
width += column.getWidth();
|
||||
nVisible++;
|
||||
}
|
||||
for (const column of this.getVisibleColumns(visibleRange, fullyVisible)) {
|
||||
width += column.getWidth();
|
||||
nVisible++;
|
||||
}
|
||||
|
||||
if (nVisible > 0) {
|
||||
@@ -130,84 +134,9 @@ class Grid {
|
||||
return width;
|
||||
}
|
||||
|
||||
private getLeftOffScreenColumn(scrollPos: Desktop.ScrollPos) {
|
||||
const leftVisible = this.getLeftmostVisibleColumn(scrollPos, true);
|
||||
if (leftVisible === null) {
|
||||
return null;
|
||||
}
|
||||
return this.getPrevColumn(leftVisible);
|
||||
}
|
||||
|
||||
private getRightOffScreenColumn(scrollPos: Desktop.ScrollPos) {
|
||||
const rightVisible = this.getRightmostVisibleColumn(scrollPos, true);
|
||||
if (rightVisible === null) {
|
||||
return null;
|
||||
}
|
||||
return this.getNextColumn(rightVisible);
|
||||
}
|
||||
|
||||
public increaseColumnWidth(column: Column) {
|
||||
const scrollPos = this.desktop.getScrollPosForColumn(column);
|
||||
if (this.width < scrollPos.width) {
|
||||
column.adjustWidth(scrollPos.width - this.width, true);
|
||||
return;
|
||||
}
|
||||
|
||||
let leftColumn = this.getLeftmostVisibleColumn(scrollPos, false);
|
||||
if (leftColumn === column) {
|
||||
leftColumn = null;
|
||||
}
|
||||
let rightColumn = this.getRightmostVisibleColumn(scrollPos, false);
|
||||
if (rightColumn === column) {
|
||||
rightColumn = null;
|
||||
}
|
||||
if (leftColumn === null && rightColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const leftVisibleWidth = leftColumn === null ? Infinity : leftColumn.getRight() - scrollPos.getLeft();
|
||||
const rightVisibleWidth = rightColumn === null ? Infinity : scrollPos.getRight() - rightColumn.getLeft();
|
||||
const expandLeft = leftVisibleWidth < rightVisibleWidth;
|
||||
const widthDelta = (expandLeft ? leftVisibleWidth : rightVisibleWidth) + this.config.gapsInnerHorizontal;
|
||||
if (expandLeft) {
|
||||
this.desktop.adjustScroll(widthDelta, false);
|
||||
}
|
||||
column.adjustWidth(widthDelta, true);
|
||||
}
|
||||
|
||||
public decreaseColumnWidth(column: Column) {
|
||||
const scrollPos = this.desktop.getScrollPosForColumn(column);
|
||||
if (this.width <= scrollPos.width) {
|
||||
column.setWidth(Math.round(column.getWidth() / 2), true);
|
||||
return;
|
||||
}
|
||||
|
||||
let leftColumn = this.getLeftOffScreenColumn(scrollPos);
|
||||
if (leftColumn === column) {
|
||||
leftColumn = null;
|
||||
}
|
||||
let rightColumn = this.getRightOffScreenColumn(scrollPos);
|
||||
if (rightColumn === column) {
|
||||
rightColumn = null;
|
||||
}
|
||||
if (leftColumn === null && rightColumn === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const leftInvisibleWidth = leftColumn === null ? Infinity : scrollPos.getLeft() - leftColumn.getLeft();
|
||||
const rightInvisibleWidth = rightColumn === null ? Infinity : rightColumn.getRight() - scrollPos.getRight();
|
||||
const shrinkLeft = leftInvisibleWidth < rightInvisibleWidth;
|
||||
const widthDelta = (shrinkLeft ? leftInvisibleWidth : rightInvisibleWidth);
|
||||
if (shrinkLeft) {
|
||||
const maxDelta = column.getWidth() - column.getMinWidth();
|
||||
this.desktop.adjustScroll(-Math.min(widthDelta, maxDelta), false);
|
||||
}
|
||||
column.adjustWidth(-widthDelta, true);
|
||||
}
|
||||
|
||||
public arrange(x: number) {
|
||||
public arrange(x: number, visibleRange: Range) {
|
||||
for (const column of this.columns.iterator()) {
|
||||
column.arrange(x);
|
||||
column.arrange(x, visibleRange, this.userResize);
|
||||
x += column.getWidth() + this.config.gapsInnerHorizontal;
|
||||
}
|
||||
|
||||
@@ -239,12 +168,12 @@ class Grid {
|
||||
this.columns.remove(column);
|
||||
this.columnsSetX(nextColumn);
|
||||
|
||||
this.desktop.onLayoutChanged();
|
||||
if (passFocus && columnToFocus !== null) {
|
||||
columnToFocus.focus();
|
||||
} else {
|
||||
this.desktop.autoAdjustScroll();
|
||||
}
|
||||
this.desktop.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onColumnMoved(column: Column, prevColumn: Column|null) {
|
||||
@@ -259,10 +188,10 @@ class Grid {
|
||||
public onColumnWidthChanged(column: Column, oldWidth: number, width: number) {
|
||||
const nextColumn = this.columns.getNext(column);
|
||||
this.columnsSetX(nextColumn);
|
||||
this.desktop.onLayoutChanged();
|
||||
if (!this.userResize) {
|
||||
this.desktop.autoAdjustScroll();
|
||||
}
|
||||
this.desktop.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onColumnFocused(column: Column) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
type LayoutConfig = {
|
||||
gapsInnerHorizontal: number,
|
||||
gapsInnerVertical: number,
|
||||
offScreenOpacity: number,
|
||||
stackColumnsByDefault: boolean,
|
||||
resizeNeighborColumn: boolean,
|
||||
reMaximize: boolean,
|
||||
skipSwitcher: boolean,
|
||||
tiledKeepBelow: boolean,
|
||||
maximizedKeepAbove: boolean,
|
||||
};
|
||||
|
||||
21
src/layout/ScrollerCentered.ts
Normal file
21
src/layout/ScrollerCentered.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
class ScrollerCentered {
|
||||
public scrollToColumn(desktop: Desktop, column: Column) {
|
||||
desktop.scrollCenterRange(column);
|
||||
}
|
||||
|
||||
public clampScrollX(desktop: Desktop, x: number) {
|
||||
return ScrollerCentered.clampScrollX(desktop, x);
|
||||
}
|
||||
|
||||
public static clampScrollX(desktop: Desktop, x: number) {
|
||||
const firstColumn = desktop.grid.getFirstColumn();
|
||||
if (firstColumn === null) {
|
||||
return 0;
|
||||
}
|
||||
const lastColumn = desktop.grid.getLastColumn()!;
|
||||
|
||||
let minScroll = Math.round((firstColumn.getWidth() - desktop.tilingArea.width) / 2);
|
||||
let maxScroll = Math.round(desktop.grid.getWidth() - (desktop.tilingArea.width + lastColumn.getWidth()) / 2);
|
||||
return clamp(x, minScroll, maxScroll);
|
||||
}
|
||||
}
|
||||
9
src/layout/ScrollerGrouped.ts
Normal file
9
src/layout/ScrollerGrouped.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
class ScrollerGrouped {
|
||||
public scrollToColumn(desktop: Desktop, column: Column) {
|
||||
desktop.scrollCenterVisible(column, true);
|
||||
}
|
||||
|
||||
public clampScrollX(desktop: Desktop, x: number) {
|
||||
return ScrollerCentered.clampScrollX(desktop, x);
|
||||
}
|
||||
}
|
||||
14
src/layout/ScrollerLazy.ts
Normal file
14
src/layout/ScrollerLazy.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
class ScrollerLazy {
|
||||
public scrollToColumn(desktop: Desktop, column: Column) {
|
||||
desktop.scrollIntoView(column);
|
||||
}
|
||||
|
||||
public clampScrollX(desktop: Desktop, x: number) {
|
||||
let minScroll = 0;
|
||||
let maxScroll = desktop.grid.getWidth() - desktop.tilingArea.width;
|
||||
if (maxScroll < 0) {
|
||||
return Math.round(maxScroll / 2);
|
||||
}
|
||||
return clamp(x, minScroll, maxScroll);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ class Window {
|
||||
public column: Column;
|
||||
public readonly client: ClientWrapper;
|
||||
public height: number;
|
||||
public readonly focusedState: WindowState;
|
||||
public readonly focusedState: Window.State;
|
||||
private skipArrange: boolean;
|
||||
|
||||
constructor(client: ClientWrapper, column: Column) {
|
||||
@@ -106,7 +106,7 @@ class Window {
|
||||
this.column.grid.desktop.onLayoutChanged();
|
||||
}
|
||||
|
||||
public onUserResize(oldGeometry: QRect, resizeNeighborColumn: boolean) {
|
||||
public onUserResize(oldGeometry: QmlRect, resizeNeighborColumn: boolean) {
|
||||
const newGeometry = this.client.kwinClient.frameGeometry;
|
||||
const widthDelta = newGeometry.width - oldGeometry.width;
|
||||
const heightDelta = newGeometry.height - oldGeometry.height;
|
||||
@@ -142,8 +142,10 @@ class Window {
|
||||
}
|
||||
}
|
||||
|
||||
type WindowState = {
|
||||
fullScreen: boolean,
|
||||
maximizedHorizontally: boolean,
|
||||
maximizedVertically: boolean,
|
||||
namespace Window {
|
||||
export type State = {
|
||||
fullScreen: boolean,
|
||||
maximizedHorizontally: boolean,
|
||||
maximizedVertically: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Delayer {
|
||||
private readonly timer: QQmlTimer;
|
||||
private readonly timer: QmlTimer;
|
||||
|
||||
constructor(delay: number, f: () => void) {
|
||||
this.timer = initQmlTimer();
|
||||
|
||||
@@ -38,7 +38,7 @@ function initWorkspaceSignalHandlers(world: World) {
|
||||
if ((horizontally || vertically) && kwinClient.tile !== null) {
|
||||
kwinClient.tile = null;
|
||||
}
|
||||
world.doIfTiled(kwinClient, false, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiled(kwinClient, false, (clientManager, desktopManager, window, column, grid) => {
|
||||
window.onMaximizedChanged(horizontally, vertically);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ class ClientWrapper {
|
||||
private readonly rulesSignalManager: SignalManager | null;
|
||||
public preferredWidth: number;
|
||||
private readonly manipulatingGeometry: Doer;
|
||||
private lastPlacement: QRect | null; // workaround for issue #19
|
||||
private lastPlacement: QmlRect | null; // workaround for issue #19
|
||||
|
||||
constructor(
|
||||
kwinClient: KwinClient,
|
||||
@@ -92,7 +92,7 @@ class ClientWrapper {
|
||||
return this.kwinClient.shade;
|
||||
}
|
||||
|
||||
public isManipulatingGeometry(newGeometry: QRect | null) {
|
||||
public isManipulatingGeometry(newGeometry: QmlRect | null) {
|
||||
if (newGeometry !== null && newGeometry === this.lastPlacement) {
|
||||
return true;
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class ClientWrapper {
|
||||
this.transients.splice(i, 1);
|
||||
}
|
||||
|
||||
public ensureTransientsVisible(screenSize: QRect) {
|
||||
public ensureTransientsVisible(screenSize: QmlRect) {
|
||||
for (const transient of this.transients) {
|
||||
if (transient.stateManager.getState() instanceof ClientState.Floating) {
|
||||
transient.ensureVisible(screenSize);
|
||||
@@ -117,15 +117,15 @@ class ClientWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
public ensureVisible(screenSize: QRect) {
|
||||
public ensureVisible(screenSize: QmlRect) {
|
||||
if (this.kwinClient.desktop !== workspace.currentDesktop) {
|
||||
return;
|
||||
}
|
||||
const frame = this.kwinClient.frameGeometry;
|
||||
if (frame.left < 0) {
|
||||
frame.x = 0;
|
||||
} else if (frame.right > screenSize.width) {
|
||||
frame.x = screenSize.width - frame.width;
|
||||
if (frame.left < screenSize.left) {
|
||||
frame.x = screenSize.left;
|
||||
} else if (frame.right > screenSize.right) {
|
||||
frame.x = screenSize.right - frame.width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class PinManager {
|
||||
this.pinnedClients.delete(kwinClient);
|
||||
}
|
||||
|
||||
public getAvailableSpace(desktopNumber: number, screen: QRect) {
|
||||
public getAvailableSpace(desktopNumber: number, screen: QmlRect) {
|
||||
const baseLot = new PinManager.Lot(screen.top, screen.bottom, screen.left, screen.right);
|
||||
let lots = [baseLot];
|
||||
for (const client of this.pinnedClients) {
|
||||
@@ -53,7 +53,7 @@ namespace PinManager {
|
||||
public readonly right: number,
|
||||
) {}
|
||||
|
||||
public split(destLots: Lot[], obstacle: QRect) {
|
||||
public split(destLots: Lot[], obstacle: QmlRect) {
|
||||
if (!this.contains(obstacle)) {
|
||||
// don't split
|
||||
destLots.push(this);
|
||||
@@ -74,9 +74,9 @@ namespace PinManager {
|
||||
}
|
||||
}
|
||||
|
||||
private contains(obstacle: QRect) {
|
||||
return obstacle.right >= this.left && obstacle.left <= this.right &&
|
||||
obstacle.bottom >= this.top && obstacle.top <= this.bottom;
|
||||
private contains(obstacle: QmlRect) {
|
||||
return obstacle.right > this.left && obstacle.left < this.right &&
|
||||
obstacle.bottom > this.top && obstacle.top < this.bottom;
|
||||
}
|
||||
|
||||
public area() {
|
||||
|
||||
@@ -21,6 +21,18 @@ class World {
|
||||
|
||||
this.pinManager = new PinManager();
|
||||
|
||||
const layoutConfig = {
|
||||
gapsInnerHorizontal: config.gapsInnerHorizontal,
|
||||
gapsInnerVertical: config.gapsInnerVertical,
|
||||
offScreenOpacity: config.offScreenOpacity / 100.0,
|
||||
stackColumnsByDefault: config.stackColumnsByDefault,
|
||||
resizeNeighborColumn: config.resizeNeighborColumn,
|
||||
reMaximize: config.reMaximize,
|
||||
skipSwitcher: config.skipSwitcher,
|
||||
tiledKeepBelow: config.tiledKeepBelow,
|
||||
maximizedKeepAbove: config.floatingKeepAbove,
|
||||
};
|
||||
|
||||
this.desktopManager = new DesktopManager(
|
||||
this.pinManager,
|
||||
{
|
||||
@@ -28,17 +40,12 @@ class World {
|
||||
marginBottom: config.gapsOuterBottom,
|
||||
marginLeft: config.gapsOuterLeft,
|
||||
marginRight: config.gapsOuterRight,
|
||||
overscroll: config.overscroll,
|
||||
},
|
||||
{
|
||||
gapsInnerHorizontal: config.gapsInnerHorizontal,
|
||||
gapsInnerVertical: config.gapsInnerVertical,
|
||||
stackColumnsByDefault: config.stackColumnsByDefault,
|
||||
resizeNeighborColumn: config.resizeNeighborColumn,
|
||||
reMaximize: config.reMaximize,
|
||||
tiledKeepBelow: config.tiledKeepBelow,
|
||||
maximizedKeepAbove: config.floatingKeepAbove,
|
||||
scroller: config.scrollingLazy ? new ScrollerLazy() :
|
||||
config.scrollingCentered ? new ScrollerCentered() :
|
||||
config.scrollingGrouped ? new ScrollerGrouped() :
|
||||
console.assert(false),
|
||||
},
|
||||
layoutConfig,
|
||||
workspace.currentActivity,
|
||||
);
|
||||
this.clientManager = new ClientManager(config, this, this.desktopManager, this.pinManager);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ClientState {
|
||||
|
||||
private static initSignalManager(world: World, kwinClient: KwinClient) {
|
||||
const manager = new SignalManager();
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QRect) => {
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QmlRect) => {
|
||||
world.onScreenResized();
|
||||
});
|
||||
return manager;
|
||||
|
||||
@@ -21,6 +21,6 @@ namespace ClientState {
|
||||
}
|
||||
|
||||
export type State = {
|
||||
destroy(passFocus: boolean): void;
|
||||
destroy(passFocus: boolean): void,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace ClientState {
|
||||
}
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QRect) => {
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QmlRect) => {
|
||||
if (kwinClient.tile === null) {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
clientManager.unpinClient(kwinClient);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
namespace ClientState {
|
||||
export class Tiled implements State {
|
||||
public readonly window: Window;
|
||||
private readonly defaultState: Tiled.WindowState;
|
||||
private readonly signalManager: SignalManager;
|
||||
|
||||
constructor(world: World, client: ClientWrapper, grid: Grid) {
|
||||
this.defaultState = { skipSwitcher: client.kwinClient.skipSwitcher };
|
||||
Tiled.prepareClientForTiling(client, grid.config);
|
||||
|
||||
const column = new Column(grid, grid.getLastFocusedColumn() ?? grid.getLastColumn());
|
||||
@@ -21,7 +23,7 @@ namespace ClientState {
|
||||
const client = window.client;
|
||||
window.destroy(passFocus);
|
||||
|
||||
Tiled.restoreClientAfterTiling(client, grid.config, grid.desktop.clientArea);
|
||||
Tiled.restoreClientAfterTiling(client, grid.config, this.defaultState, grid.desktop.clientArea);
|
||||
}
|
||||
|
||||
private static initSignalManager(world: World, window: Window) {
|
||||
@@ -81,7 +83,7 @@ namespace ClientState {
|
||||
cursorChangedAfterResizeStart = false;
|
||||
});
|
||||
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QRect) => {
|
||||
manager.connect(kwinClient.frameGeometryChanged, (kwinClient: KwinClient, oldGeometry: QmlRect) => {
|
||||
// on Wayland, this fires after `tileChanged`
|
||||
if (kwinClient.tile !== null) {
|
||||
world.do((clientManager, desktopManager) => {
|
||||
@@ -143,6 +145,9 @@ namespace ClientState {
|
||||
}
|
||||
|
||||
private static prepareClientForTiling(client: ClientWrapper, config: LayoutConfig) {
|
||||
if (config.skipSwitcher) {
|
||||
client.kwinClient.skipSwitcher = true;
|
||||
}
|
||||
if (config.tiledKeepBelow) {
|
||||
client.kwinClient.keepBelow = true;
|
||||
}
|
||||
@@ -153,7 +158,10 @@ namespace ClientState {
|
||||
client.setMaximize(false, false);
|
||||
}
|
||||
|
||||
private static restoreClientAfterTiling(client: ClientWrapper, config: LayoutConfig, screenSize: QRect) {
|
||||
private static restoreClientAfterTiling(client: ClientWrapper, config: LayoutConfig, defaultState: Tiled.WindowState, screenSize: QmlRect) {
|
||||
if (config.skipSwitcher) {
|
||||
client.kwinClient.skipSwitcher = defaultState.skipSwitcher;
|
||||
}
|
||||
if (config.tiledKeepBelow) {
|
||||
client.kwinClient.keepBelow = false;
|
||||
}
|
||||
@@ -165,4 +173,10 @@ namespace ClientState {
|
||||
client.ensureVisible(screenSize);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Tiled {
|
||||
export type WindowState = {
|
||||
skipSwitcher: boolean,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user