ALSA Sequencer connections.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
export class AlsaSequencerPortSelection {
|
||||
deserialize(json: any) {
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.sortOrder = json.sortOrder;
|
||||
return this;
|
||||
};
|
||||
static deserialize_array(input: any): AlsaSequencerPortSelection[] {
|
||||
let result: AlsaSequencerPortSelection[] = [];
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
result[i] = new AlsaSequencerPortSelection().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
id: string = "";
|
||||
name: string = "";
|
||||
sortOrder: number = 0;
|
||||
};
|
||||
|
||||
export class AlsaSequencerConfiguration {
|
||||
deserialize(input: any) {
|
||||
this.connections = AlsaSequencerPortSelection.deserialize_array(input.connections);
|
||||
return this;
|
||||
}
|
||||
deserialize_array(input: any): AlsaSequencerConfiguration[] {
|
||||
let result: AlsaSequencerConfiguration[] = [];
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
result[i] = new AlsaSequencerConfiguration().deserialize(input[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
connections: AlsaSequencerPortSelection[] = [];
|
||||
};
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { ThemeProvider, createTheme, StyledEngineProvider } from '@mui/material/styles';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
|
||||
import VirtualKeyboardHandler from './VirtualKeyboardHandler';
|
||||
import AppThemed from "./AppThemed";
|
||||
|
||||
@@ -36,7 +36,7 @@ import Slider, { SliderProps } from "@mui/material/Slider";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import TimebaseSelectorDialog from "./TimebaseselectorDialog";
|
||||
import Timebase, { TimebaseUnits } from "./Timebase";
|
||||
import { useWindowHeight } from "@react-hook/window-size";
|
||||
import useWindowSize from "./UseWindowSize";
|
||||
import Button from "@mui/material/Button";
|
||||
import PlayArrow from "@mui/icons-material/PlayArrow";
|
||||
import StopIcon from '@mui/icons-material/Stop';
|
||||
@@ -405,8 +405,8 @@ export default function LoopDialog(props: LoopDialogProps) {
|
||||
const [loopEnd, setLoopEnd] = React.useState(props.value.loopEnd);
|
||||
|
||||
|
||||
const height = useWindowHeight();
|
||||
const fullScreen = height < 500;
|
||||
const [windowSize] = useWindowSize();
|
||||
const fullScreen = windowSize.height < 500;
|
||||
|
||||
function cancelPlaying() {
|
||||
props.onCancelPlaying();
|
||||
@@ -444,7 +444,7 @@ export default function LoopDialog(props: LoopDialogProps) {
|
||||
open={props.isOpen}
|
||||
onEnterKey={() => {
|
||||
}}
|
||||
fullScreen={height < 500}
|
||||
fullScreen={fullScreen}
|
||||
sx={{
|
||||
"& .MuiDialog-container": {
|
||||
"& .MuiPaper-root": {
|
||||
|
||||
@@ -43,6 +43,7 @@ import { ColorTheme, getColorScheme, setColorScheme } from './DarkMode';
|
||||
import FilePropertyDirectoryTree from './FilePropertyDirectoryTree';
|
||||
import AudioFileMetadata from './AudioFileMetadata';
|
||||
import { pathFileName } from './FileUtils';
|
||||
import { AlsaSequencerConfiguration, AlsaSequencerPortSelection } from './AlsaSequencer';
|
||||
|
||||
|
||||
export enum State {
|
||||
@@ -452,6 +453,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
|
||||
favorites: ObservableProperty<FavoritesList> = new ObservableProperty<FavoritesList>({});
|
||||
|
||||
alsaSequencerConfiguration : ObservableProperty<AlsaSequencerConfiguration> = new ObservableProperty<AlsaSequencerConfiguration>(new AlsaSequencerConfiguration());
|
||||
|
||||
presets: ObservableProperty<PresetIndex> = new ObservableProperty<PresetIndex>
|
||||
(
|
||||
@@ -668,6 +670,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
this.handlePluginPresetsChanged(pluginUri);
|
||||
} else if (message === "onJackConfigurationChanged") {
|
||||
this.jackConfiguration.set(new JackConfiguration().deserialize(body));
|
||||
} else if (message === "onAlsaSequencerConfigurationChanged") {
|
||||
this.alsaSequencerConfiguration.set(new AlsaSequencerConfiguration().deserialize(body));
|
||||
|
||||
} else if (message === "onLoadPluginPreset") {
|
||||
let instanceId = body.instanceId as number;
|
||||
let controlValues = ControlValue.deserializeArray(body.controlValues);
|
||||
@@ -1134,6 +1139,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
this.jackSettings.set(new JackChannelSelection().deserialize(
|
||||
await this.getWebSocket().request<any>("getJackSettings")
|
||||
));
|
||||
this.alsaSequencerConfiguration.set(new AlsaSequencerConfiguration().deserialize(
|
||||
await this.getWebSocket().request<any>("getAlsaSequencerConfiguration")
|
||||
));
|
||||
this.banks.set(new BankIndex().deserialize(await this.getWebSocket().request<any>("getBankIndex")));
|
||||
|
||||
this.favorites.set(await this.getWebSocket().request<FavoritesList>("getFavorites"));
|
||||
@@ -3193,6 +3201,25 @@ export class PiPedalModel //implements PiPedalModel
|
||||
// notify the server.
|
||||
this.webSocket?.send("setPedalboardItemTitle", { instanceId: instanceId, title: title });
|
||||
}
|
||||
setAlsaSequencerConfiguration(alsaSequencerConfiguration: AlsaSequencerConfiguration): void {
|
||||
this.webSocket?.send("setAlsaSequencerConfiguration", alsaSequencerConfiguration);
|
||||
}
|
||||
async getAlsaSequencerConfiguration(): Promise<AlsaSequencerConfiguration> {
|
||||
if (this.webSocket)
|
||||
{
|
||||
let result = await this.webSocket.request<any>("getAlsaSequencerConfiguration");
|
||||
return new AlsaSequencerConfiguration().deserialize(result);
|
||||
}
|
||||
throw new Error("No connection.");
|
||||
}
|
||||
async getAlsaSequencerPorts(): Promise<AlsaSequencerPortSelection[]> {
|
||||
if (this.webSocket) {
|
||||
let result = await this.webSocket.request<AlsaSequencerPortSelection[]>("getAlsaSequencerPorts");
|
||||
return AlsaSequencerPortSelection.deserialize_array(result);
|
||||
}
|
||||
throw new Error("No connection.");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
let instance: PiPedalModel | undefined = undefined;
|
||||
|
||||
@@ -17,105 +17,191 @@
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import React from 'react';
|
||||
import { useState } from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import {AlsaMidiDeviceInfo} from './AlsaMidiDeviceInfo';
|
||||
import { AlsaSequencerConfiguration, AlsaSequencerPortSelection } from './AlsaSequencer';
|
||||
import DialogEx from './DialogEx';
|
||||
|
||||
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
|
||||
export interface SelectMidiChannelsDialogProps {
|
||||
open: boolean;
|
||||
selectedChannels: AlsaMidiDeviceInfo[];
|
||||
availableChannels: AlsaMidiDeviceInfo[];
|
||||
onClose: (selectedChannels: AlsaMidiDeviceInfo[] | null) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function isChecked(selectedChannels: AlsaMidiDeviceInfo[], channel: AlsaMidiDeviceInfo): boolean {
|
||||
for (let i = 0; i < selectedChannels.length; ++i) {
|
||||
if (selectedChannels[i].equals(channel)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function addPort(availableChannels: AlsaMidiDeviceInfo[], selectedChannels: AlsaMidiDeviceInfo[], newChannel: AlsaMidiDeviceInfo)
|
||||
:AlsaMidiDeviceInfo[]
|
||||
{
|
||||
let result: AlsaMidiDeviceInfo[] = [];
|
||||
for (let i = 0; i < availableChannels.length; ++i) {
|
||||
let channel = availableChannels[i];
|
||||
if (isChecked(selectedChannels, channel) || channel.equals(newChannel)) {
|
||||
result.push(channel);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function removePort(selectedChannels: AlsaMidiDeviceInfo[], channel: AlsaMidiDeviceInfo)
|
||||
:AlsaMidiDeviceInfo[]
|
||||
{
|
||||
let result: AlsaMidiDeviceInfo[] = [];
|
||||
for (let i = 0; i < selectedChannels.length; ++i) {
|
||||
if (!selectedChannels[i].equals(channel)) {
|
||||
result.push(selectedChannels[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
interface DialogItem {
|
||||
id: string;
|
||||
name: string;
|
||||
sortOrder: number
|
||||
offline: boolean;
|
||||
};
|
||||
|
||||
function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
|
||||
//const classes = useStyles();
|
||||
const { onClose, selectedChannels, availableChannels, open } = props;
|
||||
const [currentSelection, setCurrentSelection] = useState(selectedChannels);
|
||||
const { open, onClose } = props;
|
||||
const [availablePorts, setAvailablePorts] = useState<AlsaSequencerPortSelection[] | null>(null);
|
||||
const [configuration, setConfiguration] = useState<AlsaSequencerConfiguration | null>(null);
|
||||
const [allPorts, setAllPorts] = useState<DialogItem[] | null>(null);
|
||||
const [model] = useState<PiPedalModel>(PiPedalModelFactory.getInstance());
|
||||
const [changed, setChanged] = useState<boolean>(false);
|
||||
|
||||
|
||||
|
||||
let toggleSelect = (value: AlsaMidiDeviceInfo) => {
|
||||
if (!isChecked(currentSelection, value)) {
|
||||
setCurrentSelection(addPort(availableChannels, currentSelection, value));
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
model.getAlsaSequencerPorts().then((ports) => {
|
||||
setAvailablePorts(ports);
|
||||
}).catch((error) => {
|
||||
model.showAlert(error);
|
||||
setAvailablePorts(null);
|
||||
});
|
||||
model.getAlsaSequencerConfiguration().then((config) => {
|
||||
setConfiguration(config);
|
||||
}).catch((error) => {
|
||||
model.showAlert(error);
|
||||
setConfiguration(null);
|
||||
});
|
||||
return () => {
|
||||
}
|
||||
} else {
|
||||
setCurrentSelection(removePort(currentSelection, value));
|
||||
return () => { };
|
||||
}
|
||||
}, [open]);
|
||||
React.useEffect(() => {
|
||||
if (availablePorts !== null && configuration !== null) {
|
||||
let result: DialogItem[] = [];
|
||||
|
||||
for (let port of availablePorts) {
|
||||
result.push({
|
||||
id: port.id,
|
||||
name: port.name,
|
||||
sortOrder: port.sortOrder,
|
||||
offline: false
|
||||
});
|
||||
}
|
||||
|
||||
// include ports that have been previously selected but are not in the current list of available ports
|
||||
for (let port of configuration.connections) {
|
||||
if (!availablePorts.some((p) => p.id === port.id)) {
|
||||
result.push(
|
||||
{
|
||||
id: port.id,
|
||||
name: port.name,
|
||||
sortOrder: port.sortOrder,
|
||||
offline: true
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
result.sort((a, b) => {
|
||||
return a.sortOrder - b.sortOrder;
|
||||
});
|
||||
setAllPorts(result);
|
||||
} else {
|
||||
setAllPorts(null);
|
||||
}
|
||||
|
||||
}, [availablePorts, configuration]);
|
||||
|
||||
const isChecked = (value: DialogItem) => {
|
||||
if (availablePorts === null || configuration === null) {
|
||||
return false;
|
||||
}
|
||||
return configuration.connections.some((port) => port.id === value.id);
|
||||
};
|
||||
const setChecked = (value_: DialogItem, checked: boolean) => {
|
||||
if (availablePorts === null || configuration === null) {
|
||||
return;
|
||||
}
|
||||
let value = new AlsaSequencerPortSelection();
|
||||
value.id = value_.id;
|
||||
value.name = value_.name;
|
||||
value.sortOrder = value_.sortOrder;
|
||||
let newConnections = configuration.connections.slice();
|
||||
if (checked) {
|
||||
newConnections.push(value);
|
||||
} else {
|
||||
newConnections = newConnections.filter((port) => port.id !== value.id);
|
||||
}
|
||||
let newConfiguration = new AlsaSequencerConfiguration();
|
||||
newConfiguration.connections = newConnections;
|
||||
setConfiguration(newConfiguration);
|
||||
};
|
||||
let toggleSelect = (value: DialogItem) => {
|
||||
if (availablePorts === null || configuration === null) {
|
||||
return;
|
||||
}
|
||||
if (!isChecked(value)) {
|
||||
setChecked(value, true);
|
||||
} else {
|
||||
setChecked(value, false);
|
||||
}
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
const handleClose = (): void => {
|
||||
onClose(null);
|
||||
onClose();
|
||||
};
|
||||
const handleOk = (): void => {
|
||||
onClose(currentSelection);
|
||||
if (changed && configuration !== null) {
|
||||
model.setAlsaSequencerConfiguration(configuration);
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<DialogEx tag="midiChannels" onClose={handleClose} aria-labelledby="select-channels-title" open={open}
|
||||
onEnterKey={handleOk}
|
||||
<DialogEx tag="midiChannels" onClose={handleClose} aria-labelledby="select-midi-inputs" open={open}
|
||||
fullWidth maxWidth="xs"
|
||||
onEnterKey={handleClose}
|
||||
>
|
||||
<DialogTitle id="simple-dialog-title">Select MIDI Device</DialogTitle>
|
||||
<List>
|
||||
{availableChannels.map((channel) => (
|
||||
<ListItemButton>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox checked={isChecked(currentSelection, channel)} onClick={() => toggleSelect(channel)} key={channel.name} />
|
||||
}
|
||||
label={channel.description}
|
||||
/>
|
||||
</ListItemButton>
|
||||
)
|
||||
<DialogTitle id="select-midi-inputs">Select MIDI Inputs</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<List>
|
||||
{allPorts !== null && allPorts.length === 0 && (
|
||||
<Typography variant="body2" style={{ marginLeft: 32, marginRight: 24, marginTop: 8, marginBottom: 16 }}>
|
||||
No MIDI devices found.
|
||||
</Typography>)}
|
||||
{allPorts != null && allPorts.map((port) => (
|
||||
<ListItemButton key={port.id}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={isChecked(port)}
|
||||
onClick={() => toggleSelect(port)} />
|
||||
}
|
||||
label={
|
||||
(
|
||||
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "center" }}>
|
||||
<Typography
|
||||
color={ port.offline ? "textSecondary" : "textPrimary" }
|
||||
noWrap variant="body2"
|
||||
style={{ flex: "1 1 auto" }}
|
||||
>
|
||||
{port.name}
|
||||
</Typography>
|
||||
{port.offline && (
|
||||
<Typography color="textSecondary" variant="body2" >
|
||||
(offline)
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</ListItemButton>
|
||||
)
|
||||
|
||||
)}
|
||||
{availableChannels.length === 0 && (
|
||||
<Typography variant="body2" style={{ marginLeft: 32, marginRight: 24,marginTop:8, marginBottom: 16}}>
|
||||
No MIDI devices found.</Typography>
|
||||
)}
|
||||
</List>
|
||||
)}
|
||||
</List>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||
Cancel
|
||||
|
||||
@@ -44,9 +44,9 @@ import WifiConfigDialog from './WifiConfigDialog';
|
||||
import WifiDirectConfigDialog from './WifiDirectConfigDialog';
|
||||
import DialogEx from './DialogEx'
|
||||
import GovernorSettings from './GovernorSettings';
|
||||
import { AlsaMidiDeviceInfo } from './AlsaMidiDeviceInfo';
|
||||
import SystemMidiBindingsDialog from './SystemMidiBindingsDialog';
|
||||
import SelectThemeDialog from './SelectThemeDialog';
|
||||
import {AlsaSequencerConfiguration} from './AlsaSequencer';
|
||||
|
||||
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||
import {createStyles} from './WithStyles';
|
||||
@@ -73,6 +73,8 @@ interface SettingsDialogState {
|
||||
jackConfiguration: JackConfiguration;
|
||||
jackSettings: JackChannelSelection;
|
||||
jackServerSettings: JackServerSettings;
|
||||
alsaSequencerConfiguration: AlsaSequencerConfiguration;
|
||||
|
||||
jackStatus?: JackHostStatus;
|
||||
governorSettings: GovernorSettings;
|
||||
continueDisabled: boolean;
|
||||
@@ -184,6 +186,7 @@ const SettingsDialog = withStyles(
|
||||
jackConfiguration: this.model.jackConfiguration.get(),
|
||||
jackStatus: undefined,
|
||||
jackSettings: this.model.jackSettings.get(),
|
||||
alsaSequencerConfiguration: this.model.alsaSequencerConfiguration.get(),
|
||||
wifiConfigSettings: this.model.wifiConfigSettings.get(),
|
||||
wifiDirectConfigSettings: this.model.wifiDirectConfigSettings.get(),
|
||||
governorSettings: this.model.governorSettings.get(),
|
||||
@@ -207,6 +210,7 @@ const SettingsDialog = withStyles(
|
||||
hasWifiDevice: this.model.hasWifiDevice.get()
|
||||
};
|
||||
this.handleJackConfigurationChanged = this.handleJackConfigurationChanged.bind(this);
|
||||
this.handleAlsaSequencerConfigurationChanged = this.handleAlsaSequencerConfigurationChanged.bind(this);
|
||||
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
|
||||
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
|
||||
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
|
||||
@@ -306,6 +310,11 @@ const SettingsDialog = withStyles(
|
||||
jackConfiguration: this.model.jackConfiguration.get()
|
||||
});
|
||||
}
|
||||
handleAlsaSequencerConfigurationChanged(): void {
|
||||
this.setState({
|
||||
alsaSequencerConfiguration: this.model.alsaSequencerConfiguration.get()
|
||||
});
|
||||
}
|
||||
|
||||
mounted: boolean = false;
|
||||
active: boolean = false;
|
||||
@@ -333,6 +342,7 @@ const SettingsDialog = withStyles(
|
||||
this.model.showStatusMonitor.addOnChangedHandler(this.handleShowStatusMonitorChanged);
|
||||
this.model.jackSettings.addOnChangedHandler(this.handleJackSettingsChanged);
|
||||
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
|
||||
this.model.alsaSequencerConfiguration.addOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
||||
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||
this.model.wifiDirectConfigSettings.addOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
||||
@@ -352,6 +362,7 @@ const SettingsDialog = withStyles(
|
||||
});
|
||||
this.handleConnectionStateChanged();
|
||||
this.handleJackConfigurationChanged();
|
||||
this.handleAlsaSequencerConfigurationChanged();
|
||||
this.handleJackSettingsChanged();
|
||||
this.handleShowStatusMonitorChanged();
|
||||
this.handleJackServerSettingsChanged();
|
||||
@@ -368,6 +379,7 @@ const SettingsDialog = withStyles(
|
||||
this.model.showStatusMonitor.removeOnChangedHandler(this.handleShowStatusMonitorChanged);
|
||||
|
||||
this.model.jackConfiguration.removeOnChangedHandler(this.handleJackConfigurationChanged);
|
||||
this.model.alsaSequencerConfiguration.removeOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
||||
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
|
||||
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||
@@ -437,13 +449,7 @@ const SettingsDialog = withStyles(
|
||||
});
|
||||
|
||||
}
|
||||
handleSelectMidiDialogResult(channels: AlsaMidiDeviceInfo[] | null): void {
|
||||
if (channels) {
|
||||
let newSelection: JackChannelSelection = this.state.jackSettings.clone();
|
||||
newSelection.inputMidiDevices = channels;
|
||||
this.model.setJackSettings(newSelection);
|
||||
|
||||
}
|
||||
handleSelectMidiDialogResult(): void {
|
||||
this.setState({
|
||||
showMidiSelectDialog: false,
|
||||
|
||||
@@ -477,7 +483,7 @@ const SettingsDialog = withStyles(
|
||||
}
|
||||
|
||||
midiSummary(): string {
|
||||
let ports = this.state.jackSettings.inputMidiDevices;
|
||||
let ports = this.state.alsaSequencerConfiguration.connections;
|
||||
if (ports.length === 0) return "Disabled";
|
||||
|
||||
let result = "";
|
||||
@@ -485,7 +491,7 @@ const SettingsDialog = withStyles(
|
||||
if (result.length !== 0) {
|
||||
result += ", ";
|
||||
}
|
||||
result += port.description;
|
||||
result += port.name;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -689,7 +695,9 @@ const SettingsDialog = withStyles(
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>Select MIDI input</Typography>
|
||||
|
||||
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>{this.midiSummary()}</Typography>
|
||||
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>{
|
||||
this.midiSummary()
|
||||
}</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
<ButtonBase className={classes.setting} disabled={!isConfigValid} onClick={() => this.handleMidiMessageSettings()} >
|
||||
@@ -929,9 +937,7 @@ const SettingsDialog = withStyles(
|
||||
(this.state.showMidiSelectDialog) &&
|
||||
(
|
||||
<SelectMidiChannelsDialog open={this.state.showMidiSelectDialog}
|
||||
onClose={(selectedChannels: AlsaMidiDeviceInfo[] | null) => this.handleSelectMidiDialogResult(selectedChannels)}
|
||||
selectedChannels={this.state.jackSettings.inputMidiDevices}
|
||||
availableChannels={this.state.jackConfiguration.inputMidiDevices}
|
||||
onClose={() => this.handleSelectMidiDialogResult()}
|
||||
/>
|
||||
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ import ButtonBase from '@mui/material/ButtonBase';
|
||||
import FilePropertyDialog from './FilePropertyDialog';
|
||||
import JsonAtom from './JsonAtom';
|
||||
import { UiFileProperty } from './Lv2Plugin';
|
||||
import { Divider } from '@mui/material';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import useWindowSize from './UseWindowSize';
|
||||
import { getAlbumArtUri } from './AudioFileMetadata';
|
||||
import RepeatIcon from '@mui/icons-material/Repeat';
|
||||
|
||||
@@ -81,7 +81,7 @@ function ToolTipEx(props: ToolTipExProps) {
|
||||
if (valueTooltip === undefined) // no timeout if there's a value tooltip
|
||||
{
|
||||
if (t > 0) {
|
||||
console.log("ToolTipEx: starting timeout for ", t);
|
||||
// console.log("ToolTipEx: starting timeout for ", t);
|
||||
handle = window.setTimeout(() => {
|
||||
setOpen(true);
|
||||
},t);
|
||||
@@ -89,7 +89,7 @@ function ToolTipEx(props: ToolTipExProps) {
|
||||
}
|
||||
return () => {
|
||||
if (handle !== null) {
|
||||
console.log("ToolTipEx: clearing timeout for ", t);
|
||||
// console.log("ToolTipEx: clearing timeout for ", t);
|
||||
window.clearTimeout(handle);
|
||||
}
|
||||
};
|
||||
@@ -161,6 +161,7 @@ function ToolTipEx(props: ToolTipExProps) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let effectiveTitle: React.ReactNode | null = null;
|
||||
let placement: "top-start" | "right" = "top-start";
|
||||
if (valueTooltip !== undefined && !isLongPress) {
|
||||
@@ -181,44 +182,40 @@ function ToolTipEx(props: ToolTipExProps) {
|
||||
return (
|
||||
<div style={{ display: 'inline-block' }}
|
||||
onClickCapture={(e) => {
|
||||
console.log("ToolTipEx: onClickCapture");
|
||||
// console.log("ToolTipEx: onClickCapture");
|
||||
handleClickCapture(e);
|
||||
setTimeout(0);
|
||||
setOpen(false);
|
||||
setIsLongPress(false);
|
||||
setLongPressLeaving(true);
|
||||
}}
|
||||
|
||||
onMouseEnter={(e)=> {
|
||||
console.log("ToolTipEx: onMouseEnter");
|
||||
// console.log("ToolTipEx: onMouseEnter");
|
||||
if (!longPressLeaving) {// Don't handle mouse enter if we're in a long press leaving state
|
||||
handleMouseEnter(e);
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e)=> {
|
||||
console.log("ToolTipEx: onMouseLeave");
|
||||
// console.log("ToolTipEx: onMouseLeave");
|
||||
handleMouseLeave(e);
|
||||
}}
|
||||
|
||||
onPointerCancelCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerCancelCapture");
|
||||
// console.log("ToolTipEx: onPointerCancelCapture");
|
||||
handlePointerCancel(e);
|
||||
}}
|
||||
onContextMenuCapture={(e) => {
|
||||
console.log("ToolTipEx: onContextMenuCapture");
|
||||
// console.log("ToolTipEx: onContextMenuCapture");
|
||||
handleConextMenu(e);
|
||||
return false;
|
||||
}}
|
||||
onPointerDownCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerDownCapture");
|
||||
// console.log("ToolTipEx: onPointerDownCapture");
|
||||
handlePointerDownCapture(e);
|
||||
}}
|
||||
onPointerMoveCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerMoveCapture");
|
||||
// console.log("ToolTipEx: onPointerMoveCapture");
|
||||
handlePointerMoveCapture(e);
|
||||
}}
|
||||
onPointerUpCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerUpCapture");
|
||||
// console.log("ToolTipEx: onPointerUpCapture");
|
||||
handlePointerUpCapture(e);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -21,38 +21,43 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const getSize = () => {
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export interface WindowSize {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
export default function useWindowSize() {
|
||||
|
||||
const [size, setSize] = useState<WindowSize>(getSize());
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
let ticking = false;
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
setSize(getSize());
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
return [size];
|
||||
const [size, setSize] = useState<WindowSize>(getSize());
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
let handleResizeT = () => {
|
||||
let pendingCallback = false;
|
||||
if (!pendingCallback) {
|
||||
window.requestAnimationFrame(() => {
|
||||
if (mounted) {
|
||||
setSize(getSize());
|
||||
pendingCallback = false;
|
||||
}
|
||||
});
|
||||
pendingCallback = true;
|
||||
}
|
||||
}
|
||||
window.addEventListener('resize', handleResizeT);
|
||||
return () => {
|
||||
mounted = false;
|
||||
window.removeEventListener('resize', handleResizeT);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return [size];
|
||||
}
|
||||
Reference in New Issue
Block a user