Shuffling layer order now triggers a tile re-render

This commit is contained in:
Igor Zinken
2026-03-27 19:21:04 +01:00
parent f37cf84ea4
commit 1712ba2068
4 changed files with 64 additions and 29 deletions

View File

@@ -21,9 +21,11 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { type Store } from "vuex";
import { type Document } from "@/definitions/document";
import type { Document, RelId } from "@/definitions/document";
import { enqueueState } from "@/factories/history-state-factory";
import { createGroupTile } from "@/rendering/cache/tile-cache";
import { type BitMapperyState } from "@/store";
import { getTileByLayer } from "@/utils/timeline-util";
/**
* layerIds represents an ordered list
@@ -45,15 +47,22 @@ export const reorderLayers = ( store: Store<BitMapperyState>, activeDocument: Do
subsetIndices.forEach(( idx: number, i: number ) => {
updatedOrder[ idx ] = layerIds[ i ];
});
const tileIds = activeDocument.type === "timeline" ? [ ...layerIds.reduce(( acc, layerId ) => {
acc.add( getTileByLayer( activeDocument, layerId ));
return acc;
}, new Set<RelId>())] : [];
const commit = () => {
store.commit( "reorderLayers", { document: activeDocument, layerIds: updatedOrder } );
}
store.commit( "reorderLayers", { activeDocument, layerIds: updatedOrder } );
tileIds.forEach( tileId => createGroupTile( tileId, activeDocument ));
};
commit();
enqueueState( `reorderLayers_${layerIds.join()}`, {
undo() {
store.commit( "reorderLayers", { document: activeDocument, layerIds: originalOrder });
undo(): void {
store.commit( "reorderLayers", { activeDocument, layerIds: originalOrder });
tileIds.forEach( tileId => createGroupTile( tileId, activeDocument ));
},
redo: commit,
});

View File

@@ -147,14 +147,14 @@ const DocumentModule: Module<DocumentState, any> = {
layers[ index1 ] = layers[ index2 ];
layers[ index2 ] = obj1;
},
reorderLayers( _state: DocumentState, { document, layerIds }: { document: Document, layerIds: string[] }): void {
const oldLayers = [ ...document.layers ];
const layers = [ ...document.layers ];
reorderLayers( _state: DocumentState, { activeDocument, layerIds }: { activeDocument: Document, layerIds: string[] }): void {
const oldLayers = [ ...activeDocument.layers ];
const layers = [ ...activeDocument.layers ];
layers.splice( 0, oldLayers.length );
layerIds.forEach( id => {
layers.push( oldLayers.find( layer => layer.id === id ));
});
document.layers = layers;
activeDocument.layers = layers;
flushBlendedLayerCache( true );
},
removeLayer( state: DocumentState, index: number ): void {

View File

@@ -4,6 +4,7 @@ import { createStore, mockZCanvas } from "../../mocks";
mockZCanvas();
import type { Document } from "@/definitions/document";
import DocumentFactory from "@/factories/document-factory";
import LayerFactory from "@/factories/layer-factory";
import { type BitMapperyState } from "@/store";
@@ -14,6 +15,11 @@ vi.mock( "@/factories/history-state-factory", () => ({
enqueueState: ( ...args: any[] ) => mockEnqueueState( ...args ),
}));
const mockCreateGroupTile = vi.fn();
vi.mock( "@/rendering/cache/tile-cache", () => ({
createGroupTile: ( ...args: any[] ) => mockCreateGroupTile( ...args ),
}));
describe( "reorder layers action", () => {
const tile1layer1 = LayerFactory.create({ rel: { type: "tile", id: 0 }});
const tile1layer2 = LayerFactory.create({ rel: { type: "tile", id: 0 }});
@@ -24,24 +30,27 @@ describe( "reorder layers action", () => {
const tile3layer1 = LayerFactory.create({ rel: { type: "tile", id: 2 }});
const document = DocumentFactory.create({
layers: [
tile1layer1, tile1layer2,
tile2layer1, tile2layer2, tile2layer3,
tile3layer1,
]
});
const originalLayerIds = document.layers.map( layer => layer.id );
const shuffledLayerIds = [
tile1layer1.id, tile1layer2.id,
tile2layer3.id, tile2layer1.id, tile2layer2.id, // reorder is on this line!
tile3layer1.id,
];
let originalLayerIds: string[];
let activeDocument: Document;
let store: Store<BitMapperyState>;
beforeEach(() => {
store = createStore();
activeDocument = DocumentFactory.create({
type: "timeline",
layers: [
tile1layer1, tile1layer2,
tile2layer1, tile2layer2, tile2layer3,
tile3layer1,
]
});
originalLayerIds = activeDocument.layers.map( layer => layer.id );
});
afterEach(() => {
@@ -49,28 +58,28 @@ describe( "reorder layers action", () => {
});
it( "should be able to reorder the Layers within the Document by their ids", () => {
reorderLayers( store, document, shuffledLayerIds);
reorderLayers( store, activeDocument, shuffledLayerIds);
expect( store.commit ).toHaveBeenCalledTimes( 1 );
expect( store.commit ).toHaveBeenCalledWith( "reorderLayers", {
document,
activeDocument,
layerIds: shuffledLayerIds,
});
});
it( "should be able to reorder the Layers within the Document when receiving only a subset of all Layer content", () => {
const shuffledSubSet = [ tile2layer3.id, tile2layer1.id, tile2layer2.id ];
reorderLayers( store, document, shuffledSubSet);
reorderLayers( store, activeDocument, shuffledSubSet);
expect( store.commit ).toHaveBeenCalledTimes( 1 );
expect( store.commit ).toHaveBeenCalledWith( "reorderLayers", {
document,
activeDocument,
layerIds: shuffledLayerIds, // expect shuffled set to have been applied to full content
});
});
it( "should store the action in state history", () => {
reorderLayers( store, document, shuffledLayerIds );
reorderLayers( store, activeDocument, shuffledLayerIds );
expect( mockEnqueueState ).toHaveBeenCalledWith(
`reorderLayers_${shuffledLayerIds.join()}`, {
@@ -81,20 +90,20 @@ describe( "reorder layers action", () => {
});
it( "should restore the original order when calling undo in state history", () => {
reorderLayers( store, document, shuffledLayerIds );
reorderLayers( store, activeDocument, shuffledLayerIds );
const { undo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
undo();
expect( store.commit ).toHaveBeenCalledTimes( 2 );
expect( store.commit ).toHaveBeenNthCalledWith( 2, "reorderLayers", {
document,
activeDocument,
layerIds: originalLayerIds,
});
});
it( "should reapply the new order when calling redo in state history", () => {
reorderLayers( store, document, shuffledLayerIds );
reorderLayers( store, activeDocument, shuffledLayerIds );
const { undo, redo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
undo();
@@ -102,8 +111,25 @@ describe( "reorder layers action", () => {
expect( store.commit ).toHaveBeenCalledTimes( 3 );
expect( store.commit ).toHaveBeenNthCalledWith( 3, "reorderLayers", {
document,
activeDocument,
layerIds: shuffledLayerIds,
});
});
it( "should request a re-render of tiles associated with the layers", () => {
reorderLayers( store, activeDocument, shuffledLayerIds );
expect( mockCreateGroupTile ).toHaveBeenCalledTimes( 3 );
expect( mockCreateGroupTile ).toHaveBeenNthCalledWith( 1, 0, activeDocument );
expect( mockCreateGroupTile ).toHaveBeenNthCalledWith( 2, 1, activeDocument );
expect( mockCreateGroupTile ).toHaveBeenNthCalledWith( 3, 2, activeDocument );
});
it( "should not request a re-render of tiles associated with the layers for non-timeline type Documents", () => {
activeDocument.type = "default";
reorderLayers( store, activeDocument, shuffledLayerIds );
expect( mockCreateGroupTile ).not.toHaveBeenCalled();
});
});

View File

@@ -523,7 +523,7 @@ describe( "Vuex document module", () => {
documents: [ DocumentFactory.create({ name: "foo", layers })],
activeIndex: 0
});
mutations.reorderLayers( state, { document: state.documents[ 0 ], layerIds: [
mutations.reorderLayers( state, { activeDocument: state.documents[ 0 ], layerIds: [
layers[ 1 ].id, layers[ 2 ].id, layers[ 0 ].id, layers[ 3 ].id
] });
// note we check by reference to ensure all bindings remain