some layer upgrades
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
c1b17c1b0e
commit
4e27770284
4 changed files with 230 additions and 20 deletions
|
@ -299,6 +299,8 @@
|
|||
<!-- Base Libs -->
|
||||
<script src="js/util.js" type="text/javascript"></script>
|
||||
<script src="js/input.js" type="text/javascript"></script>
|
||||
<script src="js/layers.js" type="text/javascript"></script>
|
||||
|
||||
<script src="js/commands.js" type="text/javascript"></script>
|
||||
<script src="js/ui/history.js" type="text/javascript"></script>
|
||||
<script src="js/settingsbar.js" type="text/javascript"></script>
|
||||
|
|
16
js/index.js
16
js/index.js
|
@ -112,6 +112,22 @@ const imgCtx = imgCanvas.getContext("2d");
|
|||
const bgCanvas = document.getElementById("backgroundCanvas"); // gray bg grid
|
||||
const bgCtx = bgCanvas.getContext("2d");
|
||||
|
||||
// Layering
|
||||
const imageCollection = layers.registerCollection("image", {
|
||||
name: "Image Layers",
|
||||
scope: {
|
||||
always: {
|
||||
key: "default",
|
||||
options: {
|
||||
name: "Default Image Layer",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
layers.registerCollection("mask", {name: "Mask Layers", requiresActive: true});
|
||||
|
||||
//
|
||||
function startup() {
|
||||
testHostConfiguration();
|
||||
testHostConnection();
|
||||
|
|
180
js/layers.js
180
js/layers.js
|
@ -4,29 +4,169 @@
|
|||
* It manages canvases and their locations and sizes according to current viewport views
|
||||
*/
|
||||
|
||||
// Errors
|
||||
class LayerNestedScopesError extends Error {
|
||||
// For when a scope is created in another scope
|
||||
}
|
||||
class LayerNoScopeError extends Error {
|
||||
// For when an action that requires a scope is attempted
|
||||
// in a collection with no scope.
|
||||
}
|
||||
|
||||
const layers = {
|
||||
_layers: [],
|
||||
layers: {},
|
||||
collections: makeWriteOnce({}, "layers.collections"),
|
||||
|
||||
// Registers a new layer
|
||||
registerLayer: (name) => {
|
||||
const layer = {
|
||||
id: guid(),
|
||||
name: layer,
|
||||
// This is where black magic starts
|
||||
// A proxy for the canvas object
|
||||
canvas: new Proxy(document.createElement("canvas"), {}),
|
||||
// Registers a new collection
|
||||
registerCollection: (key, options = {}) => {
|
||||
defaultOpt(options, {
|
||||
// If collection is visible on the Layer View Toolbar
|
||||
visible: true,
|
||||
// Display name for the collection
|
||||
name: key,
|
||||
/**
|
||||
* If layer creates a layer scope
|
||||
*
|
||||
* A layer scope is a context where one, and only one layer inside it or its
|
||||
* subscopes can be active at a time. Nested scopes are not supported.
|
||||
* It receives an object of type:
|
||||
*
|
||||
* {
|
||||
* // If there must be a selected layer, pass information to create the first
|
||||
* always: {
|
||||
* key,
|
||||
* options
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
scope: null,
|
||||
// Parent collection
|
||||
parent: null,
|
||||
});
|
||||
|
||||
// Finds the closest parent with a defined scope
|
||||
const findScope = (collection = options.parent) => {
|
||||
if (!collection) return null;
|
||||
|
||||
if (collection.scope) return collection;
|
||||
return findScope(collection._parent);
|
||||
};
|
||||
},
|
||||
|
||||
// Deletes a layer
|
||||
deleteLayer: (layer) => {
|
||||
if (typeof layer === "object") {
|
||||
layers._layers = layers._layers.filter((l) => l.id === layer.id);
|
||||
delete layers[layer.id];
|
||||
} else if (typeof layer === "string") {
|
||||
layers._layers = layers._layers.filter((l) => l.id === layer);
|
||||
delete layers[layer];
|
||||
}
|
||||
// Path used for logging purposes
|
||||
const _logpath = options.parent
|
||||
? options.parent + "." + key
|
||||
: "layers.collections." + key;
|
||||
|
||||
// If we have a scope already, we can't add a new scope
|
||||
if (options.scope && findScope())
|
||||
throw new LayerNestedScopesError(`Layer scopes must not be nested`);
|
||||
|
||||
const collection = makeWriteOnce(
|
||||
{
|
||||
_parent: options.parent,
|
||||
_logpath,
|
||||
_layers: [],
|
||||
layers: {},
|
||||
|
||||
name: options.name,
|
||||
|
||||
scope: options.scope,
|
||||
// Registers a new layer
|
||||
registerLayer: (key, options = {}) => {
|
||||
defaultOpt(options, {
|
||||
// Display name for the layer
|
||||
name: key,
|
||||
});
|
||||
|
||||
// Path used for logging purposes
|
||||
const _layerlogpath = _logpath + ".layers." + key;
|
||||
const layer = makeWriteOnce(
|
||||
{
|
||||
_logpath: _layerlogpath,
|
||||
id: guid(),
|
||||
name: options.name,
|
||||
|
||||
state: new Proxy(
|
||||
{visible: true},
|
||||
{
|
||||
set(obj, opt, val) {
|
||||
switch (opt) {
|
||||
case "visible":
|
||||
layer.canvas.style.display = val ? "block" : "none";
|
||||
break;
|
||||
}
|
||||
obj[opt] = val;
|
||||
},
|
||||
}
|
||||
),
|
||||
|
||||
// This is where black magic will take place in the future
|
||||
// A proxy for the canvas object
|
||||
canvas: new Proxy(document.createElement("canvas"), {}),
|
||||
|
||||
// Activates this layer in the scope
|
||||
activate: () => {
|
||||
const scope = findScope(collection);
|
||||
if (scope) {
|
||||
scope.active = layer;
|
||||
console.debug(
|
||||
`[layers] Layer ${layer._logpath} now active in scope ${scope._logpath}`
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// Deactivates this layer in the scope
|
||||
deactivate: () => {
|
||||
const scope = findScope(collection);
|
||||
if (scope && scope.active === layer) scope.active = null;
|
||||
console.debug();
|
||||
},
|
||||
},
|
||||
_layerlogpath
|
||||
);
|
||||
|
||||
// Add to indexers
|
||||
collection._layers.push(layer);
|
||||
collection.layers[key] = layer;
|
||||
|
||||
console.info(
|
||||
`[layers] Layer '${layer.name}' at ${layer._logpath} registered`
|
||||
);
|
||||
return layer;
|
||||
},
|
||||
|
||||
// Deletes a layer
|
||||
deleteLayer: (layer) => {
|
||||
collection._layers.splice(
|
||||
collection._layers.findIndex(
|
||||
(l) => l.id === layer || l.id === layer.id
|
||||
),
|
||||
1
|
||||
);
|
||||
if (typeof layer === "object") {
|
||||
delete collection.layers[layer.id];
|
||||
} else if (typeof layer === "string") {
|
||||
delete collection.layers[layer];
|
||||
}
|
||||
|
||||
console.info(`[layers] Layer '${layer}' deleted`);
|
||||
},
|
||||
},
|
||||
_logpath
|
||||
);
|
||||
|
||||
if (parent) parent[key] = collection;
|
||||
else layers.collections[key] = collection;
|
||||
|
||||
console.info(
|
||||
`[layers] Collection '${options.name}' at ${_logpath} registered`
|
||||
);
|
||||
|
||||
// If always, we must create a layer to select
|
||||
if (options.scope && options.scope.always)
|
||||
collection
|
||||
.registerLayer(options.scope.always.key, options.scope.always.options)
|
||||
.activate();
|
||||
|
||||
return collection;
|
||||
},
|
||||
};
|
||||
|
|
52
js/util.d.ts
vendored
Normal file
52
js/util.d.ts
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Generates a random string in the following format:
|
||||
*
|
||||
* xxxx-xxxx-xxxx-...-xxxx
|
||||
*
|
||||
* @param size number of character quartets to generate
|
||||
* @return Generated ID
|
||||
*/
|
||||
declare function guid(size: number): string;
|
||||
|
||||
/**
|
||||
* Sets default values for options parameters
|
||||
*
|
||||
* @param options An object received as a parameter
|
||||
* @param defaults An object with default values for each expected key
|
||||
* @return The original options parameter
|
||||
*/
|
||||
declare function defaultOpt(
|
||||
options: {[key: string]: any},
|
||||
defaults: {[key: string]: any}
|
||||
): {[key: string]: any};
|
||||
|
||||
/**
|
||||
* Sets default values for options parameters
|
||||
*
|
||||
* @param options An object received as a parameter
|
||||
* @param defaults An object with default values for each expected key
|
||||
* @return The original options parameter
|
||||
*/
|
||||
declare function makeReadOnly(
|
||||
options: {[key: string]: any},
|
||||
defaults: {[key: string]: any}
|
||||
): {[key: string]: any};
|
||||
|
||||
/**
|
||||
* Makes an object read-only, throwing an exception when attempting to set
|
||||
*
|
||||
* @param obj Object to be proxied
|
||||
* @param name Name of the object, for logging purposes
|
||||
* @return The proxied object
|
||||
*/
|
||||
declare function makeReadOnly(obj: object, name?: string): object;
|
||||
|
||||
/**
|
||||
* Makes an object have each key be writeable only once, throwing an exception when
|
||||
* attempting to set an existing parameter
|
||||
*
|
||||
* @param obj Object to be proxied
|
||||
* @param name Name of the object, for logging purposes
|
||||
* @return The proxied object
|
||||
*/
|
||||
declare function makeWriteOnce(obj: object, name?: string): object;
|
Loading…
Reference in a new issue