[gnome] Move to Grimble tiling manager
This commit is contained in:
@ -0,0 +1,411 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user