Touch tooltips, more tooltips

This commit is contained in:
Robin E. R. Davies
2025-06-22 16:12:03 -04:00
parent aff98b0c20
commit f95ecebb6b
20 changed files with 378 additions and 155 deletions
+1
View File
@@ -1,4 +1,5 @@
channel bindings help dialog. <ChannelBindingHelpDialog
Tooltips on touch ui?
+3
View File
@@ -46,3 +46,6 @@ input[type=number] {
}
img {
user-select: none;
}
+14 -10
View File
@@ -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>
)}
+23 -11
View File
@@ -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>
);
}
+9 -7
View File
@@ -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>
+4 -3
View File
@@ -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>
+8 -5
View File
@@ -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>
)
+4 -3
View File
@@ -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>
+21 -6
View File
@@ -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}
/>
+6 -5
View File
@@ -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" >
+10 -7
View File
@@ -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>
+4 -3
View File
@@ -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>
);
+15 -16
View File
@@ -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>
+4 -3
View File
@@ -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}
+4 -3
View File
@@ -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>
+45 -51
View File
@@ -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
View File
@@ -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>
)
+4 -3
View File
@@ -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