implement ColumnLayout adjustment

This commit is contained in:
Eon S. Jeon 2019-12-21 23:20:25 +09:00
parent f119d47a4e
commit ab9b0f5a82
2 changed files with 171 additions and 8 deletions

View File

@ -53,8 +53,77 @@ class ColumnLayout implements ILayout {
this.tileWeights = new LayoutWeightMap();
}
// public adjust(area: Rect, tiles: Window[], basis: Window): void {
// }
public adjust(area: Rect, tiles: Window[], basis: Window): void {
/* TODO: make this function simpler by spliting layout-building logic into a new method */
const entry = this.tileCache[basis.id];
if (!entry) return;
const numColumns = this.columnMasters.length;
const [basisColumn, basisIndex] = entry;
const delta = WindowResizeDelta.fromWindow(basis);
/* horizontal adjustment */
if (basisColumn === null) {
/* the stack is resized. */
/* adjust colums-stack ratio. */
this.stackRatio = 1 - LayoutUtils.adjustAreaHalfWeights(area, 1 - this.stackRatio, 20,
1, delta, true);
} else if (basisColumn === numColumns - 1) {
/* the last column is resized */
/* adjust columns-stack ratio */
if (delta.east !== 0) {
const columnsDelta = new WindowResizeDelta(delta.east, 0, 0, 0);
this.stackRatio = 1 - LayoutUtils.adjustAreaHalfWeights(area, 1 - this.stackRatio, 20,
0, columnsDelta, true);
}
/* adjust colums ratio */
if (delta.west !== 0) {
const columnDelta = new WindowResizeDelta(0, delta.west, 0, 0);
const newWeights = LayoutUtils.adjustAreaWeights(area, this.columnWeights, 20,
basisColumn, columnDelta, true);
this.columnWeights = newWeights;
}
} else {
/* any other columns */
/* adjust colums weights */
const newWeights = LayoutUtils.adjustAreaWeights(area, this.columnWeights, 20,
basisColumn, delta, true);
this.columnWeights = newWeights;
}
const numColumnTiles = this.columnMasters.reduce((sum, numMaster) => sum + numMaster, 0);
const [columnsTiles, stackTiles] = partitionArray(tiles, (tile, idx) => idx < numColumnTiles);
/* vertical adjustment */
if (basisColumn === null) {
/* stack */
if (stackTiles.length > 0) {
const stackTileWeights = stackTiles.map((tile) => this.tileWeights.get(tile));
const newWeights = LayoutUtils.adjustAreaWeights(area, stackTileWeights, CONFIG.tileLayoutGap,
basisIndex, delta);
newWeights.forEach((weight, index) => {
const tile = stackTiles[index];
this.tileWeights.set(tile, weight * stackTiles.length);
});
}
} else {
/* column */
if (columnsTiles.length > 0) {
const columnTilesList = partitionArrayBySizes(columnsTiles, this.columnMasters)
.filter((arr) => arr.length > 0);
const columnTiles = columnTilesList[basisColumn];
const weights = columnTiles.map((tile) => this.tileWeights.get(tile));
const newWeights = LayoutUtils.adjustAreaWeights(area, weights, CONFIG.tileLayoutGap,
basisIndex, delta);
newWeights.forEach((weight, index) => {
const tile = columnTiles[index];
this.tileWeights.set(tile, weight * columnTiles.length);
});
}
}
}
public apply(ctx: EngineContext, tiles: Window[], area: Rect): void {
this.tileCache = {};

View File

@ -71,9 +71,103 @@ class LayoutUtils {
return LayoutUtils.splitAreaWeighted(area, [weight, 1 - weight], gap, horizontal);
}
public static calculateWeights(parts: number[]): number[] {
const length = parts.reduce((acc, partLength) => acc + partLength, 0);
return parts.map((partLength) => partLength / length);
/**
* adjustWeights recalculates the weights of subareas of the line, based on size change.
* @param line The line being aplitted
* @param weights The weight of each part
* @param gap The gap size b/w parts
* @param target The index of the part being changed.
* @param deltaFw The amount of growth towards the origin.
* @param deltaBw The amount of growth towards the infinity.
*/
public static adjustWeights(
[begin, length]: [number, number],
weights: number[],
gap: number,
target: number,
deltaFw: number,
deltaBw: number,
): number[] {
// TODO: configurable min length?
const minLength = 1;
const parts = this.splitWeighted([begin, length], weights, gap);
const [targetBase, targetLength] = parts[target];
/* apply backward delta */
if (target > 0 && deltaBw !== 0) {
const neighbor = target - 1;
const [neighborBase, neighborLength] = parts[neighbor];
/* limit delta to prevent squeezing windows */
const delta = clip(deltaBw,
minLength - targetLength,
neighborLength - minLength,
);
parts[target] = [(targetBase - delta), (targetLength + delta)];
parts[neighbor] = [neighborBase, (neighborLength - delta)];
}
/* apply forward delta */
if (target < parts.length - 1 && deltaFw !== 0) {
const neighbor = target + 1;
const [neighborBase, neighborLength] = parts[neighbor];
/* limit delta to prevent squeezing windows */
const delta = clip(deltaFw,
minLength - targetLength,
neighborLength - minLength,
);
parts[target] = [targetBase, targetLength + delta];
parts[neighbor] = [neighborBase + delta, neighborLength - delta];
}
return LayoutUtils.calculateWeights(parts);
}
/**
* adjustAreaWeights recalculates weights of subareas splitting the given area, based on size change.
* @param area The area being splitted
* @param weights The weight of each part
* @param gap The gap size b/w parts
* @param target The index of the part being changed.
* @param delta The changes in dimension of the target
* @param horizontal If true, calculate horizontal weights, instead of vertical.
*/
public static adjustAreaWeights(
area: Rect,
weights: number[],
gap: number,
target: number,
delta: WindowResizeDelta,
horizontal?: boolean,
): number[] {
const line: [number, number] = (horizontal) ? [area.x, area.width] : [area.y, area.height];
const [deltaFw, deltaBw] = (horizontal)
? [delta.east, delta.west]
: [delta.south, delta.north]
;
return LayoutUtils.adjustWeights(line, weights, gap, target, deltaFw, deltaBw);
}
public static adjustAreaHalfWeights(
area: Rect,
weight: number,
gap: number,
target: number,
delta: WindowResizeDelta,
horizontal?: boolean,
): number {
const weights = [weight, 1 - weight];
const newWeights = LayoutUtils.adjustAreaWeights(area, weights, gap, target, delta, horizontal);
return newWeights[0];
}
public static calculateWeights(parts: Array<[number, number]>): number[] {
const totalLength = parts.reduce((acc, [base, length]) => acc + length, 0);
return parts.map(([base, length]) => length / totalLength);
}
public static calculateAreaWeights(area: Rect, geometries: Rect[], gap?: number, horizontal?: boolean): number[] {
@ -81,9 +175,9 @@ class LayoutUtils {
horizontal = (horizontal !== undefined) ? horizontal : false;
const line = (horizontal) ? area.width : area.height;
const parts = (horizontal)
? geometries.map((geometry) => geometry.width)
: geometries.map((geometry) => geometry.height)
const parts: Array<[number, number]> = (horizontal)
? geometries.map((geometry) => [geometry.x, geometry.width])
: geometries.map((geometry) => [geometry.y, geometry.height])
;
return LayoutUtils.calculateWeights(parts);
}