ModGUI Sidechain select

This commit is contained in:
Robin E. R. Davies
2025-10-01 21:47:13 -04:00
parent aa26fb2fea
commit fb092016b1
9 changed files with 286 additions and 143 deletions
+2 -2
View File
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.16.0)
project(pipedal
VERSION 1.5.93
VERSION 1.5.94
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
)
execute_process( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE )
set (DISPLAY_VERSION "PiPedal v1.5.93-Experimental")
set (DISPLAY_VERSION "PiPedal v1.5.94-Experimental")
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
set (CMAKE_INSTALL_PREFIX "/usr/")
+2 -2
View File
@@ -9,11 +9,11 @@
<img src="https://img.shields.io/github/downloads/rerdavies/pipedal/total?color=%23808080&link=https%3A%2F%2Frerdavies.github.io%2Fpipedal%2Fdownload.html"/>
Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v1.5.93</a>
Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v1.5.94</a>
Website:&nbsp;[https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal).
Documentation:&nbsp;[https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html).
#### NEW version 1.5.93 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. TooB NAM calibration, manipulation of i/r files in Toob Convolution Reverb, new Parametric EQ and 3 Band EQ plugins, and MORE!
#### NEW version 1.5.94 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. TooB NAM calibration, manipulation of i/r files in Toob Convolution Reverb, new Parametric EQ and 3 Band EQ plugins, and MORE!
&nbsp;
+5 -5
View File
@@ -13,18 +13,18 @@ page_icon: img/Install4.jpg
Download the most recent Debian (.deb) package for your platform:
- [Raspberry Pi OS bookworm (aarch64) v1.5.93](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
- [Ubuntu 24.04 through 25.04 (aarch64) v1.5.93](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
- [Ubuntu 24.04 through 25.04 (amd64) v1.5.93](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_amd64.deb)
- [Raspberry Pi OS bookworm (aarch64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.04 through 25.04 (aarch64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.04 through 25.04 (amd64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_amd64.deb)
Version 1.5.93 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (aarch64). Download the appropriate package for your platform, and install using the following procedure:
Version 1.5.94 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (aarch64). Download the appropriate package for your platform, and install using the following procedure:
```
sudo apt update
sudo apt upgrade
cd ~/Downloads
sudo apt-get install ./pipedal_1.5.93_arm64.deb
sudo apt-get install ./pipedal_1.5.94_arm64.deb
```
You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies.
+7
View File
@@ -1,5 +1,12 @@
# Release Notes
## PiPedal 1.5.94 Beta
### Major Features in This Build
- **Sidechains**. You can now load plugins that have sidechain inputs. This allows you to use compressor and gate plugins that often have
sidechains. Plugins that have sidechain inputs will show a dropdown control in their control view which allows you to select the source of the sidechain input. The source plugin must for the sidechain must appear earlier in the signal chain.
## PiPedal 1.5.93 Release
### Major Features in This Build
+4 -4
View File
@@ -4,9 +4,9 @@
Download the most recent Debian (.deb) package for your platform:
- [Raspberry Pi OS bookworm (aarch64) v1.5.93 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
- [Ubuntu 24.x, 25.04 (aarch64) v1.5.93 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_arm64.deb)
- [Ubuntu 24.x, 25.04 (amd64) v1.5.93 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.93/pipedal_1.5.93_amd64.deb)
- [Raspberry Pi OS bookworm (aarch64) v1.5.94 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.x, 25.04 (aarch64) v1.5.94 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.x, 25.04 (amd64) v1.5.94 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_amd64.deb)
Install the package by running
@@ -14,7 +14,7 @@ Install the package by running
```
sudo apt update
cd ~/Downloads
sudo apt-get install ./pipedal_1.5.93_arm64.deb
sudo apt-get install ./pipedal_1.5.94_arm64.deb
```
You MUST use `apt-get` to install the package. `apt install` will NOT install the package correctly. The message about missing permissions given by `apt-get` is
expected, and can be safely ignored.
+147 -123
View File
@@ -24,17 +24,40 @@ import { withStyles } from "tss-react/mui";
import { createStyles } from './WithStyles';
import FullscreenIcon from '@mui/icons-material/Fullscreen';
import IconButtonEx from './IconButtonEx';
import { isDarkMode } from './DarkMode';
import Backdrop from '@mui/material/Backdrop';
import CloseIcon from '@mui/icons-material/Close';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Rect from './Rect';
import { css } from '@emotion/react';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
const styles = (theme: Theme) => createStyles({
pgraph: {
pgraph: css({
paddingBottom: 16
}
}),
backdrop: css({
zIndex: 1101,
overflow: "clip",
background: theme.palette.background.default,
}),
noToolbar: css({
display: "none"
}),
topToolBar: css({
position: "absolute",
zIndex: 1101,
top: 0,
display: "flex", flexFlow: "row nowrap", justifyContent: "start", columnGap: 8,
}),
rightToolbar: css({
position: "absolute",
zIndex: 1101,
top: 24,
display: "flex", flexFlow: "column nowrap", rowGap: 8
})
});
@@ -42,6 +65,7 @@ export interface AutoZoomProps extends WithStyles<typeof styles> {
leftPad: number,
rightPad: number,
children?: React.ReactNode,
toolbarChildren?: React.ReactNode[],
contentReady: boolean,
showZoomed: boolean,
setShowZoomed: (value: boolean) => void;
@@ -53,6 +77,15 @@ export interface AutoZoomState {
}
// non-react zoom state.
class UnzoomedLayout {
horizontalToolbar = false;
effectiveClientRect: Rect = new Rect();
showToolbar = false;
zoom: number = 1;
showZoomButton = false;
contentRect: Rect = new Rect();
}
const AutoZoom = withStyles(
class extends ResizeResponsiveComponent<AutoZoomProps, AutoZoomState> {
@@ -97,38 +130,43 @@ const AutoZoom = withStyles(
if (this.normalRef) {
this.updateZoom(this.normalRef);
}
if (this.maximizedRef) {
this.updateMaximizedZoom();
}
}
}
resizeObserver: ResizeObserver | null = null;
calculateZoomedClientRect(
calculateUnzoomedLayout(
clientRect: Rect,
contentWidth: number,
contentHeight: number,
buttonPadding: number
): Rect {
): UnzoomedLayout {
let result = new UnzoomedLayout();
console.log("Zoom: " + clientRect.toString() + " content: " + contentWidth + "x" + contentHeight + " buttonPadding: " + buttonPadding);
let rightButtonZoom = Math.min((clientRect.width - buttonPadding) / contentWidth, clientRect.height / contentHeight);
let topButtonZoom = Math.min((clientRect.width - buttonPadding) / contentWidth, (clientRect.height - buttonPadding) / contentHeight);
let effectiveClientRect = clientRect.copy();
if (rightButtonZoom > topButtonZoom) {
result.horizontalToolbar = false;
effectiveClientRect.width -= buttonPadding;
} else {
result.horizontalToolbar = true;
effectiveClientRect.y += buttonPadding;
effectiveClientRect.height -= buttonPadding;
}
result.effectiveClientRect = effectiveClientRect;
let zoom = Math.min(effectiveClientRect.width / contentWidth, effectiveClientRect.height / contentHeight);
if (zoom > 1) {
zoom = 1;
}
result.zoom = zoom;
result.showZoomButton = zoom !== 1;
result.showToolbar = result.showZoomButton || !!this.props.toolbarChildren;
let leftPad = (effectiveClientRect.width - contentWidth * zoom) / 2;
if (leftPad < 0) {
leftPad = 0;
@@ -137,7 +175,8 @@ const AutoZoom = withStyles(
if (topPad < 0) {
topPad = 0;
}
return new Rect(effectiveClientRect.x + leftPad, effectiveClientRect.y + topPad, contentWidth * zoom, contentHeight * zoom);
result.contentRect = new Rect(effectiveClientRect.x + leftPad, effectiveClientRect.y + topPad, contentWidth * zoom, contentHeight * zoom);
return result;
}
updateZoom(ref: HTMLDivElement) {
@@ -145,59 +184,58 @@ const AutoZoom = withStyles(
return;
}
let content = ref.firstElementChild as HTMLElement | null;
const classes = withStyles.getClasses(this.props);
if (content) {
const { leftPad, rightPad } = this.props;
let bounds = new Rect(leftPad, 0, ref.clientWidth - leftPad - rightPad, ref.clientHeight - 8);
let zoomedClientRect = this.calculateZoomedClientRect(
let layout = this.calculateUnzoomedLayout(
bounds, content.clientWidth, content.clientHeight, 48);
let contentRect = layout.contentRect;
content.style.left = "0px";
content.style.top = "0px";
content.style.transformOrigin = "0 0"; // top-left corner
content.style.transform =
`translate(${zoomedClientRect.x}px, ${zoomedClientRect.y}px) scale(${zoomedClientRect.width / content.clientWidth}, ${zoomedClientRect.height / content.clientHeight})`;
`translate(${contentRect.x}px, ${contentRect.y}px) scale(${layout.zoom}, ${layout.zoom})`;
if (this.zoomButton) {
this.zoomButton.style.display = zoomedClientRect.width < content.clientWidth ? "block" : "none";
let rightSizeSpace = ref.clientWidth - zoomedClientRect.right;
if (rightSizeSpace - rightPad >= 48) {
this.zoomButton.style.top = `${zoomedClientRect.y + 24}px`;
this.zoomButton.style.left = `${zoomedClientRect.right + 4}px`;
if (this.zoomToolbar) {
if (!layout.showToolbar) {
this.zoomToolbar.className = classes.noToolbar;
} else if (layout.horizontalToolbar) {
this.zoomToolbar.className = classes.topToolBar;
this.zoomToolbar.style.left = `${contentRect.x +16}px`
this.zoomToolbar.style.top = `${contentRect.y - 48}px`;
this.zoomToolbar.style.width = `${contentRect.width}px`;
} else {
this.zoomButton.style.top = `${zoomedClientRect.y - 48}px`;
this.zoomButton.style.left = `${zoomedClientRect.right - 64 - 12}px`;
this.zoomToolbar.className = classes.rightToolbar;
this.zoomToolbar.style.left = "";
this.zoomToolbar.style.top = `${contentRect.y}px`;
this.zoomToolbar.style.left = `${contentRect.right + 4}px`;
this.zoomToolbar.style.width = "";
}
}
if (this.zoomButton) {
this.zoomButton.style.display = layout.showZoomButton ? "block" : "none";
}
// save the position of the Mod UI in page coordinates for later zooming.
this.zoomStartRect = zoomedClientRect.copy();
this.zoomStartRect = layout.contentRect.copy();
let refBounds = ref.getBoundingClientRect();
this.zoomStartRect.x += refBounds.left;
this.zoomStartRect.y += refBounds.top;
} else {
if (this.zoomToolbar) {
this.zoomToolbar.className = classes.noToolbar;
}
}
}
updateMaximizedZoom() {
let ref = this.maximizedRef;
if (!this.mounted || !ref) {
return;
}
let content = ref.firstElementChild as HTMLElement | null;
if (content) {
let leftPad = 16; let rightPad = 16;
let topPad = 16, bottomPad = 16;
let bounds: Rect;
bounds = new Rect(leftPad, topPad, ref.clientWidth - leftPad - rightPad, ref.clientHeight - topPad - bottomPad);
let zoomedClientRect = this.calculateZoomedClientRect(
bounds, content.clientWidth, content.clientHeight, 48);
content.style.left = "0px";
content.style.top = "0px";
content.style.transformOrigin = "0 0"; // top-left corner
content.style.transform = `translate(${zoomedClientRect.x}px, ${zoomedClientRect.y}px) scale(${zoomedClientRect.width / content.clientWidth}, ${zoomedClientRect.height / content.clientHeight})`;
//console.log("Zoomed to: ", zoomedClientRect.toString());
if (this.normalRef) {
this.updateZoom(this.normalRef);
}
}
@@ -234,20 +272,20 @@ const AutoZoom = withStyles(
}
this.maximizedAnimateFrameHandle = window.requestAnimationFrame(() => {
this.maximizedAnimateFrameHandle = null;
if (this.maximizedRef) {
this.updateMaximizedZoom();
}
this.updateMaximizedZoom();
});
}
private zoomToolbar: HTMLElement | null = null;
private zoomButton: HTMLElement | null = null;
private normalRef: HTMLDivElement | null = null;
setNormalRef(ref: HTMLDivElement | null) {
setContentRef(ref: HTMLDivElement | null) {
this.normalRef = ref;
if (ref) {
this.zoomButton = ref.querySelector("#maximize-button") as HTMLDivElement | null;
this.zoomToolbar = ref.querySelector("#zoom-toolbar") as HTMLDivElement | null;
this.zoomButton = ref.querySelector("#zoom-button") as HTMLDivElement | null;
this.resizeObserver = new ResizeObserver(() => {
this.requestZoomUpdate();
});
@@ -261,36 +299,15 @@ const AutoZoom = withStyles(
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
this.zoomButton = null;
this.zoomToolbar = null;
}
this.zoomStartRect = null;
this.zoomToolbar = null;
this.zoomButton = null;
}
}
private maximizedRef: HTMLDivElement | null = null;
private maximedResizeObserver: ResizeObserver | null = null;
setMaximizedRef(ref: HTMLDivElement | null) {
this.maximizedRef = ref;
if (ref) {
this.maximedResizeObserver = new ResizeObserver(() => {
this.requestMaximizedZoomUpdate();
});
this.maximedResizeObserver.observe(ref);
let child = ref.firstElementChild as HTMLElement | null;
if (child) {
this.maximedResizeObserver.observe(child);
}
this.requestMaximizedZoomUpdate();
} else {
if (this.maximedResizeObserver) {
this.maximedResizeObserver.disconnect();
this.maximedResizeObserver = null;
}
}
}
private zoomStartRect: Rect | null = null;
startZoom() {
@@ -300,70 +317,77 @@ const AutoZoom = withStyles(
this.props.setShowZoomed(true);
}
render() {
content() {
const classes = withStyles.getClasses(this.props);
void classes; // suppress unused variable warning
const landscape = this.state.screenWidth > this.state.screenHeight;
return (<div style={{ overflow: "hidden", height: "100%", width: "100%" }} ref={(ref) => { this.setNormalRef(ref); }}>
<div style={{
display: "inline-block",
position: "absolute",
visibility: this.props.contentReady ? "visible" : "hidden",
}}>
{!this.props.showZoomed && this.props.children}
</div>
<div id="maximize-button" style={{
display: "none", position: "absolute", left: 0, top: 0, width: 52, height: 64, zIndex: 1101,
borderRadius: "0px 26px 26px 0px",
}}
return (
<div ref={(ref) => { this.setContentRef(ref); }}
style={{ width: "100%", height: "100%" }}
>
<IconButtonEx tooltip="Fullscreen view"
style={{ margin: 2, background: isDarkMode() ? "#444" : "#eee" }}
onClick={() => {
this.startZoom();
<div style={{
display: "inline-block",
position: "absolute",
visibility: this.props.contentReady ? "visible" : "hidden"
}}>
{this.props.children}
</div>
<div id="zoom-toolbar" className={classes.noToolbar}
style={{
visibility: this.props.contentReady ? "visible" : "hidden"
}}
>
<FullscreenIcon style={{ color: "white" }} />
</IconButtonEx>
</div>
{this.props.showZoomed && (
<Backdrop id="modGuiZoomBackdrop"
sx={(theme) => ({
zIndex: this.props.showZoomed ? 1101 : -1,
overflow: "clip",
background: theme.palette.mode === 'dark' ? 'rgba(0, 0, 0, 0.9)' : 'rgba(255, 255, 255, 0.9)'
})}
open={this.props.showZoomed}
>
<div ref={(ref) => { this.setMaximizedRef(ref); }}
style={{
position: "absolute", left: 0, top: 0, right: 0, bottom: 0, overflow: "hidden",
visibility: this.props.contentReady ? "visible" : "hidden"
}}>
<div style={{ display: "inline-block", position: "relative" }}>
{
this.props.showZoomed && this.props.children
}
</div>
<IconButtonEx tooltip="Exit fullscreen"
style={{
position: "absolute",
right: landscape? 80: 16, // avoid potential cutout in landscape mode
top: 16, zIndex: 1104,
{!this.props.showZoomed ? (
<IconButtonEx id="zoom-button" tooltip="Fullscreen view"
onClick={() => {
this.startZoom();
}}
>
<FullscreenIcon style={{ opacity: 0.6 }}
/>
</IconButtonEx>
) : (
<IconButtonEx tooltip="Exit fullscreen"
style={{}}
onClick={(e) => {
e.stopPropagation();
this.props.setShowZoomed(false);
}}
>
<CloseIcon />
<ArrowBackIcon style={{
opacity: 0.6,
}} />
</IconButtonEx>
</div>
)}
{this.props.toolbarChildren && this.props.toolbarChildren.map((child, index) => (
<div key={index} style={{ display: "inline-block" }}>
{child}
</div>
))}
</div>
</div>
);
}
render() {
const classes = withStyles.getClasses(this.props);
void classes; // suppress unused variable warning
return (<div style={{ overflow: "hidden", height: "100%", width: "100%" }} >
{!this.props.showZoomed && this.content()}
{this.props.showZoomed && (
<Backdrop id="modGuiZoomBackdrop"
className={classes.backdrop}
open={this.props.showZoomed}
>
{this.content()}
</Backdrop>
)}
</div>);
+44 -6
View File
@@ -22,6 +22,9 @@ import { Theme } from '@mui/material/styles';
import WithStyles, { withTheme } from './WithStyles';
import { createStyles } from './WithStyles';
import { css } from '@emotion/react';
import IconButtonEx from './IconButtonEx';
import SidechainConnectIcon from './svg/ic_sidechain_open_48.svg?react';
import AutoZoom from './AutoZoom';
@@ -37,7 +40,7 @@ import {
} from './Pedalboard';
import PluginControl from './PluginControl';
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
import SideChainSelectControl from './SideChainSelectControl';
import SideChainSelectControl, {SideChainSelectDialog} from './SideChainSelectControl';
import VuMeter from './VuMeter';
import { nullCast } from './Utility'
import { PiPedalStateError } from './PiPedalError';
@@ -373,11 +376,12 @@ type PluginControlViewState = {
dialogFileValue: string,
modGuiContentReady: boolean,
showModGuiZoomed: boolean,
sidechainDialogOpen: boolean
};
const PluginControlView =
withTheme(withStyles(
class extends ResizeResponsiveComponent<PluginControlViewProps, PluginControlViewState> {
class extends ResizeResponsiveComponent<PluginControlViewProps, PluginControlViewState> {
model: PiPedalModel;
constructor(props: PluginControlViewProps) {
@@ -393,7 +397,8 @@ const PluginControlView =
dialogFileProperty: new UiFileProperty(),
dialogFileValue: "",
modGuiContentReady: false,
showModGuiZoomed: false
showModGuiZoomed: false,
sidechainDialogOpen: false
}
this.onPedalboardChanged = this.onPedalboardChanged.bind(this);
@@ -608,15 +613,15 @@ const PluginControlView =
let title = uiPlugin.audio_side_chain_title ? uiPlugin.audio_side_chain_title : "Side chain";
return (
<SideChainSelectControl
<SideChainSelectControl
title={title}
selectItems={items}
selectedInstanceId={selectedInstanceId}
onChanged={(instanceId: number) => {
this.model.setPedalboardSideChainInput(this.props.item.instanceId, instanceId);
}}
/>
/>
);
}
@@ -876,6 +881,9 @@ const PluginControlView =
return false;
}
handleSelectSidechainInput() {
this.setState({sidechainDialogOpen: true});
}
handleModGuiFileProperty(instanceId: number, filePropertyUri: string, selectedFile: string) {
let plugin = this.model.getUiPlugin(this.props.item.uri);
@@ -900,10 +908,30 @@ const PluginControlView =
if (!uiPlugin) {
return (<div />);
}
let toolBarChildren: ReactNode[] = [];
if (uiPlugin.audio_side_chain_inputs > 0) {
toolBarChildren.push(
<IconButtonEx tooltip="Select sidechain input"
onClick={() => {
this.handleSelectSidechainInput();
}}
>
<SidechainConnectIcon
style={{
opacity: 0.6,
width: 24,
height: 24,
fill: this.props.theme.palette.text.primary
}}
/>
</IconButtonEx>
);
}
return (
<div style={{ width: "100%", height: "100%", }}>
<AutoZoom leftPad={36} rightPad={52} contentReady={this.state.modGuiContentReady}
showZoomed={this.state.showModGuiZoomed}
toolbarChildren={toolBarChildren}
setShowZoomed={
(value) => { this.setState({ showModGuiZoomed: value }); }
}
@@ -1085,6 +1113,16 @@ const PluginControlView =
}
/>
)}
{this.state.sidechainDialogOpen && (
<SideChainSelectDialog open={this.state.sidechainDialogOpen}
onClose={()=>{ this.setState({sidechainDialogOpen: false});}}
selectItems={this.getSidechainSelectItems()}
selectedInstanceId={this.props.item.sideChainInputId}
onChanged={(instanceId: number) => {
this.model.setPedalboardSideChainInput(this.props.item.instanceId, instanceId);
}}
/>
)}
{/* xxx: I don't think we need this anyore. */}
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue}
+58 -1
View File
@@ -26,6 +26,12 @@ import { Component } from 'react';
import { Theme } from '@mui/material/styles';
import { css } from '@emotion/react';
import DialogEx from './DialogEx';
import DialogTitle from '@mui/material/DialogTitle';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Toolbar from '@mui/material/Toolbar';
import DialogContent from '@mui/material/DialogContent';
import IconButtonEx from './IconButtonEx';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
import Select from '@mui/material/Select';
@@ -163,4 +169,55 @@ const SideChainSelectControl =
, styles)
)
;
export default SideChainSelectControl;
export default SideChainSelectControl;
export interface SideChainSelectDialogProps {
open: boolean,
onClose: () => void,
selectItems: { instanceId: number, title: string }[];
selectedInstanceId: number;
onChanged(instanceId: number): void;
}
export function SideChainSelectDialog(props: SideChainSelectDialogProps) {
let { open, onClose, selectItems, selectedInstanceId, onChanged } = props;
return (
<DialogEx tag="nameDialog" open={open} fullWidth maxWidth="xs" onClose={onClose} aria-labelledby="Rename-dialog-title"
style={{ userSelect: "none" }}
onEnterKey={() => { }}
>
<DialogTitle id="alert-dialog-title" style={{ userSelect: "none", padding: 0, margin: 0 }}>
<Toolbar style={{ paddingLeft: 24, paddingRight: 24, margin: 0 }} >
<IconButtonEx
tooltip="Back" edge="start" color="inherit" onClick={() => { onClose(); }} aria-label="back"
>
<ArrowBackIcon style={{opacity: 0.6}} />
</IconButtonEx>
<Typography noWrap variant="body1" style={{ flex: "1 1 auto" }}>Sidechain Input</Typography>
</Toolbar>
</DialogTitle>
<DialogContent >
<Select
variant="standard"
onChange={(event) => {
onChanged(event.target.value as number);
}}
value={selectedInstanceId}
style={{ width: "100%" }}
>
{selectItems.map((item) => {
return (
<MenuItem key={item.instanceId} value={item.instanceId}
selected={item.instanceId === selectedInstanceId}>
{item.title}
</MenuItem>
);
})}
</Select>
</DialogContent>
</DialogEx>
);
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="0 0 6.35 6.35"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<path
id="path4452"
d="M 3.1501953 1.1797729 C 2.3438819 1.1900494 1.6500313 1.6826477 1.3451375 2.38125 L 1.786971 2.38125 C 2.0624356 1.9000342 2.5807156 1.5761312 3.1760335 1.5761312 C 4.0608593 1.5761312 4.7733521 2.2912075 4.7733521 3.1760335 C 4.7733521 4.0608594 4.0608593 4.7733521 3.1760335 4.7733521 C 2.580068 4.7733521 2.0611725 4.450254 1.7859375 3.96875 L 1.3446208 3.96875 C 1.6524938 4.6748172 2.3583381 5.1697103 3.1760335 5.1697103 C 4.2753461 5.1697103 5.1697103 4.2753461 5.1697103 3.1760335 C 5.1697103 2.0767208 4.2753461 1.1797729 3.1760335 1.1797729 C 3.1674451 1.1797729 3.1587586 1.1796638 3.1501953 1.1797729 z " />
<path
id="ellipse4674"
d="M 3.1760335 2.3300903 C 2.7944551 2.3300903 2.4622893 2.5720679 2.3455933 2.9098999 L 0.64078776 2.9098999 L 0.64078776 3.4395833 L 2.3450765 3.4395833 C 2.4612025 3.7778767 2.7937654 4.019393 3.1760335 4.0193929 C 3.651938 4.0193929 4.0488484 3.6453545 4.0488485 3.1760335 C 4.0488485 2.7067124 3.6519381 2.3300904 3.1760335 2.3300903 z M 3.1760335 2.7264486 C 3.4466224 2.7264486 3.6524902 2.9307799 3.6524902 3.1760335 C 3.6524901 3.4212871 3.4466224 3.6230347 3.1760335 3.6230347 C 2.9054446 3.6230347 2.6975099 3.4212871 2.6975098 3.1760335 C 2.6975098 2.9307799 2.9054446 2.7264486 3.1760335 2.7264486 z " />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB