+
diff --git a/react/src/ControlViewFactory.tsx b/react/src/ControlViewFactory.tsx
index 8857168..673074b 100644
--- a/react/src/ControlViewFactory.tsx
+++ b/react/src/ControlViewFactory.tsx
@@ -32,7 +32,7 @@ import ToobCabSimViewFactory from './ToobCabSimView';
import ToobPowerStage2Factory from './ToobPowerStage2View';
import ToobSpectrumAnalyzerViewFactory from './ToobSpectrumAnalyzerView';
import ToobMLViewFactory from './ToobMLView';
-import {ToobTunerViewFactory,GxTunerViewFactory> from './GxTunerView';
+import {ToobTunerViewFactory,GxTunerViewFactory} from './GxTunerView';
let pluginFactories: IControlViewFactory[] = [
diff --git a/react/src/GxTunerView.tsx b/react/src/GxTunerView.tsx
index eee7f95..a0d7b91 100644
--- a/react/src/GxTunerView.tsx
+++ b/react/src/GxTunerView.tsx
@@ -41,6 +41,7 @@ const styles = (theme: Theme) => createStyles({
interface GxTunerProps extends WithStyles {
instanceId: number;
item: PedalBoardItem;
+ isToobTuner: boolean;
}
interface GxTunerState {
@@ -79,6 +80,11 @@ const GxTunerView =
{
let refFreqIndex = this.getControlIndex("REFFREQ");
let thresholdIndex = this.getControlIndex("THRESHOLD");
+ let muteIndex = -1;
+ if (this.props.isToobTuner)
+ {
+ muteIndex = this.getControlIndex("MUTE")
+ }
let result: (React.ReactNode | ControlGroup)[] = [];
@@ -87,6 +93,10 @@ const GxTunerView =
result.push(controls[refFreqIndex]);
result.push(controls[thresholdIndex]);
+ if (muteIndex !== -1)
+ {
+ result.push(controls[muteIndex]);
+ }
return result;
}
render() {
@@ -102,26 +112,24 @@ const GxTunerView =
-class GxTunerViewFactory implements IControlViewFactory {
+export class GxTunerViewFactory implements IControlViewFactory {
uri: string = GXTUNER_URI;
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
- return ();
+ return ();
}
}
// ToobTuner uses the same controls and control semantics.
-class ToobTunerViewFactory implements IControlViewFactory {
+export class ToobTunerViewFactory implements IControlViewFactory {
uri: string = TOOBTUNER_URI;
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
- return ();
+ return ();
}
}
-export ToobTunerViewFactory;
-export GxTunerViewFactory;
\ No newline at end of file
diff --git a/react/src/LoadPluginDialog.tsx b/react/src/LoadPluginDialog.tsx
index bf619fd..b37e015 100644
--- a/react/src/LoadPluginDialog.tsx
+++ b/react/src/LoadPluginDialog.tsx
@@ -55,8 +55,6 @@ const NARROW_DISPLAY_THRESHOLD = 600;
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
-let lastClickTime: number = 0;
-
const pluginGridStyles = (theme: Theme) => createStyles({
frame: {
@@ -327,7 +325,8 @@ export const LoadPluginDialog =
}
onDoubleClick(e: SyntheticEvent, uri: string): void {
- this.props.onOk(uri);
+ // handled with onClick e.detail which provides better behaviour on Chrome.
+ // this.props.onOk(uri);
}
@@ -342,18 +341,15 @@ export const LoadPluginDialog =
}
- onClick(e: SyntheticEvent, uri: string): void {
+ onClick(e: React.MouseEvent, uri: string): void {
e.preventDefault();
- let currentTime = Date.now();
- let dt = currentTime-lastClickTime;
- let DOUBLE_CLICK_TIME = 500;
- let isDoubleClick = dt < DOUBLE_CLICK_TIME;
- lastClickTime = currentTime;
-
this.setState({ selected_uri: uri });
+ // a better doubleclick: tracks how many recent clicks in the same area.
+ // regardless of whether we re-rendered in the interrim.
+ let isDoubleClick = e.detail === 2;
// we have to synthesize double clicks because
// DOM rewrites interfere with natural double click.
if (isDoubleClick)
diff --git a/react/src/MainPage.tsx b/react/src/MainPage.tsx
index 4e946fd..337f3b0 100644
--- a/react/src/MainPage.tsx
+++ b/react/src/MainPage.tsx
@@ -184,8 +184,8 @@ export const MainPage =
}
}
- handleSelectPluginPreset(instanceId: number, presetName: string) {
- this.model.loadPluginPreset(instanceId, presetName);
+ handleSelectPluginPreset(instanceId: number, presetInstanceId: number) {
+ this.model.loadPluginPreset(instanceId, presetInstanceId);
}
onPedalBoardChanged(value: PedalBoard) {
this.setState({ pedalBoard: value });
@@ -325,12 +325,11 @@ export const MainPage =
{author}
-
+
-
this.handleSelectPluginPreset(pedalBoardItem!.instanceId, presetName)}
+
diff --git a/react/src/PedalBoard.tsx b/react/src/PedalBoard.tsx
index 5b35fd2..782cc60 100644
--- a/react/src/PedalBoard.tsx
+++ b/react/src/PedalBoard.tsx
@@ -133,7 +133,7 @@ export class PedalBoardItem implements Deserializable
{
return true;
}
}
- throw new PiPedalArgumentError("Control not found.");
+ return false;
}
setMidiBinding(midiBinding: MidiBinding): boolean {
if (this.midiBindings)
diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx
index 75e1729..6e2af5d 100644
--- a/react/src/PiPedalModel.tsx
+++ b/react/src/PiPedalModel.tsx
@@ -31,7 +31,7 @@ import { BankIndex } from './Banks';
import JackHostStatus from './JackHostStatus';
import JackServerSettings from './JackServerSettings';
import MidiBinding from './MidiBinding';
-import PluginPreset from './PluginPreset';
+import {PluginUiPresets} from './PluginPreset';
import WifiConfigSettings from './WifiConfigSettings';
import WifiChannel from './WifiChannel';
import AlsaDeviceInfo from './AlsaDeviceInfo';
@@ -47,6 +47,16 @@ export enum State {
export type ControlValueChangedHandler = (key: string, value: number) => void;
+
+export type PluginPresetsChangedHandler = (pluginUri: string) => void;
+
+export interface PluginPresetsChangedHandle {
+ _id: number;
+ _handler: PluginPresetsChangedHandler;
+
+};
+
+
export interface ZoomedControlInfo {
source: HTMLElement;
instanceId: number;
@@ -72,7 +82,6 @@ export interface ControlValueChangedHandle {
_ControlValueChangedHandle: number;
};
-
interface ControlValueChangeItem {
handle: number;
instanceId: number;
@@ -341,6 +350,8 @@ export interface PiPedalModel {
saveCurrentPreset(): void;
saveCurrentPresetAs(newName: string): void;
+ saveCurrentPluginPresetAs(pluginInstanceId: number,newName: string): void;
+
loadPreset(instanceId: number): void;
updatePresets(presets: PresetIndex): Promise;
deletePresetItem(instanceId: number): Promise;
@@ -372,8 +383,10 @@ export interface PiPedalModel {
getJackStatus(): Promise;
- getPluginPresets(uri: string): Promise;
- loadPluginPreset(instanceId: number, presetName: string): void;
+ getPluginPresets(uri: string): Promise;
+ loadPluginPreset(pluginInstanceId: number, presetInstanceId: number): void;
+ updatePluginPresets(uri: string, presets: PluginUiPresets): Promise;
+ duplicatePluginPreset(uri: string, instanceId: number): Promise;
shutdown(): Promise;
restart(): Promise;
@@ -384,14 +397,17 @@ export interface PiPedalModel {
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
+ addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler) : PluginPresetsChangedHandle;
+ removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void;
+
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
cancelListenForAtomOutput(listenHandle: ListenHandle): void;
- download(targetType: string, isntanceId: number): void;
+ download(targetType: string, isntanceId: number|string): void;
- uploadPreset(file: File, uploadAfter: number): Promise;
+ uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise;
uploadBank(file: File, uploadAfter: number): Promise;
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise;
@@ -500,6 +516,9 @@ class PiPedalModelImpl implements PiPedalModel {
let presetsChangedBody = body as PresetsChangedBody;
let presets = new PresetIndex().deserialize(presetsChangedBody.presets);
this.presets.set(presets);
+ } else if (message === "onPluginPresetsChanged") {
+ let pluginUri = body as string;
+ this.handlePluginPresetsChanged(pluginUri);
} else if (message === "onJackConfigurationChanged") {
this.jackConfiguration.set(new JackConfiguration().deserialize(body));
} else if (message === "onLoadPluginPreset") {
@@ -973,6 +992,41 @@ class PiPedalModelImpl implements PiPedalModel {
}
}
+ _pluginPresetsChangedHandles: PluginPresetsChangedHandle[] = [];
+
+ addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler) : PluginPresetsChangedHandle {
+ let handle = ++this.nextListenHandle;
+ let t: PluginPresetsChangedHandle = {
+ _id: handle,
+ _handler: onPluginPresetsChanged
+ };
+ this._pluginPresetsChangedHandles.push(t);
+ return t;
+ }
+ removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void
+ {
+ let pos = -1;
+ for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i)
+ {
+ if (this._pluginPresetsChangedHandles[i]._id === handle._id)
+ {
+ pos = i;
+ break;
+ }
+ }
+ if (pos !== -1)
+ {
+ this._pluginPresetsChangedHandles.splice(pos,1);
+ }
+ }
+ firePluginPresetsChanged(pluginUri: string): void {
+ for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i)
+ {
+ this._pluginPresetsChangedHandles[i]._handler(pluginUri);
+ }
+
+ }
+
_setPedalBoardControlValue(instanceId: number, key: string, value: number, notifyServer: boolean): void {
let pedalBoard = this.pedalBoard.get();
@@ -1244,6 +1298,18 @@ class PiPedalModelImpl implements PiPedalModel {
return data;
});
}
+ saveCurrentPluginPresetAs(pluginInstanceId: number,newName: string): Promise {
+ // default behaviour is to save after the currently selected preset.
+ let request: any = {
+ instanceId: pluginInstanceId,
+ name: newName,
+ };
+ return nullCast(this.webSocket)
+ .request("savePluginPresetAs", request)
+ .then((data) => {
+ return data;
+ });
+ }
loadPreset(instanceId: number): void {
@@ -1473,18 +1539,22 @@ class PiPedalModelImpl implements PiPedalModel {
return result;
}
- presetCache: { [uri: string]: PluginPreset[]; } = {};
+ presetCache: { [uri: string]: PluginUiPresets } = {};
- getPluginPresets(uri: string): Promise {
- return new Promise((resolve, reject) => {
+ uncachePluginPreset(pluginUri: string): void {
+ delete this.presetCache[pluginUri];
+ }
+ getPluginPresets(uri: string): Promise {
+ return new Promise((resolve, reject) => {
if (this.presetCache[uri] !== undefined) {
resolve(this.presetCache[uri]);
} else {
this.webSocket!.request("getPluginPresets", uri)
.then((result) => {
- this.presetCache[uri] = PluginPreset.deserialize_array(result);
- resolve(result);
+ let presets = new PluginUiPresets().deserialize(result);
+ this.presetCache[uri] = presets;
+ resolve(presets);
})
.catch((error) => {
reject(error);
@@ -1493,10 +1563,30 @@ class PiPedalModelImpl implements PiPedalModel {
});
}
- loadPluginPreset(instanceId: number, presetName: string): void {
- this.webSocket?.send("loadPluginPreset", { instanceId: instanceId, presetName: presetName });
+ loadPluginPreset(pluginInstanceId: number, presetInstanceId: number): void {
+ this.webSocket?.send("loadPluginPreset", { pluginInstanceId: pluginInstanceId, presetInstanceId: presetInstanceId });
}
+ handlePluginPresetsChanged(pluginUri: string):void {
+ this.uncachePluginPreset(pluginUri);
+ this.firePluginPresetsChanged(pluginUri);
+ }
+
+
+ updatePluginPresets(pluginUri: string, presets: PluginUiPresets): Promise
+ {
+ this.presetCache[pluginUri] = presets;
+ this.firePluginPresetsChanged(pluginUri);
+ return nullCast(this.webSocket).request("updatePluginPresets", presets);
+ }
+ duplicatePluginPreset(pluginUri: string, instanceId: number): Promise
+ {
+ return nullCast(this.webSocket).request("copyPluginPreset", {
+ pluginUri: pluginUri, instanceId: instanceId });
+
+ }
+
+
shutdown(): Promise {
return this.webSocket!.request("shutdown");
}
@@ -1596,19 +1686,19 @@ class PiPedalModelImpl implements PiPedalModel {
this.webSocket?.send("cancelListenForAtomOutput", listenHandle._handle);
}
- download(targetType: string, instanceId: number): void {
+ download(targetType: string, instanceId: number | string): void {
if (instanceId === -1) return;
let url = this.varServerUrl + targetType + "?id=" + instanceId;
window.open(url, "_blank");
}
- uploadPreset(file: File, uploadAfter: number): Promise {
+ uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise {
let result = new Promise((resolve, reject) => {
try {
if (file.size > this.maxUploadSize) {
reject("File is too large.");
}
- let url = this.varServerUrl + "uploadPreset";
+ let url = this.varServerUrl + uploadPage;
if (uploadAfter && uploadAfter !== -1) {
url += "?uploadAfter=" + uploadAfter;
}
diff --git a/react/src/PluginInfoDialog.tsx b/react/src/PluginInfoDialog.tsx
index e0c3f30..5f0d9a6 100644
--- a/react/src/PluginInfoDialog.tsx
+++ b/react/src/PluginInfoDialog.tsx
@@ -179,7 +179,7 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
return (
diff --git a/react/src/PluginPreset.tsx b/react/src/PluginPreset.tsx
index 5a254c1..5b97869 100644
--- a/react/src/PluginPreset.tsx
+++ b/react/src/PluginPreset.tsx
@@ -19,21 +19,69 @@
-export default class PluginPreset {
- deserialize(input: any): PluginPreset {
- this.presetUri = input.presetUri;
- this.name = input.name;
+export class PluginUiPreset {
+ deserialize(input: any): PluginUiPreset {
+ this.instanceId = input.instanceId;
+ this.label = input.label;
return this;
}
- static deserialize_array(input: any) : PluginPreset[] {
- let result: PluginPreset[] = [];
+ static deserialize_array(input: any) : PluginUiPreset[] {
+ let result: PluginUiPreset[] = [];
for (let i = 0; i < input.length; ++i)
{
- result.push(new PluginPreset().deserialize(input[i]));
+ result.push(new PluginUiPreset().deserialize(input[i]));
}
return result;
}
- presetUri: string = "";
- name: string = "";
+ instanceId: number = -1;
+ label: string = "";
+
+ static equals(left: PluginUiPreset, right: PluginUiPreset): boolean
+ {
+ return left.instanceId === right.instanceId && left.label === right.label;
+ }
+}
+
+export class PluginUiPresets {
+ deserialize(input: any): PluginUiPresets {
+ this.pluginUri = input.pluginUri;
+ this.presets = PluginUiPreset.deserialize_array(input.presets);
+ return this;
+ }
+
+ clone(): PluginUiPresets {
+ return new PluginUiPresets().deserialize(this);
+ }
+ movePreset(from: number, to: number): void
+ {
+ let t = this.presets[from];
+ this.presets.splice(from,1);
+ this.presets.splice(to,0,t);
+ }
+ getItem(instanceId: number): PluginUiPreset | undefined {
+ for (let i = 0; i < this.presets.length; ++i)
+ {
+ if (this.presets[i].instanceId === instanceId)
+ {
+ return this.presets[i];
+ }
+ }
+ return undefined;
+ }
+ static equals(left: PluginUiPresets,right: PluginUiPresets): boolean
+ {
+ if (left.pluginUri !== right.pluginUri) return false;
+ if (left.presets.length !== right.presets.length) return false;
+ for (let i = 0; i < left.presets.length; ++i)
+ {
+ if (!PluginUiPreset.equals(left.presets[i],right.presets[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ pluginUri: string = "";
+ presets: PluginUiPreset[] = [];
}
\ No newline at end of file
diff --git a/react/src/PluginPresetSelector.tsx b/react/src/PluginPresetSelector.tsx
index 7d5193d..6a4f064 100644
--- a/react/src/PluginPresetSelector.tsx
+++ b/react/src/PluginPresetSelector.tsx
@@ -19,30 +19,31 @@
import { SyntheticEvent, Component } from 'react';
import IconButton from '@mui/material/IconButton';
-import Typography from '@mui/material/Typography';
-import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
+import { PiPedalModel, PiPedalModelFactory, PluginPresetsChangedHandle } from './PiPedalModel';
import { Theme } from '@mui/material/styles';
import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
-import PresetDialog from './PresetDialog';
+import PluginPresetsDialog from './PluginPresetsDialog';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';
import RenameDialog from './RenameDialog'
-import PluginPreset from './PluginPreset';
+import {PluginUiPresets} from './PluginPreset';
+
+import Divider from "@mui/material/Divider";
interface PluginPresetSelectorProps extends WithStyles {
pluginUri?: string;
-
- onSelectPreset: (presetName: string) => void;
+ instanceId: number;
}
interface PluginPresetSelectorState {
- presets: PluginPreset[] | null;
+ presets : PluginUiPresets;
enabled: boolean;
+ presetChanged: boolean;
showPresetsDialog: boolean;
showEditPresetsDialog: boolean;
presetsMenuAnchorRef: HTMLElement | null;
@@ -52,10 +53,15 @@ interface PluginPresetSelectorState {
renameDialogActionName: string;
renameDialogOnOk?: (name: string) => void;
+ saveAsName: string;
}
const styles = (theme: Theme) => createStyles({
+ itemIcon: {
+ width: 24, height: 24, marginRight: "4px", opacity: 0.6
+ }
+
});
@@ -69,25 +75,27 @@ const PluginPresetSelector =
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
- presets: null,
+ presets: new PluginUiPresets(),
enabled: false,
+ presetChanged: false,
showPresetsDialog: false,
showEditPresetsDialog: false,
presetsMenuAnchorRef: null,
renameDialogOpen: false,
renameDialogDefaultName: "",
renameDialogActionName: "",
- renameDialogOnOk: undefined
+ renameDialogOnOk: undefined,
+ saveAsName: ""
};
this.handleDialogClose = this.handleDialogClose.bind(this);
this.handlePresetsMenuClose = this.handlePresetsMenuClose.bind(this);
}
- handleLoadPluginPreset(presetName: string)
+ handleLoadPluginPreset(instanceId: number)
{
this.handlePresetsMenuClose();
- this.props.onSelectPreset(presetName);
+ this.model.loadPluginPreset(this.props.instanceId,instanceId);
}
handlePresetMenuClick(e: SyntheticEvent): void {
@@ -97,24 +105,16 @@ const PluginPresetSelector =
this.setState({ presetsMenuAnchorRef: null });
}
- handlePresetsMenuSave(e: SyntheticEvent): void {
+ handlePluginPresetsMenuSaveAs(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.stopPropagation();
- this.model.saveCurrentPreset();
- }
- handlePresetsMenuSaveAs(e: SyntheticEvent): void {
- this.handlePresetsMenuClose();
- e.stopPropagation();
-
- let currentPresets = this.model.presets.get();
- let item = currentPresets.getItem(currentPresets.selectedInstanceId);
- if (item == null) return;
- let name = item.name;
+ let name = this.state.saveAsName;
this.renameDialogOpen(name, "Save As")
.then((newName) => {
- return this.model.saveCurrentPresetAs(newName);
+ this.setState({saveAsName: newName});
+ return this.model.saveCurrentPluginPresetAs(this.props.instanceId,newName);
})
.then((newInstanceId) => {
// s'fine. dealt with by updates, but we do need error handling.
@@ -125,26 +125,6 @@ const PluginPresetSelector =
;
}
- handlePresetsMenuRename(e: SyntheticEvent): void {
- this.handlePresetsMenuClose();
- e.stopPropagation();
-
- let currentPresets = this.model.presets.get();
- let item = currentPresets.getItem(currentPresets.selectedInstanceId);
- if (item == null) return;
- let name = item.name;
-
- this.renameDialogOpen(name, "Rename")
- .then((newName) => {
- if (newName === name) return;
- return this.model.renamePresetItem(this.model.presets.get().selectedInstanceId, newName);
- })
- .catch((error) => {
- this.showError(error);
- })
- ;
- }
-
showError(error: string) {
this.model.showAlert(error);
}
@@ -168,6 +148,49 @@ const PluginPresetSelector =
return result;
}
+ loadPresets():void {
+ if (!this.currentUri) return;
+ let captureUri: string = this.currentUri;
+ this.model.getPluginPresets(captureUri)
+ .then((presets: PluginUiPresets) => {
+ if (captureUri === this.currentUri)
+ {
+ this.setState({presets: presets});
+ }
+ })
+ .catch(error => {
+ if (captureUri === this.currentUri)
+ {
+ this.setState({presets: new PluginUiPresets()});
+ }
+ });
+ }
+ onPluginPresetsChanged(pluginUri: string): void {
+ if (pluginUri === this.props.pluginUri)
+ {
+ this.loadPresets();
+
+ }
+ }
+ _pluginPresetsChangedHandle?: PluginPresetsChangedHandle;
+
+ componentDidMount()
+ {
+ this._pluginPresetsChangedHandle = this.model.addPluginPresetsChangedListener(
+ (pluginUri: string) => {
+ this.onPluginPresetsChanged(pluginUri);
+ }
+ );
+ }
+ componentWillUnmount()
+ {
+ if (this._pluginPresetsChangedHandle)
+ {
+ this.model.removePluginPresetsChangedListener(this._pluginPresetsChangedHandle);
+ this._pluginPresetsChangedHandle = undefined;
+ }
+ }
+
currentUri?: string = "";
componentDidUpdate()
{
@@ -176,23 +199,9 @@ const PluginPresetSelector =
this.currentUri = this.props.pluginUri;
if (!this.props.pluginUri)
{
- this.setState({presets: null});
+ this.setState({presets: new PluginUiPresets()});
} else {
- this.setState({presets: null});
- let captureUri = this.currentUri;
- this.model.getPluginPresets(this.props.pluginUri)
- .then((presets: PluginPreset[]) => {
- if (captureUri === this.currentUri)
- {
- this.setState({presets: presets});
- }
- })
- .catch(error => {
- if (captureUri === this.currentUri)
- {
- this.setState({presets: null});
- }
- });
+ this.loadPresets();
}
}
}
@@ -214,9 +223,9 @@ const PluginPresetSelector =
}
- handleMenuEditPresets(): void {
+ handleMenuEditPluginPresets(): void {
this.handlePresetsMenuClose();
- this.showEditPresetsDialog(true);
+ this.showEditPluginPresetsDialog(true);
}
handleSave() {
@@ -229,7 +238,7 @@ const PluginPresetSelector =
showEditPresetsDialog: false
});
}
- showEditPresetsDialog(show: boolean) {
+ showEditPluginPresetsDialog(show: boolean) {
this.setState({
showPresetsDialog: show,
showEditPresetsDialog: true
@@ -252,9 +261,11 @@ const PluginPresetSelector =
}
render() {
+ let classes = this.props.classes;
+
//let classes = this.props.classes;
//let classes = this.props.classes;
- if ((!this.props.pluginUri) || (!this.state.presets) ||this.state.presets.length === 0)
+ if ((!this.props.pluginUri))
{
return ();
}
@@ -276,23 +287,27 @@ const PluginPresetSelector =
}
>
- Plugin presets
+
+
+
+ { this.state.presets.presets.length !== 0 &&
+ ()
+ }
{
- this.state.presets.map((preset) => {
- return ();
+ this.state.presets.presets.map((preset) => {
+ return ();
})
}
- {/*
-
-
-
-
-
- */}
-
- this.handleDialogClose()} />
+ this.handleDialogClose()} />
{
+ show: boolean;
+ isEditDialog: boolean;
+ instanceId: number;
+ presets :PluginUiPresets;
+
+ onDialogClose: () => void;
+
+};
+
+interface PluginPresetsDialogState {
+
+ showActionBar: boolean;
+
+ selectedItem: number;
+
+ renameOpen: boolean;
+
+ moreMenuAnchorEl: HTMLElement | null;
+ openUploadDialog: boolean;
+
+
+};
+
+
+const styles = (theme: Theme) => createStyles({
+ dialogAppBar: {
+ position: 'relative',
+ top: 0, left: 0
+ },
+ dialogActionBar: {
+ position: 'relative',
+ top: 0, left: 0,
+ background: "black"
+ },
+ dialogTitle: {
+ marginLeft: theme.spacing(2),
+ flex: 1,
+ },
+ itemFrame: {
+ display: "flex",
+ flexDirection: "row",
+ flexWrap: "nowrap",
+ width: "100%",
+ height: "56px",
+ alignItems: "center",
+ textAlign: "left",
+ justifyContent: "center",
+ paddingLeft: 8
+ },
+ iconFrame: {
+ flex: "0 0 auto",
+
+ },
+ itemIcon: {
+ width: 24, height: 24, margin: 12, opacity: 0.6
+ },
+ itemLabel: {
+ flex: "1 1 1px",
+ marginLeft: 8
+ }
+
+});
+
+
+const Transition = React.forwardRef(function Transition(
+ props: SlideProps, ref: React.Ref
+) {
+ return ();
+});
+
+
+const PluginPresetsDialog = withStyles(styles, { withTheme: true })(
+
+ class extends Component {
+
+ model: PiPedalModel;
+
+
+
+ constructor(props: PluginPresetsDialogProps) {
+ super(props);
+ this.model = PiPedalModelFactory.getInstance();
+
+ this.handleDialogClose = this.handleDialogClose.bind(this);
+ this.state = {
+ showActionBar: false,
+ selectedItem: -1,
+ renameOpen: false,
+ moreMenuAnchorEl: null,
+ openUploadDialog: false
+
+ };
+
+ }
+
+ selectItemAtIndex(index: number) {
+ let instanceId = this.props.presets.presets[index].instanceId;
+ this.setState({ selectedItem: instanceId });
+ }
+ isEditMode() {
+ return this.state.showActionBar || this.props.isEditDialog;
+ }
+
+ onMoreClick(e: SyntheticEvent): void {
+ this.setState({ moreMenuAnchorEl: e.currentTarget as HTMLElement })
+ }
+
+ handleDownloadPresets() {
+ this.handleMoreClose();
+ if (this.props.presets.pluginUri !== "")
+ {
+ this.model.download("downloadPluginPresets", this.props.presets.pluginUri);
+ }
+ }
+ handleUploadPresets() {
+ this.handleMoreClose();
+ this.setState({ openUploadDialog: true });
+
+ }
+ handleMoreClose(): void {
+ this.setState({ moreMenuAnchorEl: null });
+
+ }
+
+
+ componentDidUpdate()
+ {
+ if (!this.props.presets.getItem(this.state.selectedItem))
+ {
+ if (this.props.presets.presets.length !== 0)
+ {
+ this.setState({selectedItem: this.props.presets.presets[0].instanceId});
+ }
+ }
+ }
+
+
+ componentDidMount() {
+ // scroll selected item into view.
+ }
+ componentWillUnmount() {
+ }
+
+ getSelectedIndex() {
+ let instanceId = this.state.selectedItem;
+ let presets = this.props.presets;
+ for (let i = 0; i < presets.presets.length; ++i) {
+ if (presets.presets[i].instanceId === instanceId) return i;
+ }
+ return -1;
+ }
+
+ handleDeleteClick() {
+ let selectedItem = this.state.selectedItem;
+ if (selectedItem !== -1) {
+ let newPresets = this.props.presets.clone();
+ for (let i = 0; i < newPresets.presets.length; ++i)
+ {
+ if (newPresets.presets[i].instanceId === selectedItem)
+ {
+ newPresets.presets.splice(i,1);
+ this.model.updatePluginPresets(newPresets.pluginUri,newPresets)
+ .catch((error) => {
+ this.model.showAlert(error);
+
+ });
+ let newPos = i;
+ if (newPos >= newPresets.presets.length) {
+ --i;
+ }
+ let newSelection = i < 0? -1: newPresets.presets[i].instanceId;
+ this.setState({selectedItem: newSelection});
+ }
+ }
+ }
+ }
+ handleDialogClose() {
+ this.props.onDialogClose();
+ }
+
+ handleItemClick(instanceId: number): void {
+ if (this.isEditMode()) {
+ this.setState({ selectedItem: instanceId });
+ } else {
+ this.model.loadPreset(instanceId);
+ this.props.onDialogClose();
+ }
+ }
+ showActionBar(show: boolean): void {
+ this.setState({ showActionBar: show });
+
+ }
+
+
+ mapElement(el: any): React.ReactNode {
+ let presetEntry = el as PluginUiPreset;
+ let classes = this.props.classes;
+ let selectedItem = this.state.selectedItem;
+ return (
+
+
+
this.handleItemClick(presetEntry.instanceId)}
+ >
+
+
+
+

+
+
+
+ {presetEntry.label}
+
+
+
+
+
+
+ );
+ }
+
+ updateServerPluginPresets(newPresets: PluginUiPresets) {
+ newPresets = newPresets.clone();
+ this.model.updatePluginPresets(this.getPluginUri(),newPresets)
+ .catch((error) => {
+ this.model.showAlert(error);
+ });
+ }
+ moveElement(from: number, to: number): void {
+ let newPresets: PluginUiPresets = this.props.presets.clone();
+ newPresets.movePreset(from, to);
+ this.setState({
+ selectedItem: newPresets.presets[to].instanceId
+ });
+ this.updateServerPluginPresets(newPresets);
+ }
+
+ getSelectedName(): string {
+ let item = this.props.presets.getItem(this.state.selectedItem);
+ if (item) return item.label;
+ return "";
+ }
+
+ handleRenameClick() {
+ let item = this.props.presets.getItem(this.state.selectedItem);
+ if (item) {
+ this.setState({ renameOpen: true });
+ }
+ }
+ handleRenameOk(text: string) {
+ let item = this.props.presets.getItem(this.state.selectedItem);
+ if (!item) return;
+ if (item.label !== text) {
+ let newPresets = this.props.presets.clone();
+ let newItem = newPresets.getItem(this.state.selectedItem);
+ if (!newItem) return;
+ newItem.label = text;
+ this.updateServerPluginPresets(newPresets);
+ }
+
+ this.setState({ renameOpen: false });
+ }
+ getPluginUri() : string {
+ let pedalBoardItem = this.model.pedalBoard.get().getItem(this.props.instanceId);
+ return pedalBoardItem.uri;
+ }
+ handleCopy() {
+ let item = this.props.presets.getItem(this.state.selectedItem);
+ if (!item) return;
+
+ this.model.duplicatePluginPreset(this.getPluginUri(),this.state.selectedItem)
+ .then((newId) => {
+ this.setState({ selectedItem: newId });
+ }).catch((error) => {
+ this.onError(error);
+ });
+ }
+
+ onError(error: string): void {
+ this.model?.showAlert(error);
+ }
+
+
+
+ render() {
+ let classes = this.props.classes;
+
+ let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar;
+ let defaultSelectedIndex = this.getSelectedIndex();
+ let title: string = "";
+ let pluginUri = this.getPluginUri();
+ let plugin = this.model.getUiPlugin(pluginUri);
+ if (plugin) {
+ title = "Presets - " + plugin.name;
+ }
+
+
+ return (
+ { this.handleDialogClose() }} TransitionComponent={Transition}
+ style={{userSelect: "none"}}>
+
+
+
+
+
+
+
+
+ {title}
+
+ this.showActionBar(true)} >
+
+
+
+
+
{ e.stopPropagation(); e.preventDefault(); }}
+ >
+
+ {(!this.props.isEditDialog) ? (
+ this.showActionBar(false)} aria-label="close">
+
+
+ ) : (
+
+
+
+
+ )}
+
+ { title }
+
+ {(this.props.presets.getItem(this.state.selectedItem) != null)
+ && (
+
+
+
+
+
{ this.setState({ renameOpen: false }) }}
+ onOk={(text: string) => {
+ this.handleRenameOk(text);
+ }
+ }
+ />
+ this.handleDeleteClick()} >
+
+
+ { this.onMoreClick(e) }} >
+
+
+
+
+
+
+ )
+ }
+
+
+
+
+
+ this.showActionBar(true)}
+ canDrag={this.isEditMode()}
+ onDragStart={(index, x, y) => { this.selectItemAtIndex(index) }}
+ moveElement={(from, to) => { this.moveElement(from, to); }}
+ scroll={ScrollDirection.Y}
+ defaultSelectedIndex={defaultSelectedIndex}
+ >
+ {
+ this.props.presets.presets.map((element) => {
+ return this.mapElement(element);
+ })
+ }
+
+
+
+ {} }
+ uploadAfter={this.state.selectedItem}
+ open={this.state.openUploadDialog}
+ onClose={() => { this.setState({ openUploadDialog: false }) }} />
+
+
+
+ );
+
+ }
+
+ }
+
+);
+
+export default PluginPresetsDialog;
\ No newline at end of file
diff --git a/react/src/PresetDialog.tsx b/react/src/PresetDialog.tsx
index a1093f3..9aca799 100644
--- a/react/src/PresetDialog.tsx
+++ b/react/src/PresetDialog.tsx
@@ -446,6 +446,9 @@ const PresetDialog = withStyles(styles, { withTheme: true })(