diff --git a/react/public/img/ic_navigate_before.svg b/react/public/img/ic_navigate_before.svg new file mode 100644 index 0000000..f4a753b --- /dev/null +++ b/react/public/img/ic_navigate_before.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/react/public/img/ic_navigate_next.svg b/react/public/img/ic_navigate_next.svg new file mode 100644 index 0000000..6f4bc0a --- /dev/null +++ b/react/public/img/ic_navigate_next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/react/public/var/config.json b/react/public/var/config.json index 20919e1..6dc7986 100644 --- a/react/public/var/config.json +++ b/react/public/var/config.json @@ -1,5 +1,5 @@ { - "socket_server_port": 8080, + "socket_server_port": 80, "socket_server_address": "*", "debug": true, "max_upload_size": 1048576, diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index a45c7d0..e5d0616 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -63,6 +63,7 @@ export interface PluginPresetsChangedHandle { export interface ZoomedControlInfo { source: HTMLElement; + name: string; instanceId: number; uiControl: UiControl; } @@ -432,6 +433,9 @@ export interface PiPedalModel { getAlsaDevices(): Promise; zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void; + + onPreviousZoomedControl() : void; + onNextZoomedControl() : void; clearZoomedControl(): void; setFavorite(pluginUrl: string, isFavorite: boolean): void; @@ -2088,8 +2092,92 @@ class PiPedalModelImpl implements PiPedalModel { } zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void { - this.zoomedUiControl.set({ source: sourceElement, instanceId: instanceId, uiControl: uiControl }); + let name = uiControl.name; + if (uiControl.port_group !== "") + { + let pedalboard = this.pedalBoard.get(); + if (pedalboard) + { + let plugin = pedalboard.getItem(instanceId); + let uiPlugin = this.getUiPlugin(plugin.uri); + if (uiPlugin) + { + for (let i = 0; i < uiPlugin.port_groups.length; ++i) + { + if (uiPlugin.port_groups[i].symbol === uiControl.port_group) + { + name = uiPlugin.port_groups[i].name + " / " + name; + break; + } + } + } + } + } + this.zoomedUiControl.set({ source: sourceElement, name: name, instanceId: instanceId, uiControl: uiControl }); } + onPreviousZoomedControl() : void + { + let currentUiControl = this.zoomedUiControl.get(); + if (!currentUiControl) return; + + let currentSymbol = currentUiControl.uiControl.symbol; + + let pedalboard = this.pedalBoard.get(); + if (!pedalboard) return; + + let pedalboardItem = pedalboard.getItem(currentUiControl.instanceId); + + let uiPlugin = this.getUiPlugin(pedalboardItem.uri); + if (!uiPlugin) return; + + let i = 0; + let ix = -1; + for (i = 0; i < uiPlugin.controls.length; ++i) + { + if (uiPlugin.controls[i].symbol === currentSymbol) { + ix = i; + break; + } + } + if (ix === -1) return; + + ++ix; + if (ix >= uiPlugin.controls.length) return; + + this.zoomUiControl(currentUiControl.source,currentUiControl.instanceId,uiPlugin.controls[ix]); + } + onNextZoomedControl() : void + { + let currentUiControl = this.zoomedUiControl.get(); + if (!currentUiControl) return; + + let currentSymbol = currentUiControl.uiControl.symbol; + + let pedalboard = this.pedalBoard.get(); + if (!pedalboard) return; + + let pedalboardItem = pedalboard.getItem(currentUiControl.instanceId); + + let uiPlugin = this.getUiPlugin(pedalboardItem.uri); + if (!uiPlugin) return; + + let i = 0; + let ix = -1; + for (i = 0; i < uiPlugin.controls.length; ++i) + { + if (uiPlugin.controls[i].symbol === currentSymbol) { + ix = i; + break; + } + } + if (ix === -1) return; + + --ix; + if (ix < 0) return; + + this.zoomUiControl(currentUiControl.source,currentUiControl.instanceId,uiPlugin.controls[ix]); + } + clearZoomedControl(): void { this.zoomedUiControl.set(undefined); } diff --git a/react/src/PluginControl.tsx b/react/src/PluginControl.tsx index 0cab300..5ebe0c3 100644 --- a/react/src/PluginControl.tsx +++ b/react/src/PluginControl.tsx @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Robin Davies + // Copyright (c) 2022 Robin Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in diff --git a/react/src/ZoomedUiControl.tsx b/react/src/ZoomedUiControl.tsx index 8539d67..dd17d8e 100644 --- a/react/src/ZoomedUiControl.tsx +++ b/react/src/ZoomedUiControl.tsx @@ -17,22 +17,42 @@ // 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} from 'react'; +import React, {ReactNode, SyntheticEvent } from 'react'; +import MenuItem from '@mui/material/MenuItem'; +import Select from '@mui/material/Select'; +import Switch from '@mui/material/Switch'; +import { UiControl, ScalePoint } from './Lv2Plugin'; import { Theme } from '@mui/material/styles'; -import { WithStyles,withStyles } from '@mui/styles'; +import { WithStyles, withStyles } from '@mui/styles'; import createStyles from '@mui/styles/createStyles'; import { ZoomedControlInfo } from './PiPedalModel'; import DialogEx from './DialogEx'; import Typography from '@mui/material/Typography'; -import { PiPedalModelFactory,PiPedalModel } from './PiPedalModel'; +import { PiPedalModelFactory, PiPedalModel } from './PiPedalModel'; import ZoomedDial from './ZoomedDial'; +import IconButton from '@mui/material/IconButton'; +import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; +import NavigateNextIcon from '@mui/icons-material/NavigateNext'; const styles = (theme: Theme) => createStyles({ + switchTrack: { + height: '100%', + width: '100%', + borderRadius: 14 / 2, + zIndex: -1, + transition: theme.transitions.create(['opacity', 'background-color'], { + duration: theme.transitions.duration.shortest + }), + backgroundColor: theme.palette.secondary.main, + opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 + } + }); interface ZoomedUiControlProps extends WithStyles { + theme: Theme; dialogOpen: boolean; onDialogClose: () => void; onDialogClosed: () => void; @@ -59,8 +79,7 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( model: PiPedalModel; - constructor(props: ZoomedUiControlProps) - { + constructor(props: ZoomedUiControlProps) { super(props); this.model = PiPedalModelFactory.getInstance(); this.state = { @@ -69,25 +88,45 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( } getCurrentValue(): number { - if (!this.props.controlInfo) - { + if (!this.props.controlInfo) { return 0; } else { let uiControl = this.props.controlInfo.uiControl; let instanceId = this.props.controlInfo.instanceId; if (instanceId === -1) return 0; let pedalBoardItem = this.model.pedalBoard.get()?.getItem(instanceId); - let value: number = pedalBoardItem?.getControlValue(uiControl.symbol)??0; + let value: number = pedalBoardItem?.getControlValue(uiControl.symbol) ?? 0; return value; } } - hasControlChanged(oldProps: ZoomedUiControlProps, newProps: ZoomedUiControlProps) - { - if (oldProps.controlInfo === null) - { + + + onSelectChanged(val: string | number) { + let v = Number.parseFloat(val.toString()); + this.setState({ value: v }); + if (this.props.controlInfo) { + this.model.setPedalBoardControlValue( + this.props.controlInfo.instanceId, + this.props.controlInfo.uiControl.symbol, + v); + } + } + + onCheckChanged(checked: boolean): void { + let v = checked ? 1: 0; + this.setState({ value: v }); + if (this.props.controlInfo) { + this.model.setPedalBoardControlValue( + this.props.controlInfo.instanceId, + this.props.controlInfo.uiControl.symbol, + v); + } + } + + hasControlChanged(oldProps: ZoomedUiControlProps, newProps: ZoomedUiControlProps) { + if (oldProps.controlInfo === null) { return newProps.controlInfo !== null; - } else if (newProps.controlInfo === null) - { + } else if (newProps.controlInfo === null) { return oldProps.controlInfo !== null; } else { if (newProps.controlInfo?.instanceId !== oldProps.controlInfo?.instanceId) return true; @@ -97,14 +136,11 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( } - componentDidUpdate(oldProps: ZoomedUiControlProps, oldState: ZoomedUiControlState) - { - if (this.hasControlChanged(oldProps,this.props)) - { + componentDidUpdate(oldProps: ZoomedUiControlProps, oldState: ZoomedUiControlState) { + if (this.hasControlChanged(oldProps, this.props)) { let currentValue = this.getCurrentValue(); - if (this.state.value !== currentValue) - { - this.setState({value: this.getCurrentValue()}); + if (this.state.value !== currentValue) { + this.setState({ value: this.getCurrentValue() }); } } } @@ -115,46 +151,129 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( return false; } + makeSelect(control: UiControl, value: number): ReactNode { + if (control.isOnOffSwitch()) { + // normal gray unchecked state. + return ( + { + this.onCheckChanged(event.target.checked); + }} + /> + ); + } + if (control.isAbToggle()) { + // unchecked color is not gray. + return ( + { + this.onCheckChanged(event.target.checked); + }} + classes={{ + track: this.props.classes.switchTrack + }} + style={{ color: this.props.theme.palette.secondary.main }} + /> + ); + } else { + return ( + + ); + } + } render() { - let displayValue = this.props.controlInfo?.uiControl.formatDisplayValue(this.state.value)??""; + if (!this.props.controlInfo) { + return false; + } + let uiControl = this.props.controlInfo.uiControl; + + let displayValue = uiControl.formatDisplayValue(this.state.value) ?? ""; + if (uiControl.isOnOffSwitch()) + { + displayValue = this.state.value !== 0 ? "On": "Off"; + } else if (uiControl.isSelect()) + { + displayValue = "\u00A0"; + } return ( - { this.props.onDialogClose()}} - onAbort={()=> { this.props.onDialogClose()}} + { this.props.onDialogClose() }} + onAbort={() => { this.props.onDialogClose() }} > -
+ { + this.model.onNextZoomedControl(); + } + } > + + +
- - {this.props.controlInfo?.uiControl.name??""} + + {this.props.controlInfo.name} - { - this.setState({value: v}); - }} - onSetValue={(v)=> { - this.setState({value: v}); - if (this.props.controlInfo) - { - this.model.setPedalBoardControlValue( - this.props.controlInfo.instanceId, - this.props.controlInfo.uiControl.symbol, - v); - } - }} + {uiControl.isDial() ? ( + + { + this.setState({ value: v }); + }} + onSetValue={(v) => { + this.setState({ value: v }); + if (this.props.controlInfo) { + this.model.setPedalBoardControlValue( + this.props.controlInfo.instanceId, + this.props.controlInfo.uiControl.symbol, + v); + } + }} /> - - + ) : +
+ {this.makeSelect(uiControl,this.state.value)} +
+ } + + {displayValue} +
+ { + this.model.onPreviousZoomedControl(); + } + } + > + +
diff --git a/src/main.cpp b/src/main.cpp index 6fc8c76..03d04f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -423,12 +423,18 @@ public: } std::string GetConfig(const std::string &fromAddress) { - std::string linkLocalAddress = GetLinkLocalAddress(fromAddress); + #define LINK_LOCAL_WEB_SOCKET 1 + #if LINK_LOCAL_WEB_SOCKET + std::string webSocketAddress = GetLinkLocalAddress(fromAddress); + Lv2Log::info(SS("Web Socket Adddress: " << webSocketAddress << ":" << portNumber)); + #else + std::string webSocketAddress = "*"; + #endif std::stringstream s; s << "{ \"socket_server_port\": " << portNumber - << ", \"socket_server_address\": \"" << linkLocalAddress << "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }"; + << ", \"socket_server_address\": \"" << webSocketAddress << "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }"; return s.str(); }