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 -->
|
<!-- Base Libs -->
|
||||||
<script src="js/util.js" type="text/javascript"></script>
|
<script src="js/util.js" type="text/javascript"></script>
|
||||||
<script src="js/input.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/commands.js" type="text/javascript"></script>
|
||||||
<script src="js/ui/history.js" type="text/javascript"></script>
|
<script src="js/ui/history.js" type="text/javascript"></script>
|
||||||
<script src="js/settingsbar.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 bgCanvas = document.getElementById("backgroundCanvas"); // gray bg grid
|
||||||
const bgCtx = bgCanvas.getContext("2d");
|
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() {
|
function startup() {
|
||||||
testHostConfiguration();
|
testHostConfiguration();
|
||||||
testHostConnection();
|
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
|
* 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 = {
|
const layers = {
|
||||||
_layers: [],
|
collections: makeWriteOnce({}, "layers.collections"),
|
||||||
layers: {},
|
|
||||||
|
|
||||||
// Registers a new layer
|
// Registers a new collection
|
||||||
registerLayer: (name) => {
|
registerCollection: (key, options = {}) => {
|
||||||
const layer = {
|
defaultOpt(options, {
|
||||||
id: guid(),
|
// If collection is visible on the Layer View Toolbar
|
||||||
name: layer,
|
visible: true,
|
||||||
// This is where black magic starts
|
// Display name for the collection
|
||||||
// A proxy for the canvas object
|
name: key,
|
||||||
canvas: new Proxy(document.createElement("canvas"), {}),
|
/**
|
||||||
|
* 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
|
// Path used for logging purposes
|
||||||
deleteLayer: (layer) => {
|
const _logpath = options.parent
|
||||||
if (typeof layer === "object") {
|
? options.parent + "." + key
|
||||||
layers._layers = layers._layers.filter((l) => l.id === layer.id);
|
: "layers.collections." + key;
|
||||||
delete layers[layer.id];
|
|
||||||
} else if (typeof layer === "string") {
|
// If we have a scope already, we can't add a new scope
|
||||||
layers._layers = layers._layers.filter((l) => l.id === layer);
|
if (options.scope && findScope())
|
||||||
delete layers[layer];
|
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