51 Commits
v0.5 ... v0.6

Author SHA1 Message Date
Peter Fajdiga
3a8baf4cd7 bump version to 0.6 2023-12-30 17:18:44 +01:00
Peter Fajdiga
dc14171ae7 config: add Wayland window classes to window rules (resolves #24) 2023-12-27 19:45:07 +01:00
Peter Fajdiga
6dcf8979c2 config: add yakuake to window rules 2023-12-27 19:41:30 +01:00
Peter Fajdiga
fe5661c07f Grid: auto-scroll after removing a non-focused column 2023-12-24 14:58:44 +01:00
Peter Fajdiga
90b783b34b Grid: remove unused methods getLeftOffScreenColumn and getRightOffScreenColumn 2023-12-24 12:52:10 +01:00
Peter Fajdiga
fb40bd9592 Grid.decreaseColumnWidth: prevent scrolling away from focused column when reaching minimum width 2023-12-24 12:50:52 +01:00
Peter Fajdiga
768d95450d refactor arrange functions 2023-12-24 09:29:31 +01:00
Peter Fajdiga
e95a0e44c9 prevent translucent windows during resizing 2023-12-24 09:21:49 +01:00
Peter Fajdiga
c1b8d05919 defaultWindowRules: update Zoom entry 2023-12-24 09:21:44 +01:00
Peter Fajdiga
e98ce18105 add opacity settings for obscured windows 2023-12-16 12:10:26 +01:00
Peter Fajdiga
25a9efc8e4 config.ui: split up parameters and behavior tabs 2023-12-16 11:39:42 +01:00
Peter Fajdiga
db48644944 package/contents/ui: add footer spacer to take up excess space 2023-12-16 11:39:42 +01:00
Peter Fajdiga
950e0de076 Desktop: prevent scrolling when unnecessary (add dirtyScroll variable) 2023-12-16 10:07:02 +01:00
Peter Fajdiga
05ffe0895e Revert "Desktop.scrollToCenterRange: force scroll"
This reverts commit dda63d68cde58c7f4a7162b11a2fd614365d36ff.
2023-12-16 09:33:26 +01:00
Peter Fajdiga
61db5ca69f use different implementations of clampScrollX in different scrollers 2023-12-16 09:33:26 +01:00
Peter Fajdiga
f7b5dd0b9c Desktop.equalizeVisibleColumnsWidths: use Desktop.RangeImpl.fromRanges 2023-12-16 09:33:26 +01:00
Peter Fajdiga
f83f60c98f Grid: reimplement increaseColumnWidth and decreaseColumnWidth 2023-12-16 09:33:26 +01:00
Peter Fajdiga
3e8734eefb Desktop.scrollToCenterRange: force scroll 2023-12-16 09:33:26 +01:00
Peter Fajdiga
bed0ea7ed8 Actions: make gridScrollFocused center focused column again 2023-12-16 09:33:26 +01:00
Peter Fajdiga
92c99f0b87 rename focusColumn -> scrollToColumn 2023-12-16 09:33:26 +01:00
Peter Fajdiga
b2024bc8aa Actions: make scrolling actions use focusColumn 2023-12-16 09:33:26 +01:00
Peter Fajdiga
58f358313b config.ui: change description for kcfg_scrollingLazy 2023-12-16 09:33:26 +01:00
Peter Fajdiga
352a7061f6 config.ui: shorten description for kcfg_scrollingCentered 2023-12-16 09:33:26 +01:00
Peter Fajdiga
7314c0ee24 add ScrollerGrouped 2023-12-16 09:33:26 +01:00
Peter Fajdiga
c65361853c config.ui: use button groups 2023-12-16 09:32:26 +01:00
Peter Fajdiga
fa53e765b3 qt.d.ts: rename QmlTimer 2023-12-16 09:30:19 +01:00
Peter Fajdiga
4d35681ee2 qt.d.ts: rename QmlSize 2023-12-16 09:30:14 +01:00
Peter Fajdiga
1824bcdf85 fix usages of QmlRect.right and .bottom 2023-12-12 17:33:10 +01:00
Peter Fajdiga
ce1b402bf2 qt.d.ts: add comments to QmlRect fields 2023-12-10 21:08:00 +01:00
Peter Fajdiga
2df6d5d8e6 qt.d.ts: rename QmlRect 2023-12-10 21:06:03 +01:00
Peter Fajdiga
e7d33030ba read and use scrolling configuration 2023-12-05 22:15:49 +01:00
Peter Fajdiga
2bd000f0a6 config.ui: add scrolling configuration 2023-12-05 22:03:22 +01:00
Peter Fajdiga
6313d8f18e implement scrollers 2023-12-05 21:59:01 +01:00
Peter Fajdiga
464ec3bcb1 Grid.increaseColumnWidth: fix scroll adjustment after resize 2023-12-02 20:57:46 +01:00
Peter Fajdiga
fae793cb09 Desktop.equalizeVisibleColumnsWidths: adjust scroll after resizing 2023-12-02 20:50:13 +01:00
Peter Fajdiga
d7346a6fab Desktop.clampScrollX: simplify 2023-12-02 20:38:14 +01:00
Peter Fajdiga
0e5efd2be7 Desktop: receive Range in parameters instead of Column 2023-12-02 17:36:29 +01:00
Peter Fajdiga
a1a315790e turn Desktop.Range into an interface (well, into a type) 2023-12-02 14:12:41 +01:00
Peter Fajdiga
9d62499bf0 Grid.Range: make fields private 2023-12-02 14:09:23 +01:00
Peter Fajdiga
97cf61d1dd Grid: extract method calculateVisibleRange 2023-12-02 14:06:28 +01:00
Peter Fajdiga
5d83c6dd2c rename Grid.ScrollPos to Grid.Range 2023-12-02 13:55:39 +01:00
Peter Fajdiga
8915e8a9da Grid.getVisibleColumnsWidth: use getVisibleColumns 2023-12-02 12:44:57 +01:00
Peter Fajdiga
22e4c47189 add action columns-width-equalize (resolves #22) 2023-12-02 12:44:57 +01:00
Peter Fajdiga
552d2b851f readme: list QML dependencies 2023-11-12 10:02:33 +01:00
Peter Fajdiga
c4ce795359 config.ui: add stacked mode description to tooltip 2023-09-29 10:23:33 +02:00
Peter Fajdiga
1ac1fc3c2b readme: update key bindings 2023-09-29 10:21:47 +02:00
Peter Fajdiga
d0e041d16a keyBindings: add comment for column-toggle-stacked 2023-09-29 10:21:26 +02:00
Peter Fajdiga
8fc3fc976d Window: WindowState -> Window.State 2023-09-29 08:58:38 +02:00
Peter Fajdiga
e0eeace9dc ClientState.Manager: ; -> , 2023-09-29 08:58:37 +02:00
Peter Fajdiga
84e2a06b35 Tiled: define type WindowState 2023-09-29 08:56:45 +02:00
Peter Fajdiga
3373e02658 add skipSwitcher setting 2023-09-29 08:52:59 +02:00
25 changed files with 603 additions and 277 deletions

View File

@@ -16,6 +16,12 @@ scrolled through horizontally.
Similar window managers include [PaperWM](https://github.com/paperwm/PaperWM) 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,7 +45,7 @@ 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 |

View File

@@ -11,12 +11,11 @@
<layout class="QVBoxLayout" name="layout_main">
<item>
<widget class="QTabWidget" name="tabContainer">
<widget class="QWidget" name="tab_general">
<widget class="QWidget" name="tab_parameters">
<attribute name="title">
<string>General</string>
<string>Parameters</string>
</attribute>
<layout class="QFormLayout" name="layout_tab_general">
<layout class="QFormLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_gapsOuterTop">
<property name="text">
@@ -185,112 +184,164 @@
</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_behavior">
<attribute name="title">
<string>Behavior</string>
</attribute>
<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>Prevent needlessly obscuring 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_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">

View File

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

View File

@@ -176,6 +176,12 @@ namespace Actions {
});
},
columnsWidthEqualize: () => {
world.do((clientManager, desktopManager) => {
desktopManager.getCurrentDesktop().equalizeVisibleColumnsWidths();
});
},
gridScrollLeft: () => {
gridScroll(world, -config.manualScrollStep);
},
@@ -208,14 +214,14 @@ namespace Actions {
gridScrollFocused: () => {
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
grid.desktop.scrollCenterColumn(column);
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 +238,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;
}

View File

@@ -7,10 +7,15 @@ type Config = {
gapsInnerVertical: number,
overscroll: number,
manualScrollStep: 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,

View File

@@ -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
},
{
@@ -92,6 +111,11 @@ const configDef = [
"type": "UInt",
"default": 200
},
{
"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",

View File

@@ -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
View File

@@ -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;

View File

@@ -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",

View File

@@ -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();
}
}

View File

@@ -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,109 @@ 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();
// calculates a Range that scrolls the contained Range into view
public calculateVisibleRange(containedRange: Desktop.Range) {
const left = containedRange.getLeft();
const right = containedRange.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));
return this.getVisibleRange(this.clampScrollX(this.scrollX));
}
const overscroll = this.getTargetOverscroll(targetScrollX, left < initialScrollPos.getLeft());
return this.getScrollPos(this.clampScrollX(targetScrollX + overscroll));
const overscroll = this.getTargetOverscroll(targetScrollX, left < initialVisibleRange.getLeft());
return this.getVisibleRange(this.clampScrollX(targetScrollX + overscroll));
}
private getTargetOverscroll(targetScrollX: number, scrollLeft: boolean) {
if (this.config.overscroll === 0) {
return 0;
}
const visibleColumnsWidth = this.grid.getVisibleColumnsWidth(this.getScrollPos(targetScrollX), true);
const visibleColumnsWidth = this.grid.getVisibleColumnsWidth(this.getVisibleRange(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 scrollToRange(range: Desktop.Range) {
this.setScroll(this.calculateVisibleRange(range).getLeft(), 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 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;
for (const column of visibleColumns) {
const columnWidth = Math.round(remainingWidth / remainingColumns);
column.setWidth(columnWidth, true);
remainingWidth -= columnWidth;
remainingColumns--;
}
const targetVisibleRange = Desktop.RangeImpl.fromRanges(
visibleColumns[0],
visibleColumns[visibleColumns.length - 1],
);
this.setScroll(this.calculateVisibleRange(targetVisibleRange).getLeft(), false);
}
public arrange() {
@@ -151,16 +161,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;
}
@@ -176,11 +188,18 @@ namespace Desktop {
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 +213,20 @@ 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 type Scroller = {
scrollToColumn(desktop: Desktop, column: Column): void;
clampScrollX(desktop: Desktop, x: number): number;
}
}

View File

@@ -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,96 @@ 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);
const visibleRange = this.desktop.calculateVisibleRange(column);
if(!column.isVisible(visibleRange, true) || column.getWidth() >= column.getMaxWidth()) {
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) {
let leftVisibleColumn = this.getLeftmostVisibleColumn(visibleRange, true);
let rightVisibleColumn = this.getRightmostVisibleColumn(visibleRange, true);
if (leftVisibleColumn === null || rightVisibleColumn === null) {
console.assert(false);
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);
const leftSpace = leftVisibleColumn.getLeft() - visibleRange.getLeft();
const rightSpace = visibleRange.getRight() - rightVisibleColumn.getRight();
if (leftSpace + rightSpace > 0) {
column.adjustWidth(leftSpace + rightSpace, true);
} else {
// left and right columns are touching the screen's edges
const leftSpace = leftVisibleColumn === column ? Infinity : leftVisibleColumn.getWidth() + this.config.gapsInnerHorizontal;
const rightSpace = rightVisibleColumn === column ? Infinity : rightVisibleColumn.getWidth() + this.config.gapsInnerHorizontal;
if (leftSpace < rightSpace) {
column.adjustWidth(leftSpace, true);
leftVisibleColumn = this.getNextColumn(leftVisibleColumn)!;
} else {
column.adjustWidth(rightSpace, true);
rightVisibleColumn = this.getPrevColumn(rightVisibleColumn)!;
}
}
column.adjustWidth(widthDelta, true);
this.desktop.scrollCenterRange(Desktop.RangeImpl.fromRanges(leftVisibleColumn, rightVisibleColumn));
}
public decreaseColumnWidth(column: Column) {
const scrollPos = this.desktop.getScrollPosForColumn(column);
if (this.width <= scrollPos.width) {
const visibleRange = this.desktop.calculateVisibleRange(column);
if (!column.isVisible(visibleRange, true)) {
return;
}
if (this.width <= visibleRange.getWidth()) {
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) {
const leftVisibleColumn = this.getLeftmostVisibleColumn(visibleRange, true);
const rightVisibleColumn = this.getRightmostVisibleColumn(visibleRange, true);
if (leftVisibleColumn === null || rightVisibleColumn === null) {
console.assert(false);
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);
let leftOffScreenColumn = this.getPrevColumn(leftVisibleColumn);
if (leftOffScreenColumn === column) {
leftOffScreenColumn = null;
}
let rightOffScreenColumn = this.getNextColumn(rightVisibleColumn);
if (rightOffScreenColumn === column) {
rightOffScreenColumn = null;
}
if (leftOffScreenColumn === null && rightOffScreenColumn === null) {
console.assert(false);
return;
}
const leftInvisibleWidth = leftOffScreenColumn === null ? Infinity : visibleRange.getLeft() - leftOffScreenColumn.getLeft();
const rightInvisibleWidth = rightOffScreenColumn === null ? Infinity : rightOffScreenColumn.getRight() - visibleRange.getRight();
const leftSpace = leftVisibleColumn.getLeft() - visibleRange.getLeft();
const rightSpace = visibleRange.getRight() - rightVisibleColumn.getRight();
if (leftInvisibleWidth < rightInvisibleWidth) {
const deltaWidth = rightSpace - leftInvisibleWidth;
column.adjustWidth(deltaWidth, true);
console.assert(leftOffScreenColumn !== null);
const newVisibleWidth = rightVisibleColumn.getRight() - leftOffScreenColumn!.getLeft();
const leftVisibleColumn = newVisibleWidth <= visibleRange.getWidth() ? leftOffScreenColumn! : this.getNextColumn(leftOffScreenColumn!)!;
this.desktop.scrollCenterRange(Desktop.RangeImpl.fromRanges(leftVisibleColumn, rightVisibleColumn));
} else {
const deltaWidth = leftSpace - rightInvisibleWidth;
column.adjustWidth(deltaWidth, true);
console.assert(rightOffScreenColumn !== null);
const newVisibleWidth = rightOffScreenColumn!.getRight() - leftVisibleColumn.getLeft();
const rightVisibleColumn = newVisibleWidth <= visibleRange.getWidth() ? rightOffScreenColumn! : this.getPrevColumn(rightOffScreenColumn!)!;
this.desktop.scrollCenterRange(Desktop.RangeImpl.fromRanges(leftVisibleColumn, rightVisibleColumn));
}
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 +255,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) {

View File

@@ -1,9 +1,11 @@
type LayoutConfig = {
gapsInnerHorizontal: number,
gapsInnerVertical: number,
offScreenOpacity: number,
stackColumnsByDefault: boolean,
resizeNeighborColumn: boolean,
reMaximize: boolean,
skipSwitcher: boolean,
tiledKeepBelow: boolean,
maximizedKeepAbove: boolean,
};

View 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);
}
}

View File

@@ -0,0 +1,87 @@
class ScrollerGrouped {
private readonly layoutConfig: LayoutConfig;
constructor(layoutConfig: LayoutConfig) {
this.layoutConfig = layoutConfig;
}
public scrollToColumn(desktop: Desktop, column: Column) {
const columnRange = new ScrollerGrouped.ColumnRange(column);
const visibleRange = desktop.getCurrentVisibleRange();
columnRange.addNeighbors(visibleRange, this.layoutConfig.gapsInnerHorizontal, true);
columnRange.addNeighbors(visibleRange, this.layoutConfig.gapsInnerHorizontal, false);
desktop.scrollCenterRange(columnRange);
}
public clampScrollX(desktop: Desktop, x: number) {
return ScrollerCentered.clampScrollX(desktop, x);
}
}
namespace ScrollerGrouped {
import Range = Desktop.Range;
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: Range, gap: number, requireVisible: boolean) {
const grid = this.left.grid;
let leftColumn: Column|null = this.left;
while (true) {
leftColumn = grid.getPrevColumn(leftColumn);
if (
leftColumn === null ||
requireVisible && !leftColumn.isVisible(visibleRange, true) ||
this.width + gap + leftColumn.getWidth() > visibleRange.getWidth()
) {
break;
}
this.addLeft(leftColumn, gap);
}
let rightColumn: Column|null = this.right;
while (true) {
rightColumn = grid.getNextColumn(rightColumn);
if (
rightColumn === null ||
requireVisible && !rightColumn.isVisible(visibleRange, true) ||
this.width + gap + rightColumn.getWidth() > visibleRange.getWidth()
) {
break;
}
this.addRight(rightColumn, gap);
}
}
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;
}
}
}

View File

@@ -0,0 +1,14 @@
class ScrollerLazy {
public scrollToColumn(desktop: Desktop, column: Column) {
desktop.scrollToRange(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);
}
}

View File

@@ -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,
}
}

View File

@@ -1,5 +1,5 @@
class Delayer {
private readonly timer: QQmlTimer;
private readonly timer: QmlTimer;
constructor(delay: number, f: () => void) {
this.timer = initQmlTimer();

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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,
{
@@ -29,16 +41,12 @@ class World {
marginLeft: config.gapsOuterLeft,
marginRight: config.gapsOuterRight,
overscroll: config.overscroll,
scroller: config.scrollingLazy ? new ScrollerLazy() :
config.scrollingCentered ? new ScrollerCentered() :
config.scrollingGrouped ? new ScrollerGrouped(layoutConfig) :
console.assert(false),
},
{
gapsInnerHorizontal: config.gapsInnerHorizontal,
gapsInnerVertical: config.gapsInnerVertical,
stackColumnsByDefault: config.stackColumnsByDefault,
resizeNeighborColumn: config.resizeNeighborColumn,
reMaximize: config.reMaximize,
tiledKeepBelow: config.tiledKeepBelow,
maximizedKeepAbove: config.floatingKeepAbove,
},
layoutConfig,
workspace.currentActivity,
);
this.clientManager = new ClientManager(config, this, this.desktopManager, this.pinManager);

View File

@@ -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;

View File

@@ -21,6 +21,6 @@ namespace ClientState {
}
export type State = {
destroy(passFocus: boolean): void;
destroy(passFocus: boolean): void,
};
}

View File

@@ -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);

View File

@@ -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,
}
}
}