diff --git a/css/ui/layers.css b/css/ui/layers.css index 3774fac..cadf9c1 100644 --- a/css/ui/layers.css +++ b/css/ui/layers.css @@ -199,5 +199,5 @@ } .expand-button:hover { - backdrop-filter: brightness(60%); + background-color: #293d3d77; } diff --git a/js/index.js b/js/index.js index 7589661..085cb31 100644 --- a/js/index.js +++ b/js/index.js @@ -343,10 +343,7 @@ function newImage(evt) { clearPaintedMask(); uil.layers.forEach(({layer}) => { commands.runCommand("eraseImage", "Clear Canvas", { - x: -layer.origin.x, - y: -layer.origin.y, - w: layer.canvas.width, - h: layer.canvas.height, + ...layer.bb, ctx: layer.ctx, }); }); diff --git a/js/initalize/layers.populate.js b/js/initalize/layers.populate.js index 57156b8..917f8ff 100644 --- a/js/initalize/layers.populate.js +++ b/js/initalize/layers.populate.js @@ -143,8 +143,6 @@ const uiCtx = uiCanvas.getContext("2d", {desynchronized: true}); debugLayer.hide(); // Hidden by default -layers.registerCollection("mask", {name: "Mask Layers", requiresActive: true}); - // Where CSS and javascript magic happens to make the canvas viewport work /** * The global viewport object (may be modularized in the future). All diff --git a/js/lib/layers.d.js b/js/lib/layers.d.js new file mode 100644 index 0000000..b847fd7 --- /dev/null +++ b/js/lib/layers.d.js @@ -0,0 +1,47 @@ +/** + * A layer + * + * @typedef {object} Layer + * @property {string} id The id of the layer + * @property {string} key A identifier for the layer + * @property {string} name The display name of the layer + * @property {BoundingBox} bb The current bounding box of the layer, in layer coordinates + * @property {Size} resolution The resolution of the layer (canvas) + * @property {boolean} full If the layer is a full layer (occupies the full collection) + * @property {number} x The x coordinate of the layer + * @property {number} y The y coordinate of the layer + * @property {number} width The width of the layer + * @property {number} w The width of the layer + * @property {number} height The height of the layer + * @property {number} h The height of the layer + * @property {Point} origin The location of the origin ((0, 0) point) of the layer (If canvas goes from -64, -32 to 128, 512, it's (64, 32)) + * @property {HTMLCanvasElement} canvas The canvas element of the layers + * @property {CanvasRenderingContext2D} ctx The context of the canvas of the layer + * @property {function} clear Clears the layer contents + * @property {function} moveAfter Moves this layer to another level (after given layer) + * @property {function} moveBefore Moves this layer to another level (before given layer) + * @property {function} moveTo Moves this layer to another location + * @property {function} resize Resizes the layer in place + * @property {function} hide Hides the layer + * @property {function} unhide Unhides the layer + */ + +/** + * A layer collection + * + * @typedef {object} LayerCollection + * @property {string} id The id of the collection + * @property {string} key A identifier for the collection + * @property {string} name The display name of the collection + * @property {HTMLDivElement} element The base element of the collection + * @property {HTMLDivElement} inputElement The element used for input handling for the collection + * @property {Point} inputOffset The offset for calculating layer coordinates from input element input information + * @property {Point} origin The location of the origin ((0, 0) point) of the collection (If canvas goes from -64, -32 to 128, 512, it's (64, 32)) + * @property {BoundingBox} bb The current bounding box of the collection, in layer coordinates + * @property {{[key: string]: Layer}} layers An object for quick access to named layers of the collection + * @property {Size} size The size of the collection (CSS) + * @property {Size} resolution The resolution of the collection (canvas) + * @property {function} expand Expands the collection and its full layers by the specified amounts + * @property {function} registerLayer Registers a new layer + * @property {function} deleteLayer Deletes a layer from the collection + */ diff --git a/js/lib/layers.js b/js/lib/layers.js index bb0e547..779cc19 100644 --- a/js/lib/layers.js +++ b/js/lib/layers.js @@ -31,6 +31,18 @@ }; }); + // Add basic get bounding box support (canvas coordinates) + Reflect.defineProperty(CanvasRenderingContext2D.prototype, "bb", { + get: function () { + return new BoundingBox({ + x: -this.origin.x, + y: -this.origin.y, + w: this.canvas.width, + h: this.canvas.height, + }); + }, + }); + // Modifying drawImage Reflect.defineProperty(CanvasRenderingContext2D.prototype, "drawImage", { value: function (...args) { @@ -161,6 +173,18 @@ const layers = { // Registers a new collection // Layer collections are a group of layers (canvases) that are rendered in tandem. (same width, height, position, transform, etc) + /** + * + * @param {string} key A key used to identify the collection + * @param {Size} size The initial size of the collection in pixels (CSS size) + * @param {object} options Extra options for the collection + * @param {string} [options.name=key] The display name of the collection + * @param {{key: string, options: object}} [options.initLayer] The configuration for the initial layer to be created + * @param {number} [options.inputSizeMultiplier=9] Size of the input area element, in pixels + * @param {HTMLElement} [options.targetElement] Element the collection will be inserted into + * @param {Size} [options.resolution=size] The resolution of the collection (canvas size). Not sure it works. + * @returns {LayerCollection} The newly created layer collection + */ registerCollection: (key, size, options = {}) => { defaultOpt(options, { // Display name for the collection @@ -208,6 +232,7 @@ const layers = { options.targetElement.appendChild(element); + /** @type {LayerCollection} */ const collection = makeWriteOnce( { id, @@ -217,6 +242,7 @@ const layers = { _layers: [], layers: {}, + key, name: options.name, element, inputElement: inputel, @@ -230,6 +256,15 @@ const layers = { return {...this._origin}; }, + get bb() { + return new BoundingBox({ + x: -this.origin.x, + y: -this.origin.y, + w: this.size.w, + h: this.size.h, + }); + }, + _resizeInputDiv() { // Set offset const oldOffset = {...this._inputOffset}; @@ -269,6 +304,14 @@ const layers = { } }, + /** + * Expands the collection and its full layers by the specified amounts + * + * @param {number} left Pixels to expand left + * @param {number} top Pixels to expand top + * @param {number} right Pixels to expand right + * @param {number} bottom Pixels to expand bottom + */ expand(left, top, right, bottom) { this._layers.forEach((layer) => { if (layer.full) layer._expand(left, top, right, bottom); @@ -301,7 +344,7 @@ const layers = { * @param {?string} options.group * @param {object} options.after * @param {object} options.ctxOptions - * @returns + * @returns {Layer} The newly created layer */ registerLayer(key = null, options = {}) { // Make ID @@ -398,7 +441,11 @@ const layers = { _logpath: _layerlogpath, _collection: collection, - bb: new BoundingBox(options.bb), + _bb: new BoundingBox(options.bb), + get bb() { + return new BoundingBox(this._bb); + }, + resolution: new Size(options.resolution), id, key, @@ -420,27 +467,27 @@ const layers = { ), get x() { - return this.bb.x; + return this._bb.x; }, get y() { - return this.bb.y; + return this._bb.y; }, get width() { - return this.bb.w; + return this._bb.w; }, get height() { - return this.bb.h; + return this._bb.h; }, get w() { - return this.bb.w; + return this._bb.w; }, get h() { - return this.bb.h; + return this._bb.h; }, get origin() { @@ -451,6 +498,16 @@ const layers = { canvas, ctx, + /** + * This is called by the collection when the layer must be expanded. + * + * Should NOT be called directly + * + * @param {number} left Pixels to expand left + * @param {number} top Pixels to expand top + * @param {number} right Pixels to expand right + * @param {number} bottom Pixels to expand bottom + */ _expand(left, top, right, bottom) { const tmpCanvas = document.createElement("canvas"); tmpCanvas.width = this.w; @@ -509,8 +566,8 @@ const layers = { * @param {number} y Y coordinate of the top left of the canvas */ moveTo(x, y) { - this.bb.x = x; - this.bb.y = y; + this._bb.x = x; + this._bb.y = y; this.canvas.style.left = `${x}px`; this.canvas.style.top = `${y}px`; }, @@ -528,8 +585,8 @@ const layers = { canvas.height = Math.round( options.resolution.h * (h / options.bb.h) ); - this.bb.w = w; - this.bb.h = h; + this._bb.w = w; + this._bb.h = h; canvas.style.width = `${w}px`; canvas.style.height = `${h}px`; }, @@ -571,7 +628,11 @@ const layers = { return layer; }, - // Deletes a layer + /** + * Deletes a layer from the collection + * + * @param {Layer} layer Layer to delete + */ deleteLayer: (layer) => { const lobj = collection._layers.splice( collection._layers.findIndex( diff --git a/js/lib/util.js b/js/lib/util.js index b79fb05..f6d5c60 100644 --- a/js/lib/util.js +++ b/js/lib/util.js @@ -363,12 +363,7 @@ function cropCanvas(sourceCanvas, options = {}) { function downloadCanvas(options = {}) { defaultOpt(options, { cropToContent: true, - canvas: uil.getVisible({ - x: -imageCollection.origin.x, - y: -imageCollection.origin.y, - w: imageCollection.size.w, - h: imageCollection.size.h, - }), + canvas: uil.getVisible(imageCollection.bb), filename: new Date() .toISOString()