Files
dotfiles/gnome/.local/share/gnome-shell/extensions/grimble@lmt.github.io/tile.js

412 lines
13 KiB
JavaScript

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