Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bda7d1a09 | ||
|
|
2ce72bcee8 | ||
|
|
ff3f6c5d6b | ||
|
|
3ab230b498 | ||
|
|
ba9f362a1c | ||
|
|
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 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
/package/contents/code/main.js
|
||||
/package/contents/config/main.xml
|
||||
/karousel.tar.gz
|
||||
/karousel*.tar.gz
|
||||
/.idea
|
||||
|
||||
3
Makefile
3
Makefile
@@ -1,6 +1,7 @@
|
||||
.PHONY: *
|
||||
|
||||
TSC_SCRIPT_FLAGS = --lib es2020 ./src/extern/qt.d.ts
|
||||
VERSION = $(shell grep '"Version":' ./package/metadata.json | grep -o '[0-9\.]*')
|
||||
|
||||
config:
|
||||
mkdir -p ./package/contents/config
|
||||
@@ -16,7 +17,7 @@ uninstall:
|
||||
kpackagetool5 --type=KWin/Script -r ./package
|
||||
|
||||
package:
|
||||
tar -czf ./karousel.tar.gz ./package
|
||||
tar -czf ./karousel_${subst .,_,${VERSION}}.tar.gz ./package
|
||||
|
||||
logs:
|
||||
journalctl -t kwin_x11 -g '^qml:|^file://.*karousel' -f
|
||||
|
||||
@@ -13,7 +13,8 @@ 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
|
||||
@@ -52,6 +53,7 @@ Here's the default ones:
|
||||
| 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,6 +11,136 @@
|
||||
<layout class="QVBoxLayout" name="layout_main">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabContainer">
|
||||
<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>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>
|
||||
@@ -143,14 +273,14 @@
|
||||
</item>
|
||||
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_overscroll">
|
||||
<widget class="QLabel" name="label_manualScrollStep">
|
||||
<property name="text">
|
||||
<string>Overscroll amount:</string>
|
||||
<string>Manual scroll step size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QSpinBox" name="kcfg_overscroll">
|
||||
<widget class="QSpinBox" name="kcfg_manualScrollStep">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
@@ -164,14 +294,14 @@
|
||||
</item>
|
||||
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_manualScrollStep">
|
||||
<widget class="QLabel" name="label_manualResizeStep">
|
||||
<property name="text">
|
||||
<string>Manual scroll step size:</string>
|
||||
<string>Manual resize step size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QSpinBox" name="kcfg_manualScrollStep">
|
||||
<widget class="QSpinBox" name="kcfg_manualResizeStep">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
@@ -207,136 +337,6 @@
|
||||
</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>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
}],
|
||||
"Id": "karousel",
|
||||
"ServiceTypes": ["KWin/Script"],
|
||||
"Version": "0.6",
|
||||
"Version": "0.7.1",
|
||||
"License": "GPLv3",
|
||||
"Website": "https://github.com/peterfajdiga/karousel",
|
||||
"BugReportUrl": "https://github.com/peterfajdiga/karousel/issues"
|
||||
|
||||
134
src/Actions.ts
134
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,120 @@ 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();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -213,7 +289,7 @@ namespace Actions {
|
||||
},
|
||||
|
||||
gridScrollFocused: () => {
|
||||
world.doIfTiledFocused(true, (world, desktopManager, window, column, grid) => {
|
||||
world.doIfTiledFocused(true, (clientManager, desktopManager, window, column, grid) => {
|
||||
grid.desktop.scrollCenterRange(column);
|
||||
})
|
||||
},
|
||||
@@ -268,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;
|
||||
@@ -279,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;
|
||||
@@ -323,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,8 +5,8 @@ type Config = {
|
||||
gapsOuterRight: number,
|
||||
gapsInnerHorizontal: number,
|
||||
gapsInnerVertical: number,
|
||||
overscroll: number,
|
||||
manualScrollStep: number,
|
||||
manualResizeStep: number,
|
||||
offScreenOpacity: number,
|
||||
untileOnDrag: boolean,
|
||||
stackColumnsByDefault: boolean,
|
||||
|
||||
@@ -101,16 +101,16 @@ 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",
|
||||
|
||||
1
src/extern/qt.d.ts
vendored
1
src/extern/qt.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
declare const console: {
|
||||
log(...args: any[]);
|
||||
trace();
|
||||
assert(boolean);
|
||||
};
|
||||
|
||||
|
||||
@@ -55,10 +55,9 @@ class Desktop {
|
||||
)
|
||||
}
|
||||
|
||||
// calculates a Range that scrolls the contained Range into view
|
||||
public calculateVisibleRange(containedRange: Desktop.Range) {
|
||||
const left = containedRange.getLeft();
|
||||
const right = containedRange.getRight();
|
||||
public scrollIntoView(range: Desktop.Range) {
|
||||
const left = range.getLeft();
|
||||
const right = range.getRight();
|
||||
const initialVisibleRange = this.getCurrentVisibleRange();
|
||||
|
||||
let targetScrollX: number;
|
||||
@@ -67,26 +66,10 @@ class Desktop {
|
||||
} else if (right > initialVisibleRange.getRight()) {
|
||||
targetScrollX = right - this.tilingArea.width;
|
||||
} else {
|
||||
return this.getVisibleRange(this.clampScrollX(this.scrollX));
|
||||
targetScrollX = initialVisibleRange.getLeft();
|
||||
}
|
||||
|
||||
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.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 scrollToRange(range: Desktop.Range) {
|
||||
this.setScroll(this.calculateVisibleRange(range).getLeft(), true);
|
||||
this.setScroll(targetScrollX, false);
|
||||
}
|
||||
|
||||
public scrollCenterRange(range: Desktop.Range) {
|
||||
@@ -95,6 +78,16 @@ class Desktop {
|
||||
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 || focusedColumn.grid !== this.grid) {
|
||||
@@ -141,18 +134,32 @@ class Desktop {
|
||||
|
||||
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 minWidths = visibleColumns.map(column => column.getMinWidth()).sort((a, b) => b - a);
|
||||
for (const minWidth of minWidths) {
|
||||
if (minWidth > remainingWidth / remainingColumns) {
|
||||
remainingWidth -= minWidth;
|
||||
remainingColumns--;
|
||||
}
|
||||
}
|
||||
|
||||
const targetVisibleRange = Desktop.RangeImpl.fromRanges(
|
||||
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],
|
||||
);
|
||||
this.setScroll(this.calculateVisibleRange(targetVisibleRange).getLeft(), false);
|
||||
));
|
||||
}
|
||||
|
||||
public arrange() {
|
||||
@@ -187,7 +194,6 @@ namespace Desktop {
|
||||
marginBottom: number,
|
||||
marginLeft: number,
|
||||
marginRight: number,
|
||||
overscroll: number,
|
||||
scroller: Desktop.Scroller,
|
||||
};
|
||||
|
||||
@@ -225,6 +231,79 @@ namespace Desktop {
|
||||
}
|
||||
}
|
||||
|
||||
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 ? -Infinity : leftColumn.getWidth();
|
||||
const rightWidth = rightColumn === null ? -Infinity : 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;
|
||||
|
||||
@@ -134,93 +134,6 @@ class Grid {
|
||||
return width;
|
||||
}
|
||||
|
||||
public increaseColumnWidth(column: Column) {
|
||||
const visibleRange = this.desktop.calculateVisibleRange(column);
|
||||
if(!column.isVisible(visibleRange, true) || column.getWidth() >= column.getMaxWidth()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let leftVisibleColumn = this.getLeftmostVisibleColumn(visibleRange, true);
|
||||
let rightVisibleColumn = this.getRightmostVisibleColumn(visibleRange, true);
|
||||
if (leftVisibleColumn === null || rightVisibleColumn === null) {
|
||||
console.assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
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)!;
|
||||
}
|
||||
}
|
||||
|
||||
this.desktop.scrollCenterRange(Desktop.RangeImpl.fromRanges(leftVisibleColumn, rightVisibleColumn));
|
||||
}
|
||||
|
||||
public decreaseColumnWidth(column: Column) {
|
||||
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;
|
||||
}
|
||||
|
||||
const leftVisibleColumn = this.getLeftmostVisibleColumn(visibleRange, true);
|
||||
const rightVisibleColumn = this.getRightmostVisibleColumn(visibleRange, true);
|
||||
if (leftVisibleColumn === null || rightVisibleColumn === null) {
|
||||
console.assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public arrange(x: number, visibleRange: Range) {
|
||||
for (const column of this.columns.iterator()) {
|
||||
column.arrange(x, visibleRange, this.userResize);
|
||||
@@ -275,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,87 +1,9 @@
|
||||
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);
|
||||
desktop.scrollCenterVisible(column, true);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class ScrollerLazy {
|
||||
public scrollToColumn(desktop: Desktop, column: Column) {
|
||||
desktop.scrollToRange(column);
|
||||
desktop.scrollIntoView(column);
|
||||
}
|
||||
|
||||
public clampScrollX(desktop: Desktop, x: number) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,10 +40,9 @@ class World {
|
||||
marginBottom: config.gapsOuterBottom,
|
||||
marginLeft: config.gapsOuterLeft,
|
||||
marginRight: config.gapsOuterRight,
|
||||
overscroll: config.overscroll,
|
||||
scroller: config.scrollingLazy ? new ScrollerLazy() :
|
||||
config.scrollingCentered ? new ScrollerCentered() :
|
||||
config.scrollingGrouped ? new ScrollerGrouped(layoutConfig) :
|
||||
config.scrollingGrouped ? new ScrollerGrouped() :
|
||||
console.assert(false),
|
||||
},
|
||||
layoutConfig,
|
||||
|
||||
Reference in New Issue
Block a user