Skip to content
This repository has been archived by the owner on Jun 16, 2024. It is now read-only.

feat: Add VerticalTileLayout #408

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/config/bismuth_config.kcfg
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
<default>false</default>
</entry>

<entry name="enableVerticalTileLayout" type="Bool">
<label>Enable/disable Vertical Tile layout</label>
<default>false</default>
</entry>

<entry name="ignoreActivity" type="String">
<label>Do not apply tiling on some activities(comma-separated list of activity names)</label>
<default></default>
Expand Down
1 change: 1 addition & 0 deletions src/core/ts-proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ QJSValue TSProxy::jsConfig()
addLayout("enableQuarterLayout", "QuarterLayout");
addLayout("enableFloatingLayout", "FloatingLayout");
addLayout("enableCascadeLayout", "CascadeLayout");
addLayout("enableVerticalTileLayout", "VerticalTileLayout");

setProp("monocleMaximize", m_config.monocleMaximize());
setProp("maximizeSoleTile", m_config.maximizeSoleTile());
Expand Down
5 changes: 5 additions & 0 deletions src/kcm/package/contents/ui/views/Layouts.qml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ Kirigami.Page {
settingName: "enableFloatingLayout"
}

ListElement {
name: "Vertical Tile"
settingName: "enableVerticalTileLayout"
}

}

KCM.ScrollView {
Expand Down
13 changes: 13 additions & 0 deletions src/kwinscript/controller/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,19 @@ export class ToggleSpiralLayout extends ToggleCurrentLayout {
}
}

export class ToggleVerticalTileLayout extends ToggleCurrentLayout {
constructor(protected engine: Engine, protected log: Log) {
super(
engine,
"VerticalTileLayout",
"toggle_vertical_tile_layout",
"Toggle Vertical Tile Layout",
"",
log
);
}
}

export class Rotate extends ActionImpl implements Action {
constructor(protected engine: Engine, protected log: Log) {
super(engine, "rotate", "Rotate", "Meta+R", log);
Expand Down
1 change: 1 addition & 0 deletions src/kwinscript/controller/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ export class ControllerImpl implements Controller {
new Action.ToggleFloatingLayout(this.engine, this.log),
new Action.ToggleQuarterLayout(this.engine, this.log),
new Action.ToggleSpiralLayout(this.engine, this.log),
new Action.ToggleVerticalTileLayout(this.engine, this.log),

new Action.Rotate(this.engine, this.log),
new Action.RotateReverse(this.engine, this.log),
Expand Down
223 changes: 223 additions & 0 deletions src/kwinscript/engine/layout/vertical_layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// SPDX-FileCopyrightText: 2022 Diogo Ferreira <[email protected]>
//
// SPDX-License-Identifier: MIT

import { WindowsLayout } from ".";
import LayoutUtils from "./layout_utils";

import { WindowState, EngineWindow } from "../window";

import {
Action,
DecreaseLayoutMasterAreaSize,
IncreaseLayoutMasterAreaSize,
IncreaseMasterAreaWindowCount,
DecreaseMasterAreaWindowCount,
Rotate,
RotateReverse,
} from "../../controller/action";

import { partitionArrayBySizes, clip, slide } from "../../util/func";
import { Rect, RectDelta } from "../../util/rect";
import { Config } from "../../config";
import { Controller } from "../../controller";
import { Engine } from "..";

/**
* A Vertical tiling layout which is tailored for portrait orientated
* monitors but might have use cases in landscape as well.
*
* The intended behavior is for the master area to take a fixed (but
* configurable via shortcut) percentage of the screen and for the
* remaining panes to split the remaining space. Multiple masters are
* supported and will share the master area equally.
*
* ---------
* | |
* | M1 |
* | |
* | ------- |
* | |
* | M2 |
* | |
* ---------
* | 1 |
* ---------
* | 2 |
* ---------
*/
export default class VerticalTileLayout implements WindowsLayout {
public static readonly MIN_MASTER_RATIO = 0.2;
public static readonly MAX_MASTER_RATIO = 0.75;
public static readonly id = "VerticalTileLayout";
public readonly classID = VerticalTileLayout.id;
public readonly name = "Vertical Tile Layout";
public readonly icon = "bismuth-vertical-tile";

public get hint(): string {
return String(this.masterCount);
}

private masterRatio: number;
private masterCount: number;

private config: Config;

constructor(config: Config) {
this.config = config;
this.masterRatio = 0.75;
this.masterCount = 1;
}

public adjust(
area: Rect,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): void {
const basisIndex = tiles.indexOf(basis);
if (basisIndex < 0) {
return;
}

if (tiles.length === 0) {
/* no tiles */
return;
} else if (tiles.length <= this.masterCount) {
/* every window takes a piece of the master area */
LayoutUtils.adjustAreaWeights(
area,
tiles.map((tile) => tile.weight),
this.config.tileLayoutGap,
tiles.indexOf(basis),
delta
).forEach((newWeight, i) => (tiles[i].weight = newWeight * tiles.length));
} else if (tiles.length > this.masterCount) {
/* Two rows */
let basisGroup;
if (basisIndex < this.masterCount) {
/* master area */
basisGroup = 1;
} else {
/* bottom stack */
basisGroup = 0;
}

/* adjust master-stack ratio */
const stackRatio = 1 - this.masterRatio;
const newRatios = LayoutUtils.adjustAreaWeights(
area,
[stackRatio, this.masterRatio, stackRatio],
this.config.tileLayoutGap,
basisGroup,
delta,
false /* vertical */
);
const newMasterRatio = newRatios[1];
const newStackRatio = basisGroup === 0 ? newRatios[0] : newRatios[2];
this.masterRatio = newMasterRatio / (newMasterRatio + newStackRatio);

/* adjust tile weight */
const bottomStackNumTiles = tiles.length - this.masterCount;
const [masterTiles, bottomStackTiles] =
partitionArrayBySizes<EngineWindow>(tiles, [
this.masterCount,
bottomStackNumTiles,
]);
const groupTiles = [masterTiles, bottomStackTiles][basisGroup];
LayoutUtils.adjustAreaWeights(
area /* we only need height */,
groupTiles.map((tile) => tile.weight),
this.config.tileLayoutGap,
groupTiles.indexOf(basis),
delta,
false /* vertical */
).forEach(
(newWeight, i) => (groupTiles[i].weight = newWeight * groupTiles.length)
);
}
}

public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
/* Tile all tileables */
tileables.forEach((tileable) => (tileable.state = WindowState.Tiled));
const tiles = tileables;

if (tiles.length <= this.masterCount) {
/* only master */
LayoutUtils.splitAreaWeighted(
area,
tiles.map((tile) => tile.weight),
this.config.tileLayoutGap,
false /* vertical */
).forEach((tileArea, i) => (tiles[i].geometry = tileArea));
} else {
/* master & bottom-stack */
const stackRatio = 1 - this.masterRatio;

/** Areas allocated to master, and bottom-stack */
const groupAreas = LayoutUtils.splitAreaWeighted(
area,
[this.masterRatio, stackRatio],
this.config.tileLayoutGap,
false /* vertical */
);

const bottomStackNumTiles = tiles.length - this.masterCount;
const [masterTiles, bottomStackTiles] =
partitionArrayBySizes<EngineWindow>(tiles, [
this.masterCount,
bottomStackNumTiles,
]);
[masterTiles, bottomStackTiles].forEach((groupTiles, group) => {
LayoutUtils.splitAreaWeighted(
groupAreas[group],
groupTiles.map((tile) => tile.weight),
this.config.tileLayoutGap,
false /* vertical */
).forEach((tileArea, i) => (groupTiles[i].geometry = tileArea));
});
}
}

public clone(): WindowsLayout {
const other = new VerticalTileLayout(this.config);
other.masterRatio = this.masterRatio;
return other;
}

public executeAction(engine: Engine, action: Action): void {
if (action instanceof DecreaseLayoutMasterAreaSize) {
this.masterRatio = clip(
slide(this.masterRatio, -0.05),
VerticalTileLayout.MIN_MASTER_RATIO,
VerticalTileLayout.MAX_MASTER_RATIO
);
} else if (action instanceof IncreaseLayoutMasterAreaSize) {
this.masterRatio = clip(
slide(this.masterRatio, +0.05),
VerticalTileLayout.MIN_MASTER_RATIO,
VerticalTileLayout.MAX_MASTER_RATIO
);
} else if (action instanceof IncreaseMasterAreaWindowCount) {
this.resizeMaster(engine, 1);
} else if (action instanceof DecreaseMasterAreaWindowCount) {
this.resizeMaster(engine, -1);
} else {
action.executeWithoutLayoutOverride();
}
}

public toString(): string {
return `VerticalTileLayout(masterCount=${this.masterCount})`;
}

private resizeMaster(engine: Engine, step: -1 | 1): void {
this.masterCount = clip(this.masterCount + step, 1, 10);
engine.showLayoutNotification();
}
}
3 changes: 3 additions & 0 deletions src/kwinscript/engine/layout_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import SpiralLayout from "./layout/spiral_layout";
import SpreadLayout from "./layout/spread_layout";
import StairLayout from "./layout/stair_layout";
import ThreeColumnLayout from "./layout/three_column_layout";
import VerticalTileLayout from "./layout/vertical_layout";

export class LayoutStoreEntry {
public get currentLayout(): WindowsLayout {
Expand Down Expand Up @@ -96,6 +97,8 @@ export class LayoutStoreEntry {
return new ThreeColumnLayout(this.config);
} else if (id == TileLayout.id) {
return new TileLayout(this.config);
} else if (id == VerticalTileLayout.id) {
return new VerticalTileLayout(this.config);
} else {
return new FloatingLayout();
}
Expand Down
5 changes: 5 additions & 0 deletions src/kwinscript/icons/16-status-bismuth-vertical-tile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/kwinscript/icons/32-status-bismuth-vertical-tile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/kwinscript/icons/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ecm_install_icons(
16-status-bismuth-spread.svg
16-status-bismuth-stair.svg
16-status-bismuth-tile.svg
16-status-bismuth-vertical-tile.svg
32-status-bismuth-column.svg
32-status-bismuth-floating.svg
32-status-bismuth-monocle.svg
Expand All @@ -19,5 +20,6 @@ ecm_install_icons(
32-status-bismuth-spread.svg
32-status-bismuth-stair.svg
32-status-bismuth-tile.svg
32-status-bismuth-vertical-tile.svg
DESTINATION
${KDE_INSTALL_ICONDIR})