// Copyright (c) 2024 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 // 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 { Theme } from '@mui/material/styles'; import BankIcon from './svg/ic_bank.svg?react'; import Typography from '@mui/material/Typography'; import SaveIconOutline from '@mui/icons-material/Save'; import WithStyles from './WithStyles'; import { createStyles } from './WithStyles'; import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import IconButtonEx from './IconButtonEx'; import SnapshotPanel from './SnapshotPanel'; import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; //import ArrowLeftOutlined from '@mui/icons-material/ArrowLeft'; //import ArrowRightOutlined from '@mui/icons-material/ArrowRight'; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import { isDarkMode } from './DarkMode'; import { BankIndex } from './Banks'; import SnapshotEditor from './SnapshotEditor'; import { Snapshot } from './Pedalboard'; import JackStatusView from './JackStatusView'; import { IDialogStackable, popDialogStack, pushDialogStack } from './DialogStack'; import { css } from '@emotion/react'; const selectColor = isDarkMode() ? "#888" : "#FFFFFF"; const styles = (theme: Theme) => createStyles({ listIcon: css({ width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary }), frame: css({ position: "absolute", display: "flex", flexDirection: "column", flexWrap: "nowrap", justifyContent: "flex-start", left: "0px", top: "0px", bottom: "0px", right: "0px", overflow: "hidden" }), select: css({ // fu fu fu.Overrides for white selector on dark background. '&:before': { borderColor: selectColor, }, '&:after': { borderColor: selectColor, }, '&:hover:not(.Mui-disabled):before': { borderColor: selectColor, } }), select_icon: css({ fill: selectColor, }), }); interface PerformanceViewProps extends WithStyles { open: boolean, onClose: () => void, } interface PerformanceViewState { wrapSelects: boolean; largeAppBar: boolean; presets: PresetIndex; banks: BankIndex; showSnapshotEditor: boolean; showStatusMonitor: boolean; snapshotEditorIndex: number; presetModified: boolean; } export const PerformanceView = withStyles( class extends ResizeResponsiveComponent implements IDialogStackable { model: PiPedalModel; constructor(props: PerformanceViewProps) { super(props); this.model = PiPedalModelFactory.getInstance(); // let pedalboard = this.model.pedalboard.get(); // let selectedPedal = pedalboard.getFirstSelectableItem(); this.state = { presets: this.model.presets.get(), banks: this.model.banks.get(), wrapSelects: false, largeAppBar: this.updateAppBarZoom(), showSnapshotEditor: false, showStatusMonitor: this.model.showStatusMonitor.get(), snapshotEditorIndex: 0, presetModified: this.model.presetChanged.get() }; this.onPresetsChanged = this.onPresetsChanged.bind(this); this.onBanksChanged = this.onBanksChanged.bind(this); this.onPresetChangedChanged = this.onPresetChangedChanged.bind(this); this.showStatusMonitorHandler = this.showStatusMonitorHandler.bind(this); } updateAppBarZoom() { return this.windowSize.width > 700; } showStatusMonitorHandler() { this.setState({ showStatusMonitor: this.model.showStatusMonitor.get() }); } getTag() { return "performView" } isOpen() { return this.props.open } onDialogStackClose() { this.props.onClose(); } private hasHooks: boolean = false; updateHooks(): void { let wantHooks = this.mounted && this.props.open; if (wantHooks !== this.hasHooks) { this.hasHooks = wantHooks; if (this.hasHooks) { pushDialogStack(this); } else { popDialogStack(this); } } } onWindowSizeChanged(width: number, height: number): void { this.setState({ wrapSelects: width < 700, largeAppBar: this.updateAppBarZoom(), }); } onPresetsChanged(newValue: PresetIndex) { this.setState({ presets: this.model.presets.get() }) } onBanksChanged(newValue: BankIndex) { this.setState({ banks: this.model.banks.get() }) } onPresetChangedChanged(newValue: boolean) { this.setState({ presetModified: newValue }); } private mounted: boolean = false; componentDidMount(): void { super.componentDidMount(); this.mounted = true; this.model.presets.addOnChangedHandler(this.onPresetsChanged); this.model.banks.addOnChangedHandler(this.onBanksChanged); this.model.presetChanged.addOnChangedHandler(this.onPresetChangedChanged) this.model.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); this.setState({ presets: this.model.presets.get(), banks: this.model.banks.get(), presetModified: this.model.presetChanged.get() }); this.updateHooks(); } componentWillUnmount(): void { this.model.presetChanged.removeOnChangedHandler(this.onPresetChangedChanged) this.model.presets.removeOnChangedHandler(this.onPresetsChanged); this.model.banks.removeOnChangedHandler(this.onBanksChanged); this.model.banks.removeOnChangedHandler(this.showStatusMonitorHandler); this.mounted = false; this.updateHooks(); super.componentWillUnmount(); } handlePresetSelectClose(event: any): void { let value = event.currentTarget.getAttribute("data-value"); if (value && value.length > 0) { this.model.loadPreset(parseInt(value)); } } handleBankSelectClose(event: any): void { let value = event.currentTarget.getAttribute("data-value"); if (value && value.length > 0) { this.model.openBank(parseInt(value)); } } handleOnEdit(index: number): boolean { // load it so we can edit it. this.model.selectSnapshot(index); this.setState({ showSnapshotEditor: true, snapshotEditorIndex: index }); return true; } handleSnapshotEditOk(index: number, name: string, color: string, newSnapshots: (Snapshot | null)[]) { // results have been sent to the server. we would perform a short-cut commit to our local state, to avoid laggy display updates. // but we don't actually have that in our state. } handlePreviousBank() { this.model.previousBank(); } handleNextBank() { this.model.nextBank(); } handlePreviousPreset() { this.model.previousPreset(); } handleNextPreset() { this.model.nextPreset(); } render() { const classes = withStyles.getClasses(this.props); let presets = this.state.presets; let banks = this.state.banks; let appBarIconSize: "large" | undefined = this.state.largeAppBar ? undefined : "large"; return (
{this.state.largeAppBar ? (
{ this.props.onClose(); }} style={{ position: "relative", top: 3, flex: "0 0 auto" }} > {/********* PRESETS *******************/}
{ this.handlePreviousPreset(); }} color="inherit" style={{ borderTopRightRadius: "3px", borderBottomRightRadius: "3px", marginRight: 4 }} > { this.handleNextPreset(); }} color="inherit" style={{ borderTopLeftRadius: "3px", borderBottomLeftRadius: "3px", marginLeft: 4 }} > { this.model.saveCurrentPreset(); }} size="medium" color="inherit" style={{ flexShrink: 0, visibility: this.state.presetModified ? "visible" : "hidden", marginRight: 16 }} >
{/********* BANKS *******************/}
{/** spacer */}
) : (
{ this.props.onClose(); }} style={{ position: "relative", top: 3, flex: "0 0 auto" }} >
{/********* BANKS *******************/}
{/**** 2nd row ****/}
{/********* PRESETS *******************/} { this.handlePreviousPreset(); }} color="inherit" style={{ borderTopRightRadius: "3px", borderBottomRightRadius: "3px", marginRight: 4 }} > { this.handleNextPreset(); }} color="inherit" style={{ borderTopLeftRadius: "3px", borderBottomLeftRadius: "3px", marginLeft: 4 }} > { this.model.saveCurrentPreset(); }} size="medium" color="inherit" style={{ flexShrink: 0, visibility: this.state.presetModified ? "visible" : "hidden" }} >
)}
{ return this.handleOnEdit(index); }} />
{!this.state.showSnapshotEditor && this.state.showStatusMonitor && ( )}
{this.state.showSnapshotEditor && ( { this.setState({ showSnapshotEditor: false }); }} onOk={(index, name, color, newSnapshots) => { this.setState({ showSnapshotEditor: false }); this.handleSnapshotEditOk(index, name, color, newSnapshots); }} /> )}
) } }, styles );