From 4b46502d22e03389ec77639eab2f7c409d30ba2b Mon Sep 17 00:00:00 2001 From: Christian Schwinne Date: Wed, 30 Jun 2021 01:01:15 +0200 Subject: [PATCH] Playlist UI (#2046) * Test 1 * State 2 * Playlist UI progress * Playlist saving * Playlist saving * Playlist object array * Added Offset to segment options * Positioning * Playlist UI complete --- wled00/data/index.css | 64 ++++++- wled00/data/index.htm | 12 +- wled00/data/index.js | 423 ++++++++++++++++++++++++++++++++---------- 3 files changed, 378 insertions(+), 121 deletions(-) diff --git a/wled00/data/index.css b/wled00/data/index.css index f4b4eaf15..20ba28d98 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -402,9 +402,8 @@ button { } #imgw { - width: 200px; - height: 55px; display: inline-block; + margin: 8px; } #kv, #kn { @@ -432,6 +431,12 @@ img { max-height: 100%; } +.wi { + image-rendering: pixelated; + image-rendering: crisp-edges; + width: 210px; +} + @keyframes fadein { from {bottom: 0; opacity: 0;} to {bottom: calc(var(--bh) + 22px); opacity: 1;} @@ -519,12 +524,16 @@ input[type=range]:active + .sliderbubble { position: relative; } +.sbs { + margin: 0px -20px 5px -6px; +} + .sws { - width: 212px; + width: 230px; } .sis { - width: 192px !important; + width: 214px !important; } .hd { @@ -561,12 +570,11 @@ input[type=range]:active + .sliderbubble { } .btn-s { - padding: 9px; width: 276px; background-color: var(--c-2); } .btn-i { - padding-bottom: 3px; + padding-bottom: 4px; } .btn-icon { margin: 0px 8px 4px 0; @@ -578,6 +586,19 @@ input[type=range]:active + .sliderbubble { .btn-p { width: 216px; } +.btn-xs { + width: 39px; +} +.btn-pl-add { + position: absolute; + bottom: -29px; + left: 94px; +} +.btn-pl-del { + position: absolute; + right: 2px; + bottom: -3px; +} #qcs-w { margin-top: 10px; @@ -601,6 +622,16 @@ input[type=range]:active + .sliderbubble { width: 42px; } +.sel-pl { + width: 200px; + background-position: 176px 16px; +} + +.sel-ple { + width: 216px; + background-position: 192px 16px; +} + select { -webkit-appearance: none; -moz-appearance: none; @@ -665,6 +696,11 @@ input[type=text] { margin: 26px 0 6px 12px !important; } +.plentry { + margin-top: 16px; + position: relative; +} + .stxt { width: 50px !important; } @@ -741,9 +777,9 @@ input[type=number]::-webkit-outer-spin-button { .cnf-s { position: absolute; - top: 66px; - right: 28px; - padding: 43px 6px; + top: 172px; + right: 23px; + padding: 7.5px 22px; } .pwr { @@ -1016,6 +1052,16 @@ input[type="text"].search:not(:placeholder-shown) { color: red; } +.hrz { + width: auto; + height: 2px; + background-color: var(--c-e); +} +.hrz-pl { + margin: 20px 0; + position: relative; +} + ::-webkit-scrollbar { width: 6px; } diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 825832e4d..a998a9f3f 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -165,15 +165,7 @@
Loading...

-
- First preset:
- Last preset:
- Time per preset: s
- Transition: s + Transition s @@ -189,7 +181,7 @@
- - - - - - - - - -
Start LEDStop LED
- - - - - - - - - -
GroupingSpacing
-
- -
- -
-
- - +
+ +
+ +
+
+
+ + + + + + + + + + + +
Start LEDStop LEDOffset
+ + + + + + + + + + + +
GroupingSpacingApply
+
+
`; @@ -1184,37 +1192,148 @@ function resetUtil() { d.getElementById('segutil').innerHTML = cn; } -function makeP(i) { +var plJson = {"0":{ + "ps": [0], + "dur": [100], + "transition": [-1], //to be inited to default transition dur + "repeat": 0, + "r": false, + "end": 0 +}}; + +var plSelContent = ""; +function makePlSel(arr) { + plSelContent = ""; + for (var i = 0; i < arr.length; i++) { + var n = arr[i][1].n ? arr[i][1].n : "Preset " + arr[i][0]; + if (arr[i][1].playlist && arr[i][1].playlist.ps) continue; //remove playlists, sub-playlists not yet supported + plSelContent += `` + } +} + +function refreshPlE(p) { + var plEDiv = d.getElementById(`ple${p}`); + if (!plEDiv) return; + var content = ""; + for (var i = 0; i < plJson[p].ps.length; i++) { + content += makePlEntry(p,i); + } + plEDiv.innerHTML = content; + var dels = plEDiv.getElementsByClassName("btn-pl-del"); + if (dels.length < 2 && p > 0) dels[0].style.display = "none"; + + var sels = d.getElementById(`seg${p+100}`).getElementsByClassName("sel"); + for (var i of sels) { + if (i.dataset.val) { + if (parseInt(i.dataset.val) > 0) i.value = i.dataset.val; + else plJson[p].ps[i.dataset.index] = parseInt(i.value); + } + } +} + +//p: preset ID, i: ps index +function addPl(p,i) { + plJson[p].ps.splice(i+1,0,0); + plJson[p].dur.splice(i+1,0,plJson[p].dur[i]); + plJson[p].transition.splice(i+1,0,plJson[p].transition[i]); + refreshPlE(p); +} + +function delPl(p,i) { + if (plJson[p].ps.length < 2) {if (p == 0) resetPUtil(); return;} + plJson[p].ps.splice(i,1); + plJson[p].dur.splice(i,1); + plJson[p].transition.splice(i,1); + refreshPlE(p); +} + +function plePs(p,i,field) { + plJson[p].ps[i] = parseInt(field.value); +} + +function pleDur(p,i,field) { + if (field.validity.valid) + plJson[p].dur[i] = Math.floor(field.value*10); +} + +function pleTr(p,i,field) { + if (field.validity.valid) + plJson[p].transition[i] = Math.floor(field.value*10); +} + +function plR(p) { + var pl = plJson[p]; + pl.r = d.getElementById(`pl${p}rtgl`).checked; + if (d.getElementById(`pl${p}rptgl`).checked) { //infinite + pl.repeat = 0; + delete pl.end; + d.getElementById(`pl${p}o1`).style.display = "none"; + } else { + pl.repeat = parseInt(d.getElementById(`pl${p}rp`).value); + pl.end = parseInt(d.getElementById(`pl${p}selEnd`).value); + d.getElementById(`pl${p}o1`).style.display = "block"; + } +} + +function makeP(i,pl) { + var content = ""; + if (pl) { + var rep = plJson[i].repeat ? plJson[i].repeat : 0; + content = ` +
+ +
+
Repeat 0?rep:1}> times
+ End preset:
+ +
+ `; + } + else content = ` + `; + return `
Quick load label:
(leave empty for no Quick load button)
+

-
- API command
- -
-
- - + ${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"} + + +
+
+ API command
+ +
+
+ ${content} +
Save to ID 0)?i:getLowestUnusedP()}>
- - ${(i>0)?'': - ''} + + ${(i>0)?'
@@ -1228,11 +1347,48 @@ function makePUtil() { New preset
${makeP(0)}
`; - updateTrail(d.getElementById('p0p')); +} + +function makePlEntry(p,i) { + return ` +
+ ${i+1}: + + + + + + + + + + +
DurationTransition
s
+
+
+
+ `; +} + +function makePlUtil() { + if (pNum < 2) { + showToast("You need at least 2 presets to make a playlist!"); return; + } + if (plJson[0].transition[0] < 0) plJson[0].transition[0] = tr; + d.getElementById('putil').innerHTML = `
+
+ New playlist
+
+ ${makeP(0,true)}
`; + + refreshPlE(0); } function resetPUtil() { - var cn = `
`; + var cn = `
+
`; d.getElementById('putil').innerHTML = cn; } @@ -1266,8 +1422,10 @@ function setSeg(s){ { var grp = parseInt(d.getElementById(`seg${s}grp`).value); var spc = parseInt(d.getElementById(`seg${s}spc`).value); + var ofs = parseInt(d.getElementById(`seg${s}of` ).value); obj.seg.grp = grp; obj.seg.spc = spc; + obj.seg.of = ofs; } requestJson(obj); } @@ -1339,7 +1497,7 @@ function setPalette(paletteId = null) function setBri() { var obj = {"bri": parseInt(d.getElementById('sliderBri').value)}; - obj.transition = parseInt(d.getElementById('cyctt').value*10); + obj.transition = parseInt(d.getElementById('tt').value*10); requestJson(obj); } @@ -1358,17 +1516,6 @@ function setLor(i) { requestJson(obj); } -function toggleCY() { - var obj = {"pl" : -1}; - if (d.getElementById('cyToggle').checked) - { - obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}}; - obj.transition = parseInt(d.getElementById('cyctt').value*10); - } - - requestJson(obj); -} - function setPreset(i) { var obj = {"ps": i}; @@ -1377,12 +1524,14 @@ function setPreset(i) { requestJson(obj); } -function saveP(i) { +function saveP(i,pl) { pI = parseInt(d.getElementById(`p${i}id`).value); if (!pI || pI < 1) pI = (i>0) ? i : getLowestUnusedP(); pN = d.getElementById(`p${i}txt`).value; - if (pN == "") pN = "Preset " + pI; + + if (pN == "") pN = (pl?"Playlist ":"Preset ") + pI; var obj = {}; + if (!d.getElementById(`p${i}cstgl`).checked) { var raw = d.getElementById(`p${i}api`).value; try { @@ -1396,15 +1545,21 @@ function saveP(i) { d.getElementById(`p${i}warn`).innerHTML = "⚠ Syntax error in custom JSON API command"; return; } else if (raw.indexOf("Please") == 0) { - d.getElementById(`p${i}warn`).innerHTML = "⚠ Please refresh the page before modifying this preset"; + d.getElementById(`p${i}warn`).innerHTML = "⚠ Please refresh the page before modifying this preset"; return; - } + } } obj.o = true; } else { - obj.ib = d.getElementById(`p${i}ibtgl`).checked; - obj.sb = d.getElementById(`p${i}sbtgl`).checked; + if (pl) { + obj.playlist = plJson[i]; + obj.o = true; + } else { + obj.ib = d.getElementById(`p${i}ibtgl`).checked; + obj.sb = d.getElementById(`p${i}sbtgl`).checked; + } } + obj.psave = pI; obj.n = pN; var pQN = d.getElementById(`p${i}ql`).value; if (pQN.length > 0) obj.ql = pQN; @@ -1426,11 +1581,36 @@ function saveP(i) { resetPUtil(); } -function delP(i) { - var obj = {"pdel": i}; +function testPl(i,bt) { + if (bt.dataset.test == 1) { + bt.dataset.test = 0; + bt.innerHTML = "Test"; + stopPl(); + return; + } + bt.dataset.test = 1; + bt.innerHTML = "Stop"; + var obj = {}; + obj.playlist = plJson[i]; requestJson(obj); - delete pJson[i]; - populatePresets(); +} + +function stopPl() { + requestJson({playlist:{}}) +} + +function delP(i) { + var bt = d.getElementById(`p${i}del`); + if (bt.dataset.cnf == 1) { + var obj = {"pdel": i}; + requestJson(obj); + delete pJson[i]; + populatePresets(); + } else { + bt.style.color = "#f00"; + bt.innerHTML = "Confirm delete"; + bt.dataset.cnf = 1; + } } function selectSlot(b) { @@ -1530,7 +1710,7 @@ function setColor(sr) { } updateHex(); updateRgb(); - obj.transition = parseInt(d.getElementById('cyctt').value*10); + obj.transition = parseInt(d.getElementById('tt').value*10); requestJson(obj); } @@ -1658,22 +1838,61 @@ function cancelSearch(ic) { searchField.focus(); } +//make sure "dur" and "transition" are arrays with at least the length of "ps" +function formatArr(pl) { + var l = pl.ps.length; + if (!Array.isArray(pl.dur)) { + var v = pl.dur; + if (isNaN(v)) v = 100; + pl.dur = [v]; + } + var l2 = pl.dur.length; + if (l2 < l) + { + for (var i = 0; i < l - l2; i++) + pl.dur.push(pl.dur[l2-1]); + } + + if (!Array.isArray(pl.transition)) { + var v = pl.transition; + if (isNaN(v)) v = tr; + pl.transition = [v]; + } + var l2 = pl.transition.length; + if (l2 < l) + { + for (var i = 0; i < l - l2; i++) + pl.transition.push(pl.transition[l2-1]); + } +} + function expand(i,a) { if (!a) expanded[i] = !expanded[i]; d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none"; d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)"; - if (i > 100) { //presets - var p = i-100; - d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)"; - if (d.getElementById('seg' +i).innerHTML == "") { - d.getElementById('seg' +i).innerHTML = makeP(p); - var papi = papiVal(p); - d.getElementById(`p${p}api`).value = papi; - if (papi.indexOf("Please") == 0) d.getElementById(`p${p}cstgl`).checked = true; - tglCs(p); - } + if (i < 100) return; //no preset, we are done + + var p = i-100; + d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)"; + if (d.getElementById('seg' +i).innerHTML != "") return; + if (isPlaylist(p)) { + plJson[p] = pJson[p].playlist; + //make sure all keys are present in plJson[p] + formatArr(plJson[p]); + if (isNaN(plJson[p].repeat)) plJson[p].repeat = 0; + if (!plJson[p].r) plJson[p].r = false; + if (isNaN(plJson[p].end)) plJson[p].end = 0; + + d.getElementById('seg' +i).innerHTML = makeP(p,true); + refreshPlE(p); + } else { + d.getElementById('seg' +i).innerHTML = makeP(p); } + var papi = papiVal(p); + d.getElementById(`p${p}api`).value = papi; + if (papi.indexOf("Please") == 0) d.getElementById(`p${p}cstgl`).checked = true; + tglCs(p); } function unfocusSliders() {