Allow dragging of Splits

This commit is contained in:
Robin E.R. Davies
2026-06-15 09:30:52 -04:00
parent 9c9096b984
commit 84b9469896
3 changed files with 112 additions and 45 deletions
+2
View File
@@ -251,6 +251,7 @@ const Draggable =
if (this.pointerType !== "touch") { if (this.pointerType !== "touch") {
this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")"; this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")";
this.dragTarget.style.zIndex = "3"; this.dragTarget.style.zIndex = "3";
this.dragTarget.style.position = "absolute";
} }
} }
} }
@@ -259,6 +260,7 @@ const Draggable =
element.style.transform = ""; element.style.transform = "";
element.style.zIndex = this.savedIndex; element.style.zIndex = this.savedIndex;
element.style.opacity = this.savedOpacity; element.style.opacity = this.savedOpacity;
element.style.position = "relative";
} }
onPointerCancel(e: PointerEvent<HTMLDivElement>) { onPointerCancel(e: PointerEvent<HTMLDivElement>) {
+17
View File
@@ -144,6 +144,23 @@ export class PedalboardItem implements Deserializable<PedalboardItem> {
} }
isChild(instanceId: number): boolean {
if (this.instanceId === instanceId) return true;
if (this.isSplit())
{
let splitItem = this as unknown as PedalboardSplitItem;
for (let topItem of splitItem.topChain)
{
if (topItem.isChild(instanceId)) return true;
}
for (let bottomItem of splitItem.bottomChain)
{
if (bottomItem.isChild(instanceId)) return true;
}
}
return false;
}
getControlValue(key: string): number { getControlValue(key: string): number {
for (let i = 0; i < this.controlValues.length; ++i) { for (let i = 0; i < this.controlValues.length; ++i) {
let v = this.controlValues[i]; let v = this.controlValues[i];
+93 -45
View File
@@ -147,24 +147,36 @@ const pedalboardStyles = (theme: Theme) => createStyles({
top: -6, top: -6,
fill: theme.palette.text.secondary fill: theme.palette.text.secondary
}), }),
iconFrame: css({
display: "flex", pedalButton: css({
alignItems: "center",
justifyContent: "center",
position: "relative", position: "relative",
background: theme.palette.background.default,
marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2,
marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, marginRight: (CELL_WIDTH - FRAME_SIZE) / 2,
marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2,
marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2,
width: FRAME_SIZE,
height: FRAME_SIZE,
padding: 0,
borderRadius: 8
}),
iconFrame: css({
display: "flex",
alignItems: "center",
justifyContent: "center",
position: "relative",
background: theme.palette.background.paper,
width: FRAME_SIZE, width: FRAME_SIZE,
height: FRAME_SIZE, height: FRAME_SIZE,
borderColor: "#777", borderColor: "#777",
borderWidth: 2, borderWidth: 2,
borderStyle: "solid", borderStyle: "solid",
padding: 1, overflow: "hidden",
padding: 0,
borderRadius: 8 borderRadius: 8
}), }),
selectedIconFrame: css({ selectedIconFrame: css({
@@ -173,12 +185,7 @@ const pedalboardStyles = (theme: Theme) => createStyles({
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
position: "relative", position: "relative",
background: theme.palette.background.paper,
background: theme.palette.background.default,
marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2,
marginRight: (CELL_WIDTH - FRAME_SIZE) / 2,
marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2,
marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2,
width: FRAME_SIZE, width: FRAME_SIZE,
height: FRAME_SIZE, height: FRAME_SIZE,
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
@@ -195,10 +202,6 @@ const pedalboardStyles = (theme: Theme) => createStyles({
justifyContent: "center", justifyContent: "center",
background: "transparent", background: "transparent",
marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2,
marginRight: (CELL_WIDTH - FRAME_SIZE) / 2,
marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2,
marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2,
width: FRAME_SIZE, width: FRAME_SIZE,
height: FRAME_SIZE, height: FRAME_SIZE,
border: "0px #666 solid", border: "0px #666 solid",
@@ -458,6 +461,20 @@ const PedalboardView =
// touchyMove. :-/ // touchyMove. :-/
} }
isSplitterChild(item: PedalboardItem, splitterInstanceId: number) {
if (item.instanceId === splitterInstanceId) {
return true;
}
let pedalboard: Pedalboard | undefined = this.state.pedalboard;
if (!pedalboard) return false;
let splitter = pedalboard.maybeGetItem(splitterInstanceId);
if (splitter === null) {
return false;
}
return splitter.isChild(item.instanceId);
}
onDragEnd(instanceId: number, clientX: number, clientY: number) { onDragEnd(instanceId: number, clientX: number, clientY: number) {
if (!this.props.enableStructureEditing) { if (!this.props.enableStructureEditing) {
return; return;
@@ -483,10 +500,18 @@ const PedalboardView =
if (item.isSplitter() && item.pedalItem) { if (item.isSplitter() && item.pedalItem) {
if (item.bounds.contains(clientX, clientY)) { if (item.bounds.contains(clientX, clientY)) {
if (clientX < item.bounds.x + CELL_WIDTH / 2) { if (clientX < item.bounds.x + CELL_WIDTH / 2) {
if (this.isSplitterChild(item.pedalItem, instanceId))
{
return;
}
this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
} else if (clientX > item.bounds.right - CELL_WIDTH / 2) { } else if (clientX > item.bounds.right - CELL_WIDTH / 2) {
if (this.isSplitterChild(item.pedalItem, instanceId))
{
return;
}
this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -501,6 +526,11 @@ const PedalboardView =
if (clientX < item.topChildren[0].bounds.x) { if (clientX < item.topChildren[0].bounds.x) {
let topPedalItem = item.topChildren[0].pedalItem; let topPedalItem = item.topChildren[0].pedalItem;
if (topPedalItem) { if (topPedalItem) {
if (this.isSplitterChild(topPedalItem, instanceId))
{
return;
}
this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -509,6 +539,9 @@ const PedalboardView =
let lastTop = item.topChildren[item.topChildren.length - 1]; let lastTop = item.topChildren[item.topChildren.length - 1];
if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) {
if (lastTop.pedalItem) { if (lastTop.pedalItem) {
if (this.isSplitterChild(lastTop.pedalItem, instanceId)) {
return;
}
this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -522,6 +555,9 @@ const PedalboardView =
if (clientX < item.bottomChildren[0].bounds.x) { if (clientX < item.bottomChildren[0].bounds.x) {
let bottomPedalItem = item.bottomChildren[0].pedalItem; let bottomPedalItem = item.bottomChildren[0].pedalItem;
if (bottomPedalItem) { if (bottomPedalItem) {
if (this.isSplitterChild(bottomPedalItem, instanceId)) {
return;
}
this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -530,6 +566,9 @@ const PedalboardView =
let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; let lastBottom = item.bottomChildren[item.bottomChildren.length - 1];
if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) {
if (lastBottom.pedalItem) { if (lastBottom.pedalItem) {
if (this.isSplitterChild(lastBottom.pedalItem, instanceId)) {
return;
}
this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -552,10 +591,22 @@ const PedalboardView =
if (item.pedalItem) { if (item.pedalItem) {
let margin = (CELL_WIDTH - FRAME_SIZE) / 2; let margin = (CELL_WIDTH - FRAME_SIZE) / 2;
if (clientX < item.bounds.x + margin) { if (clientX < item.bounds.x + margin) {
if (this.isSplitterChild(item.pedalItem,instanceId))
{
return;
}
this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId);
} else if (clientX > item.bounds.right - margin) { } else if (clientX > item.bounds.right - margin) {
if (this.isSplitterChild(item.pedalItem,instanceId))
{
return;
}
this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId);
} else { } else {
if (this.isSplitterChild(item.pedalItem,instanceId))
{
return;
}
this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId);
} }
this.setSelection(instanceId); this.setSelection(instanceId);
@@ -1036,39 +1087,35 @@ const PedalboardView =
} }
return ( return (
<div className={frameStyle} onContextMenu={(e) => { e.preventDefault(); }} <div style={{ width: "100%", height: "100%" }}>
> <ButtonBase className={classes.pedalButton}
{/* {!enabled && (
<div className={classes.midiConnectorDecoration} >
<CloseIcon style={{ width: 16, height: 16, opacity: 0.6, fill: this.props.theme.palette.text.secondary }} />
</div>
)} */}
<ButtonBase style={{ width: "100%", height: "100%" }}
onClick={(e) => { this.onItemClick(e, instanceId); }} onClick={(e) => { this.onItemClick(e, instanceId); }}
onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }} onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }}
onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }} onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }}
> >
<SelectHoverBackground selected={instanceId === this.props.selectedId} showHover={true} <div className={frameStyle} style={{ position: "absolute" }} onContextMenu={(e) => { e.preventDefault(); }}
clipChildren={true}
> >
<Draggable draggable={draggable && (this.props.enableStructureEditing)} getScrollContainer={() => this.getScrollContainer()} <SelectHoverBackground selected={instanceId === this.props.selectedId} showHover={true}
onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} clipChildren={false}
style={{ opacity: enabled ? 0.99 : 0.3 }}
> >
<div id="childIcon" style={{ position: "relative", display: "flex", justifyContent: "center", alignItems: "center" }} > </SelectHoverBackground>
<PluginIcon pluginType={iconType} </div>
size={24} <Draggable draggable={draggable && (this.props.enableStructureEditing)} getScrollContainer={() => this.getScrollContainer()}
color={getIconColor(iconColor)} onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }}
pluginMissing={pluginNotFound} style={{ opacity: enabled ? 0.99 : 0.3 }}
/>
</div>
</Draggable> >
</SelectHoverBackground> <div id="childIcon" style={{ position: "relative", display: "flex", justifyContent: "center", alignItems: "center" }} >
<PluginIcon pluginType={iconType}
size={24}
color={getIconColor(iconColor)}
pluginMissing={pluginNotFound}
/>
</div>
</Draggable>
</ButtonBase> </ButtonBase>
</div> </div>
); );
@@ -1146,7 +1193,7 @@ const PedalboardView =
result.push(<div key={this.renderKey++} className={classes.splitItem} style={{ left: item.bounds.x, top: item.bounds.y, width: item.bounds.width }} > result.push(<div key={this.renderKey++} className={classes.splitItem} style={{ left: item.bounds.x, top: item.bounds.y, width: item.bounds.width }} >
<div className={classes.splitStart} > <div className={classes.splitStart} >
{this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), "", false, true, true, false, false)} {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), "", true, true, true, false, false)}
</div> </div>
</div>); </div>);
@@ -1157,9 +1204,10 @@ const PedalboardView =
position: "absolute", left: item.bounds.x, width: CELL_WIDTH, top: item.bounds.bottom - 12, paddingLeft: 2, paddingRight: 2 position: "absolute", left: item.bounds.x, width: CELL_WIDTH, top: item.bounds.bottom - 12, paddingLeft: 2, paddingRight: 2
}}> }}>
<Typography variant="caption" display="block" noWrap={true} <Typography variant="caption" display="block" noWrap={true}
style={{ width: CELL_WIDTH - 4, textAlign: "center", flex: "0 1 auto", style={{
opacity: item.pedalItem?.isEnabled??true ? 1.0 : 0.4 width: CELL_WIDTH - 4, textAlign: "center", flex: "0 1 auto",
}} opacity: item.pedalItem?.isEnabled ?? true ? 1.0 : 0.4
}}
>{item.name}</Typography> >{item.name}</Typography>
</div> </div>
) )