Zoom all UI controls on small touch displays.

This commit is contained in:
Robin Davies
2022-05-24 01:02:51 -04:00
parent 37a923cbac
commit 20d8296f50
7 changed files with 271 additions and 56 deletions
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M14 18.2 7.8 12 14 5.8 15.6 7.4 11 12 15.6 16.6Z"/></svg>

After

Width:  |  Height:  |  Size: 129 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M9.4 18.2 7.8 16.6 12.4 12 7.8 7.4 9.4 5.8 15.6 12Z"/></svg>

After

Width:  |  Height:  |  Size: 132 B

+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"socket_server_port": 8080, "socket_server_port": 80,
"socket_server_address": "*", "socket_server_address": "*",
"debug": true, "debug": true,
"max_upload_size": 1048576, "max_upload_size": 1048576,
+89 -1
View File
@@ -63,6 +63,7 @@ export interface PluginPresetsChangedHandle {
export interface ZoomedControlInfo { export interface ZoomedControlInfo {
source: HTMLElement; source: HTMLElement;
name: string;
instanceId: number; instanceId: number;
uiControl: UiControl; uiControl: UiControl;
} }
@@ -432,6 +433,9 @@ export interface PiPedalModel {
getAlsaDevices(): Promise<AlsaDeviceInfo[]>; getAlsaDevices(): Promise<AlsaDeviceInfo[]>;
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void; zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void;
onPreviousZoomedControl() : void;
onNextZoomedControl() : void;
clearZoomedControl(): void; clearZoomedControl(): void;
setFavorite(pluginUrl: string, isFavorite: boolean): void; setFavorite(pluginUrl: string, isFavorite: boolean): void;
@@ -2088,8 +2092,92 @@ class PiPedalModelImpl implements PiPedalModel {
} }
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void { 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 { clearZoomedControl(): void {
this.zoomedUiControl.set(undefined); this.zoomedUiControl.set(undefined);
} }
+1 -1
View File
@@ -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 // 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 // this software and associated documentation files (the "Software"), to deal in
+170 -51
View File
@@ -17,22 +17,42 @@
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // 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. // 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 { Theme } from '@mui/material/styles';
import { WithStyles,withStyles } from '@mui/styles'; import { WithStyles, withStyles } from '@mui/styles';
import createStyles from '@mui/styles/createStyles'; import createStyles from '@mui/styles/createStyles';
import { ZoomedControlInfo } from './PiPedalModel'; import { ZoomedControlInfo } from './PiPedalModel';
import DialogEx from './DialogEx'; import DialogEx from './DialogEx';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { PiPedalModelFactory,PiPedalModel } from './PiPedalModel'; import { PiPedalModelFactory, PiPedalModel } from './PiPedalModel';
import ZoomedDial from './ZoomedDial'; 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({ 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<typeof styles> { interface ZoomedUiControlProps extends WithStyles<typeof styles> {
theme: Theme;
dialogOpen: boolean; dialogOpen: boolean;
onDialogClose: () => void; onDialogClose: () => void;
onDialogClosed: () => void; onDialogClosed: () => void;
@@ -59,8 +79,7 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })(
model: PiPedalModel; model: PiPedalModel;
constructor(props: ZoomedUiControlProps) constructor(props: ZoomedUiControlProps) {
{
super(props); super(props);
this.model = PiPedalModelFactory.getInstance(); this.model = PiPedalModelFactory.getInstance();
this.state = { this.state = {
@@ -69,25 +88,45 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })(
} }
getCurrentValue(): number { getCurrentValue(): number {
if (!this.props.controlInfo) if (!this.props.controlInfo) {
{
return 0; return 0;
} else { } else {
let uiControl = this.props.controlInfo.uiControl; let uiControl = this.props.controlInfo.uiControl;
let instanceId = this.props.controlInfo.instanceId; let instanceId = this.props.controlInfo.instanceId;
if (instanceId === -1) return 0; if (instanceId === -1) return 0;
let pedalBoardItem = this.model.pedalBoard.get()?.getItem(instanceId); 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; 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; return newProps.controlInfo !== null;
} else if (newProps.controlInfo === null) } else if (newProps.controlInfo === null) {
{
return oldProps.controlInfo !== null; return oldProps.controlInfo !== null;
} else { } else {
if (newProps.controlInfo?.instanceId !== oldProps.controlInfo?.instanceId) return true; if (newProps.controlInfo?.instanceId !== oldProps.controlInfo?.instanceId) return true;
@@ -97,14 +136,11 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })(
} }
componentDidUpdate(oldProps: ZoomedUiControlProps, oldState: ZoomedUiControlState) componentDidUpdate(oldProps: ZoomedUiControlProps, oldState: ZoomedUiControlState) {
{ if (this.hasControlChanged(oldProps, this.props)) {
if (this.hasControlChanged(oldProps,this.props))
{
let currentValue = this.getCurrentValue(); let currentValue = this.getCurrentValue();
if (this.state.value !== currentValue) if (this.state.value !== currentValue) {
{ this.setState({ value: this.getCurrentValue() });
this.setState({value: this.getCurrentValue()});
} }
} }
} }
@@ -115,46 +151,129 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })(
return false; return false;
} }
makeSelect(control: UiControl, value: number): ReactNode {
if (control.isOnOffSwitch()) {
// normal gray unchecked state.
return (
<Switch checked={value !== 0} color="secondary"
onChange={(event) => {
this.onCheckChanged(event.target.checked);
}}
/>
);
}
if (control.isAbToggle()) {
// unchecked color is not gray.
return (
<Switch checked={value !== 0} color="secondary"
onChange={(event) => {
this.onCheckChanged(event.target.checked);
}}
classes={{
track: this.props.classes.switchTrack
}}
style={{ color: this.props.theme.palette.secondary.main }}
/>
);
} else {
return (
<Select variant="standard"
value={control.clampSelectValue(value)}
onChange={(e)=> this.onSelectChanged(e.target.value)}
inputProps={{
name: control.name,
id: 'id' + control.symbol,
style: { fontSize: "1.0em" }
}}
style={{ marginLeft: 4, marginRight: 4, width: 140 }}
>
{control.scale_points.map((scale_point: ScalePoint) => (
<MenuItem key={scale_point.value} value={scale_point.value}>{scale_point.label}</MenuItem>
))}
</Select>
);
}
}
render() { 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 ( return (
<DialogEx tag="zoomedControlDlg" open={this.props.dialogOpen} <DialogEx tag="zoomedControlDlg" open={this.props.dialogOpen}
onClose={()=> { this.props.onDialogClose()}} onClose={() => { this.props.onDialogClose() }}
onAbort={()=> { this.props.onDialogClose()}} onAbort={() => { this.props.onDialogClose() }}
> >
<div style={{ width: 300, height: 300, background: "#FFF", <div style={{
display: "flex", flexFlow: "column", alignItems: "center", alignContent: "center", justifyContent: "center" width: 380, height: 300, background: "FFFF",
display: "flex", flexFlow: "row", alignItems: "center", alignContent: " center", justifyContent: "center"
}}>
<IconButton sx={{ height: "100%", width: 48, borderRadius: "0% 50% 50% 0%" }} onClick={
() => {
this.model.onNextZoomedControl();
}
} >
<NavigateBeforeIcon />
</IconButton>
<div style={{
width: 200, flexGrow: 1, height: 300, background: "#FFF",
display: "flex", flexFlow: "column", alignItems: "center", alignContent: "center", justifyContent: "center"
}} }}
onDrag={this.onDrag} onDrag={this.onDrag}
> >
<Typography <Typography
display="block" variant="h6" color="textPrimary" style={{textAlign: "center"}} display="block" variant="h6" color="textPrimary" style={{ textAlign: "center" }}
> >
{this.props.controlInfo?.uiControl.name??""} {this.props.controlInfo.name}
</Typography> </Typography>
<ZoomedDial size={200} controlInfo={this.props.controlInfo} {uiControl.isDial() ? (
onPreviewValue={(v)=> {
this.setState({value: v}); <ZoomedDial size={200} controlInfo={this.props.controlInfo}
}} onPreviewValue={(v) => {
onSetValue={(v)=> { this.setState({ value: v });
this.setState({value: v}); }}
if (this.props.controlInfo) onSetValue={(v) => {
{ this.setState({ value: v });
this.model.setPedalBoardControlValue( if (this.props.controlInfo) {
this.props.controlInfo.instanceId, this.model.setPedalBoardControlValue(
this.props.controlInfo.uiControl.symbol, this.props.controlInfo.instanceId,
v); this.props.controlInfo.uiControl.symbol,
} v);
}} }
}}
/> />
) :
<Typography <div style={{height: 200, paddingTop: 80}}>
display="block" variant="h6" color="textPrimary" style={{textAlign: "center"}} {this.makeSelect(uiControl,this.state.value)}
> </div>
}
<Typography
display="block" variant="h6" color="textPrimary" style={{ textAlign: "center" }}
>
{displayValue} {displayValue}
</Typography> </Typography>
</div>
<IconButton sx={{ height: "100%", width: 48, borderRadius: "50% 0% 0% 50%" }}
onClick={() => {
this.model.onPreviousZoomedControl();
}
}
>
<NavigateNextIcon />
</IconButton>
</div> </div>
</DialogEx> </DialogEx>
+8 -2
View File
@@ -423,12 +423,18 @@ public:
} }
std::string GetConfig(const std::string &fromAddress) 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; std::stringstream s;
s << "{ \"socket_server_port\": " << portNumber 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(); return s.str();
} }