Copy prests between banks; preset multi-select.
This commit is contained in:
+5
-1
@@ -228,7 +228,11 @@ namespace pipedal
|
||||
{
|
||||
newSelection = presets_[i]->instanceId();
|
||||
}
|
||||
else if (presets_.size() > 1)
|
||||
else if (i == presets_.size() && i != 0) {
|
||||
newSelection = presets_[i-1]->instanceId();
|
||||
|
||||
}
|
||||
else if (presets_.size() >= 1)
|
||||
{
|
||||
newSelection = presets_[0]->instanceId();
|
||||
}
|
||||
|
||||
+10
-5
@@ -1174,12 +1174,12 @@ int64_t PiPedalModel::DeleteBank(int64_t clientId, int64_t instanceId)
|
||||
return newSelection;
|
||||
}
|
||||
|
||||
int64_t PiPedalModel::DeletePreset(int64_t clientId, int64_t instanceId)
|
||||
int64_t PiPedalModel::DeletePresets(int64_t clientId, const std::vector<int64_t> &presetInstanceIds)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||
int64_t oldSelection = storage.GetCurrentPresetId();
|
||||
int64_t newSelection = storage.DeletePreset(instanceId);
|
||||
this->FirePresetsChanged(clientId); // fire now.
|
||||
int64_t newSelection = storage.DeletePresets(presetInstanceIds);
|
||||
this->FirePresetsChanged(clientId); // fire BEFORE we load a new preset.
|
||||
if (oldSelection != newSelection)
|
||||
{
|
||||
this->LoadPreset(
|
||||
@@ -3270,10 +3270,15 @@ int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::v
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
uint64_t lastAdded = storage.ImportPresetsFromBank(bankInstanceId, presets);
|
||||
if (lastAdded != -1) {
|
||||
|
||||
}
|
||||
FirePresetsChanged(-1);
|
||||
return lastAdded;
|
||||
|
||||
}
|
||||
int64_t PiPedalModel::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
uint64_t lastAdded = storage.CopyPresetsToBank(bankInstanceId, presets);
|
||||
return lastAdded;
|
||||
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ namespace pipedal
|
||||
void LoadPreset(int64_t clientId, int64_t instanceId);
|
||||
bool UpdatePresets(int64_t clientId, const PresetIndex &presets);
|
||||
void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
|
||||
int64_t DeletePreset(int64_t clientId, int64_t instanceId);
|
||||
int64_t DeletePresets(int64_t clientId, const std::vector<int64_t> &presetInstanceIds);
|
||||
bool RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name);
|
||||
int64_t CopyPreset(int64_t clientId, int64_t fromIndex, int64_t toIndex);
|
||||
uint64_t CopyPluginPreset(const std::string &pluginUri, uint64_t presetId);
|
||||
@@ -496,6 +496,7 @@ namespace pipedal
|
||||
|
||||
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
|
||||
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||
int64_t CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||
};
|
||||
|
||||
} // namespace pipedal.
|
||||
+22
-5
@@ -46,6 +46,17 @@
|
||||
using namespace std;
|
||||
using namespace pipedal;
|
||||
|
||||
class CopyPresetsToBankBody {
|
||||
public:
|
||||
int64_t bankInstanceId_;
|
||||
std::vector<int64_t> presets_;
|
||||
DECLARE_JSON_MAP(CopyPresetsToBankBody);
|
||||
};
|
||||
JSON_MAP_BEGIN(CopyPresetsToBankBody)
|
||||
JSON_MAP_REFERENCE(CopyPresetsToBankBody, bankInstanceId)
|
||||
JSON_MAP_REFERENCE(CopyPresetsToBankBody, presets)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
class ImportPresetsFromBankBody {
|
||||
public:
|
||||
@@ -1470,12 +1481,12 @@ public:
|
||||
model.RequestShutdown(true);
|
||||
this->Reply(replyTo, "restart");
|
||||
}
|
||||
else if (message == "deletePresetItem")
|
||||
else if (message == "deletePresetItems")
|
||||
{
|
||||
int64_t instanceId = 0;
|
||||
pReader->read(&instanceId);
|
||||
int64_t result = model.DeletePreset(this->clientId, instanceId);
|
||||
this->Reply(replyTo, "deletePresetItem", result);
|
||||
std::vector<int64_t> items;
|
||||
pReader->read(&items);
|
||||
int64_t result = model.DeletePresets(this->clientId, items);
|
||||
this->Reply(replyTo, "deletePresetItems", result);
|
||||
}
|
||||
else if (message == "deleteBankItem")
|
||||
{
|
||||
@@ -1831,6 +1842,12 @@ public:
|
||||
auto result = this->model.ImportPresetsFromBank(args.bankInstanceId_, args.presets_);
|
||||
this->Reply(replyTo,"importPresetsFromBank",result);
|
||||
}
|
||||
else if (message == "copyPresetsToBank") {
|
||||
CopyPresetsToBankBody args;
|
||||
pReader->read(&args);
|
||||
auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_);
|
||||
this->Reply(replyTo,"copyPresetsToBank",result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error("Unknown message received: %s", message.c_str());
|
||||
|
||||
+34
-2
@@ -764,6 +764,35 @@ int64_t Storage::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector
|
||||
SaveCurrentBank();
|
||||
return lastPresetId;
|
||||
}
|
||||
int64_t Storage::CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
|
||||
{
|
||||
if (bankIndex.selectedBank() == bankInstanceId) {
|
||||
throw std::runtime_error("Can't copy to self.");
|
||||
}
|
||||
|
||||
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
|
||||
BankFile bankFile;
|
||||
LoadBankFile(indexEntry.name(),&bankFile);
|
||||
|
||||
std::set<int64_t> presetsSet { presets.begin(), presets.end()};
|
||||
|
||||
std::set<std::string> existingNames;
|
||||
|
||||
for (auto&preset: bankFile.presets()) {
|
||||
existingNames.insert(preset->preset().name());
|
||||
}
|
||||
for (auto &presetEntry: this->currentBank.presets()) {
|
||||
if (presetsSet.contains(presetEntry->instanceId())) {
|
||||
std::string uniqueName = makeUniqueName(presetEntry->preset().name(),existingNames);
|
||||
existingNames.insert(uniqueName);
|
||||
Pedalboard t = presetEntry->preset();
|
||||
t.name(uniqueName);
|
||||
bankFile.addPreset(t);
|
||||
}
|
||||
}
|
||||
SaveBankFile(indexEntry.name(),bankFile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<PresetIndexEntry> Storage::RequestBankPresets(int64_t bankInstanceId)
|
||||
{
|
||||
@@ -852,9 +881,12 @@ Pedalboard Storage::GetPreset(int64_t instanceId) const
|
||||
throw PiPedalException("Not found.");
|
||||
}
|
||||
|
||||
int64_t Storage::DeletePreset(int64_t presetId)
|
||||
int64_t Storage::DeletePresets(const std::vector<int64_t> &presetInstanceIds)
|
||||
{
|
||||
int64_t newSelection = currentBank.deletePreset(presetId);
|
||||
int64_t newSelection = currentBank.selectedPreset();
|
||||
for (auto presetId: presetInstanceIds) {
|
||||
newSelection = currentBank.deletePreset(presetId);
|
||||
}
|
||||
SaveCurrentBank();
|
||||
return newSelection;
|
||||
}
|
||||
|
||||
+2
-1
@@ -162,7 +162,7 @@ public:
|
||||
|
||||
|
||||
bool LoadPreset(int64_t presetId);
|
||||
int64_t DeletePreset(int64_t presetId);
|
||||
int64_t DeletePresets(const std::vector<int64_t>& presetInstanceIds);
|
||||
bool RenamePreset(int64_t presetId, const std::string&name);
|
||||
int64_t CopyPreset(int64_t fromId, int64_t toId = -1);
|
||||
int64_t CreateNewPreset();
|
||||
@@ -271,6 +271,7 @@ public:
|
||||
|
||||
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
|
||||
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||
int64_t CopyPresetsToBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||
|
||||
};
|
||||
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sudo /bin/bash
|
||||
rm -rf /etc/pipedal/react
|
||||
mkdir -p /etc/pipedal/react
|
||||
cp -r dist/* /etc/pipedal/react/
|
||||
@@ -39,6 +39,12 @@ declare module '@mui/material/styles' {
|
||||
mainBackground?: React.CSSProperties['color'];
|
||||
toolbarColor?: React.CSSProperties['color'];
|
||||
}
|
||||
interface Palette {
|
||||
actionBar: Palette['primary'];
|
||||
}
|
||||
interface PaletteOptions {
|
||||
actionBar: PaletteOptions['primary'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -143,7 +149,12 @@ const theme = createTheme(
|
||||
},
|
||||
secondary: {
|
||||
main: "#FF6060"
|
||||
},
|
||||
actionBar: {
|
||||
main: '#130b22ff',
|
||||
contrastText: '#FFFFFF'
|
||||
}
|
||||
|
||||
},
|
||||
mainBackground: "#222",
|
||||
toolbarColor: '#222'
|
||||
@@ -209,8 +220,14 @@ const theme = createTheme(
|
||||
},
|
||||
secondary: {
|
||||
main: "#FF6060"
|
||||
},
|
||||
actionBar: {
|
||||
main: '#130b22ff',
|
||||
contrastText: '#FFFFFF'
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
mainBackground: "#FFFFFF",
|
||||
toolbarColor: '#FFFFFF'
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
// 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 Toolbar from "@mui/material/Toolbar";
|
||||
import IconButtonEx from "./IconButtonEx";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
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 } from './PiPedalModel';
|
||||
import { BankIndexEntry } from './Banks';
|
||||
|
||||
|
||||
export interface CopyPresetsToBankDialogProps {
|
||||
open: boolean,
|
||||
onOk: (bankInstanceId: number) => void,
|
||||
onClose: () => void
|
||||
};
|
||||
|
||||
export interface CopyPresetsToBankDialogState {
|
||||
selectedBank: number;
|
||||
banks: BankIndexEntry[];
|
||||
fullScreen: boolean;
|
||||
};
|
||||
|
||||
function setSavedDefaultBankSelection(bankInstanceId: number) {
|
||||
localStorage.setItem('pipedal_copyTo_defaultBankSelection', bankInstanceId.toString());
|
||||
}
|
||||
function getSavedDefaultBankSelection() {
|
||||
let defaultValue = localStorage.getItem('pipedal_copyTo_defaultBankSelection');
|
||||
if (defaultValue) {
|
||||
return parseInt(defaultValue);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
export default class CopyPresetsToBankDialog extends ResizeResponsiveComponent<CopyPresetsToBankDialogProps, CopyPresetsToBankDialogState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
constructor(props: CopyPresetsToBankDialogProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModel.getInstance();
|
||||
let banks = this.getBanks();
|
||||
let selectedBank = this.getDefaultBankSelection(banks);
|
||||
this.state = {
|
||||
banks: banks,
|
||||
selectedBank: selectedBank,
|
||||
fullScreen: false,
|
||||
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
}
|
||||
|
||||
handleBankChanged(bankId: number) {
|
||||
setSavedDefaultBankSelection(bankId);
|
||||
this.setState({ selectedBank: bankId });
|
||||
}
|
||||
|
||||
handleOk() {
|
||||
this.props.onOk(this.state.selectedBank);
|
||||
}
|
||||
|
||||
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" : undefined }}>
|
||||
{!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 }}>
|
||||
Copy Presets to 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>
|
||||
<DialogActions style={{ flex: "0 0 auto" }}>
|
||||
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => { this.handleOk(); }} variant="dialogPrimary"
|
||||
disabled={false}
|
||||
>
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</div>
|
||||
</DialogEx>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export interface DraggableGridProps
|
||||
|
||||
defaultSelectedIndex?: number;
|
||||
|
||||
onLongPress?: (itemIndex: number) => void;
|
||||
onLongPress?: (event: PointerEvent<HTMLDivElement>, itemIndex: number) => void;
|
||||
|
||||
canDrag?: boolean;
|
||||
|
||||
@@ -209,7 +209,7 @@ const DraggableGrid =
|
||||
}
|
||||
|
||||
bringSelectedItemIntoView() {
|
||||
if (this.state.indexToSelect && this.state.indexToSelect >= 0)
|
||||
if (this.state.indexToSelect !== undefined && this.state.indexToSelect >= 0)
|
||||
{
|
||||
let grid = this.refGrid.current;
|
||||
if (grid)
|
||||
@@ -295,6 +295,7 @@ const DraggableGrid =
|
||||
startClientX: number = 0;
|
||||
startClientY: number = 0;
|
||||
dragStarted: boolean = false;
|
||||
dragThresholdWasExceeded: boolean = false;
|
||||
savedIndex: string = "";
|
||||
savedBackground: string = "";
|
||||
lastPointerDown: number = 0;
|
||||
@@ -529,6 +530,11 @@ const DraggableGrid =
|
||||
if (this.startIndex !== this.currentIndex) {
|
||||
this.props.moveElement(this.startIndex, this.currentIndex);
|
||||
}
|
||||
if (!this.dragThresholdWasExceeded) {
|
||||
if (this.props.onLongPress) {
|
||||
this.props.onLongPress(e,this.startIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cancelDrag();
|
||||
}
|
||||
@@ -693,6 +699,7 @@ const DraggableGrid =
|
||||
///window.addEventListener("touchstart",this.handleTouchStart, {passive: false});
|
||||
|
||||
this.dragStarted = false;
|
||||
this.dragThresholdWasExceeded = false;
|
||||
this.startX = e.clientX;
|
||||
this.startY = e.clientY;
|
||||
this.startClientX = e.clientX;
|
||||
@@ -756,8 +763,11 @@ const DraggableGrid =
|
||||
|
||||
handlePointerMove(e: PointerEvent<HTMLDivElement>): boolean {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
if (!this.dragStarted && this.dragThresholdExceeded(e)) {
|
||||
this.startDrag();
|
||||
if (this.dragThresholdExceeded(e)) {
|
||||
if (!this.dragStarted) {
|
||||
this.startDrag();
|
||||
}
|
||||
this.dragThresholdWasExceeded = true;
|
||||
}
|
||||
if (this.dragStarted) {
|
||||
this.lastX = e.clientX;
|
||||
|
||||
@@ -158,44 +158,78 @@ export default class ImportPresetFromBankDialog extends ResizeResponsiveComponen
|
||||
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>
|
||||
{!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>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "top", flex: "0 0 auto", paddingTop: 8, paddingBottom: 8, paddingLeft: 16, paddingRight: 16 }}>
|
||||
<IconButtonEx
|
||||
tooltip="Close"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="cancel"
|
||||
style={{ opacity: 0.6 }}
|
||||
onClick={() => { onClose(); }}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButtonEx>
|
||||
|
||||
<div style={{ marginLeft: 24, display: "flex", flex: "0 0 auto", flexFlow: "column nowrap", justifyContent: "start" }}>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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) => {
|
||||
@@ -242,7 +276,7 @@ export default class ImportPresetFromBankDialog extends ResizeResponsiveComponen
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</div>
|
||||
</DialogEx>
|
||||
</DialogEx >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import SnapshotPanel from './SnapshotPanel';
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||
@@ -250,7 +249,6 @@ export const PerformanceView =
|
||||
|
||||
render() {
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
let wrapSelects = this.state.wrapSelects;
|
||||
let presets = this.state.presets;
|
||||
let banks = this.state.banks;
|
||||
let appBarIconSize: "large" | undefined = this.state.largeAppBar ? undefined : "large";
|
||||
|
||||
@@ -2192,8 +2192,8 @@ export class PiPedalModel //implements PiPedalModel
|
||||
}
|
||||
|
||||
|
||||
deletePresetItem(instanceId: number): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("deletePresetItem", instanceId);
|
||||
deletePresetItems(instanceIds: Set<number>): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("deletePresetItems", Array.from(instanceIds));
|
||||
|
||||
}
|
||||
deleteBankItem(instanceId: number): Promise<number> {
|
||||
@@ -3549,6 +3549,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
importPresetsFromBank(bankInstanceId: number, presets: number[]): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("importPresetsFromBank", {bankInstanceId: bankInstanceId, presets: presets});
|
||||
}
|
||||
copyPresetsToBank(bankInstanceId: number, presets: number[]): Promise<number> {
|
||||
return nullCast(this.webSocket).request<number>("copyPresetsToBank", {bankInstanceId: bankInstanceId, presets: presets});
|
||||
}
|
||||
};
|
||||
|
||||
let instance: PiPedalModel | undefined = undefined;
|
||||
|
||||
+242
-137
@@ -17,8 +17,9 @@
|
||||
// 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 } from 'react';
|
||||
import ImportPresetFromBankDialog from './ImportPresetFromBankDialog';
|
||||
import CopyPresetsToBankDialog from './CopyPresetsToBankDialog';
|
||||
import Icon from '@mui/material/Icon';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import { css } from '@emotion/react';
|
||||
@@ -27,23 +28,22 @@ import IconButtonEx from './IconButtonEx';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } 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 CloseIcon from '@mui/icons-material/Close';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
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 UploadPresetDialog from './UploadPresetDialog';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
|
||||
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';
|
||||
@@ -56,10 +56,13 @@ import { withStyles } from "tss-react/mui";
|
||||
import DownloadIcon from './svg/file_download_black_24dp.svg?react';
|
||||
import UploadIcon from './svg/file_upload_black_24dp.svg?react';
|
||||
|
||||
// function isTouchUi() {
|
||||
// return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
// }
|
||||
|
||||
|
||||
interface PresetDialogProps extends WithStyles<typeof styles> {
|
||||
show: boolean;
|
||||
isEditDialog: boolean;
|
||||
onDialogClose: () => void;
|
||||
|
||||
};
|
||||
@@ -67,13 +70,16 @@ interface PresetDialogProps extends WithStyles<typeof styles> {
|
||||
interface PresetDialogState {
|
||||
presets: PresetIndex;
|
||||
|
||||
showActionBar: boolean;
|
||||
multiSelect: boolean;
|
||||
showTouchActionBar: boolean;
|
||||
|
||||
|
||||
currentItem: number;
|
||||
selectedItems: Set<number>;
|
||||
|
||||
renameOpen: boolean;
|
||||
importOpen: boolean;
|
||||
copyToOpen: boolean;
|
||||
|
||||
moreMenuAnchorEl: HTMLElement | null;
|
||||
openUploadPresetDialog: boolean;
|
||||
@@ -93,7 +99,8 @@ const styles = (theme: Theme) => createStyles({
|
||||
dialogActionBar: css({
|
||||
position: 'relative',
|
||||
top: 0, left: 0,
|
||||
background: "black"
|
||||
background: theme.palette.actionBar.main,
|
||||
color: theme.palette.actionBar.contrastText
|
||||
}),
|
||||
dialogTitle: css({
|
||||
marginLeft: theme.spacing(2),
|
||||
@@ -111,7 +118,7 @@ const styles = (theme: Theme) => createStyles({
|
||||
alignItems: "center",
|
||||
textAlign: "left",
|
||||
justifyContent: "center",
|
||||
paddingLeft: 8
|
||||
paddibomngLeft: 8
|
||||
}),
|
||||
iconFrame: css({
|
||||
flex: "0 0 auto",
|
||||
@@ -136,7 +143,7 @@ const Transition = React.forwardRef(function Transition(
|
||||
|
||||
|
||||
const PresetDialog = withStyles(
|
||||
class extends Component<PresetDialogProps, PresetDialogState> {
|
||||
class extends ResizeResponsiveComponent<PresetDialogProps, PresetDialogState> {
|
||||
|
||||
model: PiPedalModel;
|
||||
|
||||
@@ -150,32 +157,49 @@ const PresetDialog = withStyles(
|
||||
let presets = this.model.presets.get();
|
||||
this.state = {
|
||||
presets: presets,
|
||||
showActionBar: false,
|
||||
multiSelect: false,
|
||||
showTouchActionBar: false,
|
||||
currentItem: presets.selectedInstanceId,
|
||||
selectedItems: new Set<number>(),
|
||||
selectedItems: new Set<number>([presets.selectedInstanceId]),
|
||||
renameOpen: false,
|
||||
importOpen: false,
|
||||
copyToOpen: false,
|
||||
moreMenuAnchorEl: null,
|
||||
openUploadPresetDialog: false
|
||||
|
||||
};
|
||||
this.handlePresetsChanged = this.handlePresetsChanged.bind(this);
|
||||
|
||||
|
||||
}
|
||||
|
||||
onWindowSizeChanged(width: number, height: number) {
|
||||
super.onWindowSizeChanged(width, height);
|
||||
}
|
||||
handleSelectionUpdated(instanceIds: Set<number>) {
|
||||
this.setState({
|
||||
multiSelect: instanceIds.size > 1
|
||||
})
|
||||
|
||||
}
|
||||
setSelections(instanceIds: Set<number>) {
|
||||
this.setState({
|
||||
selectedItems: instanceIds,
|
||||
});
|
||||
this.handleSelectionUpdated(instanceIds);
|
||||
}
|
||||
setSelection(instanceId: number) {
|
||||
this.setState({
|
||||
let selectedItems = new Set<number>([instanceId]);
|
||||
this.setState({
|
||||
currentItem: instanceId,
|
||||
selectedItems: new Set<number>([instanceId])
|
||||
});
|
||||
selectedItems: selectedItems
|
||||
});
|
||||
this.handleSelectionUpdated(selectedItems);
|
||||
}
|
||||
|
||||
selectItemAtIndex(index: number) {
|
||||
let instanceId = this.state.presets.presets[index].instanceId;
|
||||
this.setSelection(instanceId);
|
||||
}
|
||||
isEditMode() {
|
||||
return this.state.showActionBar || this.props.isEditDialog;
|
||||
}
|
||||
|
||||
onMoreClick(e: SyntheticEvent): void {
|
||||
this.setState({ moreMenuAnchorEl: e.currentTarget as HTMLElement })
|
||||
@@ -203,9 +227,10 @@ const PresetDialog = withStyles(
|
||||
{
|
||||
// if we don't have a valid selection, then use the current preset.
|
||||
if (this.state.presets.getItem(this.state.currentItem) == null) {
|
||||
this.setState({ presets: presets, currentItem: presets.selectedInstanceId,
|
||||
this.setState({
|
||||
presets: presets, currentItem: presets.selectedInstanceId,
|
||||
selectedItems: new Set<number>([presets.selectedInstanceId])
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({ presets: presets });
|
||||
}
|
||||
@@ -222,8 +247,10 @@ const PresetDialog = withStyles(
|
||||
this.model.presets.removeOnChangedHandler(this.handlePresetsChanged);
|
||||
}
|
||||
|
||||
getSelectedIndex() {
|
||||
let instanceId = this.isEditMode() ? this.state.currentItem : this.state.presets.selectedInstanceId;
|
||||
getSelectedIndex(instanceId?: number): number {
|
||||
if (instanceId === undefined) {
|
||||
instanceId = this.state.currentItem;
|
||||
}
|
||||
let presets = this.state.presets;
|
||||
for (let i = 0; i < presets.presets.length; ++i) {
|
||||
if (presets.presets[i].instanceId === instanceId) return i;
|
||||
@@ -232,33 +259,59 @@ const PresetDialog = withStyles(
|
||||
}
|
||||
|
||||
handleDeleteClick() {
|
||||
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);
|
||||
});
|
||||
|
||||
if (this.state.selectedItems.size === 0) {
|
||||
return;
|
||||
}
|
||||
this.model.deletePresetItems(this.state.selectedItems)
|
||||
.then((currentItem: number) => {
|
||||
this.setSelection(currentItem);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
});
|
||||
}
|
||||
handleDialogClose() {
|
||||
this.props.onDialogClose();
|
||||
}
|
||||
|
||||
handleItemClick(instanceId: number): void {
|
||||
if (this.isEditMode()) {
|
||||
this.setSelection(instanceId);
|
||||
handleItemClick(e: React.MouseEvent<HTMLElement>, instanceId: number): void {
|
||||
e.stopPropagation();
|
||||
if (e.ctrlKey || this.state.showTouchActionBar) {
|
||||
let selectedItems = new Set<number>(this.state.selectedItems);
|
||||
if (selectedItems.has(instanceId)) {
|
||||
selectedItems.delete(instanceId);
|
||||
this.setSelections(selectedItems);
|
||||
} else {
|
||||
selectedItems.add(instanceId);
|
||||
this.setState({
|
||||
selectedItems: selectedItems,
|
||||
currentItem: instanceId
|
||||
});
|
||||
this.handleSelectionUpdated(selectedItems);
|
||||
}
|
||||
} else if (e.shiftKey) {
|
||||
let presets = this.state.presets;
|
||||
let startIndex = this.getSelectedIndex();
|
||||
let endIndex = this.getSelectedIndex(instanceId);
|
||||
if (startIndex === -1 || endIndex === -1) return; // should not happen.
|
||||
|
||||
let selectedItems = new Set<number>();
|
||||
|
||||
if (endIndex < startIndex) {
|
||||
let t = startIndex;
|
||||
startIndex = endIndex;
|
||||
endIndex = t;
|
||||
}
|
||||
for (let i = startIndex; i <= endIndex; ++i) {
|
||||
selectedItems.add(presets.presets[i].instanceId);
|
||||
}
|
||||
this.setSelections(selectedItems);
|
||||
} else {
|
||||
this.model.loadPreset(instanceId);
|
||||
this.props.onDialogClose();
|
||||
this.setSelection(instanceId);
|
||||
}
|
||||
}
|
||||
showActionBar(show: boolean): void {
|
||||
this.setState({ showActionBar: show });
|
||||
showTouchActionBar(show: boolean): void {
|
||||
this.setState({ showTouchActionBar: show });
|
||||
|
||||
}
|
||||
|
||||
@@ -266,29 +319,23 @@ const PresetDialog = withStyles(
|
||||
mapElement(el: any): React.ReactNode {
|
||||
let presetEntry = el as PresetIndexEntry;
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
let selected =
|
||||
this.isEditMode() ? this.state.selectedItems.has(presetEntry.instanceId) :
|
||||
(presetEntry.instanceId === this.state.presets.selectedInstanceId);
|
||||
let selected = this.state.selectedItems.has(presetEntry.instanceId);
|
||||
return (
|
||||
<div key={presetEntry.instanceId} id={"psetdlgItem_" + presetEntry.instanceId} className="itemBackground">
|
||||
|
||||
<ButtonBase style={{ width: "100%", height: 48 }}
|
||||
onClick={() => this.handleItemClick(presetEntry.instanceId)}
|
||||
<ListItemButton
|
||||
selected={selected}
|
||||
onClick={(e) => this.handleItemClick(e, presetEntry.instanceId)}
|
||||
style={{ height: 48 }}
|
||||
>
|
||||
<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="" />
|
||||
</div>
|
||||
<div className={classes.itemLabel}>
|
||||
<Typography>
|
||||
{presetEntry.name}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
<ListItemIcon>
|
||||
<img
|
||||
src={isDarkMode() ? "img/ic_presets_white.svg" : "img/ic_presets.svg"}
|
||||
className={classes.itemIcon} alt="" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={presetEntry.name}
|
||||
/>
|
||||
|
||||
</ListItemButton>
|
||||
</div>
|
||||
|
||||
);
|
||||
@@ -343,10 +390,10 @@ const PresetDialog = withStyles(
|
||||
if (!item) return;
|
||||
this.model.duplicatePreset(this.state.currentItem)
|
||||
.then((newId) => {
|
||||
this.setState({
|
||||
this.setState({
|
||||
currentItem: newId,
|
||||
selectedItems: new Set<number>([newId])
|
||||
});
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.onError(error);
|
||||
});
|
||||
@@ -356,8 +403,15 @@ const PresetDialog = withStyles(
|
||||
this.model?.showAlert(error);
|
||||
}
|
||||
|
||||
handleImportPresetsFromBank()
|
||||
{
|
||||
handleCloseActionBar(e: SyntheticEvent) {
|
||||
e.stopPropagation();
|
||||
this.setState({
|
||||
showTouchActionBar: false,
|
||||
selectedItems: new Set<number>([this.state.currentItem]),
|
||||
multiSelect: false
|
||||
});
|
||||
}
|
||||
handleImportPresetsFromBank() {
|
||||
this.handleMoreClose();
|
||||
this.setState({ importOpen: true });
|
||||
}
|
||||
@@ -372,21 +426,40 @@ const PresetDialog = withStyles(
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
},0);
|
||||
}, 0);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
});
|
||||
}
|
||||
handleCopyToBankDialogOk(bankInstanceId: number): void {
|
||||
this.setState({ copyToOpen: false });
|
||||
let selectedItems: number[] = [];
|
||||
for (let i = 0; i < this.state.presets.presets.length; ++i) {
|
||||
let preset = this.state.presets.presets[i];
|
||||
if (this.state.selectedItems.has(preset.instanceId)) {
|
||||
selectedItems.push(preset.instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
this.model.copyPresetsToBank(bankInstanceId, selectedItems)
|
||||
.then((instanceId) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
});
|
||||
}
|
||||
|
||||
handleCopyPresetsToBank() {
|
||||
this.handleMoreClose();
|
||||
this.setState({ copyToOpen: true });
|
||||
}
|
||||
|
||||
render() {
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar;
|
||||
let defaultSelectedIndex = this.getSelectedIndex();
|
||||
|
||||
const showActionBar = this.state.multiSelect || this.state.showTouchActionBar;
|
||||
return (
|
||||
<DialogEx tag="preset" fullScreen open={this.props.show}
|
||||
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}
|
||||
@@ -395,37 +468,37 @@ const PresetDialog = withStyles(
|
||||
>
|
||||
<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" }} >
|
||||
<AppBar className={classes.dialogActionBar} style={{ display: showActionBar ? "block" : "none" }}
|
||||
>
|
||||
<Toolbar>
|
||||
<IconButtonEx tooltip="Back" edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
disabled={this.isEditMode()}
|
||||
<IconButtonEx tooltip="Back" edge="start" color="inherit" aria-label="back"
|
||||
onClick={(e) => { this.handleCloseActionBar(e); }}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
<CloseIcon />
|
||||
</IconButtonEx>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Presets
|
||||
<Typography variant="h6" className={classes.dialogTitle} noWrap>
|
||||
{this.state.selectedItems.size.toString() + " items selected"}
|
||||
</Typography>
|
||||
<IconButtonEx tooltip="Edit" color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<EditIcon />
|
||||
<Button color="inherit"
|
||||
onClick={() => {
|
||||
this.handleCopyPresetsToBank();
|
||||
}} >
|
||||
Copy to
|
||||
</Button>
|
||||
<IconButtonEx tooltip="Delete" 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 }} />
|
||||
</IconButtonEx>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AppBar className={actionBarClass} style={{ display: this.isEditMode() ? "block" : "none" }}
|
||||
<AppBar className={classes.dialogAppBar} style={{ display: showActionBar ? "none" : "block" }}
|
||||
onClick={(e) => { e.stopPropagation(); e.preventDefault(); }}
|
||||
>
|
||||
<Toolbar>
|
||||
{(!this.props.isEditDialog) ? (
|
||||
<IconButtonEx tooltip="Close" edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<CloseIcon />
|
||||
</IconButtonEx>
|
||||
) : (
|
||||
<IconButtonEx edge="start" tooltip="Back" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButtonEx>
|
||||
|
||||
)}
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
<IconButtonEx edge="start" tooltip="Back" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButtonEx>
|
||||
<Typography variant="h6" className={classes.dialogTitle} noWrap>
|
||||
Presets
|
||||
</Typography>
|
||||
{(this.state.presets.getItem(this.state.currentItem) != null)
|
||||
@@ -435,26 +508,39 @@ const PresetDialog = withStyles(
|
||||
flex: "0 0 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center"
|
||||
|
||||
}}>
|
||||
<Button color="inherit" onClick={(e) => this.handleCopy()}>
|
||||
Copy
|
||||
</Button>
|
||||
<Button color="inherit" onClick={() => this.handleRenameClick()}>
|
||||
Rename
|
||||
</Button>
|
||||
<RenameDialog
|
||||
title="Rename"
|
||||
open={this.state.renameOpen}
|
||||
defaultName={this.getSelectedName()}
|
||||
acceptActionName={"Rename"}
|
||||
onClose={() => { this.setState({ renameOpen: false }) }}
|
||||
onOk={(text: string) => {
|
||||
this.handleRenameOk(text);
|
||||
}
|
||||
}
|
||||
/>
|
||||
<IconButtonEx tooltip="Delete" 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 }} />
|
||||
</IconButtonEx>
|
||||
{this.state.selectedItems.size === 1 && (
|
||||
<Button color="inherit" onClick={(e) => this.handleCopy()}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
)}
|
||||
{this.state.selectedItems.size === 1 && (
|
||||
|
||||
<Button color="inherit" onClick={() => this.handleRenameClick()}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
)}
|
||||
{this.state.renameOpen && (
|
||||
<RenameDialog
|
||||
title="Rename"
|
||||
open={this.state.renameOpen}
|
||||
defaultName={this.getSelectedName()}
|
||||
acceptActionName={"Rename"}
|
||||
onClose={() => { this.setState({ renameOpen: false }) }}
|
||||
onOk={(text: string) => {
|
||||
this.handleRenameOk(text);
|
||||
}
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{this.state.selectedItems.size === 1 && (
|
||||
<IconButtonEx tooltip="Delete" color="inherit" onClick={(e) => this.handleDeleteClick()}
|
||||
style={{ opacity: this.state.selectedItems.size !== 1 ? 0.5 : 1.0 }}
|
||||
>
|
||||
<img src="/img/old_delete_outline_white_24dp.svg" alt="Delete" style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||
</IconButtonEx>
|
||||
)}
|
||||
<IconButtonEx tooltip="More..." color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
|
||||
<MoreVertIcon />
|
||||
</IconButtonEx>
|
||||
@@ -466,15 +552,21 @@ const PresetDialog = withStyles(
|
||||
onClose={() => this.handleMoreClose()}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Copy to bank...
|
||||
</ListItemText>
|
||||
{this.state.selectedItems.size !== 0 && (
|
||||
<MenuItem onClick={() => {
|
||||
this.handleMoreClose();
|
||||
this.handleCopyPresetsToBank();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Copy to bank...
|
||||
</ListItemText>
|
||||
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={() => { this.handleImportPresetsFromBank(); }} >
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
@@ -486,15 +578,17 @@ const PresetDialog = withStyles(
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||
<ListItemIcon>
|
||||
<DownloadIcon className={classes.listIcon} />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Download preset
|
||||
</ListItemText>
|
||||
{this.state.selectedItems.size !== 0 && (
|
||||
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||
<ListItemIcon>
|
||||
<DownloadIcon className={classes.listIcon} />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
Download preset
|
||||
</ListItemText>
|
||||
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={() => { this.handleUploadPreset() }}>
|
||||
<ListItemIcon>
|
||||
<UploadIcon className={classes.listIcon} />
|
||||
@@ -516,12 +610,16 @@ const PresetDialog = withStyles(
|
||||
</div>
|
||||
<div style={{ flex: "1 1 auto", position: "relative", overflow: "hidden" }} >
|
||||
<DraggableGrid
|
||||
onLongPress={(item) => this.showActionBar(true)}
|
||||
canDrag={this.isEditMode()}
|
||||
onLongPress={(e,item) => {
|
||||
if (e.pointerType === "touch")
|
||||
{
|
||||
this.showTouchActionBar(true);
|
||||
}
|
||||
}}
|
||||
canDrag={!this.state.multiSelect && !this.state.showTouchActionBar}
|
||||
onDragStart={(index, x, y) => { this.selectItemAtIndex(index) }}
|
||||
moveElement={(from, to) => { this.moveElement(from, to); }}
|
||||
scroll={ScrollDirection.Y}
|
||||
defaultSelectedIndex={defaultSelectedIndex}
|
||||
>
|
||||
{
|
||||
this.state.presets.presets.map((element) => {
|
||||
@@ -536,16 +634,23 @@ const PresetDialog = withStyles(
|
||||
extension='.piPreset'
|
||||
uploadPage='uploadPreset'
|
||||
onUploaded={(instanceId) => this.setSelection(instanceId)}
|
||||
uploadAfter={this.state.currentItem }
|
||||
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)}
|
||||
/>
|
||||
)}
|
||||
{this.state.importOpen && (
|
||||
<ImportPresetFromBankDialog
|
||||
open={this.state.importOpen}
|
||||
onClose={() => this.setState({ importOpen: false })}
|
||||
onOk={(bankInstanceId, presets) => this.handleImportDialogOk(bankInstanceId, presets)}
|
||||
/>
|
||||
)}
|
||||
{this.state.copyToOpen && (
|
||||
<CopyPresetsToBankDialog
|
||||
open={this.state.copyToOpen}
|
||||
onClose={() => this.setState({ copyToOpen: false })}
|
||||
onOk={(bankInstanceId) => this.handleCopyToBankDialogOk(bankInstanceId)}
|
||||
/>
|
||||
)}
|
||||
|
||||
</DialogEx>
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
// 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 { SyntheticEvent, Component } from 'react';
|
||||
import { SyntheticEvent } from 'react';
|
||||
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel';
|
||||
import SaveIconOutline from '@mui/icons-material/Save';
|
||||
@@ -39,6 +40,7 @@ import ImportPresetFromBankDialog from './ImportPresetFromBankDialog';
|
||||
import Select from '@mui/material/Select';
|
||||
import UploadPresetDialog from './UploadPresetDialog';
|
||||
import { isDarkMode } from './DarkMode';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
|
||||
interface PresetSelectorProps extends WithStyles<typeof styles> {
|
||||
|
||||
@@ -51,6 +53,8 @@ interface PresetSelectorState {
|
||||
showPresetsDialog: boolean;
|
||||
showEditPresetsDialog: boolean;
|
||||
presetsMenuAnchorRef: HTMLElement | null;
|
||||
presetsSubmenuAnchorRef: HTMLElement | null;
|
||||
compactHorizontalLayoutMenu: boolean;
|
||||
|
||||
saveAsDialogOpen: boolean;
|
||||
importDialogOpen: boolean;
|
||||
@@ -87,8 +91,8 @@ const styles = (theme: Theme) => createStyles({
|
||||
|
||||
const PresetSelector =
|
||||
withStyles(
|
||||
class extends Component<PresetSelectorProps, PresetSelectorState> {
|
||||
|
||||
class extends ResizeResponsiveComponent<PresetSelectorProps, PresetSelectorState> {
|
||||
private MENU_THRESHOLD: number = 570;
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: PresetSelectorProps) {
|
||||
@@ -98,9 +102,11 @@ const PresetSelector =
|
||||
presets:
|
||||
this.model.presets.get(),
|
||||
enabled: false,
|
||||
compactHorizontalLayoutMenu: this.windowSize.height < this.MENU_THRESHOLD,
|
||||
showPresetsDialog: false,
|
||||
showEditPresetsDialog: false,
|
||||
presetsMenuAnchorRef: null,
|
||||
presetsSubmenuAnchorRef: null,
|
||||
renameDialogOpen: false,
|
||||
saveAsDialogOpen: false,
|
||||
importDialogOpen: false,
|
||||
@@ -117,12 +123,28 @@ const PresetSelector =
|
||||
this.handlePresetsMenuClose = this.handlePresetsMenuClose.bind(this);
|
||||
}
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
super.onWindowSizeChanged(width,height);
|
||||
this.setState({ compactHorizontalLayoutMenu: height < this.MENU_THRESHOLD });
|
||||
}
|
||||
|
||||
handlePresetMenuClick(event: SyntheticEvent): void {
|
||||
handlePresetsMenuClick(event: SyntheticEvent): void {
|
||||
this.setState({ presetsMenuAnchorRef: (event.currentTarget as HTMLElement) });
|
||||
}
|
||||
handlePresetsSubmenuClick(event: SyntheticEvent): void {
|
||||
this.setState({ presetsSubmenuAnchorRef: (event.currentTarget as HTMLElement) });
|
||||
}
|
||||
handlePresetsMenuClose(): void {
|
||||
this.setState({ presetsMenuAnchorRef: null });
|
||||
this.setState({
|
||||
presetsMenuAnchorRef: null,
|
||||
presetsSubmenuAnchorRef: null,
|
||||
});
|
||||
}
|
||||
handlePresetsSubmenuClose(): void {
|
||||
this.setState({
|
||||
presetsSubmenuAnchorRef: null,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
handlePresetsMenuImport(e: SyntheticEvent): void {
|
||||
@@ -284,11 +306,13 @@ const PresetSelector =
|
||||
this.updatePresetState();
|
||||
}
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.model.presets.addOnChangedHandler(this.handlePresetsChanged);
|
||||
this.updatePresetState();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.model.presets.removeOnChangedHandler(this.handlePresetsChanged)
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
showPresetDialog(show: boolean) {
|
||||
this.setState({
|
||||
@@ -368,7 +392,7 @@ const PresetSelector =
|
||||
<IconButtonEx
|
||||
tooltip="More..."
|
||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||
onClick={(e) => this.handlePresetMenuClick(e)}
|
||||
onClick={(e) => this.handlePresetsMenuClick(e)}
|
||||
size="large"
|
||||
>
|
||||
<MoreVertIcon style={{ opacity: 0.75 }} color="inherit" />
|
||||
@@ -381,10 +405,24 @@ const PresetSelector =
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
<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>
|
||||
|
||||
{this.state.compactHorizontalLayoutMenu ? (
|
||||
<MenuItem key="a" onClick={(e) => this.handlePresetsSubmenuClick(e)}
|
||||
style={{ display: 'flex', flexFlow: "row nowrap", alignItems: 'center', justifyContent: 'space-between' }}
|
||||
>
|
||||
Edit
|
||||
<ArrowRightIcon/>
|
||||
</MenuItem>
|
||||
)
|
||||
:(
|
||||
[
|
||||
(<MenuItem key="b" onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save preset as...</MenuItem>),
|
||||
(<MenuItem key="c" onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>),
|
||||
(<MenuItem key="d" onClick={(e) => this.handlePresetsMenuImport(e)}>Import from bank...</MenuItem>),
|
||||
(<MenuItem key="e" onClick={(e) => this.handlePresetsMenuNew(e)}>New...</MenuItem>),
|
||||
]
|
||||
|
||||
)}
|
||||
<Divider />
|
||||
<MenuItem onClick={(e) => { this.handleDownloadPreset(e); }} >Download preset</MenuItem>
|
||||
<MenuItem onClick={(e) => { this.handleUploadPreset(e) }}>Upload preset</MenuItem>
|
||||
@@ -392,8 +430,24 @@ const PresetSelector =
|
||||
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()}
|
||||
/>
|
||||
|
||||
{/* Submenu */}
|
||||
<Menu key="b" onClose={() => this.handlePresetsSubmenuClose()}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
anchorPosition={{left: 48, top: 0}}
|
||||
anchorEl={this.state.presetsSubmenuAnchorRef}
|
||||
open={this.state.presetsSubmenuAnchorRef !== null} >
|
||||
<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>
|
||||
</Menu>
|
||||
|
||||
|
||||
{this.state.showPresetsDialog&& (
|
||||
<PresetDialog show={this.state.showPresetsDialog} onDialogClose={() => this.handleDialogClose()}
|
||||
/>
|
||||
)}
|
||||
{this.state.saveAsDialogOpen && (
|
||||
<SavePresetAsDialog open={this.state.saveAsDialogOpen}
|
||||
defaultName={presets.getItem(presets.selectedInstanceId)?.name ?? "My Preset"}
|
||||
|
||||
Reference in New Issue
Block a user