mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
refactor: keep Scene prompts with their controls
This commit is contained in:
1 parent
d42036d74f
commit
9b6d34cb8a
5 files changed
+77
-15
No files matched your search
@@ -323,7 +323,7 @@ original Display groups, independent scroll positions and current focus owner.
|
|||||||
|
|
||||||
## Scene controls
|
## Scene controls
|
||||||
|
|
||||||
`ui/scenes` owns Scene panel input, project/shot rows, playback button and runtime
|
`ui/scenes` owns Scene prompts, panel input, project/shot rows, playback button and runtime
|
||||||
presentation. It receives project reads and explicit actions, with no imports of
|
presentation. It receives project reads and explicit actions, with no imports of
|
||||||
the director, source modules, camera engine or storage. Replacement and disposal
|
the director, source modules, camera engine or storage. Replacement and disposal
|
||||||
release listeners; pending action feedback is limited to its current owner.
|
release listeners; pending action feedback is limited to its current owner.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Scene control ownership
|
## Scene control ownership
|
||||||
|
|
||||||
Scene controls own panel listeners, shot rows, playback/recording presentation
|
Scene controls own creation/deletion prompts, panel listeners, shot rows, playback/recording presentation
|
||||||
and keyboard cancellation. The director supplies project reads and explicit
|
and keyboard cancellation. The director supplies project reads and explicit
|
||||||
editing/playback actions while retaining persistence, camera and layer sequencing.
|
editing/playback actions while retaining persistence, camera and layer sequencing.
|
||||||
Replacing rows revokes their old listeners. Disposal stops controls immediately;
|
Replacing rows revokes their old listeners. Disposal stops controls immediately;
|
||||||
|
|||||||
+3
-10
@@ -417,7 +417,7 @@ export class SceneDirector {
|
|||||||
this._saveProject();
|
this._saveProject();
|
||||||
this._renderShotList();
|
this._renderShotList();
|
||||||
},
|
},
|
||||||
create: () => this._createScene(),
|
create: (name) => this._createScene(name),
|
||||||
deleteScene: () => this._deleteSelectedScene(),
|
deleteScene: () => this._deleteSelectedScene(),
|
||||||
capture: () => this.captureShot(),
|
capture: () => this.captureShot(),
|
||||||
update: () => this.updateSelectedShot(),
|
update: () => this.updateSelectedShot(),
|
||||||
@@ -474,9 +474,8 @@ export class SceneDirector {
|
|||||||
return { scene, shot };
|
return { scene, shot };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Prompt the user for a name and append a new empty scene to the project. */
|
/** Append a named empty scene after the controls accept the creation prompt. */
|
||||||
_createScene() {
|
_createScene(sceneName) {
|
||||||
const sceneName = window.prompt('New scene name', `Scene ${this._project.scenes.length + 1}`);
|
|
||||||
if (!sceneName) return;
|
if (!sceneName) return;
|
||||||
|
|
||||||
const scene = {
|
const scene = {
|
||||||
@@ -498,9 +497,6 @@ export class SceneDirector {
|
|||||||
const scene = this._getSelectedScene();
|
const scene = this._getSelectedScene();
|
||||||
if (!scene) return;
|
if (!scene) return;
|
||||||
|
|
||||||
const ok = window.confirm(`Delete scene "${scene.title}" and all shots?`);
|
|
||||||
if (!ok) return;
|
|
||||||
|
|
||||||
this._project.scenes = this._project.scenes.filter((item) => item.id !== scene.id);
|
this._project.scenes = this._project.scenes.filter((item) => item.id !== scene.id);
|
||||||
// Restore default recipes if the user deleted all scenes
|
// Restore default recipes if the user deleted all scenes
|
||||||
if (!this._project.scenes.length) {
|
if (!this._project.scenes.length) {
|
||||||
@@ -596,9 +592,6 @@ export class SceneDirector {
|
|||||||
const { scene, shot } = this._getShot(sceneId, shotId);
|
const { scene, shot } = this._getShot(sceneId, shotId);
|
||||||
if (!scene || !shot) return;
|
if (!scene || !shot) return;
|
||||||
|
|
||||||
const ok = window.confirm(`Delete shot "${shot.title}"?`);
|
|
||||||
if (!ok) return;
|
|
||||||
|
|
||||||
scene.shots = scene.shots.filter((item) => item.id !== shot.id);
|
scene.shots = scene.shots.filter((item) => item.id !== shot.id);
|
||||||
this._selectedShotId = scene.shots[0]?.id || null;
|
this._selectedShotId = scene.shots[0]?.id || null;
|
||||||
this._saveProject();
|
this._saveProject();
|
||||||
|
|||||||
+30
-3
@@ -25,8 +25,6 @@ export class SceneControls {
|
|||||||
this.run('selectScene', elements.select.value),
|
this.run('selectScene', elements.select.value),
|
||||||
);
|
);
|
||||||
for (const [element, action] of Object.entries({
|
for (const [element, action] of Object.entries({
|
||||||
new: 'create',
|
|
||||||
delete: 'deleteScene',
|
|
||||||
capture: 'capture',
|
capture: 'capture',
|
||||||
update: 'update',
|
update: 'update',
|
||||||
next: 'next',
|
next: 'next',
|
||||||
@@ -35,6 +33,8 @@ export class SceneControls {
|
|||||||
})) {
|
})) {
|
||||||
this.listen(elements[element], 'click', () => this.run(action));
|
this.listen(elements[element], 'click', () => this.run(action));
|
||||||
}
|
}
|
||||||
|
this.listen(elements.new, 'click', () => this.createScene());
|
||||||
|
this.listen(elements.delete, 'click', () => this.deleteSelectedScene());
|
||||||
this.listen(elements.start, 'click', () =>
|
this.listen(elements.start, 'click', () =>
|
||||||
this.run('start', this.read().selectedSceneId),
|
this.run('start', this.read().selectedSceneId),
|
||||||
);
|
);
|
||||||
@@ -79,6 +79,33 @@ export class SceneControls {
|
|||||||
if (!this.destroyed) renderSceneOptions(this.elements.select, this.read());
|
if (!this.destroyed) renderSceneOptions(this.elements.select, this.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createScene() {
|
||||||
|
if (this.destroyed) return;
|
||||||
|
const name = window.prompt(
|
||||||
|
'New scene name',
|
||||||
|
`Scene ${this.read().scenes.length + 1}`,
|
||||||
|
);
|
||||||
|
if (name) this.run('create', name);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteSelectedScene() {
|
||||||
|
if (this.destroyed) return;
|
||||||
|
const state = this.read();
|
||||||
|
const scene = state.scenes.find(
|
||||||
|
(item) => item.id === state.selectedSceneId,
|
||||||
|
);
|
||||||
|
if (scene && window.confirm(`Delete scene "${scene.title}" and all shots?`))
|
||||||
|
this.run('deleteScene');
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteShot(sceneId, shotId) {
|
||||||
|
if (this.destroyed) return;
|
||||||
|
const scene = this.read().scenes.find((item) => item.id === sceneId);
|
||||||
|
const shot = scene?.shots.find((item) => item.id === shotId);
|
||||||
|
if (shot && window.confirm(`Delete shot "${shot.title}"?`))
|
||||||
|
this.run('deleteShot', sceneId, shotId);
|
||||||
|
}
|
||||||
|
|
||||||
renderShotList() {
|
renderShotList() {
|
||||||
if (this.destroyed) return;
|
if (this.destroyed) return;
|
||||||
for (const remove of this.rowRemovers.splice(0)) remove();
|
for (const remove of this.rowRemovers.splice(0)) remove();
|
||||||
@@ -89,7 +116,7 @@ export class SceneControls {
|
|||||||
rename: (sceneId, shotId, title) =>
|
rename: (sceneId, shotId, title) =>
|
||||||
this.run('renameShot', sceneId, shotId, title),
|
this.run('renameShot', sceneId, shotId, title),
|
||||||
load: (sceneId, shotId) => this.run('load', sceneId, shotId),
|
load: (sceneId, shotId) => this.run('load', sceneId, shotId),
|
||||||
remove: (sceneId, shotId) => this.run('deleteShot', sceneId, shotId),
|
remove: (sceneId, shotId) => this.deleteShot(sceneId, shotId),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,3 +262,45 @@ test('Scene action failures are visible only while that action still owns presen
|
|||||||
f.restore();
|
f.restore();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('creation and deletion prompts precede their model actions and remain inert after disposal', () => {
|
||||||
|
const f = fixture();
|
||||||
|
try {
|
||||||
|
let prompts = 0;
|
||||||
|
window.prompt = () => {
|
||||||
|
prompts++;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
window.confirm = () => {
|
||||||
|
prompts++;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
f.elements.new.click();
|
||||||
|
f.elements.delete.click();
|
||||||
|
f.owner.deleteShot('scene-a', 'shot-a');
|
||||||
|
assert.deepEqual(f.calls, []);
|
||||||
|
window.prompt = () => {
|
||||||
|
prompts++;
|
||||||
|
return 'New title';
|
||||||
|
};
|
||||||
|
window.confirm = () => {
|
||||||
|
prompts++;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
f.elements.new.click();
|
||||||
|
f.elements.delete.click();
|
||||||
|
f.owner.deleteShot('scene-a', 'shot-a');
|
||||||
|
assert.deepEqual(f.calls, [
|
||||||
|
['create', 'New title'],
|
||||||
|
['deleteScene'],
|
||||||
|
['deleteShot', 'scene-a', 'shot-a'],
|
||||||
|
]);
|
||||||
|
f.owner.destroy();
|
||||||
|
f.owner.createScene();
|
||||||
|
f.owner.deleteSelectedScene();
|
||||||
|
f.owner.deleteShot('scene-a', 'shot-a');
|
||||||
|
assert.equal(prompts, 6);
|
||||||
|
} finally {
|
||||||
|
f.restore();
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in new issue
Block a user