From 3b6a57024d40bb0398ed9faf12e165901bec9f27 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sun, 14 Apr 2024 17:36:04 +0200 Subject: [PATCH] audioLayer panel injection dependencies hashes: openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501 ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3 ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239 ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8 theatre 15b2b9543be7a9ed5c8889e04df715f91d479d45 --- bin/em/variabletime/web/css/theatre.css | 16 ++ bin/em/variabletime/web/js/audioLayer.js | 263 ++++++++++++++++++--- bin/em/variabletime/web/js/audioPlayer.js | 33 ++- bin/em/variabletime/web/js/main.js | 3 + bin/em/variabletime/web/js/theatre-play.js | 5 + bin/em/variabletime/web/js/utils.js | 7 + 6 files changed, 281 insertions(+), 46 deletions(-) diff --git a/bin/em/variabletime/web/css/theatre.css b/bin/em/variabletime/web/css/theatre.css index 883ba64..8349f9e 100644 --- a/bin/em/variabletime/web/css/theatre.css +++ b/bin/em/variabletime/web/css/theatre.css @@ -732,3 +732,19 @@ input:disabled { background: darkgrey; color: lightgrey; } + +/* ASYA: most the following code does not work*/ +.onOffIndicatorWrapper input[type=checkbox] + label::after { + content: ' OFF'; +} + +.onOffIndicatorWrapper input[type=checkbox]:checked + label{ + color: #1cba94; +} + +.onOffIndicatorWrapper input[type=checkbox]:checked + label::after{ + content: ' ON'; +} +.onOffIndicatorWrapper { + width: auto; +} diff --git a/bin/em/variabletime/web/js/audioLayer.js b/bin/em/variabletime/web/js/audioLayer.js index b9759ad..04731e6 100644 --- a/bin/em/variabletime/web/js/audioLayer.js +++ b/bin/em/variabletime/web/js/audioLayer.js @@ -1,26 +1,28 @@ import { sanitizeTheatreKey, arraysEqual, + clone, } from './utils.js'; const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { // private let props = { }; if (typeof values === 'string') { - const filename = sanitizeTheatreKey(values); + const audioID = sanitizeTheatreKey(values); values = {}; - values[filename] = false; + values[audioID] = false; } if (typeof values === 'object') { props[Object.keys(values)[0]] = tp.core.types.boolean(false); } const onValuesChangeCallbacks = []; + let hierarchyPanelButton = null; + let panelFinderTimeout = false; // private functions // const onValuesChange = (values) => { - console.log(values); const n_callbacks = onValuesChangeCallbacks.length; const successes = []; if (n_callbacks > 0) { @@ -38,18 +40,173 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { }); } }; - - const findInjectPanel = () => { - console.log('AudioLayer::findInjectPanel'); - const sequencePanel = tp.getSequencePanel(); - const sequencePanelLeft = tp.getSequencePanelLeft(); + const getHierarchyPanelButton = () => { + if (hierarchyPanelButton === null) { + hierarchyPanelButton = tp.shadowRoot.querySelector(`.layerMover${this.id()}`); + } + return hierarchyPanelButton; }; - const setTime = (filename, start, stop) => { - filename = sanitizeTheatreKey(filename); + const getPanelPropDiamond = (panelPropTitle) => { + return panelPropTitle.previousSibling; + }; + + const getPanelPropInput = (panelPropTitle) => { + return panelPropTitle + .parentElement + .nextSibling + .querySelector('input'); + }; + + const findInjectPanel = () => { + let doItAgain = true; + + console.log('AudioLayer::findInjectPanel'); + const panel = tp.getPanel(); + + let panelSuccesses = 0; + const audioIDs = Object.keys(this.theatreObject.value); + { // PANEL + for (let i = 0; i < audioIDs.length; i++) { + const audioID = audioIDs[i]; + let panelPropTitle = tp.getPanelPropTitle(audioID); + const panelPropContainer = tp.getPanelPropContainer(panelPropTitle); + if (panelPropTitle === null || panelPropContainer == null) { + continue; + } + + // Phase 1) prevent unwanted interaction + + // remove theatrejs listeners + // such as contextmenu + // NOTE: this does not work with stop(Immediate)Propagation here + const tmp = panelPropTitle.cloneNode(true); + panelPropTitle.parentNode.replaceChild(tmp, panelPropTitle); + panelPropTitle = tmp; + + // remove diamond + const diamond = getPanelPropDiamond(panelPropTitle); + if (diamond !== null) { + diamond.remove(); + } + // unchangeable boolean + const boolean = getPanelPropInput(panelPropTitle); + boolean.disabled = true; + boolean.parentElement.classList.add('onOffIndicatorWrapper'); + boolean.classList.add('onOffIndicator'); + boolean.style.width = 'auto'; + + const container = document.createElement('div'); + container.innerHTML = ` +
+ delete +
+ `; + for (let i = 0; i < container.children.length; i++) { + panelPropContainer.append(container.children[i]); + }; + panelPropContainer.querySelector(`.button.delete${audioID}`) + .addEventListener('click', () => { + this.removeFile(audioID); + }); + + panelSuccesses++; + }; + } + if (panelSuccesses === audioIDs.length) { + doItAgain = false; + } + + if (doItAgain) { + clearTimeout(panelFinderTimeout); + panelFinderTimeout = setTimeout(() => { + this.findInjectPanel(); + }, 30); + } + } + const findInjectSequencePanel = () => { + let doItAgain = true; + let sequencePanelSuccess = 0; + const sequencePanel = tp.getSequencePanel(); + const sequencePanelLeft = tp.getSequencePanelLeft(); + if (sequencePanelLeft !== null) { // SEQUENCE PANEL LEFT + const propTitles = sequencePanelLeft.querySelectorAll('[data-testid="SequencePanel-PropTitle"]'); + const audioLayerTitle = (() => { + const titles = sequencePanelLeft.querySelectorAll('[data-testid="SequencePanel-Title"]'); + for (let i = 0; i < titles.length; i++) { + if (titles[i].innerHTML === this.id()) { + return titles[i]; + } + } + return null; + })(); + if (audioLayerTitle !== null) { + const audioSection = audioLayerTitle.closest('li'); + if (audioSection !== null) { + const audioPropTitles = audioSection.querySelectorAll('[data-testid="SequencePanel-PropTitle"]'); + window.audioPropTitles = audioPropTitles; + audioPropTitles.forEach((audioPropTitle) => { + const diamond = (() => { + const potential = audioPropTitle.nextSibling; + if (potential !== null + && potential.nodeName.toLowerCase() === 'div' + && potential.querySelectorAll(':scope > [data-pi-key]').length === 3) { + return potential; + } + return null; + })(); + if (diamond !== null) { + diamond.style.display = 'none'; + } + if (audioPropTitle.innerHTML.length > config.audio.maxFilenameLength) { + const file = audioPropTitle.innerHTML; + const m = (config.audio.maxFilenameLength / 2) - 2; + audioPropTitle.innerHTML = file.substr(0,m) + '..' + file.substr(file.length - m, m); + } + }); + sequencePanelSuccess++; + } + } + } + if (sequencePanelLeft !== null) { // SEQUENCE PANEL RIGHT + const audioSectionAnchor = sequencePanelLeft + .nextSibling + .querySelector(`[data-pi-key*="${this.id()}"]`); + if (audioSectionAnchor !== null) { + const audioSection = audioSectionAnchor.closest('li'); + if (audioSection !== null) { + audioSection.addEventListener('contextmenu', (e) => { + e.stopPropagation(); + e.stopImmediatePropagation(); + }); + audioSection.querySelectorAll('*').forEach((element) => { + element.addEventListener('contextmenu', (e) => { + e.stopPropagation(); + e.stopImmediatePropagation(); + }); + }); + sequencePanelSuccess++; + } + } + } + + if (sequencePanelSuccess === 2) { + doItAgain = false; + } + + if (doItAgain) { + clearTimeout(panelFinderTimeout); + panelFinderTimeout = setTimeout(() => { + this.findInjectSequencePanel(); + }, 30); + } + }; + + const setTime = (audioID, start, stop) => { + audioID = sanitizeTheatreKey(audioID); return new Promise((resolve) => { const keyframes = [{ - path: [filename], + path: [audioID], keyframes: [{ position: -1, value: false, @@ -67,11 +224,11 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { }, ], }]; - if (tp.getKeyframes(this, [filename]).length > 0) { + if (tp.getKeyframes(this, [audioID]).length > 0) { tp.studio.transaction(({ __experimental_deleteKeyframes }) => { - __experimental_deleteKeyframes(this.theatreObject.props[filename]); + __experimental_deleteKeyframes(this.theatreObject.props[audioID]); }); } tp.addKeyframes(this, keyframes).then(() => { @@ -80,6 +237,15 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { }); }; + const getValue = (audioID) => { + const values = this.theatreObject.value; + if (typeof values[audioID] !== 'undefined') { + return values[audioID]; + } else { + return null; + } + }; + const init = () => { return new Promise((resolve) => { this.theatreObject = tp.addObject(audioLayerID, this.props, onValuesChange); @@ -97,33 +263,21 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { }; const remove = () => { + tp.removeObject(audioLayerID); }; // public this.props = props; this.findInjectPanel = findInjectPanel; + this.findInjectSequencePanel = findInjectSequencePanel; - this.setFile = (filename) => { - filename = sanitizeTheatreKey(filename); - const values = {}; - values[filename] = false; - props = {}; - props[filename] = tp.core.types.boolean(values[filename]); - this.props = props; - tp.changeObject(audioLayerID, this.props); - tp.studio.transaction(({ - set - }) => { - set(this.theatreObject.props, values); - }); - }; - this.addFile = (filename) => { + this.addFile = (audioID) => { return new Promise((resolve) => { - filename = sanitizeTheatreKey(filename); + audioID = sanitizeTheatreKey(audioID); const values = this.theatreObject.value; - values[filename] = false; - props[filename] = tp.core.types.boolean(values[filename]); + values[audioID] = false; + props[audioID] = tp.core.types.boolean(values[audioID]); this.props = props; const setDummy = () => { @@ -154,12 +308,12 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { setDummy(); }); }; - this.removeFile = (filename) => { + this.removeFile = (audioID) => { return new Promise((resolve) => { - filename = sanitizeTheatreKey(filename); - const values = this.theatreObject.value; - delete values[filename]; - delete props[filename]; + audioID = sanitizeTheatreKey(audioID); + const values = clone(this.theatreObject.value); + delete values[audioID]; + delete props[audioID]; this.props = props; const setDummy = () => { tp.changeObject(audioLayerID, { @@ -175,16 +329,53 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) { const givenValueKeys = Object.keys(againChangedValues); if (arraysEqual(expectedValueKeys, givenValueKeys)) { resolve(); + return true; + } else { + return false; } }); tp.changeObject(audioLayerID, this.props); + return true; + } else { + return false; } }); setDummy(); + + if (!audio.audioPlayer.remove(audioID)) { + console.log('AudioLayer::remove', `could not remove ${audioID} from audioPlayer`); + } }); }; this.setTime = setTime; + this.getValue = getValue; + + this.isSelected = () => { + const panel = getHierarchyPanelButton(); + if (panel === null) { + return false; + } else { + const notSelected = panel.querySelector('.not-selected'); + return !notSelected; + } + }; + + this.select = () => { + const panel = getHierarchyPanelButton(); + const selectables = panel.querySelector('.not-selected'); + if (selectables !== null && typeof selectables.dispatchEvent === 'function') { + var clickEvent = new MouseEvent("click", { + "view": window, + "bubbles": true, + "cancelable": false + }); + selectables.dispatchEvent(clickEvent); + } else { + window.debugElement = panel; + } + }; + this.init = init; this.id = () => { diff --git a/bin/em/variabletime/web/js/audioPlayer.js b/bin/em/variabletime/web/js/audioPlayer.js index d3c7ee2..956a64b 100644 --- a/bin/em/variabletime/web/js/audioPlayer.js +++ b/bin/em/variabletime/web/js/audioPlayer.js @@ -59,19 +59,32 @@ const AudioPlayer = function() { startTime }); }; + this.remove = (audioID) => { + const index = audioElements.findIndex((e) => { return e.audioID === audioID; }); + if (index < 0) { + return false; + } else { + audioElements.splice(index, 1); + return true; + } + }; this.update = () => { audioElements.forEach((audioElement, i) => { if (tp.isPlaying() && !record.isRecording()) { - const shouldBePlaying = getAudioLayer().theatreObject.value[audioElement.audioID]; - if (shouldBePlaying && audioElement.audioDomElement.paused) { - // sequence position is always greater than startTime - // this is true, as it is written - const diff = tp.sheet.sequence.position - audioElement.startTime; - audioElement.audioDomElement.currentTime = diff; - audioElement.audioDomElement.play(); - } else if (!shouldBePlaying && !audioElement.audioDomElement.paused) { - audioElement.audioDomElement.pause(); - audioElement.audioDomElement.currentTime = 0; + const shouldBePlaying = getAudioLayer().getValue([audioElement.audioID]); + if (shouldBePlaying !== null) { + if (shouldBePlaying && audioElement.audioDomElement.paused) { + // sequence position is always greater than startTime + // this is true, as it is written + const diff = tp.sheet.sequence.position - audioElement.startTime; + audioElement.audioDomElement.currentTime = diff; + audioElement.audioDomElement.play(); + } else if (!shouldBePlaying && !audioElement.audioDomElement.paused) { + audioElement.audioDomElement.pause(); + audioElement.audioDomElement.currentTime = 0; + } + } else { + console.log('AudioPlayer::update',`${audioElement.audioID} does not exist in audioLayer.`); } } else if (!audioElement.audioDomElement.paused) { audioElement.audioDomElement.pause(); diff --git a/bin/em/variabletime/web/js/main.js b/bin/em/variabletime/web/js/main.js index 60ca1ee..93f389a 100644 --- a/bin/em/variabletime/web/js/main.js +++ b/bin/em/variabletime/web/js/main.js @@ -586,6 +586,9 @@ const deleteLayer = (id, saveProject = true) => { const addAudioLayer = (filename = false) => { return new Promise((resolve) => { const audioLayerID = (() => { + if (audioLayers.length === 0) { + return 'audio'; + } let index = 0; for (let i = 0; i < audioLayers.length; i++) { if (audioLayers.findIndex((audioLayer) => audioLayer.id() === `audio-${index}`) < 0) { diff --git a/bin/em/variabletime/web/js/theatre-play.js b/bin/em/variabletime/web/js/theatre-play.js index 5a36a2e..b5a71ef 100644 --- a/bin/em/variabletime/web/js/theatre-play.js +++ b/bin/em/variabletime/web/js/theatre-play.js @@ -284,6 +284,11 @@ const TheatrePlay = function(autoInit = false) { friendlySequenceNames(); }, 1000); } + + // dirty, should be not in here: + if (getAudioLayers().length > 0) { + getAudioLayer().findInjectSequencePanel(); + } }; //public diff --git a/bin/em/variabletime/web/js/utils.js b/bin/em/variabletime/web/js/utils.js index 8fe1a20..1d31e9a 100644 --- a/bin/em/variabletime/web/js/utils.js +++ b/bin/em/variabletime/web/js/utils.js @@ -372,6 +372,12 @@ function getParents(elem, until = null) { return parents; } +function removeAllEventListeners(elem) { + const tmp = elem.cloneNode(true); + elem.parentNode.replaceChild(tmp, elem); + elem = tmp; +} + function arraysEqual(a, b, sortingMatters = false) { if (a === b) return true; if (a == null || b == null) return false; @@ -618,6 +624,7 @@ export { mixObject, clone, getParents, + removeAllEventListeners, arraysEqual, mapValue, smoothValue,