Touch tooltips, more tooltips
This commit is contained in:
@@ -46,3 +46,6 @@ input[type=number] {
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
user-select: none;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import { withStyles } from "tss-react/mui";
|
||||
import WithStyles from './WithStyles';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism';
|
||||
import MenuButton from '@mui/icons-material/Menu';
|
||||
import { TemporaryDrawer } from './TemporaryDrawer';
|
||||
@@ -802,21 +802,23 @@ export
|
||||
(
|
||||
<AppBar position="absolute" >
|
||||
<Toolbar variant="dense" className={classes.toolBar} >
|
||||
<IconButton
|
||||
<IconButtonEx tooltip="Menu"
|
||||
edge="start"
|
||||
aria-label="menu"
|
||||
color="inherit"
|
||||
onClick={() => { this.showDrawer() }}
|
||||
size="large">
|
||||
<MenuButton style={{ opacity: 0.75 }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<div style={{ flex: "0 1 400px", minWidth: 100 }}>
|
||||
<PresetSelector />
|
||||
</div>
|
||||
<div style={{ flex: "2 2 30px" }} />
|
||||
{this.state.canFullScreen &&
|
||||
<IconButton
|
||||
aria-label="menu"
|
||||
<IconButtonEx
|
||||
aria-label="maximise/minimise"
|
||||
tooltip={this.state.isFullScreen ? "Exit full screen" : "Full screen"}
|
||||
style={{ marginRight: 8, marginLeft: 8 }}
|
||||
onClick={() => { this.toggleFullScreen(); }}
|
||||
color="inherit"
|
||||
size="large">
|
||||
@@ -827,22 +829,24 @@ export
|
||||
|
||||
)}
|
||||
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
) : (
|
||||
<div className={classes.toolBarContent} >
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Menu"
|
||||
style={{ position: "absolute", left: 12, top: 8, zIndex: 2 }}
|
||||
aria-label="menu"
|
||||
onClick={() => { this.showDrawer() }}
|
||||
color="inherit"
|
||||
size="large">
|
||||
<MenuButton />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
{this.state.canFullScreen && (
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip={this.state.isFullScreen ? "Exit full screen" : "Full screen"}
|
||||
style={{ position: "absolute", right: 8, top: 8, zIndex: 2 }}
|
||||
aria-label="menu"
|
||||
color="inherit"
|
||||
@@ -855,7 +859,7 @@ export
|
||||
|
||||
)}
|
||||
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -9,30 +9,42 @@ import ToolTipEx from './ToolTipEx'
|
||||
interface ControlTooltipProps {
|
||||
children: ReactElement,
|
||||
uiControl: UiControl
|
||||
valueTooltip?: React.ReactNode;
|
||||
}
|
||||
|
||||
|
||||
export default function ControlTooltip(props: ControlTooltipProps) {
|
||||
let { children, uiControl } = props;
|
||||
let { children, uiControl, valueTooltip } = props;
|
||||
if (uiControl.comment && (uiControl.comment !== uiControl.name)) {
|
||||
return (
|
||||
<ToolTipEx title={(
|
||||
<React.Fragment>
|
||||
<Typography variant="caption">{uiControl.name}</Typography>
|
||||
<Divider />
|
||||
<Typography variant="caption">{uiControl.comment}</Typography>
|
||||
<ToolTipEx
|
||||
valueTooltip={valueTooltip}
|
||||
title={
|
||||
(
|
||||
<React.Fragment>
|
||||
<Typography variant="caption">{uiControl.name}</Typography>
|
||||
<Divider />
|
||||
<Typography variant="caption">{uiControl.comment}</Typography>
|
||||
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
</ToolTipEx>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ToolTipEx title={uiControl.name}
|
||||
<ToolTipEx valueTooltip={valueTooltip}
|
||||
title={
|
||||
(
|
||||
<Typography variant="caption">{uiControl.name}</Typography>
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
<div >
|
||||
{children}
|
||||
</div>
|
||||
</ToolTipEx>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import OldDeleteIcon from './OldDeleteIcon';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
@@ -995,7 +994,8 @@ export default withStyles(
|
||||
}} >
|
||||
{this.state.reordering && (
|
||||
<Toolbar style={{ padding: 0 }}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Close"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="cancel"
|
||||
@@ -1004,7 +1004,7 @@ export default withStyles(
|
||||
}
|
||||
>
|
||||
<CloseIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
Reorder files
|
||||
</Typography>
|
||||
@@ -1013,7 +1013,8 @@ export default withStyles(
|
||||
)}
|
||||
{this.state.multiSelect && (
|
||||
<Toolbar style={{ padding: 0 }}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Close"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="cancel"
|
||||
@@ -1023,7 +1024,7 @@ export default withStyles(
|
||||
}}
|
||||
>
|
||||
<CloseIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
{this.state.selectedFiles.length.toString() + " files selected."}
|
||||
</Typography>
|
||||
@@ -1071,7 +1072,8 @@ export default withStyles(
|
||||
{!(this.state.reordering || this.state.multiSelect) && (
|
||||
<>
|
||||
<Toolbar style={{ padding: 0 }}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="back"
|
||||
@@ -1079,7 +1081,7 @@ export default withStyles(
|
||||
onClick={() => { this.props.onCancel(); }}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
{this.props.fileProperty.label}
|
||||
</Typography>
|
||||
|
||||
@@ -29,7 +29,7 @@ import TextField, { StandardTextFieldProps } from "@mui/material/TextField";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import DialogEx from "./DialogEx";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import IconButtonEx from "./IconButtonEx";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Slider, { SliderProps } from "@mui/material/Slider";
|
||||
@@ -456,7 +456,8 @@ export default function LoopDialog(props: LoopDialogProps) {
|
||||
>
|
||||
<DialogTitle style={{ paddingTop: 0 }}>
|
||||
<Toolbar style={{ padding: 0 }}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="back"
|
||||
@@ -467,7 +468,7 @@ export default function LoopDialog(props: LoopDialogProps) {
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
Loop
|
||||
</Typography>
|
||||
|
||||
@@ -33,7 +33,8 @@ import Utility from './Utility';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined';
|
||||
import MicOutlinedIcon from '@mui/icons-material/MicOutlined';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
|
||||
import NumericInput from './NumericInput';
|
||||
|
||||
|
||||
@@ -259,7 +260,8 @@ const MidiBindingView =
|
||||
this.generateMidiNoteSelects()
|
||||
}
|
||||
</Select>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Listen for MIDI input"
|
||||
onClick={() => {
|
||||
if (this.props.listen) {
|
||||
this.props.onListen(-2, "", false)
|
||||
@@ -273,7 +275,7 @@ const MidiBindingView =
|
||||
) : (
|
||||
<MicNoneOutlinedIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -292,7 +294,8 @@ const MidiBindingView =
|
||||
this.generateControlSelects()
|
||||
}
|
||||
</Select>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Listen for MIDI input"
|
||||
onClick={() => {
|
||||
if (this.props.listen) {
|
||||
this.props.onListen(-2, "", false)
|
||||
@@ -306,7 +309,7 @@ const MidiBindingView =
|
||||
) : (
|
||||
<MicNoneOutlinedIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ import { withStyles } from "tss-react/mui";
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import MidiBinding from './MidiBinding';
|
||||
import MidiBindingView from './MidiBindingView';
|
||||
import Snackbar from '@mui/material/Snackbar';
|
||||
@@ -350,14 +350,15 @@ export const MidiBindingDialog =
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} >
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
onClick={this.handleClose}
|
||||
aria-label="back"
|
||||
size="large">
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap variant="h6" className={classes.dialogTitle}>
|
||||
Preset MIDI Bindings
|
||||
</Typography>
|
||||
|
||||
@@ -27,8 +27,7 @@ import DialogActions from '@mui/material/DialogActions';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import ChannelBindingHelpDialog from './ChannelBindingsHelpDialog';
|
||||
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
@@ -167,7 +166,9 @@ function MidiChannelBindingDialog(props: MidiChannelBindingDialogProps) {
|
||||
} label="Allow Program Changes"
|
||||
/>
|
||||
{false&&( // wait until the implementation stabilizes before exposing the help dialog.
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Help"
|
||||
aria-label="help"
|
||||
onClick={() => { setHelpDialog(true); }}
|
||||
size="large">
|
||||
<InfoOutlinedIcon color='inherit'
|
||||
@@ -176,7 +177,7 @@ function MidiChannelBindingDialog(props: MidiChannelBindingDialogProps) {
|
||||
opacity: 0.6
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ export interface PluginControlProps extends WithStyles<typeof pluginControlStyle
|
||||
type PluginControlState = {
|
||||
error: boolean;
|
||||
editFocused: boolean;
|
||||
previewValue?: string;
|
||||
};
|
||||
|
||||
const PluginControl =
|
||||
@@ -167,7 +168,9 @@ const PluginControl =
|
||||
|
||||
this.state = {
|
||||
error: false,
|
||||
editFocused: false
|
||||
editFocused: false,
|
||||
previewValue: undefined
|
||||
|
||||
};
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.imgRef = React.createRef();
|
||||
@@ -406,6 +409,7 @@ const PluginControl =
|
||||
img.style.opacity = "" + SELECTED_OPACITY;
|
||||
}
|
||||
}
|
||||
this.setState({ previewValue: undefined });
|
||||
|
||||
} else {
|
||||
if (this.isExtraTouch(e)) {
|
||||
@@ -494,7 +498,9 @@ const PluginControl =
|
||||
if (this.pointersDown !== 0) {
|
||||
--this.pointersDown;
|
||||
}
|
||||
|
||||
if (this.pointersDown === 0) {
|
||||
this.setState({ previewValue: undefined });
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -543,7 +549,7 @@ const PluginControl =
|
||||
}
|
||||
|
||||
clickSlop() {
|
||||
return 3.5; // maybe larger on touch devices.
|
||||
return 5; // maybe larger on touch devices.
|
||||
}
|
||||
onPointerMove(e: PointerEvent<SVGSVGElement>): void {
|
||||
if (this.isCapturedPointer(e)) {
|
||||
@@ -604,6 +610,7 @@ const PluginControl =
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setState({ previewValue: undefined});
|
||||
}
|
||||
previewInputValue(value: number, commitValue: boolean) {
|
||||
let range = this.valueToRange(value);
|
||||
@@ -667,6 +674,12 @@ const PluginControl =
|
||||
if (displayValue) {
|
||||
let v = this.formatDisplayValue(this.props.uiControl, value);
|
||||
displayValue.childNodes[0].textContent = v;
|
||||
|
||||
if (commitValue) {
|
||||
this.setState({ previewValue: undefined });
|
||||
} else {
|
||||
this.setState({ previewValue: v });
|
||||
}
|
||||
}
|
||||
let selectElement = this.selectRef.current;
|
||||
if (selectElement) {
|
||||
@@ -1026,7 +1039,8 @@ const PluginControl =
|
||||
)
|
||||
: (isGraphicEq) ? (
|
||||
<div style={{ flex: "0 1 auto" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
|
||||
<ControlTooltip uiControl={control} valueTooltip={this.state.previewValue}>
|
||||
<GraphicEqCtl
|
||||
imgRef={this.imgRef}
|
||||
position={this.getEqPosition()}
|
||||
@@ -1045,7 +1059,8 @@ const PluginControl =
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ flex: "0 1 auto" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<ControlTooltip uiControl={control}
|
||||
valueTooltip={this.state.previewValue}>
|
||||
<DialIcon ref={this.imgRef}
|
||||
style={{
|
||||
overscrollBehavior: "none", touchAction: "none", fill: dialColor,
|
||||
@@ -1053,7 +1068,7 @@ const PluginControl =
|
||||
}}
|
||||
onTouchStart={this.onTouchStart} onTouchMove={this.onTouchMove}
|
||||
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp}
|
||||
onPointerMoveCapture={this.onPointerMove}
|
||||
onPointerMove={this.onPointerMove}
|
||||
onDrag={this.onDrag}
|
||||
|
||||
/>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import { SyntheticEvent, Component } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel';
|
||||
import SaveIconOutline from '@mui/icons-material/Save';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
@@ -300,12 +300,12 @@ const PresetSelector =
|
||||
justifyContent: "left", flexWrap: "nowrap", alignItems: "center", height: "100%", position: "relative"
|
||||
}}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<IconButton
|
||||
<IconButtonEx tooltip="Save current preset"
|
||||
style={{ flex: "0 0 auto", opacity: this.state.presets.presetChanged ? 1.0 : 0.0, color: "#FFFFFF" }}
|
||||
onClick={(e) => { this.handleSave(); }}
|
||||
size="large">
|
||||
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: "1 1 auto", minWidth: 60, maxWidth: 300, position: "relative", paddingRight: 12 }} >
|
||||
@@ -337,13 +337,14 @@ const PresetSelector =
|
||||
</Select>
|
||||
</div>
|
||||
<div style={{ flex: "0 0 auto"}}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="More..."
|
||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||
onClick={(e) => this.handlePresetMenuClick(e)}
|
||||
size="large"
|
||||
>
|
||||
<MoreVertIcon style={{ opacity: 0.75 }} color="inherit" />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Menu
|
||||
id="edit-presets-menu"
|
||||
anchorEl={this.state.presetsMenuAnchorRef}
|
||||
|
||||
@@ -24,6 +24,7 @@ import ListItemButton from '@mui/material/ListItemButton';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
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';
|
||||
@@ -110,6 +111,10 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
|
||||
)
|
||||
|
||||
)}
|
||||
{availableChannels.length === 0 && (
|
||||
<Typography variant="body2" style={{ marginLeft: 32, marginRight: 24,marginTop:8, marginBottom: 16}}>
|
||||
No MIDI devices found.</Typography>
|
||||
)}
|
||||
</List>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||
|
||||
@@ -27,7 +27,7 @@ import CssBaseline from '@mui/material/CssBaseline';
|
||||
import {createStyles} from './WithStyles';
|
||||
|
||||
import { withStyles } from "tss-react/mui";
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import FullscreenIcon from '@mui/icons-material/Fullscreen';
|
||||
import FullscreenExitIcon from '@mui/icons-material/FullscreenExit';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
@@ -271,14 +271,15 @@ const SnapshotEditor = withStyles(
|
||||
<CssBaseline />
|
||||
<AppBar position="static" sx={{ bgcolor: isDarkMode() ? getBackgroundColor("purple") : "#200040" }} >
|
||||
<Toolbar variant="dense" >
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
aria-label="menu"
|
||||
color="inherit"
|
||||
onClick={() => { this.handleOk(); }}
|
||||
size="large">
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
{!this.state.collapseLabel && (
|
||||
<Typography style={{ flex: "0 1 auto", opacity: 0.66 }}
|
||||
variant="body1" noWrap
|
||||
@@ -310,7 +311,8 @@ const SnapshotEditor = withStyles(
|
||||
this.setState({ color: newColor });
|
||||
}} />
|
||||
{this.state.canFullScreen &&
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip={this.state.isFullScreen ? "Exit Full Screen" : "Full Screen"}
|
||||
aria-label="full-screen"
|
||||
onClick={() => { this.toggleFullScreen(); }}
|
||||
color="inherit"
|
||||
@@ -322,15 +324,16 @@ const SnapshotEditor = withStyles(
|
||||
|
||||
)}
|
||||
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
}
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Cancel"
|
||||
aria-label="cancel"
|
||||
onClick={() => { this.props.onClose(); }}
|
||||
color="inherit"
|
||||
size="medium">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
@@ -36,7 +36,7 @@ import MidiBinding from './MidiBinding';
|
||||
import Utility from './Utility';
|
||||
import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined';
|
||||
import MicOutlinedIcon from '@mui/icons-material/MicOutlined';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
|
||||
|
||||
|
||||
@@ -195,7 +195,8 @@ const SystemMidiBindingView =
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Listen for MIDI input"
|
||||
onClick={() => {
|
||||
if (this.props.listen) {
|
||||
this.props.onListen(-2, "", false)
|
||||
@@ -209,7 +210,7 @@ const SystemMidiBindingView =
|
||||
) : (
|
||||
<MicNoneOutlinedIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -31,13 +31,13 @@ import { PiPedalModel, PiPedalModelFactory, ListenHandle } from './PiPedalModel'
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
import WithStyles from './WithStyles';
|
||||
import {createStyles} from './WithStyles';
|
||||
import { createStyles } from './WithStyles';
|
||||
|
||||
import { withStyles } from "tss-react/mui";
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import MidiBinding from './MidiBinding';
|
||||
import SystemMidiBindingView from './SystemMidiBindingView';
|
||||
import Snackbar from '@mui/material/Snackbar';
|
||||
@@ -69,11 +69,11 @@ const styles = (theme: Theme) => createStyles({
|
||||
paddingTop: 12
|
||||
}),
|
||||
plainRow: css({
|
||||
borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: "transparent"
|
||||
borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: "transparent"
|
||||
}),
|
||||
dividerRow: css({
|
||||
borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: theme.palette.divider
|
||||
})
|
||||
borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: theme.palette.divider
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
@@ -139,7 +139,7 @@ export const SystemMidiBindingDialog =
|
||||
}
|
||||
else if (item.symbol === "nextProgram") {
|
||||
displayName = "Next Preset";
|
||||
}else if (item.symbol === "prevProgram") {
|
||||
} else if (item.symbol === "prevProgram") {
|
||||
displayName = "Previous Preset";
|
||||
}
|
||||
else if (item.symbol === "snapshot1") {
|
||||
@@ -170,8 +170,7 @@ export const SystemMidiBindingDialog =
|
||||
} else {
|
||||
found = false;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
if (found) {
|
||||
result.push(new BindingEntry(displayName, listenInstanceId, item));
|
||||
++listenInstanceId;
|
||||
}
|
||||
@@ -277,12 +276,11 @@ export const SystemMidiBindingDialog =
|
||||
|
||||
for (var item of items) {
|
||||
let symbol = item.midiBinding.symbol;
|
||||
let hasDivider = symbol === "snapshot1" || symbol === "stopHotspot" || symbol === "shotdown";
|
||||
if (hasDivider)
|
||||
{
|
||||
let hasDivider = symbol === "snapshot1" || symbol === "stopHotspot" || symbol === "shotdown";
|
||||
if (hasDivider) {
|
||||
result.push(
|
||||
<tr>
|
||||
<td colSpan={2} className={classes.dividerRow}><div style={{height: 1}} /></td>
|
||||
<td colSpan={2} className={classes.dividerRow}><div style={{ height: 1 }} /></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
@@ -321,7 +319,7 @@ export const SystemMidiBindingDialog =
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
let { open} = props;
|
||||
let { open } = props;
|
||||
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
if (!open) {
|
||||
@@ -332,20 +330,21 @@ export const SystemMidiBindingDialog =
|
||||
<DialogEx tag="systemMidiBindings" open={open} fullWidth onClose={this.handleClose} aria-labelledby="Rename-dialog-title"
|
||||
fullScreen={true}
|
||||
style={{ userSelect: "none" }}
|
||||
onEnterKey={()=>{}}
|
||||
onEnterKey={() => { }}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} >
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
onClick={this.handleClose}
|
||||
aria-label="back"
|
||||
size="large">
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
System MIDI Bindings
|
||||
</Typography>
|
||||
|
||||
@@ -23,7 +23,7 @@ import { withStyles } from "tss-react/mui";
|
||||
import {createStyles} from './WithStyles';
|
||||
|
||||
|
||||
import IconButton from '@mui/material/Toolbar';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
@@ -100,9 +100,10 @@ export const TemporaryDrawer = withStyles(
|
||||
>
|
||||
<div style={{ display: "flex", flexFlow: "row nowrap", justifyContent: "flex-start", alignItems: "center", width: "100%"}}>
|
||||
|
||||
<IconButton style={{ flex: "0 0 auto" }} >
|
||||
<IconButtonEx tooltip="Back"
|
||||
style={{ flex: "0 0 auto" }} >
|
||||
<ArrowBackIcon style={{ fill: '#666' }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<img src="img/Pi-Logo-3.png" alt="" style={{height: 36}} />
|
||||
</div>
|
||||
{this.props.children}
|
||||
|
||||
@@ -32,7 +32,7 @@ import TextField, { StandardTextFieldProps } from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import Timebase, { TimebaseUnits } from './Timebase';
|
||||
|
||||
@@ -198,7 +198,8 @@ export default function TimebaseSelectorDialog(props: TimebaseSelectorDialogProp
|
||||
>
|
||||
<DialogTitle style={{ paddingTop: 0 }}>
|
||||
<Toolbar style={{ padding: 0 }}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="back"
|
||||
@@ -206,7 +207,7 @@ export default function TimebaseSelectorDialog(props: TimebaseSelectorDialogProp
|
||||
onClick={() => { handleClose(); }}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
Timebase Settings
|
||||
</Typography>
|
||||
|
||||
@@ -27,13 +27,12 @@ import LoopDialog from './LoopDialog';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ButtonEx from './ButtonEx';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Pause from '@mui/icons-material/Pause';
|
||||
import PlayArrow from '@mui/icons-material/PlayArrow';
|
||||
import FastForward from '@mui/icons-material/FastForward';
|
||||
import FastRewind from '@mui/icons-material/FastRewind';
|
||||
import { PiPedalModelFactory, State } from './PiPedalModel';
|
||||
import ButtonTooltip from './ButtonTooltip';
|
||||
import { pathFileNameOnly } from './FileUtils';
|
||||
import ButtonBase from '@mui/material/ButtonBase';
|
||||
import FilePropertyDialog from './FilePropertyDialog';
|
||||
@@ -351,61 +350,56 @@ export default function ToobPlayerControl(
|
||||
},
|
||||
})}
|
||||
>
|
||||
<ButtonTooltip title="Previous track">
|
||||
<IconButton
|
||||
style={{ opacity: (pluginState === PluginState.Idle ? 0.2 : 0.7) }}
|
||||
onClick={() => {
|
||||
if (position > start + 3.0) {
|
||||
<IconButtonEx tooltip="Previous track"
|
||||
style={{ opacity: (pluginState === PluginState.Idle ? 0.2 : 0.7) }}
|
||||
onClick={() => {
|
||||
if (position > start + 3.0) {
|
||||
model.sendPedalboardControlTrigger(
|
||||
props.instanceId,
|
||||
"stop",
|
||||
1
|
||||
);
|
||||
} else {
|
||||
onPreviousTrack();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FastRewind fontSize="large" />
|
||||
</IconButtonEx>
|
||||
<IconButtonEx
|
||||
tooltip={paused ? "Play" : "Pause"}
|
||||
|
||||
style={{ opacity: (pluginState === PluginState.Idle ? 0.2 : 0.7) }}
|
||||
onClick={() => {
|
||||
if (pluginState != PluginState.Idle) {
|
||||
if (paused) {
|
||||
model.sendPedalboardControlTrigger(
|
||||
props.instanceId,
|
||||
"stop",
|
||||
"play",
|
||||
1
|
||||
);
|
||||
} else {
|
||||
onPreviousTrack();
|
||||
model.sendPedalboardControlTrigger(
|
||||
props.instanceId,
|
||||
"pause",
|
||||
1
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FastRewind fontSize="large" />
|
||||
</IconButton>
|
||||
</ButtonTooltip>
|
||||
<ButtonTooltip title="Play/Pause">
|
||||
<IconButton
|
||||
|
||||
style={{ opacity: (pluginState === PluginState.Idle ? 0.2 : 0.7) }}
|
||||
onClick={() => {
|
||||
if (pluginState != PluginState.Idle) {
|
||||
if (paused) {
|
||||
model.sendPedalboardControlTrigger(
|
||||
props.instanceId,
|
||||
"play",
|
||||
1
|
||||
);
|
||||
} else {
|
||||
model.sendPedalboardControlTrigger(
|
||||
props.instanceId,
|
||||
"pause",
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{paused ? (
|
||||
<PlayArrow sx={{ fontSize: '3rem' }} />
|
||||
) : (
|
||||
<Pause sx={{ fontSize: '3rem' }} />
|
||||
)}
|
||||
</IconButton>
|
||||
</ButtonTooltip>
|
||||
<ButtonTooltip title="Next track">
|
||||
<IconButton
|
||||
style={{ opacity: (pluginState == PluginState.Idle ? 0.2 : 0.7) }}
|
||||
onClick={() => { onNextTrack(); }}
|
||||
>
|
||||
<FastForward fontSize="large" />
|
||||
</IconButton>
|
||||
</ButtonTooltip>
|
||||
}
|
||||
}}
|
||||
>
|
||||
{paused ? (
|
||||
<PlayArrow sx={{ fontSize: '3rem' }} />
|
||||
) : (
|
||||
<Pause sx={{ fontSize: '3rem' }} />
|
||||
)}
|
||||
</IconButtonEx>
|
||||
<IconButtonEx tooltip="Next track"
|
||||
style={{ opacity: (pluginState == PluginState.Idle ? 0.2 : 0.7) }}
|
||||
onClick={() => { onNextTrack(); }}
|
||||
>
|
||||
<FastForward fontSize="large" />
|
||||
</IconButtonEx>
|
||||
</Box>
|
||||
|
||||
);
|
||||
|
||||
+189
-15
@@ -25,39 +25,213 @@ import React from 'react';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
|
||||
export interface ToolTipExProps extends React.ComponentProps<typeof Tooltip> {
|
||||
valueTooltip?: React.ReactNode; // For use with value prop, to show a tooltip on the value.
|
||||
}
|
||||
|
||||
/* ToolTipEx: a reimplementation of the MUI Tooltip that works with pointer events and long presses. */
|
||||
|
||||
function ToolTipEx(props: ToolTipExProps) {
|
||||
let [disableToolTip, setDisableToolTip] = React.useState(false);
|
||||
let {title, ...extras} = props;
|
||||
function handleMouseDownCapture(event: React.PointerEvent<HTMLDivElement>) {
|
||||
if ((event as any).pointerType === "mouse") {
|
||||
setDisableToolTip(true);
|
||||
let {title, valueTooltip, ...extras} = props;
|
||||
let [open,setOpen]= React.useState(false);
|
||||
let [isLongPress, setIsLongPress] = React.useState(false);
|
||||
let [longPressLeaving, setLongPressLeaving] = React.useState(false);
|
||||
let [timeout, setTimeout] = React.useState<number>(0);
|
||||
let [timeoutInstance, setTimeoutInstance] = React.useState<number>(0);
|
||||
let [pointerDownPoint, setPointerdownPoint] = React.useState<{x: number, y: number} | null>(null);
|
||||
|
||||
const hoverTimeout = 1250;
|
||||
const longpressTimeout = 500;
|
||||
const longpressLeaveTimeout = 1500;
|
||||
|
||||
function startTimeout(timeout: number) {
|
||||
setTimeout(timeout);
|
||||
setTimeoutInstance(timeoutInstance + 1); // make useeffect run.
|
||||
}
|
||||
function stopTimeout() {
|
||||
if (timeout > 0) {
|
||||
startTimeout(0);
|
||||
}
|
||||
}
|
||||
function handleMouseEnter(event: React.MouseEvent<HTMLDivElement>) {
|
||||
setOpen(false);
|
||||
startTimeout(hoverTimeout);
|
||||
}
|
||||
|
||||
function handleMouseLeave(event: React.MouseEvent<HTMLDivElement>) {
|
||||
setOpen(false);
|
||||
stopTimeout();
|
||||
}
|
||||
|
||||
function handlePointerDownCapture(event: React.PointerEvent<HTMLDivElement>) {
|
||||
let pointerType = (event as any).pointerType || "n/a";
|
||||
setIsLongPress(false);
|
||||
if (pointerType === "mouse") {
|
||||
setOpen(false);
|
||||
stopTimeout(); // Reset hover timeout for mouse
|
||||
} else { // pen, or touch.
|
||||
setTimeout(longpressTimeout);
|
||||
setOpen(false);
|
||||
setPointerdownPoint({x: event.clientX, y: event.clientY});
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerMoveCapture(event: React.PointerEvent<HTMLDivElement>) {
|
||||
if ((event as any).pointerType !== "mouse") {
|
||||
setDisableToolTip(false);
|
||||
React.useEffect(()=> {
|
||||
let t = timeout;
|
||||
let handle: number | null = null;
|
||||
if (valueTooltip === undefined) // no timeout if there's a value tooltip
|
||||
{
|
||||
if (t > 0) {
|
||||
console.log("ToolTipEx: starting timeout for ", t);
|
||||
handle = window.setTimeout(() => {
|
||||
setOpen(true);
|
||||
},t);
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
if (handle !== null) {
|
||||
console.log("ToolTipEx: clearing timeout for ", t);
|
||||
window.clearTimeout(handle);
|
||||
}
|
||||
};
|
||||
},[timeoutInstance]);
|
||||
|
||||
React.useEffect(()=> {
|
||||
if (longPressLeaving) {
|
||||
let handle: number | null = null;
|
||||
handle = window.setTimeout(() => {
|
||||
setIsLongPress(false);
|
||||
setLongPressLeaving(false);
|
||||
}, longpressLeaveTimeout);
|
||||
return () => {
|
||||
if (handle !== null) {
|
||||
window.clearTimeout(handle);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return () => { };
|
||||
}
|
||||
},[longPressLeaving]);
|
||||
|
||||
function handlePointerCancel(event: React.PointerEvent<HTMLDivElement>) {
|
||||
setOpen(false);
|
||||
setIsLongPress(false);
|
||||
stopTimeout();
|
||||
setPointerdownPoint(null); // Reset the mouse down point
|
||||
}
|
||||
function handlePointerMoveCapture(event: React.PointerEvent<HTMLDivElement>) {
|
||||
let pointerType = (event as any).pointerType || "n/a";
|
||||
if (pointerType === "mouse") {
|
||||
setOpen(false);
|
||||
startTimeout(hoverTimeout); // Reset hover timeout for mouse
|
||||
} else { // pen, or touch.
|
||||
if (pointerDownPoint) {
|
||||
const dx = event.clientX - pointerDownPoint.x;
|
||||
const dy = event.clientY - pointerDownPoint.y;
|
||||
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
|
||||
// If the mouse has moved more than 5 pixels, re-enable the tooltip
|
||||
setOpen(false);
|
||||
setIsLongPress(false);
|
||||
stopTimeout();
|
||||
setPointerdownPoint(null); // Reset the mouse down point
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleConextMenu(event: React.MouseEvent<HTMLDivElement>) {
|
||||
event.preventDefault(); // Prevent the default context menu from appearing
|
||||
event.stopPropagation(); // Prevent the default context menu from appearing
|
||||
if (pointerDownPoint) // touch sequence. This is a long press.
|
||||
{
|
||||
setIsLongPress(true);
|
||||
setOpen(true);
|
||||
stopTimeout();
|
||||
}
|
||||
} // Reset hover timeout for context menu
|
||||
function handlePointerUpCapture(event: React.PointerEvent<HTMLDivElement>) {
|
||||
let pointerType = (event as any).pointerType || "n/a";
|
||||
if (pointerType === "mouse") {
|
||||
setOpen(false);
|
||||
stopTimeout(); // Reset hover timeout for mouse
|
||||
} else { // pen, or touch.
|
||||
stopTimeout();
|
||||
setOpen(false);
|
||||
if (isLongPress) {
|
||||
// If this is a long press, we want to leave the tooltip open for a while.
|
||||
setLongPressLeaving(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
let effectiveTitle: React.ReactNode | null = null;
|
||||
let placement: "top-start" | "right" = "top-start";
|
||||
if (valueTooltip !== undefined && !isLongPress) {
|
||||
effectiveTitle = valueTooltip; // Show value tooltip if available
|
||||
placement = "right";
|
||||
}
|
||||
else {
|
||||
if (open || isLongPress) {
|
||||
effectiveTitle = title; // Show regular title if no value tooltip
|
||||
}
|
||||
}
|
||||
function handleClickCapture(event: React.MouseEvent<HTMLDivElement>) {
|
||||
setLongPressLeaving(false);
|
||||
setIsLongPress(false);
|
||||
setOpen(false);
|
||||
stopTimeout(); // Reset hover timeout on click
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onClickCapture={(e) => {
|
||||
console.log("ToolTipEx: onClickCapture");
|
||||
handleClickCapture(e);
|
||||
}}
|
||||
|
||||
onMouseEnter={(e)=> {
|
||||
console.log("ToolTipEx: onMouseEnter");
|
||||
|
||||
handleMouseEnter(e);
|
||||
}}
|
||||
onMouseLeave={(e)=> {
|
||||
console.log("ToolTipEx: onMouseLeave");
|
||||
handleMouseLeave(e);
|
||||
}}
|
||||
|
||||
onPointerCancelCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerCancelCapture");
|
||||
handlePointerCancel(e);
|
||||
}}
|
||||
onContextMenuCapture={(e) => {
|
||||
console.log("ToolTipEx: onContextMenuCapture");
|
||||
handleConextMenu(e);
|
||||
return false;
|
||||
}}
|
||||
onPointerDownCapture={(e) => {
|
||||
handleMouseDownCapture(e);
|
||||
console.log("ToolTipEx: onPointerDownCapture");
|
||||
handlePointerDownCapture(e);
|
||||
}}
|
||||
onPointerMoveCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerMoveCapture");
|
||||
handlePointerMoveCapture(e);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setDisableToolTip(false);
|
||||
}
|
||||
}
|
||||
onPointerUpCapture={(e) => {
|
||||
console.log("ToolTipEx: onPointerUpCapture");
|
||||
handlePointerUpCapture(e);
|
||||
}}
|
||||
>
|
||||
<Tooltip {...extras}
|
||||
title={disableToolTip? undefined: title}
|
||||
placement="top-start" arrow enterDelay={1500} enterNextDelay={1500}
|
||||
open={open || isLongPress || valueTooltip !== undefined}
|
||||
disableInteractive={true}
|
||||
disableTouchListener={true}
|
||||
disableHoverListener={true}
|
||||
disableFocusListener={true}
|
||||
|
||||
title={effectiveTitle}
|
||||
placement={ placement}
|
||||
arrow enterDelay={1500} enterNextDelay={1500}
|
||||
slotProps={{
|
||||
transition: {
|
||||
timeout: 0 // Disable transition for the tooltip
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -305,14 +305,15 @@ const WifiDirectConfigDialog =
|
||||
{this.state.landscapeLayout && (
|
||||
<DialogContent >
|
||||
<div style={{ display: "flex", gap: 16, alignItems: "left" }}>
|
||||
<IconButton
|
||||
<IconButtonEx
|
||||
tooltip="Back"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
onClick={() => { this.props.onClose(); }}
|
||||
aria-label="back"
|
||||
size="large">
|
||||
<ArrowBackIcon htmlColor="#888" />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<FormControlLabel
|
||||
control={(
|
||||
<Switch
|
||||
|
||||
Reference in New Issue
Block a user