From 34e99f09fda7eef2efece18821d88ab0ca311f83 Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 11 Oct 2024 12:52:54 +0200 Subject: [PATCH] lock mode --- common.js | 12 +- enums.js | 20 +-- lfogui.css | 401 +++++++++++++++++++++++++++++++------------------- lfogui.js | 29 +++- modulators.js | 14 +- 5 files changed, 298 insertions(+), 178 deletions(-) diff --git a/common.js b/common.js index 96cb58e..ba94045 100644 --- a/common.js +++ b/common.js @@ -5,7 +5,8 @@ function isNumeric(str) { } function DropDown(props) { - return e('select', {type: "number", onChange: props.onChange, value: props.value}, + let className = props.locked ? 'locked-component' : ''; + return e('select', {className, type: "number", onChange: props.onChange, value: props.value}, ...props.options.map((item) => Option(item))); } @@ -18,11 +19,13 @@ function Label(text){ } function NumberBox(props){ - return e('input', {type: "number", onChange: props.onChange, step: props.step, value: props.value, className: props.className}, null); + let extraClassName = props.locked ? ' locked-component' : ''; + return e('input', {type: "number", onChange: props.onChange, step: props.step, value: props.value, className: props.className + extraClassName}, null); } function TextBox(props){ - return e('input', {type: "text", value: props.value, onChange: props.onChange, id: props.id}); + let className = props.locked ? 'locked-component' : ''; + return e('input', {className, type: "text", value: props.value, onChange: props.onChange, id: props.id}); } function Option(str, value){ @@ -30,7 +33,8 @@ function Option(str, value){ } function Button(props){ - return e('button', {onClick: props.onClick}, props.text); + let className = props.locked ? 'locked-component' : ''; + return e('button', {onClick: props.onClick, className}, props.text); } function Switch(props){ diff --git a/enums.js b/enums.js index 0c911af..03edde9 100644 --- a/enums.js +++ b/enums.js @@ -5,13 +5,13 @@ // NOT A REACT FUNCTIONAL COMPONENT. MERELY RETURNS AN ARRAY WHICH IS UNPACKED -function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames, setEnumNames, djParam){ +function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames, setEnumNames, djParam, locked){ let items = []; for (let i = 0; i < MAXENUMPOINTS; i++){ - items.push(ListItem(e(TextBox, {onChange: CreateMatrixParamChanger(enumNames, setEnumNames, index, i), value: enumNames[index][i], id:`text-${djParam}-${enumNames[index][i]}`}, null))); + items.push(ListItem(e(TextBox, {locked, onChange: CreateMatrixParamChanger(enumNames, setEnumNames, index, i), value: enumNames[index][i], id:`text-${djParam}-${enumNames[index][i]}`}, null))); // Add 1 to make up for the lower enum bound - items.push(ListItem(e(NumberBox, {onChange: CreateMatrixParamChanger(enumBreakPoints, setEnumBreakPoints, index, i + 1), value:enumBreakPoints[index][i + 1]}, null))); + items.push(ListItem(e(NumberBox, {locked, onChange: CreateMatrixParamChanger(enumBreakPoints, setEnumBreakPoints, index, i + 1), value:enumBreakPoints[index][i + 1]}, null))); } return items; } @@ -20,13 +20,13 @@ function EnumeratorRow(props){ let linkedText = props.linked ? "<- mods" : ""; let content = e('ul', {className: 'lfo-item', id: `${props.djParam}-enum-row`}, - ListItem(DropDown({onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), - ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), - ListItem(e(NumberBox, {onChange: props.setEnumItemCounts, step:1, value:props.enumItems, className: 'enum-count'}, null)), - ListItem(e(NumberBox, {onChange: CreateMatrixParamChanger(props.enumBreakPoints, props.setEnumBreakPoints, props.index, 0), value:props.enumBreakPoints[props.index][0], step:0.1}, null)), - ...(EnumeratorItems(props.index, props.enumBreakPoints, props.setEnumBreakPoints, props.enumNames, props.setEnumNames, props.djParam).slice(0, props.enumItems * 2)), - ListItem(e(Button, {text:'+', onClick: props.addEnum}, null)), - ListItem(e(Button, {text:'-', onClick: props.removeEnum}, null)), + ListItem(DropDown({locked:props.locked, onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), + ListItem(DropDown({locked:props.locked, onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), + ListItem(e(NumberBox, {locked:props.locked, onChange: props.setEnumItemCounts, step:1, value:props.enumItems, className: 'enum-count'}, null)), + ListItem(e(NumberBox, {locked:props.locked, onChange: CreateMatrixParamChanger(props.enumBreakPoints, props.setEnumBreakPoints, props.index, 0), value:props.enumBreakPoints[props.index][0], step:0.1}, null)), + ...(EnumeratorItems(props.index, props.enumBreakPoints, props.setEnumBreakPoints, props.enumNames, props.setEnumNames, props.djParam, props.locked).slice(0, props.enumItems * 2)), + ListItem(e(Button, {locked:props.locked, text:'+', onClick: props.addEnum}, null)), + ListItem(e(Button, {locked:props.locked, text:'-', onClick: props.removeEnum}, null)), ListItem(e("div", {className:"linked"}, linkedText)) ); if (props.visible){ diff --git a/lfogui.css b/lfogui.css index 5860643..17f5ca3 100644 --- a/lfogui.css +++ b/lfogui.css @@ -1,181 +1,272 @@ -html, - body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - } +* { + --locked-color: #5fadbf; + --unlocked-color: #ff5153; +} - ul { - list-style-type: none; - margin: 0; - padding: 0; - overflow: hidden; - background-color: #333333; - } +html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ +} - li { - float: left; - } +ul { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #333333; +} - - input[type=number] { - width: 50px; - margin: 0; - padding: 0; - } +li { + float: left; +} - input[type=text] { - width: 60px; - margin: 0; - padding: 0; - font-weight: bold; - } - .timeInput { - width: 80px; - margin: 0; - padding: 0; - } +input[type=number] { + width: 50px; + margin: 0; + padding: 0; +} - #matrix { - background-color: aquamarine; - height: 100%; - width: 100%; - } +input[type=text] { + width: 60px; + margin: 0; + padding: 0; + font-weight: bold; +} - .numbox-unclicked { - user-select: none; - border: solid; - font-size: 12vw; - } +.timeInput { + width: 80px; + margin: 0; + padding: 0; +} - .numbox-clicked { - user-select: none; - border : solid; - font-size: 12vw; - } +#matrix { + background-color: aquamarine; + height: 100%; + width: 100%; +} - .param-input-label { - width: 93%; - font-size: 5vw; - } +.numbox-unclicked { + user-select: none; + border: solid; + font-size: 12vw; +} - .lfo-input-label { - width: 40%; - font-size: 5vw; - } +.numbox-clicked { + user-select: none; + border : solid; + font-size: 12vw; +} - /* The switch - the box around the slider */ - .switch { - position: relative; - display: inline-block; - width: 30px; - height: 17px; - } +.param-input-label { + width: 93%; + font-size: 5vw; +} - /* Hide default HTML checkbox */ - .switch input { - opacity: 0; - width: 0; - height: 0; - } +.lfo-input-label { + width: 40%; + font-size: 5vw; +} - /* The slider */ - .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - -webkit-transition: .4s; - transition: .4s; - } + /* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 30px; + height: 17px; +} - .slider:before { - position: absolute; - content: ""; - height: 13px; - width: 13px; - left: 2px; - bottom: 2px; - background-color: white; - -webkit-transition: .4s; - transition: .4s; - } +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} - input:checked + .slider { - background-color: #2196F3; - } +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} - input:focus + .slider { - box-shadow: 0 0 1px #2196F3; - } +.slider:before { + position: absolute; + content: ""; + height: 13px; + width: 13px; + left: 2px; + bottom: 2px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} - input:checked + .slider:before { - -webkit-transform: translateX(13px); - -ms-transform: translateX(13px); - transform: translateX(13px); - } +input:checked + .slider { + background-color: #2196F3; +} - /* Rounded sliders */ - .slider.round { - border-radius: 17px; - } +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} - .slider.round:before { - border-radius: 50%; - } +input:checked + .slider:before { + -webkit-transform: translateX(13px); + -ms-transform: translateX(13px); + transform: translateX(13px); +} - h5 { - margin: 0; - padding: 0; - } +/* Rounded sliders */ +.slider.round { + border-radius: 17px; +} - .enum-count { - background-color: aquamarine; - } +.slider.round:before { + border-radius: 50%; +} - .label { - background-color: aliceblue; - padding: 0 4px 0 4px; - margin: 0 2px 0 2px; - border-color: #333333; - border-width: 1px; - } +h5 { + margin: 0; + padding: 0; +} - .base-val { - background-color: lightgray; - border-color: #333333; - border-width: 1px; - width: 50px; - margin-left: 2px; - margin-top: 1px; - } +.enum-count { + background-color: aquamarine; +} - .linked { - color: red; - border-width: 1px; - width: 50px; - font-size: small; - margin-left: 2px; - margin-top: 5px; - } - +.label { + background-color: aliceblue; + padding: 0 4px 0 4px; + margin: 0 2px 0 2px; + border-color: #333333; + border-width: 1px; +} - @keyframes pulse-animation { - 0% { - color: black; - } - 100% { - color: red; - } - } +.base-val { + background-color: lightgray; + border-color: #333333; + border-width: 1px; + width: 50px; + margin-left: 2px; + margin-top: 1px; +} - #pulse { - animation: pulse-animation 0.2s normal; - } \ No newline at end of file +.linked { + color: red; + border-width: 1px; + width: 50px; + font-size: small; + margin-left: 2px; + margin-top: 5px; +} + + +@keyframes pulse-animation { + 0% { + color: black; + } + 100% { + color: red; + } +} + +#pulse { + animation: pulse-animation 0.2s normal; +} + +/* :::::::::::::: LOCK CSS */ + +.locked-component { + pointer-events: none; + background-color: #333333; + color : white; +} + +.container { + display: flex; + width: 100%; + justify-content: space-between; +} + +/* Locked */ +.lock { + margin-top: 14px; + width: 24px; + height: 21px; + border: 3px solid var(--locked-color); + border-radius: 5px; + position: relative; + cursor: pointer; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +.lock:after { + content: ""; + display: block; + background: var(--locked-color); + width: 3px; + height: 7px; + position: absolute; + top: 50%; + left: 50%; + margin: -3.5px 0 0 -2px; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +.lock:before { + content: ""; + display: block; + width: 10px; + height: 10px; + bottom: 100%; + position: absolute; + left: 50%; + margin-left: -8px; + border: 3px solid var(--locked-color); + border-top-right-radius: 50%; + border-top-left-radius: 50%; + border-bottom: 0; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +/* Locked Hover */ +.lock:hover:before { + height: 12px; +} +/* Unlocked */ +.unlocked { + transform: rotate(10deg); +} +.unlocked:before { + bottom: 130%; + left: 31%; + margin-left: -11.5px; + transform: rotate(-45deg); +} +.unlocked, +.unlocked:before { + border-color: var(--unlocked-color); +} +.unlocked:after { + background: var(--unlocked-color); +} +/* Unlocked Hover */ +.unlocked:hover { + transform: rotate(3deg); +} +.unlocked:hover:before { + height: 10px; + left: 40%; + bottom: 124%; + transform: rotate(-30deg); +} \ No newline at end of file diff --git a/lfogui.js b/lfogui.js index 1a1d9cb..96b1317 100644 --- a/lfogui.js +++ b/lfogui.js @@ -20,6 +20,11 @@ const ViewModes = Object.freeze({ ENUM: 1 }); +const LockModes = Object.freeze({ + UNLOCK: 0, + LOCK: 1 +}); + var modPhases = Array(MAXLFOS).fill(0); var firstUpdateTime = Date.now(); @@ -43,6 +48,14 @@ function MasterLfoHandler(){ setViewMode(ViewModes.MOD); }; + const [lockMode, setLockMode] = React.useState(LockModes.LOCK); + const toggleLockMode = () => { + if (lockMode === LockModes.UNLOCK) + setLockMode(LockModes.LOCK); + else + setLockMode(LockModes.UNLOCK); + }; + /// MODULATOR ARRAYS const [userDefinedWave, setUserDefinedWave] = React.useState(Array(50).fill(0)); @@ -262,6 +275,7 @@ function MasterLfoHandler(){ modContents.push( e(LfoRow, { + locked : lockMode, instanceNum : modInstanceNumArr[i], setInstanceNum: CreateParamChanger(modInstanceNumArr, setModInstanceNumArr, i), @@ -343,7 +357,7 @@ function MasterLfoHandler(){ enumContents.push( e(EnumeratorRow, { index: i, - + locked : lockMode, instanceNum : enumInstanceNumArr[i], setInstanceNum: CreateParamChanger(enumInstanceNumArr, setEnumInstanceNumArr, i), enumItems: enumItemCounts[i], @@ -425,8 +439,19 @@ function MasterLfoHandler(){ labels = ENUMERATORLABELS; } + let lockClass; + if (lockMode == LockModes.LOCK){ + lockClass = 'lock'; + } + else { + lockClass = 'lock unlocked'; + } + return e('div', null, - e(Switch, {ontoggle: toggleViewMode}, null), + e('div', {className: 'container'}, + e(Switch, {ontoggle: toggleViewMode}, null), + e('span', {className: lockClass, onClick: toggleLockMode}, null)), + e('h5', null, title), e('ul', null, ...labels.map(x => ListItem(Label(x)))), e('div', {id: 'grid'}, ...grid) diff --git a/modulators.js b/modulators.js index 618fd2d..c036c9a 100644 --- a/modulators.js +++ b/modulators.js @@ -34,17 +34,17 @@ function LfoRow(props){ let typeOption = null; if (props.type == "LFO"){ - typeOption = ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})); + typeOption = ListItem(DropDown({locked:props.locked, onChange: props.setShape, value:props.shape, options: SHAPETYPES})); } else if (props.type == "Noise"){ - typeOption = ListItem(DropDown({onChange: props.setNoise, value:props.noise, options: NOISETYPES})); + typeOption = ListItem(DropDown({locked:props.locked, onChange: props.setNoise, value:props.noise, options: NOISETYPES})); } let content = e('ul', {className: 'lfo-item'}, - ListItem(DropDown({onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), - ListItem(DropDown({options: TYPEOPTIONS, onChange: props.setType, value:props.type})), + ListItem(DropDown({locked:props.locked, onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), + ListItem(DropDown({locked:props.locked, options: TYPEOPTIONS, onChange: props.setType, value:props.type})), typeOption, - ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), + ListItem(DropDown({locked:props.locked, onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), ListItem(e("input", {onChange:props.setFreq, value:props.freq, className:"timeInput"}, null)), ListItem(e(NumberBox, {onChange:props.setMin, value:props.min, step:0.1}, null)), ListItem(e(NumberBox, {onChange:props.setMax, value:props.max, step:0.1}, null)), @@ -52,8 +52,8 @@ function LfoRow(props){ ListItem(e(NumberBox, {onChange:props.setPhase, value:props.phase, step:0.1}, null)), ListItem(e("div", {className:"base-val"}, center.toString())), ListItem(e("input", {type: 'range', min: 0, max: 1, step: 0.01, readonly: true, id: `slider-${props.instanceNum}-${props.djParam}`})), - ListItem(e(Button, {text:'+', onClick: props.addLfo}, null)), - ListItem(e(Button, {text:'-', onClick: props.removeLfo}, null)), + ListItem(e(Button, {text:'+', onClick: props.addLfo, locked: props.locked}, null)), + ListItem(e(Button, {text:'-', onClick: props.removeLfo, locked: props.locked}, null)), ListItem(e("div", {className:"linked"}, linkedText)), ); if (props.visible){