412 lines
13 KiB
JavaScript
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;
|
|
}
|
|
}
|