Plugin Presets.
This commit is contained in:
Executable
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path d="M19.495,8.991l-0.003-3.993c0-1.1-0.891-2-1.99-2h-4h-3h-4c-1.1,0-1.99,0.9-1.99,2L4.509,8.991L4.502,8.998v10
|
||||
c0,1.1,0.891,2,1.99,2h0.01h11h0.01c1.1,0,1.99-0.9,1.99-2v-10L19.495,8.991z M17.502,9.998v9h-11v-9v-5h5h1h5V9.998z"/>
|
||||
<line fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="6.002" y1="11.998" x2="12.002" y2="11.998"/>
|
||||
<line fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="15.261" y1="11.998" x2="18.066" y2="11.998"/>
|
||||
<path d="M12.002,7.998c-1.474,0-2.75,0.81-3.443,2h3.443c1.103,0,2,0.896,2,2s-0.897,2-2,2H8.558c0.694,1.189,1.97,2,3.444,2
|
||||
c2.206,0,4-1.794,4-4S14.208,7.998,12.002,7.998z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"socket_server_port": 80,
|
||||
"socket_server_port": 8080,
|
||||
"socket_server_address": "*",
|
||||
|
||||
"max_upload_size": 1048576,
|
||||
@@ -8,4 +8,4 @@
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
@@ -801,7 +801,7 @@ const AppThemed = withStyles(appStyles)(class extends ResizeResponsiveComponent<
|
||||
<div className={classes.errorContentMask} />
|
||||
<div style={{ flex: "2 2 3px", height: 20 }} > </div>
|
||||
<div className={classes.errorMessageBox} style={{ position: "relative" }} >
|
||||
<div style={{ fontSize: "30px", position: "absolute", left: 0, top: 0, color: "#A00" }}>
|
||||
<div style={{ fontSize: "30px", position: "absolute", left: 0, top: 3, color: "#A00" }}>
|
||||
<ErrorOutlineIcon color="inherit" fontSize="inherit" style={{ float: "left", marginRight: "12px" }} />
|
||||
</div>
|
||||
<div style={{ marginLeft: 40, marginTop: 3 }}>
|
||||
|
||||
@@ -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[] = [
|
||||
|
||||
@@ -41,6 +41,7 @@ const styles = (theme: Theme) => createStyles({
|
||||
interface GxTunerProps extends WithStyles<typeof styles> {
|
||||
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 (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
return (<GxTunerView isToobTuner={false} instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 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 (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
return (<GxTunerView isToobTuner={true} instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export ToobTunerViewFactory;
|
||||
export GxTunerViewFactory;
|
||||
@@ -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<HTMLDivElement>, 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)
|
||||
|
||||
@@ -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 =
|
||||
<span className={classes.author}>{author}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<div style={{ flex: "0 0 auto", verticalAlign: "center" }}>
|
||||
<PluginInfoDialog plugin_uri={pluginUri} />
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<PluginPresetSelector pluginUri={presetsUri}
|
||||
onSelectPreset={(presetName) => this.handleSelectPluginPreset(pedalBoardItem!.instanceId, presetName)}
|
||||
<PluginPresetSelector pluginUri={presetsUri} instanceId={pedalBoardItem?.instanceId ?? 0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -133,7 +133,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new PiPedalArgumentError("Control not found.");
|
||||
return false;
|
||||
}
|
||||
setMidiBinding(midiBinding: MidiBinding): boolean {
|
||||
if (this.midiBindings)
|
||||
|
||||
+106
-16
@@ -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<void>;
|
||||
deletePresetItem(instanceId: number): Promise<number>;
|
||||
@@ -372,8 +383,10 @@ export interface PiPedalModel {
|
||||
|
||||
getJackStatus(): Promise<JackHostStatus>;
|
||||
|
||||
getPluginPresets(uri: string): Promise<PluginPreset[]>;
|
||||
loadPluginPreset(instanceId: number, presetName: string): void;
|
||||
getPluginPresets(uri: string): Promise<PluginUiPresets>;
|
||||
loadPluginPreset(pluginInstanceId: number, presetInstanceId: number): void;
|
||||
updatePluginPresets(uri: string, presets: PluginUiPresets): Promise<void>;
|
||||
duplicatePluginPreset(uri: string, instanceId: number): Promise<number>;
|
||||
|
||||
shutdown(): Promise<void>;
|
||||
restart(): Promise<void>;
|
||||
@@ -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<number>;
|
||||
uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise<number>;
|
||||
uploadBank(file: File, uploadAfter: number): Promise<number>;
|
||||
|
||||
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>;
|
||||
@@ -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<number> {
|
||||
// default behaviour is to save after the currently selected preset.
|
||||
let request: any = {
|
||||
instanceId: pluginInstanceId,
|
||||
name: newName,
|
||||
};
|
||||
return nullCast(this.webSocket)
|
||||
.request<number>("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<PluginPreset[]> {
|
||||
return new Promise<PluginPreset[]>((resolve, reject) => {
|
||||
uncachePluginPreset(pluginUri: string): void {
|
||||
delete this.presetCache[pluginUri];
|
||||
}
|
||||
getPluginPresets(uri: string): Promise<PluginUiPresets> {
|
||||
return new Promise<PluginUiPresets>((resolve, reject) => {
|
||||
if (this.presetCache[uri] !== undefined) {
|
||||
resolve(this.presetCache[uri]);
|
||||
} else {
|
||||
this.webSocket!.request<any>("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<void>
|
||||
{
|
||||
this.presetCache[pluginUri] = presets;
|
||||
this.firePluginPresetsChanged(pluginUri);
|
||||
return nullCast(this.webSocket).request<void>("updatePluginPresets", presets);
|
||||
}
|
||||
duplicatePluginPreset(pluginUri: string, instanceId: number): Promise<number>
|
||||
{
|
||||
return nullCast(this.webSocket).request<number>("copyPluginPreset", {
|
||||
pluginUri: pluginUri, instanceId: instanceId });
|
||||
|
||||
}
|
||||
|
||||
|
||||
shutdown(): Promise<void> {
|
||||
return this.webSocket!.request<void>("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<number> {
|
||||
uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise<number> {
|
||||
let result = new Promise<number>((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;
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
|
||||
return (
|
||||
<div>
|
||||
<IconButton
|
||||
style={{ display: (props.plugin_uri !== "") ? "block" : "none" }}
|
||||
style={{ display: (props.plugin_uri !== "") ? "inline-flex" : "none" }}
|
||||
onClick={handleClickOpen}
|
||||
size="large">
|
||||
<InfoOutlinedIcon />
|
||||
|
||||
@@ -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[] = [];
|
||||
}
|
||||
@@ -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<typeof styles> {
|
||||
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 (<div/>);
|
||||
}
|
||||
@@ -276,23 +287,27 @@ const PluginPresetSelector =
|
||||
}
|
||||
|
||||
>
|
||||
<Typography variant="caption" color="textSecondary" style={{marginLeft: 16,marginTop:4,marginBottom: 4}}>Plugin presets</Typography>
|
||||
<MenuItem onClick={(e) => this.handlePluginPresetsMenuSaveAs(e)}>Save plugin preset...</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handleMenuEditPluginPresets()}>Manage plugin presets...</MenuItem>
|
||||
|
||||
{ this.state.presets.presets.length !== 0 &&
|
||||
(<Divider/>)
|
||||
}
|
||||
{
|
||||
this.state.presets.map((preset) => {
|
||||
return (<MenuItem key={preset.presetUri}
|
||||
onClick={(e) => this.handleLoadPluginPreset(preset.presetUri)}>{preset.name}</MenuItem>);
|
||||
this.state.presets.presets.map((preset) => {
|
||||
return (<MenuItem key={preset.instanceId}
|
||||
onClick={(e) => this.handleLoadPluginPreset(preset.instanceId)}
|
||||
><img src="img/ic_pluginpreset2.svg" className={classes.itemIcon} alt="" />
|
||||
{preset.label}</MenuItem>);
|
||||
})
|
||||
}
|
||||
{/*
|
||||
<Divider />
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuSave(e)}>Save plugin preset</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save plugin preset as...</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
|
||||
*/}
|
||||
|
||||
</Menu>
|
||||
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()} />
|
||||
<PluginPresetsDialog
|
||||
instanceId={this.props.instanceId}
|
||||
presets={this.state.presets}
|
||||
show={this.state.showPresetsDialog}
|
||||
isEditDialog={this.state.showEditPresetsDialog}
|
||||
onDialogClose={() => this.handleDialogClose()} />
|
||||
<RenameDialog open={this.state.renameDialogOpen}
|
||||
defaultName={this.state.renameDialogDefaultName}
|
||||
acceptActionName={this.state.renameDialogActionName}
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
// Copyright (c) 2021 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import React, { SyntheticEvent,Component } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import Button from "@mui/material/Button";
|
||||
import ButtonBase from "@mui/material/ButtonBase";
|
||||
import DialogEx from './DialogEx';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import DraggableGrid, { ScrollDirection } from './DraggableGrid';
|
||||
import MoreVertIcon from '@mui/icons-material//MoreVert';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Fade from '@mui/material/Fade';
|
||||
import UploadDialog from './UploadDialog';
|
||||
import {PluginUiPresets,PluginUiPreset} from './PluginPreset';
|
||||
|
||||
import SelectHoverBackground from './SelectHoverBackground';
|
||||
import CloseIcon from '@mui/icons-material//Close';
|
||||
import ArrowBackIcon from '@mui/icons-material//ArrowBack';
|
||||
import EditIcon from '@mui/icons-material//Edit';
|
||||
import RenameDialog from './RenameDialog';
|
||||
|
||||
import Slide, {SlideProps} from '@mui/material/Slide';
|
||||
import { createStyles, Theme } from '@mui/material/styles';
|
||||
import { WithStyles, withStyles} from '@mui/styles';
|
||||
|
||||
|
||||
|
||||
interface PluginPresetsDialogProps extends WithStyles<typeof styles> {
|
||||
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<unknown>
|
||||
) {
|
||||
return (<Slide direction="up" ref={ref} {...props} />);
|
||||
});
|
||||
|
||||
|
||||
const PluginPresetsDialog = withStyles(styles, { withTheme: true })(
|
||||
|
||||
class extends Component<PluginPresetsDialogProps, PluginPresetsDialogState> {
|
||||
|
||||
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 (
|
||||
<div key={presetEntry.instanceId} style={{ background: "white" }} >
|
||||
|
||||
<ButtonBase style={{ width: "100%", height: 48 }}
|
||||
onClick={() => this.handleItemClick(presetEntry.instanceId)}
|
||||
>
|
||||
<SelectHoverBackground selected={presetEntry.instanceId === selectedItem} showHover={true} />
|
||||
<div className={classes.itemFrame}>
|
||||
<div className={classes.iconFrame}>
|
||||
<img src="img/ic_pluginpreset2.svg" className={classes.itemIcon} alt="" />
|
||||
</div>
|
||||
<div className={classes.itemLabel}>
|
||||
<Typography>
|
||||
{presetEntry.label}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<DialogEx tag="PluginPresetsDialog" fullScreen open={this.props.show}
|
||||
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}
|
||||
style={{userSelect: "none"}}>
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
|
||||
<Toolbar>
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
disabled={this.isEditMode()}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
{title}
|
||||
</Typography>
|
||||
<IconButton color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AppBar className={actionBarClass} style={{ display: this.isEditMode() ? "block" : "none" }}
|
||||
onClick={(e) => { e.stopPropagation(); e.preventDefault(); }}
|
||||
>
|
||||
<Toolbar>
|
||||
{(!this.props.isEditDialog) ? (
|
||||
<IconButton edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
|
||||
)}
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
{ title }
|
||||
</Typography>
|
||||
{(this.props.presets.getItem(this.state.selectedItem) != null)
|
||||
&& (
|
||||
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<Button color="inherit" onClick={(e) => this.handleCopy()}>
|
||||
Copy
|
||||
</Button>
|
||||
<Button color="inherit" onClick={() => this.handleRenameClick()}>
|
||||
Rename
|
||||
</Button>
|
||||
<RenameDialog
|
||||
open={this.state.renameOpen}
|
||||
defaultName={this.getSelectedName()}
|
||||
acceptActionName={"Rename"}
|
||||
onClose={() => { this.setState({ renameOpen: false }) }}
|
||||
onOk={(text: string) => {
|
||||
this.handleRenameOk(text);
|
||||
}
|
||||
}
|
||||
/>
|
||||
<IconButton color="inherit" onClick={(e) => this.handleDeleteClick()} >
|
||||
<img src="/img/old_delete_outline_white_24dp.svg" alt="Delete" style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
<IconButton color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="more-menu"
|
||||
anchorEl={this.state.moreMenuAnchorEl}
|
||||
keepMounted
|
||||
open={Boolean(this.state.moreMenuAnchorEl)}
|
||||
onClose={() => this.handleMoreClose()}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
<MenuItem onClick={() => { this.handleDownloadPresets(); }}
|
||||
disabled={this.props.presets.presets.length === 0}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<img src="img/file_download_black_24dp.svg" style={{ width: 24, height: 24, opacity: 0.6 }} alt="" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Download presets
|
||||
</ListItemText>
|
||||
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => { this.handleUploadPresets() }}>
|
||||
<ListItemIcon>
|
||||
<img src="img/file_upload_black_24dp.svg" style={{ width: 24, height: 24, opacity: 0.6 }} alt="" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Upload presets
|
||||
</ListItemText>
|
||||
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Toolbar>
|
||||
|
||||
</AppBar>
|
||||
</div>
|
||||
<div style={{ flex: "1 1 auto", position: "relative", overflow: "hidden" }} >
|
||||
<DraggableGrid
|
||||
onLongPress={(item) => 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);
|
||||
})
|
||||
}
|
||||
</DraggableGrid>
|
||||
</div>
|
||||
</div>
|
||||
<UploadDialog
|
||||
title="Upload Plugin Presets"
|
||||
extension='.piPluginPresets'
|
||||
uploadPage='uploadPluginPresets'
|
||||
onUploaded={(instanceId) => {} }
|
||||
uploadAfter={this.state.selectedItem}
|
||||
open={this.state.openUploadDialog}
|
||||
onClose={() => { this.setState({ openUploadDialog: false }) }} />
|
||||
|
||||
</DialogEx>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
export default PluginPresetsDialog;
|
||||
@@ -446,6 +446,9 @@ const PresetDialog = withStyles(styles, { withTheme: true })(
|
||||
</div>
|
||||
</div>
|
||||
<UploadDialog
|
||||
title='Upload preset'
|
||||
extension='.piPreset'
|
||||
uploadPage='uploadPreset'
|
||||
onUploaded={(instanceId) => this.setState({selectedItem: instanceId}) }
|
||||
uploadAfter={this.state.selectedItem}
|
||||
open={this.state.openUploadDialog}
|
||||
|
||||
@@ -369,7 +369,11 @@ const PresetSelector =
|
||||
acceptActionName={this.state.renameDialogActionName}
|
||||
onClose={() => this.handleRenameDialogClose()}
|
||||
onOk={(name: string) => this.handleRenameDialogOk(name)} />
|
||||
<UploadDialog onUploaded={(instanceId) => { this.model.loadPreset(instanceId); }}
|
||||
<UploadDialog
|
||||
title='Upload preset'
|
||||
extension='.piPreset'
|
||||
uploadPage='uploadPreset'
|
||||
onUploaded={(instanceId) => { this.model.loadPreset(instanceId); }}
|
||||
open={this.state.openUploadDialog}
|
||||
uploadAfter={-1}
|
||||
onClose={() => { this.setState({ openUploadDialog: false }) }} />
|
||||
|
||||
@@ -85,6 +85,8 @@ export default class RenameDialog extends ResizeResponsiveComponent<RenameDialog
|
||||
|
||||
const handleOk = () => {
|
||||
let text = nullCast(this.refText.current).value;
|
||||
text = text.trim();
|
||||
if (text.length === 0) return;
|
||||
onOk(text);
|
||||
}
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||
|
||||
@@ -29,14 +29,18 @@ import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
|
||||
const PRESET_EXTENSION = ".piPreset";
|
||||
const BANK_EXTENSION = ".piBank";
|
||||
// const PRESET_EXTENSION = ".piPreset";
|
||||
// const BANK_EXTENSION = ".piBank";
|
||||
// const PLUGIN_PRESETS_EXTENSION = ".piPluginPresets";
|
||||
|
||||
export interface UploadDialogProps {
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
uploadAfter: number,
|
||||
onUploaded: (instanceId: number) => void
|
||||
onUploaded: (instanceId: number) => void,
|
||||
title: string,
|
||||
extension: string,
|
||||
uploadPage: string
|
||||
|
||||
};
|
||||
|
||||
@@ -94,11 +98,11 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
|
||||
let uploadAfter = this.props.uploadAfter;
|
||||
for (let i = 0; i < fileList.length; ++i)
|
||||
{
|
||||
uploadAfter = await this.model.uploadPreset(fileList[i],uploadAfter);
|
||||
uploadAfter = await this.model.uploadPreset(this.props.uploadPage,fileList[i],uploadAfter);
|
||||
}
|
||||
this.props.onUploaded(uploadAfter);
|
||||
} catch(error) {
|
||||
this.model.showAlert(error);
|
||||
this.model.showAlert(error +"");
|
||||
};
|
||||
this.props.onClose();
|
||||
}
|
||||
@@ -132,7 +136,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
|
||||
for (let i = 0; i < fileList.files.length; ++i) {
|
||||
let file = fileList.files[i];
|
||||
let extension = this.getFileExtension(file.name);
|
||||
if (extension !== PRESET_EXTENSION && extension !== BANK_EXTENSION) return false;
|
||||
if (extension !== this.props.extension) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -157,7 +161,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
|
||||
fullScreen={this.state.fullScreen}
|
||||
style={{userSelect: "none"}}
|
||||
>
|
||||
<DialogTitle>Upload preset</DialogTitle>
|
||||
<DialogTitle>{this.props.title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<div style={{
|
||||
width: "100%", height: 100, marginBottom: 0,
|
||||
@@ -188,7 +192,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
|
||||
<input
|
||||
type="file"
|
||||
hidden
|
||||
accept=".piPreset"
|
||||
accept={this.props.extension}
|
||||
multiple
|
||||
onChange={(e)=> this.handleButtonSelect(e)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user