Import Prests from Bank
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
// Copyright (c) Robin E. R. 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 Divider from '@mui/material/Divider';
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import IconButtonEx from "./IconButtonEx";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import Select from '@mui/material/Select';
|
||||
import Button from '@mui/material/Button';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import DialogEx from './DialogEx';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import { PiPedalModel, PresetIndexEntry } from './PiPedalModel';
|
||||
import { BankIndexEntry } from './Banks';
|
||||
|
||||
|
||||
export interface ImportPresetFromBankDialogProps {
|
||||
open: boolean,
|
||||
onOk: (bankInstanceId: number, presets: number[]) => void,
|
||||
onClose: () => void
|
||||
};
|
||||
|
||||
export interface ImportPresetFromBankDialogState {
|
||||
selectedBank: number;
|
||||
banks: BankIndexEntry[];
|
||||
bankPresets: PresetIndexEntry[] | null;
|
||||
selectedPresets: number[];
|
||||
|
||||
fullScreen: boolean;
|
||||
};
|
||||
|
||||
function setSavedDefaultBankSelection(bankInstanceId: number) {
|
||||
localStorage.setItem('pipedal_defaultBankSelection', bankInstanceId.toString());
|
||||
}
|
||||
function getSavedDefaultBankSelection() {
|
||||
let defaultValue = localStorage.getItem('pipedal_defaultBankSelection');
|
||||
if (defaultValue) {
|
||||
return parseInt(defaultValue);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
export default class ImportPresetFromBankDialog extends ResizeResponsiveComponent<ImportPresetFromBankDialogProps, ImportPresetFromBankDialogState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
constructor(props: ImportPresetFromBankDialogProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModel.getInstance();
|
||||
let banks = this.getBanks();
|
||||
let selectedBank = this.getDefaultBankSelection(banks);
|
||||
this.state = {
|
||||
banks: banks,
|
||||
selectedBank: selectedBank,
|
||||
fullScreen: this.windowSize.height < 550,
|
||||
bankPresets: null,
|
||||
selectedPresets: []
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
private getBanks() {
|
||||
let bankIndex = this.model.banks.get();
|
||||
let result: BankIndexEntry[] = [];
|
||||
for (let entry of bankIndex.entries) {
|
||||
if (entry.instanceId === bankIndex.selectedBank) continue;
|
||||
result.push(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private getDefaultBankSelection(banks: BankIndexEntry[]) {
|
||||
if (banks.length === 0) return -1;
|
||||
let defaultSelection = getSavedDefaultBankSelection();
|
||||
for (let bank of banks) {
|
||||
if (bank.instanceId === defaultSelection) return defaultSelection;
|
||||
}
|
||||
return banks[0].instanceId;
|
||||
}
|
||||
|
||||
private mounted: boolean = false;
|
||||
|
||||
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
this.setState({ fullScreen: height < 550 })
|
||||
}
|
||||
|
||||
|
||||
requestPresets(bankInstanceId: number) {
|
||||
this.model.requestBankPresets(bankInstanceId).then((presets) => {
|
||||
if (!this.mounted) return;
|
||||
this.setState({ bankPresets: presets, selectedPresets: [] });
|
||||
}).catch((error) => {
|
||||
this.model.showAlert("Error requesting presets: " + error);
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.mounted = true;
|
||||
this.requestPresets(this.state.selectedBank);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
}
|
||||
checkForIllegalCharacters(filename: string) {
|
||||
}
|
||||
|
||||
handleBankChanged(bankId: number) {
|
||||
setSavedDefaultBankSelection(bankId);
|
||||
this.setState({ selectedBank: bankId });
|
||||
this.requestPresets(bankId);
|
||||
}
|
||||
|
||||
handleOk() {
|
||||
if (this.state.selectedPresets.length === 0) return;
|
||||
this.props.onOk(this.state.selectedBank, this.state.selectedPresets);
|
||||
}
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
let { open, onClose } = props;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<DialogEx tag="importPreset" open={open} fullWidth maxWidth="xs" onClose={handleClose} aria-labelledby="Rename-dialog-title"
|
||||
fullScreen={this.state.fullScreen}
|
||||
style={{ userSelect: "none" }}
|
||||
onEnterKey={() => { }}
|
||||
>
|
||||
<div style={{ display: "flex", flexFlow: "column nowrap", height: this.state.fullScreen ? "100vh" : "80vh" }}>
|
||||
{!this.state.fullScreen && (
|
||||
<DialogTitle style={{ paddingTop: 8, paddingBottom: 0 }}>
|
||||
<Toolbar style={{ padding: 0 }}>
|
||||
<IconButtonEx
|
||||
tooltip="Close"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="cancel"
|
||||
style={{ opacity: 0.6 }}
|
||||
onClick={() => { onClose(); }}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButtonEx>
|
||||
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
Import Presets from Bank
|
||||
</Typography>
|
||||
|
||||
</Toolbar>
|
||||
</DialogTitle>
|
||||
)}
|
||||
|
||||
<DialogContent style={{ flex: "0 0 auto", paddingTop: 8, paddingBottom: 8, }}>
|
||||
<InputLabel style={{ fontSize: "0.75rem", fontWeight: 400, marginTop: 0 }}>Bank</InputLabel>
|
||||
|
||||
<Select variant="standard" fullWidth value={this.state.selectedBank} style={{ marginBottom: 8 }}
|
||||
onChange={(e) => { this.handleBankChanged(e.target.value as number); }}>
|
||||
{this.state.banks.map((bankEntry) => {
|
||||
return (
|
||||
<MenuItem key={bankEntry.instanceId} value={bankEntry.instanceId}
|
||||
selected={bankEntry.instanceId === this.state.selectedBank}
|
||||
>
|
||||
{bankEntry.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</DialogContent>
|
||||
<Divider />
|
||||
<List sx={{ flex: "1 1 auto", width: '100%', bgColor: 'background.paper', overflowX: 'auto' }}>
|
||||
{this.state.bankPresets?.map((presetEntry) => {
|
||||
const labelId = `checkbox-list-label-${presetEntry.instanceId}`;
|
||||
|
||||
return (
|
||||
<ListItem key={presetEntry.instanceId}
|
||||
onClick={() => {
|
||||
let selectedPresets = this.state.selectedPresets;
|
||||
let index = selectedPresets.indexOf(presetEntry.instanceId);
|
||||
if (index === -1) {
|
||||
selectedPresets.push(presetEntry.instanceId);
|
||||
} else {
|
||||
selectedPresets.splice(index, 1);
|
||||
}
|
||||
this.setState({ selectedPresets: [...selectedPresets] });
|
||||
}}>
|
||||
<ListItemIcon style={{ marginLeft: 16 }}>
|
||||
<Checkbox
|
||||
edge="start"
|
||||
checked={this.state.selectedPresets.indexOf(presetEntry.instanceId) !== -1}
|
||||
tabIndex={-1}
|
||||
disableRipple
|
||||
inputProps={{ 'aria-labelledby': labelId }}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText id={labelId} primary={presetEntry.name} />
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
|
||||
<Divider />
|
||||
<DialogActions style={{ flex: "0 0 auto" }}>
|
||||
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => { this.handleOk(); }} variant="dialogPrimary"
|
||||
disabled={this.state.selectedPresets.length === 0}
|
||||
style={{ opacity: this.state.selectedPresets.length === 0 ? 0.5 : 1.0 }}
|
||||
>
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</div>
|
||||
</DialogEx>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2049,13 +2049,14 @@ export class PiPedalModel //implements PiPedalModel
|
||||
}
|
||||
|
||||
|
||||
saveCurrentPresetAs(newName: string, saveAfterInstanceId = -1): Promise<number> {
|
||||
saveCurrentPresetAs(bankInstanceId: number, newName: string, saveAfterInstanceId = -1): Promise<number> {
|
||||
// default behaviour is to save after the currently selected preset.
|
||||
if (saveAfterInstanceId === -1) {
|
||||
saveAfterInstanceId = this.presets.get().selectedInstanceId;
|
||||
}
|
||||
let request: any = {
|
||||
clientId: this.clientId,
|
||||
bankInstanceId: bankInstanceId,
|
||||
name: newName,
|
||||
saveAfterInstanceId: saveAfterInstanceId
|
||||
|
||||
@@ -2064,7 +2065,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
return nullCast(this.webSocket)
|
||||
.request<number>("saveCurrentPresetAs", request)
|
||||
.then((newPresetId) => {
|
||||
this.loadPreset(newPresetId);
|
||||
if (bankInstanceId === this.banks.get().selectedBank) {
|
||||
this.loadPreset(newPresetId);
|
||||
}
|
||||
return newPresetId;
|
||||
});
|
||||
}
|
||||
@@ -3539,6 +3542,13 @@ export class PiPedalModel //implements PiPedalModel
|
||||
this.webSocket?.send("setSelectedPedalboardPlugin", { clientId: this.clientId, pluginInstanceId: pluginId });
|
||||
|
||||
}
|
||||
requestBankPresets(bankInstanceId: number): Promise<PresetIndexEntry[]> {
|
||||
return nullCast(this.webSocket).request<PresetIndexEntry[]>("requestBankPresets", {bankInstanceId: bankInstanceId});
|
||||
}
|
||||
|
||||
importPresetsFromBank(bankInstanceId: number, presets: number[]): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("importPresetsFromBank", {bankInstanceId: bankInstanceId, presets: presets});
|
||||
}
|
||||
};
|
||||
|
||||
let instance: PiPedalModel | undefined = undefined;
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
// 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 React, { SyntheticEvent, Component } from 'react';
|
||||
import ImportPresetFromBankDialog from './ImportPresetFromBankDialog';
|
||||
import Icon from '@mui/material/Icon';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import { css } from '@emotion/react';
|
||||
import {isDarkMode} from './DarkMode';
|
||||
import { isDarkMode } from './DarkMode';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } from './PiPedalModel';
|
||||
@@ -43,8 +46,8 @@ 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} from './WithStyles';
|
||||
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||
import { createStyles } from './WithStyles';
|
||||
|
||||
import { Theme } from '@mui/material/styles';
|
||||
import WithStyles from './WithStyles';
|
||||
@@ -66,9 +69,11 @@ interface PresetDialogState {
|
||||
|
||||
showActionBar: boolean;
|
||||
|
||||
selectedItem: number;
|
||||
currentItem: number;
|
||||
selectedItems: Set<number>;
|
||||
|
||||
renameOpen: boolean;
|
||||
importOpen: boolean;
|
||||
|
||||
moreMenuAnchorEl: HTMLElement | null;
|
||||
openUploadPresetDialog: boolean;
|
||||
@@ -79,7 +84,7 @@ interface PresetDialogState {
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
listIcon: css({
|
||||
width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary
|
||||
width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary
|
||||
}),
|
||||
dialogAppBar: css({
|
||||
position: 'relative',
|
||||
@@ -146,19 +151,27 @@ const PresetDialog = withStyles(
|
||||
this.state = {
|
||||
presets: presets,
|
||||
showActionBar: false,
|
||||
selectedItem: presets.selectedInstanceId,
|
||||
currentItem: presets.selectedInstanceId,
|
||||
selectedItems: new Set<number>(),
|
||||
renameOpen: false,
|
||||
importOpen: false,
|
||||
moreMenuAnchorEl: null,
|
||||
openUploadPresetDialog: false
|
||||
|
||||
};
|
||||
this.handlePresetsChanged = this.handlePresetsChanged.bind(this);
|
||||
|
||||
|
||||
}
|
||||
setSelection(instanceId: number) {
|
||||
this.setState({
|
||||
currentItem: instanceId,
|
||||
selectedItems: new Set<number>([instanceId])
|
||||
});
|
||||
}
|
||||
|
||||
selectItemAtIndex(index: number) {
|
||||
let instanceId = this.state.presets.presets[index].instanceId;
|
||||
this.setState({ selectedItem: instanceId });
|
||||
this.setSelection(instanceId);
|
||||
}
|
||||
isEditMode() {
|
||||
return this.state.showActionBar || this.props.isEditDialog;
|
||||
@@ -170,7 +183,7 @@ const PresetDialog = withStyles(
|
||||
|
||||
handleDownloadPreset() {
|
||||
this.handleMoreClose();
|
||||
this.model.download("downloadPreset", this.state.selectedItem);
|
||||
this.model.download("downloadPreset", this.state.currentItem);
|
||||
}
|
||||
handleUploadPreset() {
|
||||
this.handleMoreClose();
|
||||
@@ -189,8 +202,10 @@ const PresetDialog = withStyles(
|
||||
if (!presets.areEqual(this.state.presets, false)) // avoid a bunch of peculiar effects if we update while a drag is in progress
|
||||
{
|
||||
// if we don't have a valid selection, then use the current preset.
|
||||
if (this.state.presets.getItem(this.state.selectedItem) == null) {
|
||||
this.setState({ presets: presets, selectedItem: presets.selectedInstanceId });
|
||||
if (this.state.presets.getItem(this.state.currentItem) == null) {
|
||||
this.setState({ presets: presets, currentItem: presets.selectedInstanceId,
|
||||
selectedItems: new Set<number>([presets.selectedInstanceId])
|
||||
});
|
||||
} else {
|
||||
this.setState({ presets: presets });
|
||||
}
|
||||
@@ -208,7 +223,7 @@ const PresetDialog = withStyles(
|
||||
}
|
||||
|
||||
getSelectedIndex() {
|
||||
let instanceId = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId;
|
||||
let instanceId = this.isEditMode() ? this.state.currentItem : this.state.presets.selectedInstanceId;
|
||||
let presets = this.state.presets;
|
||||
for (let i = 0; i < presets.presets.length; ++i) {
|
||||
if (presets.presets[i].instanceId === instanceId) return i;
|
||||
@@ -217,12 +232,12 @@ const PresetDialog = withStyles(
|
||||
}
|
||||
|
||||
handleDeleteClick() {
|
||||
if (!this.state.selectedItem) return;
|
||||
let selectedItem = this.state.selectedItem;
|
||||
if (selectedItem !== -1) {
|
||||
this.model.deletePresetItem(selectedItem)
|
||||
.then((selectedItem: number) => {
|
||||
this.setState({ selectedItem: selectedItem });
|
||||
if (this.state.currentItem === -1) return;
|
||||
let currentItem = this.state.currentItem;
|
||||
if (currentItem !== -1) {
|
||||
this.model.deletePresetItem(currentItem)
|
||||
.then((currentItem: number) => {
|
||||
this.setSelection(currentItem);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
@@ -236,7 +251,7 @@ const PresetDialog = withStyles(
|
||||
|
||||
handleItemClick(instanceId: number): void {
|
||||
if (this.isEditMode()) {
|
||||
this.setState({ selectedItem: instanceId });
|
||||
this.setSelection(instanceId);
|
||||
} else {
|
||||
this.model.loadPreset(instanceId);
|
||||
this.props.onDialogClose();
|
||||
@@ -251,19 +266,21 @@ const PresetDialog = withStyles(
|
||||
mapElement(el: any): React.ReactNode {
|
||||
let presetEntry = el as PresetIndexEntry;
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
let selectedItem = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId;
|
||||
let selected =
|
||||
this.isEditMode() ? this.state.selectedItems.has(presetEntry.instanceId) :
|
||||
(presetEntry.instanceId === this.state.presets.selectedInstanceId);
|
||||
return (
|
||||
<div key={presetEntry.instanceId} className="itemBackground">
|
||||
<div key={presetEntry.instanceId} id={"psetdlgItem_" + presetEntry.instanceId} className="itemBackground">
|
||||
|
||||
<ButtonBase style={{ width: "100%", height: 48 }}
|
||||
onClick={() => this.handleItemClick(presetEntry.instanceId)}
|
||||
>
|
||||
<SelectHoverBackground selected={presetEntry.instanceId === selectedItem} showHover={true} />
|
||||
<SelectHoverBackground selected={selected} showHover={true} />
|
||||
<div className={classes.itemFrame}>
|
||||
<div className={classes.iconFrame}>
|
||||
<img
|
||||
src={isDarkMode()? "img/ic_presets_white.svg": "img/ic_presets.svg"}
|
||||
className={classes.itemIcon} alt="" />
|
||||
<img
|
||||
src={isDarkMode() ? "img/ic_presets_white.svg" : "img/ic_presets.svg"}
|
||||
className={classes.itemIcon} alt="" />
|
||||
</div>
|
||||
<div className={classes.itemLabel}>
|
||||
<Typography>
|
||||
@@ -287,30 +304,33 @@ const PresetDialog = withStyles(
|
||||
moveElement(from: number, to: number): void {
|
||||
let newPresets = this.state.presets.clone();
|
||||
newPresets.movePreset(from, to);
|
||||
let toInstanceId = newPresets.presets[to].instanceId;
|
||||
this.setState({
|
||||
presets: newPresets,
|
||||
selectedItem: newPresets.presets[to].instanceId
|
||||
currentItem: toInstanceId,
|
||||
selectedItems: new Set<number>([toInstanceId])
|
||||
|
||||
});
|
||||
this.updateServerPresets(newPresets);
|
||||
}
|
||||
|
||||
getSelectedName(): string {
|
||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
||||
let item = this.state.presets.getItem(this.state.currentItem);
|
||||
if (item) return item.name;
|
||||
return "";
|
||||
}
|
||||
|
||||
handleRenameClick() {
|
||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
||||
let item = this.state.presets.getItem(this.state.currentItem);
|
||||
if (item) {
|
||||
this.setState({ renameOpen: true });
|
||||
}
|
||||
}
|
||||
handleRenameOk(text: string) {
|
||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
||||
let item = this.state.presets.getItem(this.state.currentItem);
|
||||
if (!item) return;
|
||||
if (item.name !== text) {
|
||||
this.model.renamePresetItem(this.state.selectedItem, text)
|
||||
this.model.renamePresetItem(this.state.currentItem, text)
|
||||
.catch((error) => {
|
||||
this.onError(error);
|
||||
});
|
||||
@@ -319,11 +339,14 @@ const PresetDialog = withStyles(
|
||||
this.setState({ renameOpen: false });
|
||||
}
|
||||
handleCopy() {
|
||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
||||
let item = this.state.presets.getItem(this.state.currentItem);
|
||||
if (!item) return;
|
||||
this.model.duplicatePreset(this.state.selectedItem)
|
||||
this.model.duplicatePreset(this.state.currentItem)
|
||||
.then((newId) => {
|
||||
this.setState({ selectedItem: newId });
|
||||
this.setState({
|
||||
currentItem: newId,
|
||||
selectedItems: new Set<number>([newId])
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.onError(error);
|
||||
});
|
||||
@@ -333,6 +356,29 @@ const PresetDialog = withStyles(
|
||||
this.model?.showAlert(error);
|
||||
}
|
||||
|
||||
handleImportPresetsFromBank()
|
||||
{
|
||||
this.handleMoreClose();
|
||||
this.setState({ importOpen: true });
|
||||
}
|
||||
handleImportDialogOk(bankInstanceId: number, presets: number[]): void {
|
||||
this.setState({ importOpen: false });
|
||||
this.model.importPresetsFromBank(bankInstanceId, presets)
|
||||
.then((instanceId) => {
|
||||
if (instanceId !== -1) {
|
||||
this.setSelection(instanceId);
|
||||
setTimeout(() => {
|
||||
let el = document.getElementById("psetdlgItem_" + instanceId);
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
},0);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
@@ -344,9 +390,9 @@ const PresetDialog = withStyles(
|
||||
return (
|
||||
<DialogEx tag="preset" fullScreen open={this.props.show}
|
||||
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}
|
||||
style={{userSelect: "none"}}
|
||||
onEnterKey={()=>{}}
|
||||
>
|
||||
style={{ userSelect: "none" }}
|
||||
onEnterKey={() => { }}
|
||||
>
|
||||
<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" }} >
|
||||
@@ -359,7 +405,7 @@ const PresetDialog = withStyles(
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Presets
|
||||
</Typography>
|
||||
<IconButtonEx tooltip="Edit"color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<IconButtonEx tooltip="Edit" color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<EditIcon />
|
||||
</IconButtonEx>
|
||||
</Toolbar>
|
||||
@@ -382,12 +428,13 @@ const PresetDialog = withStyles(
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Presets
|
||||
</Typography>
|
||||
{(this.state.presets.getItem(this.state.selectedItem) != null)
|
||||
{(this.state.presets.getItem(this.state.currentItem) != null)
|
||||
&& (
|
||||
|
||||
<div style={{ flex: "0 0 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center"
|
||||
|
||||
}}>
|
||||
<div style={{
|
||||
flex: "0 0 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center"
|
||||
|
||||
}}>
|
||||
<Button color="inherit" onClick={(e) => this.handleCopy()}>
|
||||
Copy
|
||||
</Button>
|
||||
@@ -419,6 +466,26 @@ const PresetDialog = withStyles(
|
||||
onClose={() => this.handleMoreClose()}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Copy to bank...
|
||||
</ListItemText>
|
||||
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => { this.handleImportPresetsFromBank(); }} >
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Import presets from bank...
|
||||
</ListItemText>
|
||||
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||
<ListItemIcon>
|
||||
<DownloadIcon className={classes.listIcon} />
|
||||
@@ -464,14 +531,21 @@ const PresetDialog = withStyles(
|
||||
</DraggableGrid>
|
||||
</div>
|
||||
</div>
|
||||
<UploadPresetDialog
|
||||
title='Upload preset'
|
||||
extension='.piPreset'
|
||||
uploadPage='uploadPreset'
|
||||
onUploaded={(instanceId) => this.setState({selectedItem: instanceId}) }
|
||||
uploadAfter={this.state.selectedItem}
|
||||
open={this.state.openUploadPresetDialog}
|
||||
onClose={() => { this.setState({ openUploadPresetDialog: false }) }} />
|
||||
<UploadPresetDialog
|
||||
title='Upload preset'
|
||||
extension='.piPreset'
|
||||
uploadPage='uploadPreset'
|
||||
onUploaded={(instanceId) => this.setSelection(instanceId)}
|
||||
uploadAfter={this.state.currentItem }
|
||||
open={this.state.openUploadPresetDialog}
|
||||
onClose={() => { this.setState({ openUploadPresetDialog: false }) }} />
|
||||
{this.state.importOpen && (
|
||||
<ImportPresetFromBankDialog
|
||||
open={this.state.importOpen}
|
||||
onClose={() => this.setState({ importOpen: false })}
|
||||
onOk={(bankInstanceId, presets) => this.handleImportDialogOk(bankInstanceId, presets)}
|
||||
/>
|
||||
)}
|
||||
|
||||
</DialogEx>
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
import WithStyles from './WithStyles';
|
||||
import { withStyles } from "tss-react/mui";
|
||||
import {createStyles} from './WithStyles';
|
||||
import { createStyles } from './WithStyles';
|
||||
|
||||
import PresetDialog from './PresetDialog';
|
||||
import Menu from '@mui/material/Menu';
|
||||
@@ -33,9 +33,12 @@ import MenuItem from '@mui/material/MenuItem';
|
||||
import Fade from '@mui/material/Fade';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import RenameDialog from './RenameDialog'
|
||||
import SavePresetAsDialog from './SavePresetAsDialog';
|
||||
import ImportPresetFromBankDialog from './ImportPresetFromBankDialog';
|
||||
|
||||
import Select from '@mui/material/Select';
|
||||
import UploadPresetDialog from './UploadPresetDialog';
|
||||
import {isDarkMode} from './DarkMode';
|
||||
import { isDarkMode } from './DarkMode';
|
||||
|
||||
interface PresetSelectorProps extends WithStyles<typeof styles> {
|
||||
|
||||
@@ -49,6 +52,9 @@ interface PresetSelectorState {
|
||||
showEditPresetsDialog: boolean;
|
||||
presetsMenuAnchorRef: HTMLElement | null;
|
||||
|
||||
saveAsDialogOpen: boolean;
|
||||
importDialogOpen: boolean;
|
||||
|
||||
renameDialogOpen: boolean;
|
||||
renameDialogTitle: string;
|
||||
renameDialogDefaultName: string;
|
||||
@@ -59,7 +65,7 @@ interface PresetSelectorState {
|
||||
}
|
||||
|
||||
|
||||
const selectColor = isDarkMode()? "#888": "#FFFFFF";
|
||||
const selectColor = isDarkMode() ? "#888" : "#FFFFFF";
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
select: { // fu fu fu.Overrides for white selector on dark background.
|
||||
@@ -96,6 +102,8 @@ const PresetSelector =
|
||||
showEditPresetsDialog: false,
|
||||
presetsMenuAnchorRef: null,
|
||||
renameDialogOpen: false,
|
||||
saveAsDialogOpen: false,
|
||||
importDialogOpen: false,
|
||||
renameDialogTitle: "",
|
||||
renameDialogDefaultName: "",
|
||||
renameDialogActionName: "",
|
||||
@@ -117,6 +125,12 @@ const PresetSelector =
|
||||
this.setState({ presetsMenuAnchorRef: null });
|
||||
}
|
||||
|
||||
handlePresetsMenuImport(e: SyntheticEvent): void {
|
||||
this.handlePresetsMenuClose();
|
||||
e.stopPropagation();
|
||||
this.setState({ importDialogOpen: true });
|
||||
}
|
||||
|
||||
handleDownloadPreset(e: SyntheticEvent) {
|
||||
this.handlePresetsMenuClose();
|
||||
e.preventDefault();
|
||||
@@ -144,21 +158,8 @@ const PresetSelector =
|
||||
let currentPresets = this.model.presets.get();
|
||||
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
|
||||
if (item == null) return;
|
||||
let name = item.name;
|
||||
|
||||
this.renameDialogOpen(name, "Save Preset As", "OK")
|
||||
.then((newName) => {
|
||||
return this.model.saveCurrentPresetAs(newName);
|
||||
})
|
||||
.then((newInstanceId) => {
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
this.showError(error);
|
||||
})
|
||||
;
|
||||
|
||||
|
||||
this.setState({ saveAsDialogOpen: true });
|
||||
}
|
||||
handlePresetsMenuRename(e: SyntheticEvent): void {
|
||||
this.handlePresetsMenuClose();
|
||||
@@ -198,7 +199,7 @@ const PresetSelector =
|
||||
showError(error: string) {
|
||||
this.model.showAlert(error);
|
||||
}
|
||||
renameDialogOpen(defaultText: string, title: string,acceptButtonText: string): Promise<string> {
|
||||
renameDialogOpen(defaultText: string, title: string, acceptButtonText: string): Promise<string> {
|
||||
let result = new Promise<string>(
|
||||
(resolve, reject) => {
|
||||
this.setState(
|
||||
@@ -219,6 +220,30 @@ const PresetSelector =
|
||||
return result;
|
||||
}
|
||||
|
||||
handleSaveAsDialogOk(bankInstanceId: number, name: string): void {
|
||||
this.setState({ saveAsDialogOpen: false });
|
||||
|
||||
this.model.saveCurrentPresetAs(bankInstanceId, name)
|
||||
.then((instanceId) => {
|
||||
this.model.loadPreset(instanceId);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.showError(error);
|
||||
});
|
||||
}
|
||||
handleImportDialogOk(bankInstanceId: number, presets: number[]): void {
|
||||
this.setState({ importDialogOpen: false });
|
||||
this.model.importPresetsFromBank(bankInstanceId, presets)
|
||||
.then((instanceId) => {
|
||||
if (instanceId !== -1) {
|
||||
this.model.loadPreset(instanceId);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
handleRenameDialogClose(): void {
|
||||
this.setState({
|
||||
renameDialogOpen: false,
|
||||
@@ -318,7 +343,7 @@ const PresetSelector =
|
||||
onChange={(e, extra) => this.handleChange(e, extra)}
|
||||
onClose={(e) => this.handleSelectClose(e)}
|
||||
displayEmpty
|
||||
value={presets.selectedInstanceId === 0? '' : presets.selectedInstanceId}
|
||||
value={presets.selectedInstanceId === 0 ? '' : presets.selectedInstanceId}
|
||||
inputProps={{
|
||||
classes: { icon: classes.icon },
|
||||
'aria-label': "Select preset"
|
||||
@@ -339,13 +364,13 @@ const PresetSelector =
|
||||
}
|
||||
</Select>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto"}}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<IconButtonEx
|
||||
tooltip="More..."
|
||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||
onClick={(e) => this.handlePresetMenuClick(e)}
|
||||
size="large"
|
||||
>
|
||||
>
|
||||
<MoreVertIcon style={{ opacity: 0.75 }} color="inherit" />
|
||||
</IconButtonEx>
|
||||
<Menu
|
||||
@@ -358,6 +383,7 @@ const PresetSelector =
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuSave(e)}>Save preset</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save preset as...</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuImport(e)}>Import from bank...</MenuItem>
|
||||
<MenuItem onClick={(e) => this.handlePresetsMenuNew(e)}>New...</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={(e) => { this.handleDownloadPreset(e); }} >Download preset</MenuItem>
|
||||
@@ -366,7 +392,23 @@ const PresetSelector =
|
||||
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()} />
|
||||
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()}
|
||||
/>
|
||||
{this.state.saveAsDialogOpen && (
|
||||
<SavePresetAsDialog open={this.state.saveAsDialogOpen}
|
||||
defaultName={presets.getItem(presets.selectedInstanceId)?.name ?? "My Preset"}
|
||||
onClose={() => { this.setState({ saveAsDialogOpen: false }) }}
|
||||
onOk={(bankInstanceId, name) => {
|
||||
this.handleSaveAsDialogOk(bankInstanceId, name);
|
||||
}} />
|
||||
)}
|
||||
{this.state.importDialogOpen && (
|
||||
<ImportPresetFromBankDialog open={this.state.importDialogOpen}
|
||||
onClose={() => { this.setState({ importDialogOpen: false }) }}
|
||||
onOk={(bankInstanceId, presets) => {
|
||||
this.handleImportDialogOk(bankInstanceId, presets);
|
||||
}} />
|
||||
)}
|
||||
<RenameDialog open={this.state.renameDialogOpen}
|
||||
title={this.state.renameDialogTitle}
|
||||
defaultName={this.state.renameDialogDefaultName}
|
||||
@@ -387,6 +429,6 @@ const PresetSelector =
|
||||
|
||||
}
|
||||
},
|
||||
styles);
|
||||
styles);
|
||||
|
||||
export default PresetSelector;
|
||||
@@ -136,7 +136,16 @@ export default class RenameDialog extends ResizeResponsiveComponent<RenameDialog
|
||||
autoFocus={!isTouchUi()}
|
||||
onKeyDown={handleKeyDown}
|
||||
variant="standard"
|
||||
autoComplete="off"
|
||||
autoCorrect="on"
|
||||
autoCapitalize="on"
|
||||
spellCheck={false}
|
||||
|
||||
slotProps={{
|
||||
inputLabel: {
|
||||
shrink: true
|
||||
},
|
||||
|
||||
input: {
|
||||
style: { scrollMargin: 24 }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright (c) Robin E. R. 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 from 'react';
|
||||
import Select from '@mui/material/Select';
|
||||
import Button from '@mui/material/Button';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import DialogEx from './DialogEx';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import { nullCast } from './Utility';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import {PiPedalModel,PiPedalModelFactory} from './PiPedalModel';
|
||||
//import TextFieldEx from './TextFieldEx';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import { BankIndex } from './Banks';
|
||||
|
||||
|
||||
export interface SavePresetAsDialogProps {
|
||||
open: boolean,
|
||||
defaultName: string,
|
||||
onOk: (bankInstanceId: number, text: string) => void,
|
||||
onClose: () => void
|
||||
};
|
||||
|
||||
export interface SavePresetAsDialogState {
|
||||
selectedBank: number;
|
||||
banks: BankIndex;
|
||||
|
||||
fullScreen: boolean;
|
||||
};
|
||||
|
||||
function isTouchUi()
|
||||
{
|
||||
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
}
|
||||
export default class SavePresetAsDialog extends ResizeResponsiveComponent<SavePresetAsDialogProps, SavePresetAsDialogState> {
|
||||
|
||||
refText: React.RefObject<HTMLInputElement|null>;
|
||||
|
||||
model: PiPedalModel;
|
||||
constructor(props: SavePresetAsDialogProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModel.getInstance();
|
||||
this.state = {
|
||||
banks: this.model.banks.get(),
|
||||
selectedBank: this.model.banks.get().selectedBank,
|
||||
fullScreen: false
|
||||
|
||||
};
|
||||
this.refText = React.createRef<HTMLInputElement>();
|
||||
}
|
||||
mounted: boolean = false;
|
||||
|
||||
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void
|
||||
{
|
||||
this.setState({fullScreen: height < 200})
|
||||
}
|
||||
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
super.componentDidMount();
|
||||
this.mounted = true;
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
super.componentWillUnmount();
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
componentDidUpdate()
|
||||
{
|
||||
}
|
||||
checkForIllegalCharacters(filename: string) {
|
||||
}
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
let { open, defaultName, onClose, onOk } = props;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
let text = nullCast(this.refText.current).value;
|
||||
text = text.trim();
|
||||
try {
|
||||
this.checkForIllegalCharacters(text);
|
||||
} catch (e:any)
|
||||
{
|
||||
let model:PiPedalModel = PiPedalModelFactory.getInstance();
|
||||
model.showAlert(e.toString());
|
||||
return;
|
||||
}
|
||||
if (text.length === 0) return;
|
||||
onOk(this.state.selectedBank,text);
|
||||
}
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||
// 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleOk();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<DialogEx tag="savePresetAs" open={open} fullWidth maxWidth="xs" onClose={handleClose} aria-labelledby="Rename-dialog-title"
|
||||
fullScreen={this.state.fullScreen}
|
||||
style={{userSelect: "none"}}
|
||||
onEnterKey={()=>{}}
|
||||
>
|
||||
<DialogTitle>
|
||||
Save Preset As
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent >
|
||||
<InputLabel style={{ fontSize: "0.75rem", fontWeight: 400, marginTop: 16 }}>Bank</InputLabel>
|
||||
|
||||
<Select variant="standard" fullWidth value={this.state.selectedBank} style={{marginBottom: 16}}
|
||||
onChange={(e) => this.setState({ selectedBank: e.target.value as number })}>
|
||||
{this.state.banks.entries.map((bankEntry) => {
|
||||
return (
|
||||
<MenuItem key={bankEntry.instanceId} value={bankEntry.instanceId}
|
||||
selected={bankEntry.instanceId === this.state.selectedBank}
|
||||
>
|
||||
{bankEntry.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
|
||||
<TextField
|
||||
autoFocus={!isTouchUi()}
|
||||
onKeyDown={handleKeyDown}
|
||||
autoComplete="off"
|
||||
autoCorrect="on"
|
||||
autoCapitalize="on"
|
||||
spellCheck={false}
|
||||
variant="standard"
|
||||
slotProps={{
|
||||
inputLabel: {
|
||||
shrink: true
|
||||
},
|
||||
input: {
|
||||
style: { scrollMargin: 24 }
|
||||
}
|
||||
}}
|
||||
id="name"
|
||||
type="text"
|
||||
label={"Name"}
|
||||
fullWidth
|
||||
defaultValue={defaultName}
|
||||
inputRef={this.refText}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions style={{flexShrink: 1}}>
|
||||
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleOk} variant="dialogPrimary" >
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DialogEx>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user