diff --git a/css/ui.css b/css/ui.css index 005ae5bb..8b72adbb 100644 --- a/css/ui.css +++ b/css/ui.css @@ -502,4 +502,11 @@ div.asset_editor { padding:16px; margin:16px; background-color:#999; + display:flex; + flex-direction:column; + align-items:center; +} +div.asset_toolbar { + padding:8px; + margin:8px; } diff --git a/doc/notes.txt b/doc/notes.txt index 56b3eaef..34806a5b 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -108,6 +108,7 @@ TODO: - metasprites - update nested data, palette/tile refs properly - throw errors when bad/no refs + - careful with mouse capture out of frame WEB WORKER FORMAT diff --git a/src/pixed/pixeleditor.ts b/src/pixed/pixeleditor.ts index 4ed39f94..c68a06ea 100644 --- a/src/pixed/pixeleditor.ts +++ b/src/pixed/pixeleditor.ts @@ -317,6 +317,33 @@ var PREDEF_LAYOUTS : {[id:string]:PixelEditorPaletteLayout} = { ///// +function equalArrays(a:UintArray, b:UintArray) : boolean { + if (a == null || b == null) + return false; + if (a.length !== b.length) + return false; + if (a === b) + return true; + for (var i=0; i { + var newimages = this.rgbimgs.map( (im:Uint32Array) => { var out = new Uint8Array(im.length); for (var i=0; i { @@ -502,20 +548,25 @@ export class Palettizer extends PixNode { } return out; }); + return true; } updateRefs() { + var newpalette; if (this.context != null) { this.paloptions = this.context.getPalettes(this.ncolors); if (this.paloptions && this.paloptions.length > 0) { - this.palette = this.paloptions[this.palindex].palette; + newpalette = this.paloptions[this.palindex].palette; } } - if (this.palette == null) { + if (newpalette == null) { if (this.ncolors <= 2) - this.palette = new Uint32Array([0xff000000, 0xffffffff]); + newpalette = new Uint32Array([0xff000000, 0xffffffff]); else - this.palette = new Uint32Array([0xff000000, 0xffff00ff, 0xffffff00, 0xffffffff]); // TODO: more palettes + newpalette = new Uint32Array([0xff000000, 0xffff00ff, 0xffffff00, 0xffffffff]); // TODO: more palettes } + if (equalArrays(this.palette, newpalette)) return false; + this.palette = newpalette; + return true; } } @@ -533,9 +584,9 @@ function dedupPalette(cols : UintArray) : Uint32Array { } return res; } - export class PaletteFormatToRGB extends PixNode { + words : UintArray; rgbimgs : Uint32Array[]; palette : Uint32Array; @@ -548,8 +599,10 @@ export class PaletteFormatToRGB extends PixNode { } updateLeft() { //TODO + return true; } updateRight() { + if (equalArrays(this.words, this.left.words)) return false; this.words = this.left.words; this.palette = dedupPalette(convertPaletteFormat(this.words, this.palfmt)); this.layout = PREDEF_LAYOUTS[this.palfmt.layout]; @@ -557,6 +610,7 @@ export class PaletteFormatToRGB extends PixNode { this.palette.forEach( (rgba:number) => { this.rgbimgs.push(new Uint32Array([rgba])); }); + return true; } getAllColors() { var arr = []; @@ -581,13 +635,15 @@ export abstract class Compositor extends PixNode { super(); this.context = context; } - updateRefs() { + updateRefs() : boolean { + var oldtilemap = this.tilemap; if (this.context != null) { this.tileoptions = this.context.getTilemaps(256); if (this.tileoptions && this.tileoptions.length > 0) { this.tilemap = this.tileoptions[this.tileindex].images; } } + return !equalNestedArrays(oldtilemap, this.tilemap); } } @@ -605,6 +661,7 @@ export class MetaspriteCompositor extends Compositor { } updateLeft() { // TODO + return false; } updateRight() { this.updateRefs(); @@ -615,6 +672,7 @@ export class MetaspriteCompositor extends Compositor { this.metadefs.forEach((meta) => { // TODO }); + return true; } } @@ -628,11 +686,11 @@ export class NESNametableConverter extends Compositor { } updateLeft() { // TODO + return false; } updateRight() { + if (!this.updateRefs() && equalArrays(this.words, this.left.words)) return false; this.words = this.left.words; - this.updateRefs(); - if (!this.words || !this.tilemap) return; this.cols = 32; this.rows = 30; this.width = this.cols * 8; @@ -665,6 +723,7 @@ export class NESNametableConverter extends Compositor { } } // TODO + return true; } } @@ -682,7 +741,6 @@ export class ImageChooser { var cscale = Math.max(2, Math.ceil(16/this.width)); // TODO var imgsperline = this.width <= 8 ? 16 : 8; // TODO var span = null; - if (!this.rgbimgs) return; this.rgbimgs.forEach((imdata, i) => { var viewer = new Viewer(); viewer.width = this.width; @@ -730,9 +788,11 @@ export class CharmapEditor extends PixNode { } updateLeft() { + return true; } updateRight() { + if (equalNestedArrays(this.rgbimgs, this.left.rgbimgs)) return false; this.rgbimgs = this.left.rgbimgs; var adual = newDiv(this.parentdiv.empty(), "asset_dual"); // contains grid and editor var agrid = newDiv(adual); @@ -765,6 +825,7 @@ export class CharmapEditor extends PixNode { palizer.refreshRight(); }); } + return true; } createEditor(aeditor : JQuery, viewer : Viewer, escale : number) : PixEditor { @@ -901,7 +962,7 @@ class PixEditor extends Viewer { createPaletteButtons() { this.palbtns = []; - var span = $(document.createElement('div')); + var span = newDiv(null, "asset_toolbar"); for (var i=0; i { this.cureditordiv[0].scrollIntoView({behavior: "smooth", block: "center"}) }, timeout); } } if (this.cureditelem) { @@ -1039,37 +1040,42 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { return result; } - addPaletteEditorViews(parentdiv:JQuery, words, palette, layout, allcolors, callback) { + addPaletteEditorViews(parentdiv:JQuery, pal2rgb:pixed.PaletteFormatToRGB, callback) { var adual = $('
').appendTo(parentdiv); var aeditor = $('
').hide(); // contains editor, when selected // TODO: they need to update when refreshed from right var allrgbimgs = []; - allcolors.forEach((rgba) => { allrgbimgs.push(new Uint32Array([rgba])); }); // array of array of 1 rgb color (for picker) + pal2rgb.getAllColors().forEach((rgba) => { allrgbimgs.push(new Uint32Array([rgba])); }); // array of array of 1 rgb color (for picker) var atable = $('').appendTo(adual); aeditor.appendTo(adual); // make default layout if not exists + var layout = pal2rgb.layout; if (!layout) { - var len = palette.length; + var len = pal2rgb.palette.length; var imgsperline = len > 32 ? 8 : 4; // TODO: use 'n'? layout = []; for (var i=0; i { - if (start < palette.length) { // skip row if out of range + if (start < pal2rgb.palette.length) { // skip row if out of range var arow = $('').appendTo(atable); $('
').text(name).appendTo(arow); var inds = []; for (var k=start; k { - var val = words[i]; - var rgb = palette[i]; - var hexcol = '#'+hex(rgb2bgr(rgb),6); - var textcol = (rgb & 0x008000) ? 'black' : 'white'; - var cell = $('').addClass('asset_cell asset_editable').text(hex(val,2)).css('background-color',hexcol).css('color',textcol).appendTo(arow); + var cell = $('').addClass('asset_cell asset_editable').appendTo(arow); + updateCell(cell, i); cell.click((e) => { var chooser = new pixed.ImageChooser(); chooser.rgbimgs = allrgbimgs; @@ -1077,6 +1083,7 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { chooser.height = 1; chooser.recreate(aeditor, (index, newvalue) => { callback(i, index); + updateCell(cell, i); }); this.setCurrentEditor(aeditor, cell); }); @@ -1106,13 +1113,14 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { firstnode.refreshRight(); // TODO: add view objects // TODO: show which one is selected? - this.addPaletteEditorViews(parentdiv, firstnode.words, - pal2rgb.palette, pal2rgb.layout, pal2rgb.getAllColors(), + this.addPaletteEditorViews(parentdiv, pal2rgb, (index, newvalue) => { console.log('set entry', index, '=', newvalue); + // TODO: this forces update of palette rgb colors and file data firstnode.words[index] = newvalue; - //firstnode.refreshRight(); - firstnode.refreshLeft(); + pal2rgb.words = null; + pal2rgb.updateRight(); + pal2rgb.refreshLeft(); }); } @@ -1124,16 +1132,17 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { // TODO: only refresh when needed if (fileid.endsWith('.chr') && data instanceof Uint8Array) { // is this a NES CHR? - let node = new pixed.FileDataNode(projectWindows, fileid, data); + let node = new pixed.FileDataNode(projectWindows, fileid); const neschrfmt = {w:8,h:8,bpp:1,count:(data.length>>4),brev:true,np:2,pofs:8,remap:[0,1,2,4,5,6,7,8,9,10,11,12]}; // TODO this.addPixelEditor(filediv, node, neschrfmt); this.registerAsset("charmap", node, true); + nassets++; } else if (typeof data === 'string') { let textfrags = this.scanFileTextForAssets(fileid, data); for (let frag of textfrags) { if (frag.fmt) { let label = fileid; // TODO: label - let node : pixed.PixNode = new pixed.TextDataNode(projectWindows, fileid, label, data, frag.start, frag.end); + let node : pixed.PixNode = new pixed.TextDataNode(projectWindows, fileid, label, frag.start, frag.end); let first = node; // rle-compressed? if (frag.fmt.comp == 'rletag') { @@ -1194,6 +1203,10 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { console.log("Found " + this.rootnodes.length + " assets"); this.deferrednodes.forEach((node) => { node.refreshRight(); }); this.deferrednodes = []; + } else { + for (var node of this.rootnodes) { + node.refreshRight(); + } } } diff --git a/test/cli/testpixelconvert.js b/test/cli/testpixelconvert.js index da4f3871..05669699 100644 --- a/test/cli/testpixelconvert.js +++ b/test/cli/testpixelconvert.js @@ -13,13 +13,15 @@ describe('Pixel editor', function() { var palfmt = {pal:332,n:16}; var paldatastr = " 0x00, 0x03, 0x19, 0x50, 0x52, 0x07, 0x1f, 0x37, 0xe0, 0xa4, 0xfd, 0xff, 0x38, 0x70, 0x7f, 0xf8, "; - var node4 = new pixed.TextDataNode(null, null, null, paldatastr, 0, paldatastr.length); + var node4 = new pixed.TextDataNode(null, null, null, 0, paldatastr.length); + node4.text = paldatastr; var node5 = new pixed.PaletteFormatToRGB(palfmt); node4.addRight(node5); node4.refreshRight(); var datastr = "1,2, 0x00,0x00,0xef,0xef,0xe0,0x00,0x00, 0x00,0xee,0xee,0xfe,0xee,0xe0,0x00, 0x0e,0xed,0xef,0xef,0xed,0xee,0x00, 0x0e,0xee,0xdd,0xdd,0xde,0xee,0x00, 0x0e,0xee,0xed,0xde,0xee,0xee,0x00, 0x00,0xee,0xee,0xde,0xee,0xe0,0x00, 0x00,0xee,0xee,0xde,0xee,0xe0,0x00, 0x00,0x00,0xed,0xdd,0xe0,0x00,0x0d, 0xdd,0xdd,0xee,0xee,0xed,0xdd,0xd0, 0x0d,0xee,0xee,0xee,0xee,0xee,0x00, 0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, 0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, 0x0e,0xe0,0xdd,0xdd,0xd0,0xde,0x00, 0x0d,0x00,0xee,0x0e,0xe0,0x0d,0x00, 0x00,0x00,0xed,0x0e,0xe0,0x00,0x00, 0x00,0x0d,0xdd,0x0d,0xdd,0x00,0x18,"; - var node1 = new pixed.TextDataNode(null, null, null, datastr, 0, datastr.length); + var node1 = new pixed.TextDataNode(null, null, null, 0, datastr.length); + node1.text = datastr; var node2 = new pixed.Mapper(fmt); node1.addRight(node2); var node3 = new pixed.Palettizer(null, fmt);