diff --git a/.gitignore b/.gitignore index d23bb67bc..d080c38d0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ package-lock.json .env.test.local .env.production.local .env +CLAUDE.md npm-debug.log* yarn-debug.log* diff --git a/engine/src/base/Layer.js b/engine/src/base/Layer.js index 52ab84730..bc98cec5f 100644 --- a/engine/src/base/Layer.js +++ b/engine/src/base/Layer.js @@ -34,6 +34,7 @@ Wick.Layer = class extends Wick.Base { this.hidden = args.hidden === undefined ? false : args.hidden; this.opacity = args.opacity === undefined ? 1 : args.opacity; this.name = args.name || null; + this._layerColor = args.layerColor || Wick.GUIElement.LAYER_LABEL_ACTIVE_FILL_COLOR } _serialize (args) { @@ -88,6 +89,17 @@ Wick.Layer = class extends Wick.Base { else this._opacity = 1; } + /** + * The color of the layer on the timeline. + * @type {paper.color} + */ + get layerColor(){ + return this._layerColor + } + set layerColor(color){ + this._layerColor = color + } + /** * Set this layer to be the active layer in its timeline. */ diff --git a/engine/src/base/Selection.js b/engine/src/base/Selection.js index 87a66330f..0996fb8c3 100644 --- a/engine/src/base/Selection.js +++ b/engine/src/base/Selection.js @@ -84,6 +84,7 @@ Wick.Selection = class extends Wick.Base { "strokeWidth", "fillColor", "strokeColor", + "layerColor", "name", "filename", "fontSize", @@ -783,6 +784,18 @@ Wick.Selection = class extends Wick.Base { this._setSingleAttribute('strokeWidth', strokeWidth); } + /** + * The color of the layer. + * @type {paper.color} + */ + get layerColor(){ + return this._getSingleAttribute('layerColor') + } + + set layerColor(layerColor){ + this._setSingleAttribute('layerColor', layerColor) + } + /** * The font family of the selected object. * @type {string} diff --git a/engine/src/gui/Layer.js b/engine/src/gui/Layer.js index da230b12a..d35006213 100644 --- a/engine/src/gui/Layer.js +++ b/engine/src/gui/Layer.js @@ -76,9 +76,60 @@ Wick.GUIElement.Layer = class extends Wick.GUIElement { if (this.model.hidden) { ctx.fillStyle = Wick.GUIElement.LAYER_LABEL_HIDDEN_FILL_COLOR; } else if (this.model.isActive) { - ctx.fillStyle = Wick.GUIElement.LAYER_LABEL_ACTIVE_FILL_COLOR; + ctx.fillStyle = this.model.layerColor; } else { - ctx.fillStyle = Wick.GUIElement.LAYER_LABEL_INACTIVE_FILL_COLOR; + let color = this.model.layerColor; //"#b7b7b7" + let red, green, blue; + if(color.includes("#")){ + // Parse original RGB values + red = parseRGB(color)[0]; + green = parseRGB(color)[1]; + blue = parseRGB(color)[2] + } + else{ + color = color.replace(/[^\d.,]/g, '').split(',').map(Number); + red = color[0] + green = color[1] + blue = color[2] + } + + // Overlay factor (0–1) + let customAlpha = 200 // edit this to tweak how light the inactive color is + let overlayAlpha = customAlpha / 255; + + // Overlay color (183,183,183) + let overlay = 183; + + // Blend each channel + red = Math.round(overlay * overlayAlpha + red * (1 - overlayAlpha)); + green = Math.round(overlay * overlayAlpha + green * (1 - overlayAlpha)); + blue = Math.round(overlay * overlayAlpha + blue * (1 - overlayAlpha)); + + ctx.fillStyle = "#" + toHex(red) + toHex(green) + toHex(blue); + } + + // Convert back to hex string + function toHex(n) { + return n.toString(16).padStart(2, '0'); + } + + function parseRGB(color) { + let red, green, blue, alpha; + if (color.includes('#')){ + // condense color down to rgba values + color = color.replace(/^#/, ''); + + // Parse original RGB values + red = parseInt(color.substring(0, 2), 16); + green = parseInt(color.substring(2, 4), 16); + blue = parseInt(color.substring(4, 6), 16); + alpha = parseInt(color.substring(6, 8)); + return [red, green, blue, alpha] + } + else { + color = color.match(/[\d.]+/g).map(Number); + return color; + } } if(this.model.isSelected) { @@ -101,15 +152,32 @@ Wick.GUIElement.Layer = class extends Wick.GUIElement { ctx.restore(); // Label text + let color = this.model.layerColor; + let warm = false; + let rgb = parseRGB(color); + if((rgb[0] - rgb[2]) > 0 && rgb[1] < 113) warm = true var maxWidth = Wick.GUIElement.LAYERS_CONTAINER_WIDTH - 10; ctx.save(); ctx.beginPath(); ctx.rect(0, 0, maxWidth, this.gridCellHeight); ctx.clip(); ctx.font = "16px " + Wick.GUIElement.LAYER_LABEL_FONT_FAMILY; - ctx.fillStyle = this.model.isActive - ? Wick.GUIElement.LAYER_LABEL_ACTIVE_FONT_COLOR - : Wick.GUIElement.LAYER_LABEL_INACTIVE_FONT_COLOR; + if(this.model.isActive == true) { + if(warm == false){ + ctx.fillStyle = Wick.GUIElement.LAYER_LABEL_ACTIVE_FONT_COLOR + } + else { + ctx.fillStyle = "#ffffff" + } + } + else{ + if(warm == false){ + ctx.fillStyle = Wick.GUIElement.LAYER_LABEL_INACTIVE_FONT_COLOR; + } + else{ + ctx.fillStyle = "#c5c5c5" + } + } ctx.fillText(this.model.name, 57, this.gridCellHeight / 2 + 6); ctx.restore(); diff --git a/jsconfig.json b/jsconfig.json index 5875dc5b6..7e7601c31 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "ignoreDeprecations": "6.0", "baseUrl": "src" }, "include": ["src"] diff --git a/package-lock.json b/package-lock.json index afee13ca5..e32ff29a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3799,7 +3799,6 @@ "version": "2.5.6", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3839,7 +3838,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3860,7 +3858,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3881,7 +3878,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3902,7 +3898,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3923,7 +3918,6 @@ "cpu": [ "arm" ], - "dev": true, "libc": [ "glibc" ], @@ -3947,7 +3941,6 @@ "cpu": [ "arm" ], - "dev": true, "libc": [ "musl" ], @@ -3971,7 +3964,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "glibc" ], @@ -3995,7 +3987,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "musl" ], @@ -4019,7 +4010,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "glibc" ], @@ -4043,7 +4033,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "musl" ], @@ -4067,7 +4056,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4088,7 +4076,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4109,7 +4096,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4127,7 +4113,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -5133,6 +5118,16 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "license": "MIT" }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, "node_modules/@types/react-transition-group": { "version": "4.4.12", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", @@ -5142,6 +5137,13 @@ "@types/react": "*" } }, + "node_modules/@types/react/node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT", + "peer": true + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -8805,7 +8807,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "optional": true, "engines": { @@ -12777,7 +12778,7 @@ "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -15101,6 +15102,13 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT", + "peer": true + }, "node_modules/js-binary-schema-parser": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz", @@ -16396,7 +16404,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -20712,7 +20719,7 @@ "version": "1.101.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^5.0.0", @@ -20771,7 +20778,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^5.0.0" @@ -20787,7 +20794,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 20.19.0" @@ -23361,6 +23368,20 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/ua-parser-js": { "version": "1.0.41", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", diff --git a/src/Editor/Editor.jsx b/src/Editor/Editor.jsx index 3d1954b3f..ae1a06c76 100644 --- a/src/Editor/Editor.jsx +++ b/src/Editor/Editor.jsx @@ -329,12 +329,12 @@ class Editor extends EditorCore { // Set color picker state. localForage.getItem(this.colorPickerTypeKey).then( (colorPickerType) => { - if (!colorPickerType) colorPickerType = "swatches"; + if (!colorPickerType) {console.log('no type');colorPickerType = "swatches";} this.setState({ colorPickerType: colorPickerType, }); } - ); + ) diff --git a/src/Editor/Panels/Inspector/Inspector.jsx b/src/Editor/Panels/Inspector/Inspector.jsx index 42e5ce414..1cf8a37c8 100644 --- a/src/Editor/Panels/Inspector/Inspector.jsx +++ b/src/Editor/Panels/Inspector/Inspector.jsx @@ -100,6 +100,7 @@ class Inspector extends Component { this.inspectorTitles = { "frame": "Frame", "multiframe": "Multi-Frame", + "layer": "Layer", "tween": "Tween", "multitween": "Multi-Tween", "clip": "Clip", @@ -288,6 +289,30 @@ class Inspector extends Component { ); } + /** + * Renders an inspector row allowing editing of a layer's colour. + */ + renderLayerColor = () => { + return ( +
+ this.setSelectionAttribute('layerColor', col)} + onChangeIntermediate1={(col) => {console.log('intermediiate'); this.setSelectionAttributeIntermediate('layerColor', col)}} + colorPickerType={this.props.colorPickerType} + changeColorPickerType={this.props.changeColorPickerType} + enableGradient={false} + selectionProps={{ + getSelection: () => this.props.project.selection, + renderSelection: () => this.props.project.root.timeline.view.render(), + }} + id={"inspector-selection-layer-color"} + /> +
+ ) + } + /** * Renders an inspector row allowing viewing and editing of the selected object's font. */ @@ -752,6 +777,7 @@ class Inspector extends Component {
{this.renderName()} {this.renderOpacity()} + {this.renderLayerColor()}
) } diff --git a/src/Editor/Panels/Inspector/InspectorRow/InspectorRowTypes/InspectorColorNumericInput.jsx b/src/Editor/Panels/Inspector/InspectorRow/InspectorRowTypes/InspectorColorNumericInput.jsx index db8981d36..c605d89e4 100644 --- a/src/Editor/Panels/Inspector/InspectorRow/InspectorRowTypes/InspectorColorNumericInput.jsx +++ b/src/Editor/Panels/Inspector/InspectorRow/InspectorRowTypes/InspectorColorNumericInput.jsx @@ -26,56 +26,94 @@ import '../_inspectorrow.scss'; class InspectorColorNumericInput extends Component { render() { let idLabel1 = this.props.tooltip1.replace(/\s+/g, '-').toLowerCase(); - let idLabel2 = this.props.tooltip2.replace(/\s+/g, '-').toLowerCase(); - return( -
- {/* Identifier1 */} - + if (this.props.tooltip2) { + let idLabel2 = this.props.tooltip2.replace(/\s+/g, '-').toLowerCase(); + return ( +
+ {/* Identifier1 */} + - {/* Input1 */} -
- -
+ {/* Input1 */} +
+ +
- {/* Identifier2 */} - + {/* Identifier2 */} + - {/* Input2 */} -
- -
-
- ); + {/* Input2 */} +
+ +
+
+ ); + } + else { + return ( +
+ {/* Identifier1 */} + + + {/* Input1 */} +
+ +
+
+ ); + } } } -export default InspectorColorNumericInput +export default InspectorColorNumericInput \ No newline at end of file