Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions locales/messages.pot
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-17 08:29:+0000\n"
"POT-Creation-Date: 2026-05-13 09:55:+0000\n"
"Project-Id-Version: PACKAGE VERSION\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
Expand All @@ -9,43 +9,43 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"

#: src/js/Controller.js:234
#: src/js/Controller.js:248
msgid "<i class=\"fa fa-exclamation-triangle\"></i> Error"
msgstr ""

#: src/js/Controller.js:235
#: src/js/Controller.js:249
msgid "<i class=\"fa fa-info-circle\"></i> Information"
msgstr ""

#: src/js/Controller.js:359
#: src/js/Controller.js:377
msgid "Document was changed. Reloading."
msgstr ""

#: src/js/Controller.js:374
#: src/js/Controller.js:392
msgid "Document was changed."
msgstr ""

#: src/js/Controller.js:1770
#: src/js/Controller.js:1801
msgid "Presentation was exported to PDF."
msgstr ""

#: src/js/Controller.js:1773
#: src/js/Controller.js:1804
msgid "Failed to write PDF file."
msgstr ""

#: src/js/Controller.js:1786
#: src/js/Controller.js:1817
msgid "Presentation was exported to PPTX."
msgstr ""

#: src/js/Controller.js:1789
#: src/js/Controller.js:1820
msgid "Failed to write PPTX file."
msgstr ""

#: src/js/Controller.js:1802
#: src/js/Controller.js:1833
msgid "Presentation was exported to video."
msgstr ""

#: src/js/Controller.js:1805
#: src/js/Controller.js:1836
msgid "Failed to write video file."
msgstr ""

Expand Down Expand Up @@ -115,7 +115,7 @@ msgstr ""
msgid "Reset layer geometry"
msgstr ""

#: src/js/view/Properties.js:96 src/js/view/Timeline.js:193
#: src/js/view/Properties.js:96 src/js/view/Timeline.js:197
msgid "Create a new frame"
msgstr ""

Expand Down Expand Up @@ -275,7 +275,7 @@ msgstr ""
msgid "Select a layer to copy"
msgstr ""

#: src/js/view/Properties.js:203 src/js/view/Timeline.js:240 src/js/view/Timeline.js:268
#: src/js/view/Properties.js:203 src/js/view/Timeline.js:244 src/js/view/Timeline.js:272
msgid "Default"
msgstr ""

Expand Down Expand Up @@ -657,39 +657,39 @@ msgstr ""
msgid "Bit rate (kbits/sec)"
msgstr ""

#: src/js/view/Timeline.js:186
#: src/js/view/Timeline.js:190
msgid "Delete the selected frames"
msgstr ""

#: src/js/view/Timeline.js:212
#: src/js/view/Timeline.js:216
msgid "Add layer"
msgstr ""

#: src/js/view/Timeline.js:213
#: src/js/view/Timeline.js:217
msgid "Add all layers"
msgstr ""

#: src/js/view/Timeline.js:228 src/js/view/Timeline.js:248
#: src/js/view/Timeline.js:232 src/js/view/Timeline.js:252
msgid "This layer is visible. Click to hide it."
msgstr ""

#: src/js/view/Timeline.js:232 src/js/view/Timeline.js:252
#: src/js/view/Timeline.js:236 src/js/view/Timeline.js:256
msgid "This layer is hidden. Click to show it."
msgstr ""

#: src/js/view/Timeline.js:256
#: src/js/view/Timeline.js:260
msgid "Remove this layer"
msgstr ""

#: src/js/view/Timeline.js:281
#: src/js/view/Timeline.js:286
msgid "Insert selection before frame %d"
msgstr ""

#: src/js/view/Timeline.js:288
#: src/js/view/Timeline.js:293
msgid "Insert selection after frame %d"
msgstr ""

#: src/js/view/Timeline.js:323 src/js/view/Timeline.js:335
#: src/js/view/Timeline.js:331 src/js/view/Timeline.js:344
msgid ""
"You should add graphic elements in the current area to help Sozi keep track "
"of this layer's position."
Expand Down
69 changes: 50 additions & 19 deletions src/js/Controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,24 @@ export class Controller extends EventEmitter {
*/
this.editableLayers = [];

/** Set for O(1) editable layer lookup.
*
* @type {Set<module:model/Presentation.Layer>}
*/
this._editableLayerSet = new Set();

/** The layers that fall in the "default" row of the timeline.
*
* @type {module:model/Presentation.Layer[]}
*/
this.defaultLayers = [];

/** Set for O(1) default layer lookup.
*
* @type {Set<module:model/Presentation.Layer>}
*/
this._defaultLayerSet = new Set();

/** The stack of operations that can be undone.
*
* @type {Array}
Expand Down Expand Up @@ -197,12 +209,14 @@ export class Controller extends EventEmitter {
*/
fromStorable(storable) {
this.editableLayers = [];
this._editableLayerSet = new Set();

if ("editableLayers" in storable) {
for (let groupId of storable.editableLayers) {
const layer = this.presentation.getLayerWithId(groupId);
if (layer && this.editableLayers.indexOf(layer) < 0) {
if (layer && !this._editableLayerSet.has(layer)) {
this.editableLayers.push(layer);
this._editableLayerSet.add(layer);
}
}
}
Expand Down Expand Up @@ -291,11 +305,13 @@ export class Controller extends EventEmitter {
* @listens module:player/Player.frameChange
*/
onFrameChange() {
const currentFrame = this.selection.currentFrame;
if (!currentFrame) return;

let changed = false;
for (let layer of this.presentation.layers) {
const layerProperties = this.selection.currentFrame.layerProperties[layer.index];
const layerProperties = currentFrame.layerProperties[layer.index];
if (layer.isVisible) {
// Update the reference SVG element if applicable.
const {element} = this.viewport.cameras[layer.index].getCandidateReferenceElement();
if (element && element !== layerProperties.referenceElement) {
layerProperties.referenceElementId = element.getAttribute("id");
Expand Down Expand Up @@ -337,9 +353,11 @@ export class Controller extends EventEmitter {
// Collect all layers that are not in the editable set
// into the "default" layer set.
this.defaultLayers = [];
this._defaultLayerSet = new Set();
for (let layer of this.presentation.layers) {
if (this.editableLayers.indexOf(layer) < 0) {
if (!this._editableLayerSet.has(layer)) {
this.defaultLayers.push(layer);
this._defaultLayerSet.add(layer);
}
}

Expand Down Expand Up @@ -564,7 +582,7 @@ export class Controller extends EventEmitter {

// Create a new frame list by removing the selected frames
// and inserting them at the target frame index.
const reorderedFrames = this.presentation.frames.filter(frame => !this.selection.hasFrames([frame]));
const reorderedFrames = this.presentation.frames.filter(frame => !this.selection.hasFrame(frame));
Array.prototype.splice.apply(reorderedFrames, [toFrameIndex, 0].concat(framesByIndex));

// Identify the frames and layers that must be unlinked after the move operation.
Expand Down Expand Up @@ -603,7 +621,7 @@ export class Controller extends EventEmitter {
*/
updateCameraSelection() {
for (let camera of this.viewport.cameras) {
camera.selected = this.selection.hasLayers([camera.layer]);
camera.selected = this.selection.hasLayer(camera.layer);
}
}

Expand All @@ -622,13 +640,16 @@ export class Controller extends EventEmitter {
*/
addLayer(layerIndex) {
const layer = this.presentation.layers[layerIndex];
if (this.editableLayers.indexOf(layer) < 0) {
if (!this._editableLayerSet.has(layer)) {
this.editableLayers.push(layer);
this._editableLayerSet.add(layer);
}

const layerIndexInDefaults = this.defaultLayers.indexOf(layer);
if (layerIndexInDefaults >= 0) {
this.defaultLayers.splice(layerIndexInDefaults, 1);
if (this._defaultLayerSet.delete(layer)) {
const layerIndexInDefaults = this.defaultLayers.indexOf(layer);
if (layerIndexInDefaults >= 0) {
this.defaultLayers.splice(layerIndexInDefaults, 1);
}
}

this.addLayerToSelection(layer);
Expand Down Expand Up @@ -657,9 +678,14 @@ export class Controller extends EventEmitter {
}

this.editableLayers.push(layer);
this._editableLayerSet.add(layer);

const layerIndexInDefaults = this.defaultLayers.indexOf(layer);
this.defaultLayers.splice(layerIndexInDefaults, 1);
if (this._defaultLayerSet.delete(layer)) {
const layerIndexInDefaults = this.defaultLayers.indexOf(layer);
if (layerIndexInDefaults >= 0) {
this.defaultLayers.splice(layerIndexInDefaults, 1);
}
}

this.addLayerToSelection(layer);
}
Expand All @@ -686,8 +712,12 @@ export class Controller extends EventEmitter {
removeLayer(layerIndex) {
const layer = this.presentation.layers[layerIndex];

const layerIndexInEditable = this.editableLayers.indexOf(layer);
this.editableLayers.splice(layerIndexInEditable, 1);
if (this._editableLayerSet.delete(layer)) {
const layerIndexInEditable = this.editableLayers.indexOf(layer);
if (layerIndexInEditable >= 0) {
this.editableLayers.splice(layerIndexInEditable, 1);
}
}

if (this.defaultLayersAreSelected) {
this.addLayerToSelection(layer);
Expand All @@ -700,6 +730,7 @@ export class Controller extends EventEmitter {
}

this.defaultLayers.push(layer);
this._defaultLayerSet.add(layer);

// Force a repaint even if the controller
// did not modify the selection
Expand Down Expand Up @@ -727,7 +758,7 @@ export class Controller extends EventEmitter {
* @type {boolean}
*/
get defaultLayersAreSelected() {
return this.defaultLayers.every(layer => this.selection.selectedLayers.indexOf(layer) >= 0);
return this.defaultLayers.every(layer => this.selection.hasLayer(layer));
}

/** `true` if there is at least one layer in the "default" set.
Expand Down Expand Up @@ -768,7 +799,7 @@ export class Controller extends EventEmitter {
* @fires module:Controller.repaint
*/
addLayerToSelection(layer) {
if (!this.selection.hasLayers([layer])) {
if (!this.selection.hasLayer(layer)) {
this.selection.addLayer(layer);
this.updateCameraSelection();
this.emit("editorStateChange");
Expand All @@ -786,7 +817,7 @@ export class Controller extends EventEmitter {
* @fires module:Controller.repaint
*/
removeLayerFromSelection(layer) {
if (this.selection.hasLayers([layer])) {
if (this.selection.hasLayer(layer)) {
this.selection.removeLayer(layer);
this.updateCameraSelection();
this.emit("editorStateChange");
Expand Down Expand Up @@ -938,7 +969,7 @@ export class Controller extends EventEmitter {
updateLayerAndFrameSelection(single, sequence, layers, frameIndex) {
const frame = this.presentation.frames[frameIndex];
if (single) {
if (this.selection.hasLayers(layers) && this.selection.hasFrames([frame])) {
if (this.selection.hasLayers(layers) && this.selection.hasFrame(frame)) {
for (let layer of layers) {
this.selection.removeLayer(layer);
}
Expand Down Expand Up @@ -1102,7 +1133,7 @@ export class Controller extends EventEmitter {
if (layer != layerToCopy) {
frame.layerProperties[layer.index].copy(frame.layerProperties[layerToCopy.index]);
frame.cameraStates[layer.index].copy(frame.cameraStates[layerToCopy.index]);
if (frame.index === 0 || !this.selection.hasFrames([this.presentation.frames[frame.index - 1]])) {
if (frame.index === 0 || !this.selection.hasFrame(this.presentation.frames[frame.index - 1])) {
frame.layerProperties[layer.index].link = false;
}
}
Expand Down
15 changes: 12 additions & 3 deletions src/js/model/Presentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1055,18 +1055,27 @@ export class Presentation extends EventEmitter {
* This method must be called to propagate the changes in some frames to
* the frames that are linked to them.
*/
updateLinkedLayers() {
updateLinkedLayers(layerIndices) {
if (!this.frames.length) {
return;
}

// If specific layer indices are given, only update those layers.
// Otherwise, update all layers.
const layersToUpdate = layerIndices ||
this.layers.map((l, i) => i).filter(layerIndex =>
this.frames.some(frame => frame.layerProperties[layerIndex].link)
);

if (!layersToUpdate.length) return;

const firstCameraStates = this.frames[0].cameraStates;
const defaultCameraState = firstCameraStates[firstCameraStates.length - 1];

const firstLayerProperties = this.frames[0].layerProperties;
const defaultLayerProperties = firstLayerProperties[firstLayerProperties.length - 1];

this.layers.forEach((layer, layerIndex) => {
for (let layerIndex of layersToUpdate) {
let cameraState = defaultCameraState;
let layerProperties = defaultLayerProperties;

Expand All @@ -1081,7 +1090,7 @@ export class Presentation extends EventEmitter {
layerProperties = frame.layerProperties[layerIndex];
}
}
});
}
}

/** Get the custom files with the given extension.
Expand Down
Loading