// 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 WithStyles from './WithStyles'; import { css } from '@emotion/react'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import CloseIcon from '@mui/icons-material/Close'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import CssBaseline from '@mui/material/CssBaseline'; import {createStyles} from './WithStyles'; import { withStyles } from "tss-react/mui"; import IconButtonEx from './IconButtonEx'; import FullscreenIcon from '@mui/icons-material/Fullscreen'; import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import { PiPedalModelFactory, PiPedalModel, State, ZoomedControlInfo } from './PiPedalModel'; import ZoomedUiControl from './ZoomedUiControl' import MainPage from './MainPage'; import JackStatusView from './JackStatusView'; import { Theme } from '@mui/material/styles'; import { isDarkMode } from './DarkMode'; import Typography from '@mui/material/Typography'; import TextField from '@mui/material/TextField'; import { Snapshot } from './Pedalboard'; import ColorDropdownButton, { DropdownAlignment } from './ColorDropdownButton'; import { getBackgroundColor } from './MaterialColors'; const selectColor = isDarkMode() ? "#888" : "#FFFFFF"; const appStyles = (theme: Theme) => createStyles({ "&": css({ // :root colorScheme: (isDarkMode() ? "dark" : "light") }), mainFrame: css({ overflow: "hidden", display: "flex", flexFlow: "column", marginTop: 8, flex: "1 1 100%" }), heroContent: css({ backgroundColor: theme.mainBackground, position: "relative", height: "100%", width: "100%" }), shadowCatcher: css({ backgroundColor: theme.mainBackground }), 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, }) }); function supportsFullScreen(): boolean { let doc: any = window.document; let docEl: any = doc.documentElement; var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; return (!!requestFullScreen); } function setFullScreen(value: boolean) { let doc: any = window.document; let docEl: any = doc.documentElement; var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { requestFullScreen.call(docEl); } else { cancelFullScreen.call(doc); } } type SnapshotEditorState = { zoomedControlInfo: ZoomedControlInfo | undefined; zoomedControlOpen: boolean; canFullScreen: boolean; isFullScreen: boolean; isDebug: boolean; collapseLabel: boolean; showStatusMonitor: boolean; name: string, color: string }; interface SnapshotEditorProps extends WithStyles { onClose: () => void; onOk: (snapshotIndex: number, name: string, color: string, newSnapshots: (Snapshot | null)[]) => void; snapshotIndex: number; } const SnapshotEditor = withStyles( class extends ResizeResponsiveComponent { // Before the component mounts, we initialise our state model_: PiPedalModel; getCollapseLabel() { return this.windowSize.width < 500; } constructor(props: SnapshotEditorProps) { super(props); this.model_ = PiPedalModelFactory.getInstance(); this.model_.zoomedUiControl.addOnChangedHandler( () => { this.setState({ zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, zoomedControlInfo: this.model_.zoomedUiControl.get() }); } ); let snapshot = this.model_.pedalboard.get().snapshots[this.props.snapshotIndex] ?? new Snapshot(); this.state = { zoomedControlOpen: false, zoomedControlInfo: this.model_.zoomedUiControl.get(), canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted(), isFullScreen: !!document.fullscreenElement, showStatusMonitor: this.model_.showStatusMonitor.get(), isDebug: true, name: snapshot.name, color: snapshot.color, collapseLabel: this.getCollapseLabel() }; this.showStatusMonitorHandler = this.showStatusMonitorHandler.bind(this); } showStatusMonitorHandler() { this.setState({ showStatusMonitor: this.model_.showStatusMonitor.get() }); } toggleFullScreen(): void { setFullScreen(this.state.isFullScreen); this.setState({ isFullScreen: !this.state.isFullScreen }); } componentDidMount() { super.componentDidMount(); this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); } updateOverscroll(): void { if (this.model_.serverVersion) { // no pull-down refresh on android devices once we're ready (unless we're debug) let preventOverscroll = this.model_.state.get() === State.Ready && !this.model_.debug; let overscrollBehavior = preventOverscroll ? "none" : "auto"; document.body.style.overscrollBehavior = overscrollBehavior; } } componentDidUpdate() { } componentWillUnmount() { super.componentWillUnmount(); this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler); } onWindowSizeChanged(width: number, height: number): void { super.onWindowSizeChanged(width, height); this.setState({ collapseLabel: this.getCollapseLabel() }); } handleOk() { let currentPedalboard = this.model_.pedalboard.get(); let selectedSnapshot = this.props.snapshotIndex; let currentSnapshot: Snapshot | null = currentPedalboard.snapshots[selectedSnapshot]; if (!currentSnapshot) { this.props.onClose(); return; } let changed = this.state.name !== currentSnapshot.name || this.state.color !== currentSnapshot.color || currentSnapshot.isModified; if (!changed) { this.props.onClose(); return; } let newSnapshots = Snapshot.cloneSnapshots(currentPedalboard.snapshots); let newSnapshot = this.model_.pedalboard.get().makeSnapshot(); newSnapshot.name = this.state.name; newSnapshot.color = this.state.color; newSnapshots[selectedSnapshot] = newSnapshot; this.model_.setSnapshots(newSnapshots, selectedSnapshot); this.props.onOk(selectedSnapshot, this.state.name, this.state.color, newSnapshots); } render() { const classes = withStyles.getClasses(this.props); return (
{ if (!this.model_.debug) { e.preventDefault(); e.stopPropagation(); } }} > { this.handleOk(); }} size="large"> {!this.state.collapseLabel && ( {'Snapshot ' + (this.props.snapshotIndex + 1)} )}
{ this.setState({ name: ev.target.value }); }} />
{ this.setState({ color: newColor }); }} /> {this.state.canFullScreen && { this.toggleFullScreen(); }} color="inherit" size="medium"> {this.state.isFullScreen ? ( ) : ( )} } { this.props.onClose(); }} color="inherit" size="medium">
{ this.setState({ zoomedControlOpen: false }); }} onDialogClosed={() => { this.model_.zoomedUiControl.set(undefined); } } /> {this.state.showStatusMonitor && ()}
); } }, appStyles ); export default SnapshotEditor;