refactor: split engine window to interface

This commit is contained in:
Mikhail Zolotukhin 2021-10-19 00:04:36 +03:00
parent 26280306fb
commit bf4244562d
18 changed files with 353 additions and 179 deletions

View File

@ -7,7 +7,7 @@
import { createMock } from "ts-auto-mock";
import { Engine } from "../engine";
import { WindowsLayout } from "../engine/layout";
import Window from "../engine/window";
import { EngineWindow } from "../engine/window";
import { NullLog } from "../util/log";
import * as Action from "./action";
@ -89,9 +89,9 @@ describe("action", () => {
describe("move window", () => {
let fakeEngine: Engine;
let fakeCurrentWindow: Window;
let fakeCurrentWindow: EngineWindow;
beforeEach(() => {
fakeCurrentWindow = createMock<Window>();
fakeCurrentWindow = createMock<EngineWindow>();
fakeEngine = createMock<Engine>({
swapOrder: jest.fn(),
@ -169,10 +169,10 @@ describe("action", () => {
describe("window resize", () => {
let fakeEngine: Engine;
let fakeCurrentWindow: Window;
let fakeCurrentWindow: EngineWindow;
beforeEach(() => {
fakeCurrentWindow = createMock<Window>();
fakeCurrentWindow = createMock<EngineWindow>();
fakeEngine = createMock<Engine>({
resizeWindow: jest.fn(),
@ -313,7 +313,7 @@ describe("action", () => {
describe("toggle floating", () => {
it("executes correctly", () => {
const fakeCurrentWindow = createMock<Window>();
const fakeCurrentWindow = createMock<EngineWindow>();
const fakeEngine = createMock<Engine>({
toggleFloat: jest.fn(),
currentWindow: jest.fn().mockReturnValue(fakeCurrentWindow),
@ -329,7 +329,7 @@ describe("action", () => {
describe("push window into master area", () => {
it("executes correctly", () => {
const fakeCurrentWindow = createMock<Window>();
const fakeCurrentWindow = createMock<EngineWindow>();
const fakeEngine = createMock<Engine>({
setMaster: jest.fn(),
currentWindow: jest.fn().mockReturnValue(fakeCurrentWindow),
@ -348,10 +348,10 @@ describe("action", () => {
describe("layout switching", () => {
let fakeEngine: Engine;
let fakeCurrentWindow: Window;
let fakeCurrentWindow: EngineWindow;
beforeEach(() => {
fakeCurrentWindow = createMock<Window>();
fakeCurrentWindow = createMock<EngineWindow>();
fakeEngine = createMock<Engine>({
cycleLayout: jest.fn(),

View File

@ -4,7 +4,7 @@
// SPDX-License-Identifier: MIT
import { Engine, TilingEngine } from "../engine";
import Window from "../engine/window";
import { EngineWindow } from "../engine/window";
import { WindowState } from "../engine/window";
import { DriverContext, KWinDriver } from "../driver";
@ -31,7 +31,7 @@ export interface Controller {
/**
* Current active window. In other words the window, that has focus.
*/
currentWindow: Window | null;
currentWindow: EngineWindow | null;
/**
* Current screen. In other words the screen, that has focus.
@ -59,13 +59,13 @@ export interface Controller {
* React to window geometry update
* @param window the window whose geometry has changed
*/
onWindowGeometryChanged(window: Window): void;
onWindowGeometryChanged(window: EngineWindow): void;
/**
* React to window resizing
* @param window the window which is resized
*/
onWindowResize(window: Window): void;
onWindowResize(window: EngineWindow): void;
/**
* React to window resize operation start. The window
@ -73,7 +73,7 @@ export interface Controller {
* the window with the mouse by the window edges.
* @param window the window which is being resized
*/
onWindowResizeStart(window: Window): void;
onWindowResizeStart(window: EngineWindow): void;
/**
* React to window resize operation end. The window
@ -81,35 +81,35 @@ export interface Controller {
* the window.
* @param window the window which was dropped
*/
onWindowResizeOver(window: Window): void;
onWindowResizeOver(window: EngineWindow): void;
/**
* React to window addition
* @param window new added window
*/
onWindowAdded(window: Window): void;
onWindowAdded(window: EngineWindow): void;
/**
* React to window removal
* @param window the window which was removed
*/
onWindowRemoved(window: Window): void;
onWindowRemoved(window: EngineWindow): void;
/**
* React to window maximization state change
* @param window the window whose maximization state changed
* @param maximized new maximization state
*/
onWindowMaximizeChanged(window: Window, maximized: boolean): void;
onWindowMaximizeChanged(window: EngineWindow, maximized: boolean): void;
// TODO: add docs
onWindowChanged(window: Window | null, comment?: string): void;
onWindowChanged(window: EngineWindow | null, comment?: string): void;
/**
* React to window being moved.
* @param window the window, which it being moved.
*/
onWindowMove(window: Window): void;
onWindowMove(window: EngineWindow): void;
/**
* React to window move operation start. The move operation starts
@ -117,7 +117,7 @@ export interface Controller {
* the mouse's button being pressed
* @param window the window which is being dragged
*/
onWindowMoveStart(window: Window): void;
onWindowMoveStart(window: EngineWindow): void;
/**
* React to window move operation over. The move operation ends
@ -125,25 +125,25 @@ export interface Controller {
* the mouse's button being released.
* @param window the window which was being dragged
*/
onWindowMoveOver(window: Window): void;
onWindowMoveOver(window: EngineWindow): void;
/**
* React to the window gaining focus, attention and love it deserves
* @param window the window which received the focus
*/
onWindowFocused(window: Window): void;
onWindowFocused(window: EngineWindow): void;
/**
* React to the window shade state change
* @param window the window whose state was changed
*/
onWindowShadeChanged(window: Window): void;
onWindowShadeChanged(window: EngineWindow): void;
/**
* Ask engine to manage the window
* @param win the window which needs to be managed.
*/
manageWindow(win: Window): void;
manageWindow(win: EngineWindow): void;
}
export class TilingController implements Controller {
@ -178,11 +178,11 @@ export class TilingController implements Controller {
return this.driver.screens;
}
public get currentWindow(): Window | null {
public get currentWindow(): EngineWindow | null {
return this.driver.currentWindow;
}
public set currentWindow(value: Window | null) {
public set currentWindow(value: EngineWindow | null) {
this.driver.currentWindow = value;
}
@ -214,7 +214,7 @@ export class TilingController implements Controller {
}
}
public onWindowAdded(window: Window): void {
public onWindowAdded(window: EngineWindow): void {
this.log.log(["onWindowAdded", { window }]);
this.engine.manage(window);
@ -236,7 +236,7 @@ export class TilingController implements Controller {
this.engine.arrange();
}
public onWindowRemoved(window: Window): void {
public onWindowRemoved(window: EngineWindow): void {
this.log.log(["onWindowRemoved", { window }]);
this.engine.unmanage(window);
@ -244,7 +244,7 @@ export class TilingController implements Controller {
// Switch to next window if monocle with config.monocleMinimizeRest
if (
!window.window.isDialog &&
!window.isDialog &&
!this.currentWindow &&
this.engine.isLayoutMonocleAndMinimizeRest()
) {
@ -257,15 +257,15 @@ export class TilingController implements Controller {
}
}
public onWindowMoveStart(_window: Window): void {
public onWindowMoveStart(_window: EngineWindow): void {
/* do nothing */
}
public onWindowMove(_window: Window): void {
public onWindowMove(_window: EngineWindow): void {
/* do nothing */
}
public onWindowMoveOver(window: Window): void {
public onWindowMoveOver(window: EngineWindow): void {
this.log.log(["onWindowMoveOver", { window }]);
/* swap window by dragging */
@ -302,11 +302,11 @@ export class TilingController implements Controller {
window.commit();
}
public onWindowResizeStart(_window: Window): void {
public onWindowResizeStart(_window: EngineWindow): void {
/* do nothing */
}
public onWindowResize(window: Window): void {
public onWindowResize(window: EngineWindow): void {
this.log.log(["onWindowResize", { window }]);
if (this.config.adjustLayout && this.config.adjustLayoutLive) {
if (window.state === WindowState.Tiled) {
@ -316,7 +316,7 @@ export class TilingController implements Controller {
}
}
public onWindowResizeOver(window: Window): void {
public onWindowResizeOver(window: EngineWindow): void {
this.log.log(["onWindowResizeOver", { window }]);
if (this.config.adjustLayout && window.tiled) {
this.engine.adjustLayout(window);
@ -326,18 +326,21 @@ export class TilingController implements Controller {
}
}
public onWindowMaximizeChanged(_window: Window, _maximized: boolean): void {
public onWindowMaximizeChanged(
_window: EngineWindow,
_maximized: boolean
): void {
this.engine.arrange();
}
public onWindowGeometryChanged(window: Window): void {
public onWindowGeometryChanged(window: EngineWindow): void {
this.log.log(["onWindowGeometryChanged", { window }]);
this.engine.enforceSize(window);
}
// NOTE: accepts `null` to simplify caller. This event is a catch-all hack
// by itself anyway.
public onWindowChanged(window: Window | null, comment?: string): void {
public onWindowChanged(window: EngineWindow | null, comment?: string): void {
if (window) {
this.log.log(["onWindowChanged", { window, comment }]);
@ -349,7 +352,7 @@ export class TilingController implements Controller {
}
}
public onWindowFocused(window: Window): void {
public onWindowFocused(window: EngineWindow): void {
try {
window.timestamp = new Date().getTime();
this.currentWindow = window;
@ -376,7 +379,7 @@ export class TilingController implements Controller {
}
}
public onWindowShadeChanged(win: Window): void {
public onWindowShadeChanged(win: EngineWindow): void {
this.log.log(`onWindowShadeChanged, window: ${win}`);
// NOTE: Float shaded windows and change their state back once unshaded
@ -391,7 +394,7 @@ export class TilingController implements Controller {
this.engine.arrange();
}
public manageWindow(win: Window): void {
public manageWindow(win: EngineWindow): void {
this.engine.manage(win);
}

View File

@ -10,7 +10,7 @@ import { KWinWindow } from "./window";
import { Controller } from "../controller";
import { Action } from "../controller/action";
import Window from "../engine/window";
import { EngineWindow, EngineWindowImpl } from "../engine/window";
import { WindowState } from "../engine/window";
@ -21,7 +21,7 @@ export interface DriverContext {
readonly screens: DriverSurface[];
currentSurface: DriverSurface;
currentWindow: Window | null;
currentWindow: EngineWindow | null;
showNotification(text: string): void;
@ -63,12 +63,12 @@ export class KWinDriver implements DriverContext {
}
}
public get currentWindow(): Window | null {
public get currentWindow(): EngineWindow | null {
const client = this.kwinApi.workspace.activeClient;
return client ? this.windowMap.get(client) : null;
}
public set currentWindow(window: Window | null) {
public set currentWindow(window: EngineWindow | null) {
if (window !== null) {
this.kwinApi.workspace.activeClient = (
window.window as KWinWindow
@ -94,7 +94,7 @@ export class KWinDriver implements DriverContext {
}
private controller: Controller;
private windowMap: WrapperMap<KWin.Client, Window>;
private windowMap: WrapperMap<KWin.Client, EngineWindow>;
private entered: boolean;
private qml: Bismuth.Qml.Main;
@ -126,7 +126,7 @@ export class KWinDriver implements DriverContext {
this.windowMap = new WrapperMap(
(client: KWin.Client) => KWinWindow.generateID(client),
(client: KWin.Client) =>
new Window(
new EngineWindowImpl(
new KWinWindow(client, this.qml, this.kwinApi, this.config, this.log),
this.config,
this.log
@ -349,7 +349,7 @@ export class KWinDriver implements DriverContext {
}
}
private bindWindowEvents(window: Window, client: KWin.Client): void {
private bindWindowEvents(window: EngineWindow, client: KWin.Client): void {
let moving = false;
let resizing = false;

View File

@ -15,7 +15,7 @@ import { Log } from "../util/log";
import Rect from "../util/rect";
import TileLayout from "./layout/tile_layout";
import LayoutStore from "./layout_store";
import Window, { WindowState } from "./window";
import { EngineWindow, WindowState } from "./window";
import WindowStore from "./window_store";
describe("arrange", () => {
@ -47,12 +47,12 @@ describe("arrangeScreen", () => {
const configMock = createMock<Config>();
const engine = new TilingEngine(controllerMock, configMock, logMock);
const window1 = createMock<Window>({
const window1 = createMock<EngineWindow>({
shouldFloat: false,
state: WindowState.Undecided,
});
const window2 = createMock<Window>({
const window2 = createMock<EngineWindow>({
shouldFloat: true,
state: WindowState.Undecided,
});

View File

@ -7,8 +7,7 @@ import MonocleLayout from "./layout/monocle_layout";
import LayoutStore from "./layout_store";
import WindowStore from "./window_store";
import Window from "./window";
import { WindowState } from "./window";
import { EngineWindow, EngineWindowImpl, WindowState } from "./window";
import { Controller } from "../controller";
@ -28,27 +27,27 @@ export interface Engine {
windows: WindowStore;
arrange(): void;
manage(window: Window): void;
unmanage(window: Window): void;
adjustLayout(basis: Window): void;
manage(window: EngineWindow): void;
unmanage(window: EngineWindow): void;
adjustLayout(basis: EngineWindow): void;
resizeFloat(
window: Window,
window: EngineWindow,
dir: "east" | "west" | "south" | "north",
step: -1 | 1
): void;
resizeTile(
basis: Window,
basis: EngineWindow,
dir: "east" | "west" | "south" | "north",
step: -1 | 1
): void;
resizeWindow(
window: Window,
window: EngineWindow,
dir: "east" | "west" | "south" | "north",
step: -1 | 1
): void;
enforceSize(window: Window): void;
enforceSize(window: EngineWindow): void;
currentLayoutOnCurrentSurface(): WindowsLayout;
currentWindow(): Window | null;
currentWindow(): EngineWindow | null;
/**
* Focus next or previous window
@ -57,14 +56,14 @@ export interface Engine {
*/
focusOrder(step: -1 | 1, includeHidden: boolean): void;
focusDir(dir: Direction): void;
swapOrder(window: Window, step: -1 | 1): void;
swapOrder(window: EngineWindow, step: -1 | 1): void;
swapDirOrMoveFloat(dir: Direction): void;
setMaster(window: Window): void;
toggleFloat(window: Window): void;
setMaster(window: EngineWindow): void;
toggleFloat(window: EngineWindow): void;
floatAll(srf: DriverSurface): void;
cycleLayout(step: 1 | -1): void;
setLayout(layoutClassID: string): void;
minimizeOthers(window: Window): void;
minimizeOthers(window: EngineWindow): void;
isLayoutMonocleAndMinimizeRest(): boolean;
showNotification(text: string): void;
}
@ -93,7 +92,7 @@ export class TilingEngine implements Engine {
*
* Used when tile is resized using mouse.
*/
public adjustLayout(basis: Window): void {
public adjustLayout(basis: EngineWindow): void {
const srf = basis.surface;
const layout = this.layouts.getCurrentLayout(srf);
if (layout.adjust) {
@ -114,7 +113,7 @@ export class TilingEngine implements Engine {
* @param window a floating window
*/
public resizeFloat(
window: Window,
window: EngineWindow,
dir: "east" | "west" | "south" | "north",
step: -1 | 1
): void {
@ -153,7 +152,7 @@ export class TilingEngine implements Engine {
* Used by grow/shrink shortcuts.
*/
public resizeTile(
basis: Window,
basis: EngineWindow,
dir: "east" | "west" | "south" | "north",
step: -1 | 1
): void {
@ -220,14 +219,14 @@ export class TilingEngine implements Engine {
* @param step which direction. 1 means outward, -1 means inward.
*/
public resizeWindow(
window: Window,
window: EngineWindow,
dir: "east" | "west" | "south" | "north",
step: -1 | 1
): void {
const state = window.state;
if (Window.isFloatingState(state)) {
if (EngineWindowImpl.isFloatingState(state)) {
this.resizeFloat(window, dir, step);
} else if (Window.isTiledState(state)) {
} else if (EngineWindowImpl.isTiledState(state)) {
this.resizeTile(window, dir, step);
}
}
@ -265,7 +264,7 @@ export class TilingEngine implements Engine {
]);
// Set correct window state for new windows
visibleWindows.forEach((win: Window) => {
visibleWindows.forEach((win: EngineWindow) => {
if (win.state === WindowState.Undecided) {
win.state = win.shouldFloat ? WindowState.Floating : WindowState.Tiled;
}
@ -303,7 +302,7 @@ export class TilingEngine implements Engine {
}
// Commit window assigned properties
visibleWindows.forEach((win: Window) => win.commit());
visibleWindows.forEach((win: EngineWindow) => win.commit());
this.log.log(["arrangeScreen/finished", { screenSurface }]);
}
@ -311,7 +310,7 @@ export class TilingEngine implements Engine {
return this.layouts.getCurrentLayout(this.controller.currentSurface);
}
public currentWindow(): Window | null {
public currentWindow(): EngineWindow | null {
return this.controller.currentWindow;
}
@ -322,7 +321,7 @@ export class TilingEngine implements Engine {
* which is straight against the purpose of tiling WM. This operation
* move/resize such windows back to where/how they should be.
*/
public enforceSize(window: Window): void {
public enforceSize(window: EngineWindow): void {
if (window.tiled && !window.actualGeometry.equals(window.geometry)) {
window.commit();
}
@ -331,7 +330,7 @@ export class TilingEngine implements Engine {
/**
* Register the given window to WM.
*/
public manage(window: Window): void {
public manage(window: EngineWindow): void {
if (!window.shouldIgnore) {
/* engine#arrange will update the state when required. */
window.state = WindowState.Undecided;
@ -346,7 +345,7 @@ export class TilingEngine implements Engine {
/**
* Unregister the given window from WM.
*/
public unmanage(window: Window): void {
public unmanage(window: EngineWindow): void {
this.windows.remove(window);
}
@ -414,7 +413,7 @@ export class TilingEngine implements Engine {
/**
* Swap the position of the current window with the next or previous window.
*/
public swapOrder(window: Window, step: -1 | 1): void {
public swapOrder(window: EngineWindow, step: -1 | 1): void {
const srf = window.surface;
const visibles = this.windows.getVisibleWindows(srf);
if (visibles.length < 2) {
@ -455,7 +454,7 @@ export class TilingEngine implements Engine {
* @param window a floating window
* @param dir which direction
*/
public moveFloat(window: Window, dir: Direction): void {
public moveFloat(window: EngineWindow, dir: Direction): void {
const srf = window.surface;
// TODO: configurable step size?
@ -492,9 +491,9 @@ export class TilingEngine implements Engine {
}
const state = window.state;
if (Window.isFloatingState(state)) {
if (EngineWindowImpl.isFloatingState(state)) {
this.moveFloat(window, dir);
} else if (Window.isTiledState(state)) {
} else if (EngineWindowImpl.isTiledState(state)) {
this.swapDirection(dir);
}
}
@ -502,7 +501,7 @@ export class TilingEngine implements Engine {
/**
* Toggle float mode of window.
*/
public toggleFloat(window: Window): void {
public toggleFloat(window: EngineWindow): void {
window.state = !window.tileable ? WindowState.Tiled : WindowState.Floating;
}
@ -541,7 +540,7 @@ export class TilingEngine implements Engine {
* Some layouts depend on this assumption, and will make such windows more
* visible than others.
*/
public setMaster(window: Window): void {
public setMaster(window: EngineWindow): void {
this.windows.setMaster(window);
}
@ -591,7 +590,7 @@ export class TilingEngine implements Engine {
* Minimize all windows on the surface except the given window.
* Used mainly in Monocle mode with config.monocleMinimizeRest
*/
public minimizeOthers(window: Window): void {
public minimizeOthers(window: EngineWindow): void {
for (const tile of this.windows.getVisibleTiles(window.surface)) {
if (
tile.screen == window.screen &&
@ -612,7 +611,10 @@ export class TilingEngine implements Engine {
);
}
private getNeighborByDirection(basis: Window, dir: Direction): Window | null {
private getNeighborByDirection(
basis: EngineWindow,
dir: Direction
): EngineWindow | null {
let vertical: boolean;
let sign: -1 | 1;
switch (dir) {

View File

@ -6,8 +6,7 @@
import { WindowsLayout } from ".";
import { Engine } from "..";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import {
Action,
@ -66,7 +65,11 @@ export default class CascadeLayout implements WindowsLayout {
/* nothing */
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
const [vertStep, horzStep] = CascadeLayout.decomposeDirection(this.dir);
// TODO: adjustable step size

View File

@ -5,8 +5,7 @@
import { WindowsLayout } from ".";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import Rect from "../../util/rect";
import { Controller } from "../../controller";
@ -20,11 +19,11 @@ export default class FloatingLayout implements WindowsLayout {
public apply(
_controller: Controller,
tileables: Window[],
tileables: EngineWindow[],
_area: Rect
): void {
tileables.forEach(
(tileable: Window) => (tileable.state = WindowState.TiledAfloat)
(tileable: EngineWindow) => (tileable.state = WindowState.TiledAfloat)
);
}

View File

@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT
import Window from "../window";
import { EngineWindow } from "../window";
import { Engine } from "..";
import { Controller } from "../../controller";
@ -22,8 +22,13 @@ export interface WindowsLayout {
readonly capacity?: number;
readonly description: string;
adjust?(area: Rect, tiles: Window[], basis: Window, delta: RectDelta): void;
apply(controller: Controller, tileables: Window[], area: Rect): void;
adjust?(
area: Rect,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): void;
apply(controller: Controller, tileables: EngineWindow[], area: Rect): void;
executeAction?(engine: Engine, action: Action): void;
toString(): string;

View File

@ -5,7 +5,7 @@
import LayoutUtils from "./layout_utils";
import Window from "../window";
import { EngineWindow } from "../window";
import Config from "../../config";
import Rect from "../../util/rect";
@ -14,25 +14,25 @@ import RectDelta from "../../util/rectdelta";
export interface ILayoutPart {
adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): RectDelta;
apply(area: Rect, tiles: Window[]): Rect[];
apply(area: Rect, tiles: EngineWindow[]): Rect[];
}
export class FillLayoutPart implements ILayoutPart {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): RectDelta {
/* do nothing */
return delta;
}
public apply(area: Rect, tiles: Window[]): Rect[] {
public apply(area: Rect, tiles: EngineWindow[]): Rect[] {
return tiles.map((_tile) => {
return area;
});
@ -74,8 +74,8 @@ export class HalfSplitLayoutPart<L extends ILayoutPart, R extends ILayoutPart>
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): RectDelta {
const basisIndex = tiles.indexOf(basis);
@ -141,7 +141,7 @@ export class HalfSplitLayoutPart<L extends ILayoutPart, R extends ILayoutPart>
}
}
public apply(area: Rect, tiles: Window[]): Rect[] {
public apply(area: Rect, tiles: EngineWindow[]): Rect[] {
if (tiles.length <= this.primarySize) {
/* primary only */
return this.primary.apply(area, tiles);
@ -183,8 +183,8 @@ export class StackLayoutPart implements ILayoutPart {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): RectDelta {
const weights = LayoutUtils.adjustAreaWeights(
@ -209,7 +209,7 @@ export class StackLayoutPart implements ILayoutPart {
);
}
public apply(area: Rect, tiles: Window[]): Rect[] {
public apply(area: Rect, tiles: EngineWindow[]): Rect[] {
const weights = tiles.map((tile) => tile.weight);
return LayoutUtils.splitAreaWeighted(area, weights, this.gap);
}
@ -220,8 +220,8 @@ export class RotateLayoutPart<T extends ILayoutPart> implements ILayoutPart {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): RectDelta {
// let area = area, delta = delta;
@ -260,7 +260,7 @@ export class RotateLayoutPart<T extends ILayoutPart> implements ILayoutPart {
return delta;
}
public apply(area: Rect, tiles: Window[]): Rect[] {
public apply(area: Rect, tiles: EngineWindow[]): Rect[] {
switch (this.angle) {
case 0:
break;

View File

@ -5,8 +5,7 @@
import { WindowsLayout } from ".";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import {
Action,
@ -35,7 +34,11 @@ export default class MonocleLayout implements WindowsLayout {
this.config = config;
}
public apply(controller: Controller, tileables: Window[], area: Rect): void {
public apply(
controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
/* Tile all tileables */
tileables.forEach((tile) => {
tile.state = this.config.monocleMaximize

View File

@ -5,8 +5,7 @@
import { WindowsLayout } from ".";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import { clip } from "../../util/func";
import Rect from "../../util/rect";
@ -41,8 +40,8 @@ export default class QuarterLayout implements WindowsLayout {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): void {
if (tiles.length <= 1 || tiles.length > 4) {
@ -113,7 +112,11 @@ export default class QuarterLayout implements WindowsLayout {
return other;
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
for (let i = 0; i < 4 && i < tileables.length; i++) {
tileables[i].state = WindowState.Tiled;
}

View File

@ -7,8 +7,7 @@ import { HalfSplitLayoutPart } from "./layout_part";
import { FillLayoutPart } from "./layout_part";
import { WindowsLayout } from ".";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import Rect from "../../util/rect";
import RectDelta from "../../util/rectdelta";
@ -42,14 +41,18 @@ export default class SpiralLayout implements WindowsLayout {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): void {
this.parts.adjust(area, tiles, basis, delta);
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
tileables.forEach((tileable) => (tileable.state = WindowState.Tiled));
this.bore(tileables.length);

View File

@ -5,8 +5,7 @@
import { WindowsLayout } from ".";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import {
Action,
@ -30,7 +29,11 @@ export default class SpreadLayout implements WindowsLayout {
this.space = 0.07;
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
/* Tile all tileables */
tileables.forEach((tileable) => (tileable.state = WindowState.Tiled));
const tiles = tileables;

View File

@ -5,8 +5,7 @@
import { WindowsLayout } from ".";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import {
Action,
@ -30,7 +29,11 @@ export default class StairLayout implements WindowsLayout {
this.space = 24;
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
/* Tile all tileables */
tileables.forEach((tileable) => (tileable.state = WindowState.Tiled));
const tiles = tileables;

View File

@ -6,8 +6,7 @@
import { WindowsLayout } from ".";
import LayoutUtils from "./layout_utils";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import {
Action,
@ -48,8 +47,8 @@ export default class ThreeColumnLayout implements WindowsLayout {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): void {
const basisIndex = tiles.indexOf(basis);
@ -128,7 +127,10 @@ export default class ThreeColumnLayout implements WindowsLayout {
/* adjust tile weight */
const rstackNumTile = Math.floor((tiles.length - this.masterSize) / 2);
const [masterTiles, rstackTiles, lstackTiles] =
partitionArrayBySizes<Window>(tiles, [this.masterSize, rstackNumTile]);
partitionArrayBySizes<EngineWindow>(tiles, [
this.masterSize,
rstackNumTile,
]);
const groupTiles = [lstackTiles, masterTiles, rstackTiles][basisGroup];
LayoutUtils.adjustAreaWeights(
area /* we only need height */,
@ -142,7 +144,11 @@ export default class ThreeColumnLayout implements WindowsLayout {
}
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
/* Tile all tileables */
tileables.forEach((tileable) => (tileable.state = WindowState.Tiled));
const tiles = tileables;
@ -185,7 +191,10 @@ export default class ThreeColumnLayout implements WindowsLayout {
const rstackSize = Math.floor((tiles.length - this.masterSize) / 2);
const [masterTiles, rstackTiles, lstackTiles] =
partitionArrayBySizes<Window>(tiles, [this.masterSize, rstackSize]);
partitionArrayBySizes<EngineWindow>(tiles, [
this.masterSize,
rstackSize,
]);
[lstackTiles, masterTiles, rstackTiles].forEach((groupTiles, group) => {
LayoutUtils.splitAreaWeighted(
groupAreas[group],

View File

@ -10,8 +10,7 @@ import {
StackLayoutPart,
} from "./layout_part";
import Window from "../window";
import { WindowState } from "../window";
import { WindowState, EngineWindow } from "../window";
import {
Action,
@ -82,14 +81,18 @@ export default class TileLayout implements WindowsLayout {
public adjust(
area: Rect,
tiles: Window[],
basis: Window,
tiles: EngineWindow[],
basis: EngineWindow,
delta: RectDelta
): void {
this.parts.adjust(area, tiles, basis, delta);
}
public apply(_controller: Controller, tileables: Window[], area: Rect): void {
public apply(
_controller: Controller,
tileables: EngineWindow[],
area: Rect
): void {
tileables.forEach((tileable) => (tileable.state = WindowState.Tiled));
this.parts.apply(area, tileables).forEach((geometry, i) => {

View File

@ -12,14 +12,20 @@ import Rect from "../util/rect";
import RectDelta from "../util/rectdelta";
export enum WindowState {
/* initial value */
/**
* Initial value
*/
Unmanaged,
/* script-external state - overrides internal state */
/**
* Script-external state - overrides internal state
*/
NativeFullscreen,
NativeMaximized,
/* script-internal state */
/**
* Script-internal state
*/
Floating,
Maximized,
Tiled,
@ -27,7 +33,135 @@ export enum WindowState {
Undecided,
}
export default class Window {
/**
* Window with the convenient for the Engine Interface
*/
export interface EngineWindow {
/**
* Window unique id
*/
readonly id: string;
/**
* If this window ***can be*** tiled by layout.
*/
readonly tileable: boolean;
/**
* If this window is ***already*** tiled, thus a part of the current layout.
*/
readonly tiled: boolean;
/**
* If this window is floating, thus its geometry is not tightly managed.
*/
readonly floating: boolean;
/**
* Whether the window is shaded (collapsed to the title bar)
*/
readonly shaded: boolean;
/**
* Low-level implementation, usable for Driver
*/
readonly window: DriverWindow;
/**
* Difference between geometry and actual geometry
*/
readonly geometryDelta: RectDelta;
/**
* Actual geometry
*/
readonly actualGeometry: Readonly<Rect>;
/**
* Whether the window is a dialog window
*/
readonly isDialog: boolean;
/**
* Whether the window should be set to floating state
*/
readonly shouldFloat: boolean;
/**
* Whether the window should be ignored by the script
*/
readonly shouldIgnore: boolean;
/**
* State to which the window was asked to be changed
* previously. This can be the same state, as the current
* one.
*/
readonly statePreviouslyAskedToChangeTo: WindowState;
/**
* Screen number, on which the window is present
*/
readonly screen: number;
/**
* Whether the window is minimized
*/
minimized: boolean;
/**
* Geometry of a window, while in floated state
*/
floatGeometry: Rect;
/**
* Window geometry
*/
geometry: Rect;
/**
* Surface, the window is currently on
*/
surface: DriverSurface;
/**
* General state of the window: floating, maximized, tiled etc.
*/
state: WindowState;
/**
* The timestamp when the last time Window was focused.
*/
timestamp: number;
/**
* Window weight.
* TODO: This needs a better explanation. This has something to do with ThreeColumnLayout.
*/
weight: number;
/**
* Whether the window is visible on concrete surface
* @param surface the surface visibility on which is checked
*/
visible(surface: DriverSurface): boolean;
/**
* Force apply the geometry *immediately*.
*
* This method is a quick hack created for engine#resizeFloat, thus should
* not be used in other places.
*/
forceSetGeometry(geometry: Rect): void;
/**
* Update changed window properties on the KWin side.
* I.e. make the changes visible to the end user.
*/
commit(): void;
}
export class EngineWindowImpl implements EngineWindow {
public static isTileableState(state: WindowState): boolean {
return (
state === WindowState.Tiled ||
@ -69,17 +203,16 @@ export default class Window {
this.window.minimized = min;
}
/** If this window ***can be*** tiled by layout. */
public get tileable(): boolean {
return Window.isTileableState(this.state);
return EngineWindowImpl.isTileableState(this.state);
}
/** If this window is ***already*** tiled, thus a part of the current layout. */
public get tiled(): boolean {
return Window.isTiledState(this.state);
return EngineWindowImpl.isTiledState(this.state);
}
/** If this window is floating, thus its geometry is not tightly managed. */
public get floating(): boolean {
return Window.isFloatingState(this.state);
return EngineWindowImpl.isFloatingState(this.state);
}
public get geometryDelta(): RectDelta {
@ -125,13 +258,13 @@ export default class Window {
if (
(winState === WindowState.Unmanaged ||
Window.isTileableState(winState)) &&
Window.isFloatingState(value)
EngineWindowImpl.isTileableState(winState)) &&
EngineWindowImpl.isFloatingState(value)
) {
this.shouldCommitFloat = true;
} else if (
Window.isFloatingState(winState) &&
Window.isTileableState(value)
EngineWindowImpl.isFloatingState(winState) &&
EngineWindowImpl.isTileableState(value)
) {
/* save the current geometry before leaving floating state */
this.floatGeometry = this.actualGeometry;
@ -167,6 +300,10 @@ export default class Window {
this.weightMap[srfID] = value;
}
public get isDialog(): boolean {
return this.window.isDialog;
}
private internalState: WindowState;
private internalStatePreviouslyAskedToChangeTo: WindowState;
private shouldCommitFloat: boolean;
@ -236,12 +373,6 @@ export default class Window {
}
}
/**
* Force apply the geometry *immediately*.
*
* This method is a quick hack created for engine#resizeFloat, thus should
* not be used in other places.
*/
public forceSetGeometry(geometry: Rect): void {
this.window.commit(geometry);
}

View File

@ -3,18 +3,22 @@
//
// SPDX-License-Identifier: MIT
import Window from "./window";
import { EngineWindow } from "./window";
import { DriverSurface } from "../driver/surface";
export default class WindowStore {
public list: Window[];
public list: EngineWindow[];
constructor(windows?: Window[]) {
constructor(windows?: EngineWindow[]) {
this.list = windows || [];
}
public move(srcWin: Window, destWin: Window, after?: boolean): void {
public move(
srcWin: EngineWindow,
destWin: EngineWindow,
after?: boolean
): void {
const srcIdx = this.list.indexOf(srcWin);
const destIdx = this.list.indexOf(destWin);
if (srcIdx === -1 || destIdx === -1) {
@ -25,7 +29,7 @@ export default class WindowStore {
this.list.splice(after ? destIdx + 1 : destIdx, 0, srcWin);
}
public setMaster(window: Window): void {
public setMaster(window: EngineWindow): void {
const idx = this.list.indexOf(window);
if (idx === -1) {
return;
@ -34,7 +38,7 @@ export default class WindowStore {
this.list.splice(0, 0, window);
}
public swap(alpha: Window, beta: Window): void {
public swap(alpha: EngineWindow, beta: EngineWindow): void {
const alphaIndex = this.list.indexOf(alpha);
const betaIndex = this.list.indexOf(beta);
if (alphaIndex < 0 || betaIndex < 0) {
@ -51,26 +55,26 @@ export default class WindowStore {
return this.list.length;
}
public at(idx: number): Window {
public at(idx: number): EngineWindow {
return this.list[idx];
}
public indexOf(window: Window): number {
public indexOf(window: EngineWindow): number {
return this.list.indexOf(window);
}
public push(window: Window): void {
public push(window: EngineWindow): void {
this.list.push(window);
}
public remove(window: Window): void {
public remove(window: EngineWindow): void {
const idx = this.list.indexOf(window);
if (idx >= 0) {
this.list.splice(idx, 1);
}
}
public unshift(window: Window): void {
public unshift(window: EngineWindow): void {
this.list.unshift(window);
}
//#endregion
@ -78,12 +82,12 @@ export default class WindowStore {
//#region Querying Windows
/** Returns all visible windows on the given surface. */
public getVisibleWindows(srf: DriverSurface): Window[] {
public getVisibleWindows(srf: DriverSurface): EngineWindow[] {
return this.list.filter((win) => win.visible(srf));
}
/** Return all visible "Tile" windows on the given surface. */
public getVisibleTiles(srf: DriverSurface): Window[] {
public getVisibleTiles(srf: DriverSurface): EngineWindow[] {
return this.list.filter((win) => win.tiled && win.visible(srf));
}
@ -91,19 +95,19 @@ export default class WindowStore {
* Return all visible "tileable" windows on the given surface
* @see Window#tileable
*/
public getVisibleTileables(srf: DriverSurface): Window[] {
public getVisibleTileables(srf: DriverSurface): EngineWindow[] {
return this.list.filter((win) => win.tileable && win.visible(srf));
}
/**
* Return all "tileable" windows on the given surface, including hidden
*/
public getAllTileables(srf: DriverSurface): Window[] {
public getAllTileables(srf: DriverSurface): EngineWindow[] {
return this.list.filter((win) => win.tileable && win.surface.id === srf.id);
}
/** Return all windows on this surface, including minimized windows */
public getAllWindows(srf: DriverSurface): Window[] {
public getAllWindows(srf: DriverSurface): EngineWindow[] {
return this.list.filter((win) => win.surface.id === srf.id);
}