import {Position} from './position.js'; import Meta from 'gi://Meta'; import {Direction, TileWindowManager} from './tileWindowManager.js'; import GLib from 'gi://GLib'; export var Orientation; (function (Orientation) { Orientation[Orientation['Vertical'] = 1] = 'Vertical'; Orientation[Orientation['Horizontal'] = 2] = 'Horizontal'; Orientation[Orientation['None'] = 3] = 'None'; })(Orientation || (Orientation = {})); export var TileState; (function (TileState) { TileState[TileState['DEFAULT'] = 0] = 'DEFAULT'; TileState[TileState['MINIMIZED'] = 1] = 'MINIMIZED'; TileState[TileState['MAXIMIZED'] = 2] = 'MAXIMIZED'; TileState[TileState['ALONE_MAXIMIZED'] = 3] = 'ALONE_MAXIMIZED'; })(TileState || (TileState = {})); export class Tile { id; _leaf; _parent; _child1; _child2; _position; _window; _state; _orientation; _updateSource; _workspace; _nr_tiles; _monitor; _adjacents; static padding = 0; static id_count = 0; static fullscreen; constructor() { this.id = Tile.id_count++; this._position = new Position(); this._window = null; this._state = TileState.DEFAULT; this._leaf = true; this._parent = null; this._child1 = null; this._child2 = null; this._orientation = Orientation.None; this._nr_tiles = 0; this._workspace = 0; this._updateSource = null; // west / east / north / south this._adjacents = [false, false, false, false]; } /** Tile factory * * @param {Meta.Window} window * @param {Position} position * @param {number} monitor * @param {Tile | null} parent * @returns */ static createTileLeaf(window, position, monitor, parent = null) { let tile = new Tile(); tile._window = window; tile._position = position; tile._monitor = monitor; tile._parent = parent; tile._leaf = true; tile._nr_tiles = 1; tile._workspace = 0; tile.findAdjacents(); return tile; } decrementTiles() { this._nr_tiles--; if (this._parent) this._parent.decrementTiles(); } addWindowOnBlock(window) { if (this.leaf) { if (!this._window) throw new Error('A leaf must have a window'); let newPositions = this._position.split(); let c1 = Tile.createTileLeaf(this._window, newPositions[0], this.monitor, this); c1._state = this._state; this._window.tile = c1; let c2 = Tile.createTileLeaf(window, newPositions[1], this.monitor, this); window.tile = c2; this._window = null; this._leaf = false; this._child1 = c1; this._child2 = c2; this._nr_tiles++; this._orientation = this._position.width > this._position.height ? Orientation.Horizontal : Orientation.Vertical; c1.findAdjacents(); c2.findAdjacents(); } else if (this._child1 && this._child2) { if (this._child1?._nr_tiles > this._child2?._nr_tiles) this._child2.addWindowOnBlock(window); else this._child1.addWindowOnBlock(window); this._nr_tiles++; } else { throw new Error('Unexpected state to add window'); } } removeTile() { if (this._window == null || this._child1 || this._child2 || !this._leaf) throw new Error('Invalid remove state'); let parent = this._parent; if (!parent) return null; if (parent._child1 === this && parent._child2) { parent._leaf = parent._child2._leaf; parent._orientation = parent._child2._orientation; parent._window = parent._child2._window; parent._child1 = parent._child2._child1; parent._child2 = parent._child2._child2; } else if (parent._child2 === this && parent._child1) { parent._leaf = parent._child1._leaf; parent._orientation = parent._child1._orientation; parent._window = parent._child1._window; parent._child2 = parent._child1._child2; parent._child1 = parent._child1._child1; } else { return this; } if (parent._child1) parent._child1._parent = parent; if (parent._child2) parent._child2._parent = parent; if (parent._window) parent._window.tile = parent; parent.resize(parent._position); parent.decrementTiles(); return parent; } /** Update tile position and its children size * * @param {Position} position */ resize(position) { this._position = position; if (!this._window) { let newPositions = this._position.split(this._orientation); this._child1?.resize(newPositions[this._child1.position.index]); this._child2?.resize(newPositions[this._child2.position.index]); } } forEach(fn) { fn(this); this._child1?.forEach(fn); this._child2?.forEach(fn); } update() { if (this._window) { if (!this._window.isAlive) return; if (this._state === TileState.MAXIMIZED) { this._window?._originalMaximize(Meta.MaximizeFlags.BOTH); return; } else if (this._state === TileState.MINIMIZED) { this._window?._originalMinimize(); return; } if (this._window.minimized) this._window.unminimize(); if (this._position.proportion === 1) { this.state = TileState.ALONE_MAXIMIZED; if (this._window.maximized_horizontally || this._window.maximized_vertically) this._window.unmaximize(Meta.MaximizeFlags.BOTH); const workspc = this._window.get_workspace(); const area = workspc?.get_work_area_for_monitor(this._monitor ? this._monitor : 0); if (!area) return; this._position.x = 0; this._position.y = 0; this._position.width = area.width; this._position.height = area.height; this._window?.move_resize_frame(true, area.x + this._position.x + (this._adjacents[0] ? Tile.padding / 2 : Tile.padding), area.y + this._position.y + (this._adjacents[2] ? Tile.padding / 2 : Tile.padding), this._position.width - (this._adjacents[1] ? Tile.padding * 1.5 : Tile.padding * 2), this._position.height - (this._adjacents[3] ? Tile.padding * 1.5 : Tile.padding * 2)); if (this._updateSource !== null) GLib.Source.remove(this._updateSource); this._updateSource = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { this._window?.move_frame(true, area.x + this._position.x + (this._adjacents[0] ? Tile.padding / 2 : Tile.padding), area.y + this._position.y + (this._adjacents[2] ? Tile.padding / 2 : Tile.padding)); this._updateSource = null; return GLib.SOURCE_REMOVE; }); } else { this.state = TileState.DEFAULT; if (this._window.maximized_horizontally || this._window.maximized_vertically) this._window.unmaximize(Meta.MaximizeFlags.BOTH); const workspc = this._window.get_workspace(); const area = workspc?.get_work_area_for_monitor(this._monitor ? this._monitor : 0); if (!area) return; this._window?.move_resize_frame(true, area.x + this._position.x + (this._adjacents[0] ? Tile.padding / 2 : Tile.padding), area.y + this._position.y + (this._adjacents[2] ? Tile.padding / 2 : Tile.padding), this._position.width - (this._adjacents[1] ? Tile.padding * 1.5 : Tile.padding * 2), this._position.height - (this._adjacents[3] ? Tile.padding * 1.5 : Tile.padding * 2)); if (this._updateSource !== null) GLib.Source.remove(this._updateSource); this._updateSource = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { this._window?.move_frame(true, area.x + this._position.x + (this._adjacents[0] ? Tile.padding / 2 : Tile.padding), area.y + this._position.y + (this._adjacents[2] ? Tile.padding / 2 : Tile.padding)); this._updateSource = null; return GLib.SOURCE_REMOVE; }); } } else { this._child1?.update(); this._child2?.update(); } } /** Leaf to root research * * @param {(el : Tile) => boolean} fn * @returns */ findParent(fn) { if (fn(this)) { if (!this._parent) return this; let res = this._parent.findParent(fn); if (res) return res; else return this; } else { return null; } } /** Classic recursive traversal on tree * * @param {(el : Tile) => boolean} fn * @returns */ find(fn) { if (fn(this)) { return this; } else { let r1 = this.child1?.find(fn); if (r1) return r1; let r2 = this.child2?.find(fn); if (r2) return r2; return null; } } getSibling() { if (this === this?._parent?._child1) return this?._parent?._child2; else if (this === this?._parent?._child2) return this?._parent?._child1; else return null; } contains(window) { if (this._window?.get_id() === window.get_id()) return true; return !!(this.child1?.contains(window) || this.child2?.contains(window)); } addToChild() { if (this._child1 && this._child2) { this._child1.parent = this; this._child2.parent = this; this._child1.addToChild(); this._child2.addToChild(); } } static fromObject(obj, parent = null) { let tile = new Tile(); if (obj._child1 && obj._child2) tile.setChild(Tile.fromObject(obj._child1, tile), Tile.fromObject(obj._child2, tile)); tile.position = Position.fromObject(obj._position); tile.state = obj._state; tile.leaf = obj._leaf; tile.orientation = obj._orientation; tile.monitor = obj._monitor; tile.nr_tiles = obj._nr_tiles; tile.window = obj._window; tile.adjacents = obj._adjacents; tile.workspace = obj._workspace; if (parent) tile.parent = parent; if (tile.window) tile.window.tile = tile; return tile; } findAdjacents() { let _monitor = TileWindowManager.getMonitors()[this.monitor]; let w = _monitor.closestTile(this, Direction.West); let e = _monitor.closestTile(this, Direction.East); let n = _monitor.closestTile(this, Direction.North); let s = _monitor.closestTile(this, Direction.South); this._adjacents = [w !== null, e !== null, n !== null, s !== null]; } setChild(child1, child2) { this._child1 = child1; this._child2 = child2; } destroy() { if (this._updateSource !== null) GLib.Source.remove(this._updateSource); this._updateSource = null; this._window = null; } /** *********************/ /* GETTERS AND SETTERS */ set orientation(o) { this._orientation = o; } get orientation() { return this._orientation; } set parent(p) { this._parent = p; } get parent() { return this._parent; } get nr_tiles() { return this._nr_tiles; } set nr_tiles(n) { this._nr_tiles = n; } get child1() { return this._child1; } get child2() { return this._child2; } set position(pos) { this._position = pos; } get position() { return this._position; } set window(w) { this._window = w; } get window() { return this._window; } set state(value) { this._state = value; } get state() { return this._state; } set monitor(m) { this._monitor = m; } get monitor() { return this._monitor ? this._monitor : 0; } get adjacents() { return this._adjacents; } set adjacents(adj) { this._adjacents = adj; } set workspace(w) { this._workspace = w; } get workspace() { return this._workspace; } get leaf() { return this._leaf; } set leaf(b) { this._leaf = b; } }