Simplify useDrag()
's API and implement middle-button dragging in SequenceEditor
This commit is contained in:
parent
e12d495f29
commit
202b61c48c
17 changed files with 995 additions and 972 deletions
|
@ -21,21 +21,16 @@ const PanelDragZone: React.FC<
|
||||||
const [ref, node] = useRefAndState<HTMLDivElement>(null as $IntentionalAny)
|
const [ref, node] = useRefAndState<HTMLDivElement>(null as $IntentionalAny)
|
||||||
|
|
||||||
const dragOpts: Parameters<typeof useDrag>[1] = useMemo(() => {
|
const dragOpts: Parameters<typeof useDrag>[1] = useMemo(() => {
|
||||||
let stuffBeforeDrag = panelStuffRef.current
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let unlock: VoidFn | undefined
|
|
||||||
return {
|
return {
|
||||||
debugName: 'PanelDragZone',
|
debugName: 'PanelDragZone',
|
||||||
lockCursorTo: 'move',
|
lockCursorTo: 'move',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
stuffBeforeDrag = panelStuffRef.current
|
const stuffBeforeDrag = panelStuffRef.current
|
||||||
if (unlock) {
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
const u = unlock
|
|
||||||
unlock = undefined
|
const unlock = panelStuff.addBoundsHighlightLock()
|
||||||
u()
|
|
||||||
}
|
return {
|
||||||
unlock = panelStuff.addBoundsHighlightLock()
|
|
||||||
},
|
|
||||||
onDrag(dx, dy) {
|
onDrag(dx, dy) {
|
||||||
const newDims: typeof panelStuff['dims'] = {
|
const newDims: typeof panelStuff['dims'] = {
|
||||||
...stuffBeforeDrag.dims,
|
...stuffBeforeDrag.dims,
|
||||||
|
@ -56,17 +51,14 @@ const PanelDragZone: React.FC<
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (unlock) {
|
unlock()
|
||||||
const u = unlock
|
|
||||||
unlock = undefined
|
|
||||||
u()
|
|
||||||
}
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
tempTransaction?.commit()
|
tempTransaction?.commit()
|
||||||
} else {
|
} else {
|
||||||
tempTransaction?.discard()
|
tempTransaction?.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import type {$IntentionalAny, VoidFn} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
import {lighten} from 'polished'
|
import {lighten} from 'polished'
|
||||||
import React, {useMemo, useRef, useState} from 'react'
|
import React, {useMemo, useRef} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import {panelDimsToPanelPosition, usePanel} from './BasePanel'
|
import {panelDimsToPanelPosition, usePanel} from './BasePanel'
|
||||||
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
||||||
|
@ -142,27 +142,19 @@ const PanelResizeHandle: React.FC<{
|
||||||
const panelStuff = usePanel()
|
const panelStuff = usePanel()
|
||||||
const panelStuffRef = useRef(panelStuff)
|
const panelStuffRef = useRef(panelStuff)
|
||||||
panelStuffRef.current = panelStuff
|
panelStuffRef.current = panelStuff
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
|
||||||
|
|
||||||
const [ref, node] = useRefAndState<HTMLDivElement>(null as $IntentionalAny)
|
const [ref, node] = useRefAndState<HTMLDivElement>(null as $IntentionalAny)
|
||||||
const dragOpts: Parameters<typeof useDrag>[1] = useMemo(() => {
|
const dragOpts: Parameters<typeof useDrag>[1] = useMemo(() => {
|
||||||
let stuffBeforeDrag = panelStuffRef.current
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let unlock: VoidFn | undefined
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'PanelResizeHandle',
|
debugName: 'PanelResizeHandle',
|
||||||
lockCursorTo: cursors[which],
|
lockCursorTo: cursors[which],
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
stuffBeforeDrag = panelStuffRef.current
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
setIsDragging(true)
|
|
||||||
if (unlock) {
|
const stuffBeforeDrag = panelStuffRef.current
|
||||||
const u = unlock
|
const unlock = panelStuff.addBoundsHighlightLock()
|
||||||
unlock = undefined
|
|
||||||
u()
|
return {
|
||||||
}
|
|
||||||
unlock = panelStuff.addBoundsHighlightLock()
|
|
||||||
},
|
|
||||||
onDrag(dx, dy) {
|
onDrag(dx, dy) {
|
||||||
const newDims: typeof panelStuff['dims'] = {
|
const newDims: typeof panelStuff['dims'] = {
|
||||||
...stuffBeforeDrag.dims,
|
...stuffBeforeDrag.dims,
|
||||||
|
@ -215,23 +207,19 @@ const PanelResizeHandle: React.FC<{
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (unlock) {
|
unlock()
|
||||||
const u = unlock
|
|
||||||
unlock = undefined
|
|
||||||
u()
|
|
||||||
}
|
|
||||||
setIsDragging(false)
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
tempTransaction?.commit()
|
tempTransaction?.commit()
|
||||||
} else {
|
} else {
|
||||||
tempTransaction?.discard()
|
tempTransaction?.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [which])
|
}, [which])
|
||||||
|
|
||||||
useDrag(node, dragOpts)
|
const [isDragging] = useDrag(node, dragOpts)
|
||||||
const Comp = els[which]
|
const Comp = els[which]
|
||||||
|
|
||||||
const isOnCorner = which.length <= 6
|
const isOnCorner = which.length <= 6
|
||||||
|
|
|
@ -8,13 +8,8 @@ import {lighten} from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useMemo, useRef} from 'react'
|
import {useMemo, useRef} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import type {
|
|
||||||
SequenceEditorPanelLayout,
|
|
||||||
DopeSheetSelection,
|
|
||||||
} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
|
||||||
import {DOT_SIZE_PX} from './KeyframeDot'
|
import {DOT_SIZE_PX} from './KeyframeDot'
|
||||||
import type KeyframeEditor from './KeyframeEditor'
|
import type KeyframeEditor from './KeyframeEditor'
|
||||||
import type Sequence from '@theatre/core/sequences/Sequence'
|
|
||||||
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
|
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
|
||||||
import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover'
|
import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover'
|
||||||
import CurveEditorPopover from './CurveEditorPopover/CurveEditorPopover'
|
import CurveEditorPopover from './CurveEditorPopover/CurveEditorPopover'
|
||||||
|
@ -156,22 +151,18 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) {
|
||||||
propsRef.current = props
|
propsRef.current = props
|
||||||
|
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let propsAtStartOfDrag: IProps
|
|
||||||
let selectionDragHandlers:
|
|
||||||
| ReturnType<DopeSheetSelection['getDragHandlers']>
|
|
||||||
| undefined
|
|
||||||
let sequence: Sequence
|
|
||||||
return {
|
return {
|
||||||
debugName: 'useDragKeyframe',
|
debugName: 'useDragKeyframe',
|
||||||
lockCursorTo: 'ew-resize',
|
lockCursorTo: 'ew-resize',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
const props = propsRef.current
|
const props = propsRef.current
|
||||||
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
if (props.selection) {
|
if (props.selection) {
|
||||||
const {selection, leaf} = props
|
const {selection, leaf} = props
|
||||||
const {sheetObject} = leaf
|
const {sheetObject} = leaf
|
||||||
selectionDragHandlers = selection.getDragHandlers({
|
return selection
|
||||||
|
.getDragHandlers({
|
||||||
...sheetObject.address,
|
...sheetObject.address,
|
||||||
pathToProp: leaf.pathToProp,
|
pathToProp: leaf.pathToProp,
|
||||||
trackId: leaf.trackId,
|
trackId: leaf.trackId,
|
||||||
|
@ -180,20 +171,18 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) {
|
||||||
positionAtStartOfDrag:
|
positionAtStartOfDrag:
|
||||||
props.trackData.keyframes[props.index].position,
|
props.trackData.keyframes[props.index].position,
|
||||||
})
|
})
|
||||||
selectionDragHandlers.onDragStart?.(event)
|
.onDragStart(event)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
propsAtStartOfDrag = props
|
const propsAtStartOfDrag = props
|
||||||
sequence = val(propsAtStartOfDrag.layoutP.sheet).getSequence()
|
const sequence = val(propsAtStartOfDrag.layoutP.sheet).getSequence()
|
||||||
|
|
||||||
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
|
const toUnitSpace = val(
|
||||||
},
|
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
onDrag(dx, dy, event) {
|
onDrag(dx, dy, event) {
|
||||||
if (selectionDragHandlers) {
|
|
||||||
selectionDragHandlers.onDrag(dx, dy, event)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const delta = toUnitSpace(dx)
|
const delta = toUnitSpace(dx)
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
|
@ -219,11 +208,6 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (selectionDragHandlers) {
|
|
||||||
selectionDragHandlers.onDragEnd?.(dragHappened)
|
|
||||||
|
|
||||||
selectionDragHandlers = undefined
|
|
||||||
}
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
tempTransaction.commit()
|
tempTransaction.commit()
|
||||||
|
@ -233,7 +217,8 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -256,7 +256,7 @@ function useKeyframeDrag(
|
||||||
lockCursorTo: 'move',
|
lockCursorTo: 'move',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
setFrozen(true)
|
setFrozen(true)
|
||||||
},
|
return {
|
||||||
onDrag(dx, dy) {
|
onDrag(dx, dy) {
|
||||||
if (!svgNode) return
|
if (!svgNode) return
|
||||||
|
|
||||||
|
@ -266,6 +266,8 @@ function useKeyframeDrag(
|
||||||
setFrozen(false)
|
setFrozen(false)
|
||||||
props.onCancelCurveChange()
|
props.onCancelCurveChange()
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[svgNode, props.trackData],
|
[svgNode, props.trackData],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import type {
|
|
||||||
DopeSheetSelection,
|
|
||||||
SequenceEditorPanelLayout,
|
|
||||||
} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
||||||
|
@ -148,14 +144,6 @@ function useDragKeyframe(
|
||||||
propsRef.current = props
|
propsRef.current = props
|
||||||
|
|
||||||
const useDragOpts = useMemo<UseDragOpts>(() => {
|
const useDragOpts = useMemo<UseDragOpts>(() => {
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let propsAtStartOfDrag: IKeyframeDotProps
|
|
||||||
|
|
||||||
let selectionDragHandlers:
|
|
||||||
| ReturnType<DopeSheetSelection['getDragHandlers']>
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'KeyframeDot/useDragKeyframe',
|
debugName: 'KeyframeDot/useDragKeyframe',
|
||||||
|
|
||||||
|
@ -164,7 +152,8 @@ function useDragKeyframe(
|
||||||
if (props.selection) {
|
if (props.selection) {
|
||||||
const {selection, leaf} = props
|
const {selection, leaf} = props
|
||||||
const {sheetObject} = leaf
|
const {sheetObject} = leaf
|
||||||
selectionDragHandlers = selection.getDragHandlers({
|
return selection
|
||||||
|
.getDragHandlers({
|
||||||
...sheetObject.address,
|
...sheetObject.address,
|
||||||
pathToProp: leaf.pathToProp,
|
pathToProp: leaf.pathToProp,
|
||||||
trackId: leaf.trackId,
|
trackId: leaf.trackId,
|
||||||
|
@ -173,19 +162,18 @@ function useDragKeyframe(
|
||||||
positionAtStartOfDrag:
|
positionAtStartOfDrag:
|
||||||
props.trackData.keyframes[props.index].position,
|
props.trackData.keyframes[props.index].position,
|
||||||
})
|
})
|
||||||
selectionDragHandlers.onDragStart?.(event)
|
.onDragStart(event)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
propsAtStartOfDrag = props
|
const propsAtStartOfDrag = props
|
||||||
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
|
const toUnitSpace = val(
|
||||||
},
|
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
||||||
|
)
|
||||||
|
|
||||||
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
|
return {
|
||||||
onDrag(dx, dy, event) {
|
onDrag(dx, dy, event) {
|
||||||
if (selectionDragHandlers) {
|
|
||||||
selectionDragHandlers.onDrag(dx, dy, event)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const original =
|
const original =
|
||||||
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
|
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
|
||||||
const newPosition = Math.max(
|
const newPosition = Math.max(
|
||||||
|
@ -215,14 +203,10 @@ function useDragKeyframe(
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (selectionDragHandlers) {
|
|
||||||
selectionDragHandlers.onDragEnd?.(dragHappened)
|
|
||||||
|
|
||||||
selectionDragHandlers = undefined
|
|
||||||
}
|
|
||||||
if (dragHappened) tempTransaction?.commit()
|
if (dragHappened) tempTransaction?.commit()
|
||||||
else tempTransaction?.discard()
|
else tempTransaction?.discard()
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -79,7 +79,8 @@ function useCaptureSelection(
|
||||||
}
|
}
|
||||||
|
|
||||||
val(layoutP.selectionAtom).setState({current: undefined})
|
val(layoutP.selectionAtom).setState({current: undefined})
|
||||||
},
|
|
||||||
|
return {
|
||||||
onDrag(_dx, _dy, event) {
|
onDrag(_dx, _dy, event) {
|
||||||
// const state = ref.current!
|
// const state = ref.current!
|
||||||
const rect = containerNode!.getBoundingClientRect()
|
const rect = containerNode!.getBoundingClientRect()
|
||||||
|
@ -102,6 +103,8 @@ function useCaptureSelection(
|
||||||
ref.current = null
|
ref.current = null
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}, [layoutP, containerNode, ref]),
|
}, [layoutP, containerNode, ref]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -183,14 +186,14 @@ namespace utils {
|
||||||
type: 'DopeSheetSelection',
|
type: 'DopeSheetSelection',
|
||||||
byObjectKey: {},
|
byObjectKey: {},
|
||||||
getDragHandlers(origin) {
|
getDragHandlers(origin) {
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
|
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
return {
|
return {
|
||||||
debugName: 'DopeSheetSelectionView/boundsToSelection',
|
debugName: 'DopeSheetSelectionView/boundsToSelection',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
toUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
},
|
|
||||||
|
const toUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
||||||
|
|
||||||
|
return {
|
||||||
onDrag(dx, _, event) {
|
onDrag(dx, _, event) {
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
|
@ -208,12 +211,15 @@ namespace utils {
|
||||||
delta = toUnitSpace(dx)
|
delta = toUnitSpace(dx)
|
||||||
}
|
}
|
||||||
|
|
||||||
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio()!.tempTransaction(
|
||||||
|
({stateEditors}) => {
|
||||||
const transformKeyframes =
|
const transformKeyframes =
|
||||||
stateEditors.coreByProject.historic.sheetsById.sequence
|
stateEditors.coreByProject.historic.sheetsById.sequence
|
||||||
.transformKeyframes
|
.transformKeyframes
|
||||||
|
|
||||||
for (const objectKey of Object.keys(selection.byObjectKey)) {
|
for (const objectKey of Object.keys(
|
||||||
|
selection.byObjectKey,
|
||||||
|
)) {
|
||||||
const {byTrackId} = selection.byObjectKey[objectKey]!
|
const {byTrackId} = selection.byObjectKey[objectKey]!
|
||||||
for (const trackId of Object.keys(byTrackId)) {
|
for (const trackId of Object.keys(byTrackId)) {
|
||||||
const {byKeyframeId} = byTrackId[trackId]!
|
const {byKeyframeId} = byTrackId[trackId]!
|
||||||
|
@ -223,19 +229,22 @@ namespace utils {
|
||||||
translate: delta,
|
translate: delta,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
origin: 0,
|
origin: 0,
|
||||||
snappingFunction: sheet.getSequence().closestGridPosition,
|
snappingFunction:
|
||||||
|
sheet.getSequence().closestGridPosition,
|
||||||
objectKey,
|
objectKey,
|
||||||
projectId: origin.projectId,
|
projectId: origin.projectId,
|
||||||
sheetId: origin.sheetId,
|
sheetId: origin.sheetId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (dragHappened) tempTransaction?.commit()
|
if (dragHappened) tempTransaction?.commit()
|
||||||
else tempTransaction?.discard()
|
else tempTransaction?.discard()
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import type Sequence from '@theatre/core/sequences/Sequence'
|
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
|
@ -50,7 +49,7 @@ const HorizontallyScrollableArea: React.FC<{
|
||||||
)
|
)
|
||||||
|
|
||||||
useHandlePanAndZoom(layoutP, containerNode)
|
useHandlePanAndZoom(layoutP, containerNode)
|
||||||
useDragHandlers(layoutP, containerNode)
|
useDragPlayheadHandlers(layoutP, containerNode)
|
||||||
useUpdateScrollFromClippedSpaceRange(layoutP, containerNode)
|
useUpdateScrollFromClippedSpaceRange(layoutP, containerNode)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -70,31 +69,13 @@ const HorizontallyScrollableArea: React.FC<{
|
||||||
|
|
||||||
export default HorizontallyScrollableArea
|
export default HorizontallyScrollableArea
|
||||||
|
|
||||||
function useDragHandlers(
|
function useDragPlayheadHandlers(
|
||||||
layoutP: Pointer<SequenceEditorPanelLayout>,
|
layoutP: Pointer<SequenceEditorPanelLayout>,
|
||||||
containerEl: HTMLDivElement | null,
|
containerEl: HTMLDivElement | null,
|
||||||
) {
|
) {
|
||||||
const handlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
const handlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
||||||
let posBeforeSeek = 0
|
|
||||||
let sequence: Sequence
|
|
||||||
let scaledSpaceToUnitSpace: typeof layoutP.scaledSpace.toUnitSpace.$$__pointer_type
|
|
||||||
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'HorizontallyScrollableArea',
|
debugName: 'HorizontallyScrollableArea',
|
||||||
onDrag(dx: number, _, event) {
|
|
||||||
const deltaPos = scaledSpaceToUnitSpace(dx)
|
|
||||||
const unsnappedPos = clamp(posBeforeSeek + deltaPos, 0, sequence.length)
|
|
||||||
|
|
||||||
let newPosition = unsnappedPos
|
|
||||||
|
|
||||||
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {})
|
|
||||||
if (snapPos != null) {
|
|
||||||
newPosition = snapPos
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence.position = newPosition
|
|
||||||
},
|
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
if (event.target instanceof HTMLInputElement) {
|
if (event.target instanceof HTMLInputElement) {
|
||||||
// editing some value
|
// editing some value
|
||||||
|
@ -124,18 +105,40 @@ function useDragHandlers(
|
||||||
Infinity,
|
Infinity,
|
||||||
)
|
)
|
||||||
|
|
||||||
sequence = val(layoutP.sheet).getSequence()
|
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
|
||||||
|
|
||||||
|
const sequence = val(layoutP.sheet).getSequence()
|
||||||
|
|
||||||
sequence.position = initialPositionInUnitSpace
|
sequence.position = initialPositionInUnitSpace
|
||||||
|
|
||||||
posBeforeSeek = initialPositionInUnitSpace
|
const posBeforeSeek = initialPositionInUnitSpace
|
||||||
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
||||||
setIsSeeking(true)
|
setIsSeeking(true)
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDrag(dx: number, _, event) {
|
||||||
|
const deltaPos = scaledSpaceToUnitSpace(dx)
|
||||||
|
const unsnappedPos = clamp(
|
||||||
|
posBeforeSeek + deltaPos,
|
||||||
|
0,
|
||||||
|
sequence.length,
|
||||||
|
)
|
||||||
|
|
||||||
|
let newPosition = unsnappedPos
|
||||||
|
|
||||||
|
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {})
|
||||||
|
if (snapPos != null) {
|
||||||
|
newPosition = snapPos
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence.position = newPosition
|
||||||
},
|
},
|
||||||
onDragEnd() {
|
onDragEnd() {
|
||||||
setIsSeeking(false)
|
setIsSeeking(false)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}, [layoutP, containerEl])
|
}, [layoutP, containerEl])
|
||||||
|
|
||||||
const [isDragging] = useDrag(containerEl, handlers)
|
const [isDragging] = useDrag(containerEl, handlers)
|
||||||
|
@ -233,6 +236,39 @@ function useHandlePanAndZoom(
|
||||||
node.removeEventListener('wheel', receiveWheelEvent, listenerOptions)
|
node.removeEventListener('wheel', receiveWheelEvent, listenerOptions)
|
||||||
}
|
}
|
||||||
}, [node, layoutP])
|
}, [node, layoutP])
|
||||||
|
|
||||||
|
useDrag(
|
||||||
|
node,
|
||||||
|
useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
|
return {
|
||||||
|
onDragStart(e) {
|
||||||
|
const oldRange = val(layoutP.clippedSpace.range)
|
||||||
|
const setRange = val(layoutP.clippedSpace.setRange)
|
||||||
|
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDrag(dx, dy, _, __, deltaYFromLastEvent) {
|
||||||
|
receiveVerticalWheelEvent({deltaY: -deltaYFromLastEvent})
|
||||||
|
const delta = -scaledSpaceToUnitSpace(dx)
|
||||||
|
|
||||||
|
const newRange = mapValues(
|
||||||
|
oldRange,
|
||||||
|
(originalPos) => originalPos + delta,
|
||||||
|
)
|
||||||
|
|
||||||
|
setRange(newRange)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
debugName: 'HorizontallyScrollableArea Middle Button Drag',
|
||||||
|
buttons: [1],
|
||||||
|
lockCursorTo: 'grab',
|
||||||
|
}
|
||||||
|
}, [layoutP]),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalize(value: number, [min, max]: [min: number, max: number]) {
|
function normalize(value: number, [min, max]: [min: number, max: number]) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {usePrism} from '@theatre/react'
|
import {usePrism} from '@theatre/react'
|
||||||
import type {Pointer} from '@theatre/dataverse'
|
import type {Pointer} from '@theatre/dataverse'
|
||||||
import {val} from '@theatre/dataverse'
|
import {val} from '@theatre/dataverse'
|
||||||
import React, {useMemo, useRef, useState} from 'react'
|
import React, {useMemo, useRef} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
||||||
|
@ -10,7 +10,6 @@ import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type Sheet from '@theatre/core/sheets/Sheet'
|
|
||||||
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
|
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
|
||||||
import {
|
import {
|
||||||
includeLockFrameStampAttrs,
|
includeLockFrameStampAttrs,
|
||||||
|
@ -219,28 +218,23 @@ function useDragBulge(
|
||||||
): [isDragging: boolean] {
|
): [isDragging: boolean] {
|
||||||
const propsRef = useRef(props)
|
const propsRef = useRef(props)
|
||||||
propsRef.current = props
|
propsRef.current = props
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
|
||||||
|
|
||||||
useLockFrameStampPosition(isDragging, -1)
|
|
||||||
|
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let propsAtStartOfDrag: IProps
|
|
||||||
let sheet: Sheet
|
|
||||||
let initialLength: number
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'LengthIndicator/useDragBulge',
|
debugName: 'LengthIndicator/useDragBulge',
|
||||||
lockCursorTo: 'ew-resize',
|
lockCursorTo: 'ew-resize',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
setIsDragging(true)
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
propsAtStartOfDrag = propsRef.current
|
|
||||||
sheet = val(propsRef.current.layoutP.sheet)
|
|
||||||
initialLength = sheet.getSequence().length
|
|
||||||
|
|
||||||
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
|
const propsAtStartOfDrag = propsRef.current
|
||||||
},
|
const sheet = val(propsRef.current.layoutP.sheet)
|
||||||
|
const initialLength = sheet.getSequence().length
|
||||||
|
|
||||||
|
const toUnitSpace = val(
|
||||||
|
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
onDrag(dx, dy, event) {
|
onDrag(dx, dy, event) {
|
||||||
const delta = toUnitSpace(dx)
|
const delta = toUnitSpace(dx)
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
|
@ -248,14 +242,15 @@ function useDragBulge(
|
||||||
tempTransaction = undefined
|
tempTransaction = undefined
|
||||||
}
|
}
|
||||||
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
||||||
stateEditors.coreByProject.historic.sheetsById.sequence.setLength({
|
stateEditors.coreByProject.historic.sheetsById.sequence.setLength(
|
||||||
|
{
|
||||||
...sheet.address,
|
...sheet.address,
|
||||||
length: initialLength + delta,
|
length: initialLength + delta,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
setIsDragging(false)
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
tempTransaction.commit()
|
tempTransaction.commit()
|
||||||
|
@ -265,12 +260,14 @@ function useDragBulge(
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useDrag(node, gestureHandlers)
|
const [isDragging] = useDrag(node, gestureHandlers)
|
||||||
|
useLockFrameStampPosition(isDragging, -1)
|
||||||
|
|
||||||
return [isDragging]
|
return [isDragging]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import type {VoidFn} from '@theatre/shared/utils/types'
|
|
||||||
import {val} from '@theatre/dataverse'
|
import {val} from '@theatre/dataverse'
|
||||||
import {clamp} from 'lodash-es'
|
import {clamp} from 'lodash-es'
|
||||||
import React, {useMemo, useRef} from 'react'
|
import React, {useMemo, useRef} from 'react'
|
||||||
|
@ -125,26 +123,24 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
|
||||||
propsRef.current = props
|
propsRef.current = props
|
||||||
|
|
||||||
const handlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const handlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
let scaledToUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
let verticalToExtremumSpace: SequenceEditorPanelLayout['graphEditorVerticalSpace']['toExtremumSpace']
|
|
||||||
let propsAtStartOfDrag: IProps
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let unlockExtremums: VoidFn | undefined
|
|
||||||
return {
|
return {
|
||||||
debugName: 'CurveHandler/useOurDrags',
|
debugName: 'CurveHandler/useOurDrags',
|
||||||
lockCursorTo: 'move',
|
lockCursorTo: 'move',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
propsAtStartOfDrag = propsRef.current
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
scaledToUnitSpace = val(
|
const propsAtStartOfDrag = propsRef.current
|
||||||
|
|
||||||
|
const scaledToUnitSpace = val(
|
||||||
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
||||||
)
|
)
|
||||||
verticalToExtremumSpace = val(
|
const verticalToExtremumSpace = val(
|
||||||
propsAtStartOfDrag.layoutP.graphEditorVerticalSpace.toExtremumSpace,
|
propsAtStartOfDrag.layoutP.graphEditorVerticalSpace.toExtremumSpace,
|
||||||
)
|
)
|
||||||
|
|
||||||
unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
|
const unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
|
||||||
},
|
|
||||||
|
return {
|
||||||
onDrag(dxInScaledSpace, dy) {
|
onDrag(dxInScaledSpace, dy) {
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
|
@ -163,17 +159,25 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
|
||||||
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
|
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
|
||||||
|
|
||||||
const dYInValueSpace =
|
const dYInValueSpace =
|
||||||
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(dYInExtremumSpace)
|
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(
|
||||||
|
dYInExtremumSpace,
|
||||||
|
)
|
||||||
|
|
||||||
const curValue = props.isScalar ? (cur.value as number) : 0
|
const curValue = props.isScalar ? (cur.value as number) : 0
|
||||||
const nextValue = props.isScalar ? (next.value as number) : 1
|
const nextValue = props.isScalar ? (next.value as number) : 1
|
||||||
const dyInKeyframeDiffSpace = dYInValueSpace / (nextValue - curValue)
|
const dyInKeyframeDiffSpace =
|
||||||
|
dYInValueSpace / (nextValue - curValue)
|
||||||
|
|
||||||
if (propsAtStartOfDrag.which === 'left') {
|
if (propsAtStartOfDrag.which === 'left') {
|
||||||
const handleX = clamp(cur.handles[2] + dPosInKeyframeDiffSpace, 0, 1)
|
const handleX = clamp(
|
||||||
|
cur.handles[2] + dPosInKeyframeDiffSpace,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
)
|
||||||
const handleY = cur.handles[3] + dyInKeyframeDiffSpace
|
const handleY = cur.handles[3] + dyInKeyframeDiffSpace
|
||||||
|
|
||||||
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio()!.tempTransaction(
|
||||||
|
({stateEditors}) => {
|
||||||
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
|
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
|
||||||
{
|
{
|
||||||
...propsAtStartOfDrag.sheetObject.address,
|
...propsAtStartOfDrag.sheetObject.address,
|
||||||
|
@ -184,17 +188,28 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
|
||||||
keyframes: [
|
keyframes: [
|
||||||
{
|
{
|
||||||
...cur,
|
...cur,
|
||||||
handles: [cur.handles[0], cur.handles[1], handleX, handleY],
|
handles: [
|
||||||
|
cur.handles[0],
|
||||||
|
cur.handles[1],
|
||||||
|
handleX,
|
||||||
|
handleY,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
const handleX = clamp(next.handles[0] + dPosInKeyframeDiffSpace, 0, 1)
|
const handleX = clamp(
|
||||||
|
next.handles[0] + dPosInKeyframeDiffSpace,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
)
|
||||||
const handleY = next.handles[1] + dyInKeyframeDiffSpace
|
const handleY = next.handles[1] + dyInKeyframeDiffSpace
|
||||||
|
|
||||||
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio()!.tempTransaction(
|
||||||
|
({stateEditors}) => {
|
||||||
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
|
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
|
||||||
{
|
{
|
||||||
...propsAtStartOfDrag.sheetObject.address,
|
...propsAtStartOfDrag.sheetObject.address,
|
||||||
|
@ -215,15 +230,12 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (unlockExtremums) {
|
unlockExtremums()
|
||||||
const unlock = unlockExtremums
|
|
||||||
unlockExtremums = undefined
|
|
||||||
unlock()
|
|
||||||
}
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
if (tempTransaction) {
|
if (tempTransaction) {
|
||||||
tempTransaction.commit()
|
tempTransaction.commit()
|
||||||
|
@ -233,7 +245,8 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import type {VoidFn} from '@theatre/shared/utils/types'
|
|
||||||
import {val} from '@theatre/dataverse'
|
import {val} from '@theatre/dataverse'
|
||||||
import React, {useMemo, useRef, useState} from 'react'
|
import React, {useMemo, useRef, useState} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
@ -107,24 +105,21 @@ function useDragKeyframe(
|
||||||
propsRef.current = _props
|
propsRef.current = _props
|
||||||
|
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
|
|
||||||
let propsAtStartOfDrag: IProps
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let unlockExtremums: VoidFn | undefined
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'GraphEditorDotNonScalar/useDragKeyframe',
|
debugName: 'GraphEditorDotNonScalar/useDragKeyframe',
|
||||||
lockCursorTo: 'ew-resize',
|
lockCursorTo: 'ew-resize',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
setIsDragging(true)
|
setIsDragging(true)
|
||||||
|
const propsAtStartOfDrag = propsRef.current
|
||||||
|
|
||||||
propsAtStartOfDrag = propsRef.current
|
const toUnitSpace = val(
|
||||||
|
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
||||||
|
)
|
||||||
|
|
||||||
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
|
const unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
|
||||||
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
|
return {
|
||||||
},
|
|
||||||
onDrag(dx, dy) {
|
onDrag(dx, dy) {
|
||||||
const original =
|
const original =
|
||||||
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
|
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
|
||||||
|
@ -158,17 +153,14 @@ function useDragKeyframe(
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
setIsDragging(false)
|
setIsDragging(false)
|
||||||
if (unlockExtremums) {
|
unlockExtremums()
|
||||||
const unlock = unlockExtremums
|
|
||||||
unlockExtremums = undefined
|
|
||||||
unlock()
|
|
||||||
}
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
tempTransaction?.commit()
|
tempTransaction?.commit()
|
||||||
} else {
|
} else {
|
||||||
tempTransaction?.discard()
|
tempTransaction?.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import type {VoidFn} from '@theatre/shared/utils/types'
|
|
||||||
import {val} from '@theatre/dataverse'
|
import {val} from '@theatre/dataverse'
|
||||||
import React, {useMemo, useRef, useState} from 'react'
|
import React, {useMemo, useRef, useState} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
@ -108,29 +106,25 @@ function useDragKeyframe(
|
||||||
propsRef.current = _props
|
propsRef.current = _props
|
||||||
|
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
|
|
||||||
let propsAtStartOfDrag: IProps
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let verticalToExtremumSpace: SequenceEditorPanelLayout['graphEditorVerticalSpace']['toExtremumSpace']
|
|
||||||
let unlockExtremums: VoidFn | undefined
|
|
||||||
let keepSpeeds = false
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'GraphEditorDotScalar/useDragKeyframe',
|
debugName: 'GraphEditorDotScalar/useDragKeyframe',
|
||||||
lockCursorTo: 'move',
|
lockCursorTo: 'move',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
setIsDragging(true)
|
setIsDragging(true)
|
||||||
keepSpeeds = !!event.altKey
|
const keepSpeeds = !!event.altKey
|
||||||
|
|
||||||
propsAtStartOfDrag = propsRef.current
|
const propsAtStartOfDrag = propsRef.current
|
||||||
|
|
||||||
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
|
const toUnitSpace = val(
|
||||||
verticalToExtremumSpace = val(
|
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
|
||||||
|
)
|
||||||
|
const verticalToExtremumSpace = val(
|
||||||
propsAtStartOfDrag.layoutP.graphEditorVerticalSpace.toExtremumSpace,
|
propsAtStartOfDrag.layoutP.graphEditorVerticalSpace.toExtremumSpace,
|
||||||
)
|
)
|
||||||
unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
|
const unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
|
||||||
},
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
|
return {
|
||||||
onDrag(dx, dy) {
|
onDrag(dx, dy) {
|
||||||
const original =
|
const original =
|
||||||
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
|
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
|
||||||
|
@ -140,7 +134,9 @@ function useDragKeyframe(
|
||||||
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
|
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
|
||||||
|
|
||||||
const dYInValueSpace =
|
const dYInValueSpace =
|
||||||
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(dYInExtremumSpace)
|
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(
|
||||||
|
dYInExtremumSpace,
|
||||||
|
)
|
||||||
|
|
||||||
const updatedKeyframes: Keyframe[] = []
|
const updatedKeyframes: Keyframe[] = []
|
||||||
|
|
||||||
|
@ -155,11 +151,14 @@ function useDragKeyframe(
|
||||||
|
|
||||||
if (keepSpeeds) {
|
if (keepSpeeds) {
|
||||||
const prev =
|
const prev =
|
||||||
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index - 1]
|
propsAtStartOfDrag.trackData.keyframes[
|
||||||
|
propsAtStartOfDrag.index - 1
|
||||||
|
]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prev &&
|
prev &&
|
||||||
Math.abs((original.value as number) - (prev.value as number)) > 0
|
Math.abs((original.value as number) - (prev.value as number)) >
|
||||||
|
0
|
||||||
) {
|
) {
|
||||||
const newPrev: Keyframe = {
|
const newPrev: Keyframe = {
|
||||||
...prev,
|
...prev,
|
||||||
|
@ -175,11 +174,14 @@ function useDragKeyframe(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const next =
|
const next =
|
||||||
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index + 1]
|
propsAtStartOfDrag.trackData.keyframes[
|
||||||
|
propsAtStartOfDrag.index + 1
|
||||||
|
]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
next &&
|
next &&
|
||||||
Math.abs((original.value as number) - (next.value as number)) > 0
|
Math.abs((original.value as number) - (next.value as number)) >
|
||||||
|
0
|
||||||
) {
|
) {
|
||||||
const newNext: Keyframe = {
|
const newNext: Keyframe = {
|
||||||
...next,
|
...next,
|
||||||
|
@ -212,17 +214,14 @@ function useDragKeyframe(
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
setIsDragging(false)
|
setIsDragging(false)
|
||||||
if (unlockExtremums) {
|
unlockExtremums()
|
||||||
const unlock = unlockExtremums
|
|
||||||
unlockExtremums = undefined
|
|
||||||
unlock()
|
|
||||||
}
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
tempTransaction?.commit()
|
tempTransaction?.commit()
|
||||||
} else {
|
} else {
|
||||||
tempTransaction?.discard()
|
tempTransaction?.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type {Pointer} from '@theatre/dataverse'
|
import type {Pointer} from '@theatre/dataverse'
|
||||||
import {prism, val} from '@theatre/dataverse'
|
import {prism, val} from '@theatre/dataverse'
|
||||||
import {usePrism, useVal} from '@theatre/react'
|
import {usePrism, useVal} from '@theatre/react'
|
||||||
import type {$IntentionalAny, IRange} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
|
@ -179,31 +179,22 @@ const FocusRangeStrip: React.FC<{
|
||||||
const sheet = useVal(layoutP.sheet)
|
const sheet = useVal(layoutP.sheet)
|
||||||
|
|
||||||
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
||||||
let sequence = sheet.getSequence()
|
|
||||||
let startPosBeforeDrag: number,
|
|
||||||
endPosBeforeDrag: number,
|
|
||||||
tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let dragHappened = false
|
|
||||||
let existingRange: {enabled: boolean; range: IRange<number>} | undefined
|
|
||||||
let target: HTMLDivElement | undefined
|
|
||||||
let newStartPosition: number, newEndPosition: number
|
let newStartPosition: number, newEndPosition: number
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'FocusRangeStrip',
|
debugName: 'FocusRangeStrip',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
existingRange = existingRangeD.getValue()
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
let existingRange = existingRangeD.getValue()
|
||||||
|
if (!existingRange) return false
|
||||||
|
|
||||||
if (existingRange) {
|
const startPosBeforeDrag = existingRange.range.start
|
||||||
startPosBeforeDrag = existingRange.range.start
|
const endPosBeforeDrag = existingRange.range.end
|
||||||
endPosBeforeDrag = existingRange.range.end
|
let dragHappened = false
|
||||||
dragHappened = false
|
const sequence = val(layoutP.sheet).getSequence()
|
||||||
sequence = val(layoutP.sheet).getSequence()
|
|
||||||
target = event.target as HTMLDivElement
|
|
||||||
isDraggingRef.current = true
|
isDraggingRef.current = true
|
||||||
} else {
|
|
||||||
return false
|
return {
|
||||||
}
|
|
||||||
},
|
|
||||||
onDrag(dx) {
|
onDrag(dx) {
|
||||||
existingRange = existingRangeD.getValue()
|
existingRange = existingRangeD.getValue()
|
||||||
if (existingRange) {
|
if (existingRange) {
|
||||||
|
@ -226,7 +217,8 @@ const FocusRangeStrip: React.FC<{
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
|
|
||||||
tempTransaction = getStudio().tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio().tempTransaction(
|
||||||
|
({stateEditors}) => {
|
||||||
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
|
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
|
||||||
{
|
{
|
||||||
...sheet.address,
|
...sheet.address,
|
||||||
|
@ -237,7 +229,8 @@ const FocusRangeStrip: React.FC<{
|
||||||
enabled: existingRange?.enabled ?? true,
|
enabled: existingRange?.enabled ?? true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDragEnd() {
|
onDragEnd() {
|
||||||
|
@ -248,12 +241,11 @@ const FocusRangeStrip: React.FC<{
|
||||||
} else if (tempTransaction) {
|
} else if (tempTransaction) {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
|
||||||
}
|
|
||||||
if (target !== undefined) {
|
|
||||||
target = undefined
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
lockCursorTo: 'grabbing',
|
lockCursorTo: 'grabbing',
|
||||||
}
|
}
|
||||||
}, [sheet, scaledSpaceToUnitSpace])
|
}, [sheet, scaledSpaceToUnitSpace])
|
||||||
|
|
|
@ -22,7 +22,6 @@ import {
|
||||||
useLockFrameStampPosition,
|
useLockFrameStampPosition,
|
||||||
} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider'
|
} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider'
|
||||||
import {focusRangeStripTheme, RangeStrip} from './FocusRangeStrip'
|
import {focusRangeStripTheme, RangeStrip} from './FocusRangeStrip'
|
||||||
import type Sheet from '@theatre/core/sheets/Sheet'
|
|
||||||
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
|
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
|
||||||
|
|
||||||
const TheDiv = styled.div<{enabled: boolean; type: 'start' | 'end'}>`
|
const TheDiv = styled.div<{enabled: boolean; type: 'start' | 'end'}>`
|
||||||
|
@ -172,33 +171,28 @@ const FocusRangeThumb: React.FC<{
|
||||||
)
|
)
|
||||||
|
|
||||||
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
||||||
let defaultRange: IRange
|
|
||||||
let range: IRange
|
|
||||||
let focusRangeEnabled: boolean
|
|
||||||
let posBeforeDrag: number
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let minFocusRangeStripWidth: number
|
|
||||||
let sheet: Sheet
|
|
||||||
let scaledSpaceToUnitSpace: (s: number) => number
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'FocusRangeThumb',
|
debugName: 'FocusRangeThumb',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
sheet = val(layoutP.sheet)
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
let range: IRange
|
||||||
|
|
||||||
|
const sheet = val(layoutP.sheet)
|
||||||
const sequence = sheet.getSequence()
|
const sequence = sheet.getSequence()
|
||||||
defaultRange = {start: 0, end: sequence.length}
|
const defaultRange = {start: 0, end: sequence.length}
|
||||||
let existingRange = existingRangeD.getValue() || {
|
let existingRange = existingRangeD.getValue() || {
|
||||||
range: defaultRange,
|
range: defaultRange,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
}
|
}
|
||||||
focusRangeEnabled = existingRange.enabled
|
const focusRangeEnabled = existingRange.enabled
|
||||||
|
|
||||||
posBeforeDrag = existingRange.range[thumbType]
|
const posBeforeDrag = existingRange.range[thumbType]
|
||||||
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
||||||
minFocusRangeStripWidth = scaledSpaceToUnitSpace(
|
const minFocusRangeStripWidth = scaledSpaceToUnitSpace(
|
||||||
focusRangeStripTheme.rangeStripMinWidth,
|
focusRangeStripTheme.rangeStripMinWidth,
|
||||||
)
|
)
|
||||||
},
|
|
||||||
|
return {
|
||||||
onDrag(dx, _, event) {
|
onDrag(dx, _, event) {
|
||||||
let newPosition: number
|
let newPosition: number
|
||||||
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {
|
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {
|
||||||
|
@ -255,6 +249,8 @@ const FocusRangeThumb: React.FC<{
|
||||||
else tempTransaction?.discard()
|
else tempTransaction?.discard()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}, [layoutP])
|
}, [layoutP])
|
||||||
|
|
||||||
const [isDragging] = useDrag(hitZoneNode, gestureHandlers)
|
const [isDragging] = useDrag(hitZoneNode, gestureHandlers)
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import type Sequence from '@theatre/core/sequences/Sequence'
|
|
||||||
import type Sheet from '@theatre/core/sheets/Sheet'
|
|
||||||
import type {Pointer} from '@theatre/dataverse'
|
import type {Pointer} from '@theatre/dataverse'
|
||||||
import {prism, val} from '@theatre/dataverse'
|
import {prism, val} from '@theatre/dataverse'
|
||||||
import {usePrism} from '@theatre/react'
|
import {usePrism} from '@theatre/react'
|
||||||
import type {$IntentionalAny, VoidFn} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
import getStudio from '@theatre/studio/getStudio'
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
import {
|
import {
|
||||||
panelDimsToPanelPosition,
|
panelDimsToPanelPosition,
|
||||||
|
@ -102,32 +100,26 @@ function usePanelDragZoneGestureHandlers(
|
||||||
const focusRangeCreationGestureHandlers = (): Parameters<
|
const focusRangeCreationGestureHandlers = (): Parameters<
|
||||||
typeof useDrag
|
typeof useDrag
|
||||||
>[1] => {
|
>[1] => {
|
||||||
let startPosInUnitSpace: number,
|
|
||||||
tempTransaction: CommitOrDiscard | undefined
|
|
||||||
|
|
||||||
let clippedSpaceToUnitSpace: (s: number) => number
|
|
||||||
let scaledSpaceToUnitSpace: (s: number) => number
|
|
||||||
let sequence: Sequence
|
|
||||||
let sheet: Sheet
|
|
||||||
let minFocusRangeStripWidth: number
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'FocusRangeZone/focusRangeCreationGestureHandlers',
|
debugName: 'FocusRangeZone/focusRangeCreationGestureHandlers',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
clippedSpaceToUnitSpace = val(layoutP.clippedSpace.toUnitSpace)
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
|
||||||
sheet = val(layoutP.sheet)
|
const clippedSpaceToUnitSpace = val(layoutP.clippedSpace.toUnitSpace)
|
||||||
sequence = sheet.getSequence()
|
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
||||||
|
const sheet = val(layoutP.sheet)
|
||||||
|
const sequence = sheet.getSequence()
|
||||||
|
|
||||||
const targetElement: HTMLElement = event.target as HTMLElement
|
const targetElement: HTMLElement = event.target as HTMLElement
|
||||||
const rect = targetElement!.getBoundingClientRect()
|
const rect = targetElement!.getBoundingClientRect()
|
||||||
startPosInUnitSpace = clippedSpaceToUnitSpace(
|
const startPosInUnitSpace = clippedSpaceToUnitSpace(
|
||||||
event.clientX - rect.left,
|
event.clientX - rect.left,
|
||||||
)
|
)
|
||||||
minFocusRangeStripWidth = scaledSpaceToUnitSpace(
|
const minFocusRangeStripWidth = scaledSpaceToUnitSpace(
|
||||||
focusRangeStripTheme.rangeStripMinWidth,
|
focusRangeStripTheme.rangeStripMinWidth,
|
||||||
)
|
)
|
||||||
},
|
|
||||||
|
return {
|
||||||
onDrag(dx) {
|
onDrag(dx) {
|
||||||
const deltaPos = scaledSpaceToUnitSpace(dx)
|
const deltaPos = scaledSpaceToUnitSpace(dx)
|
||||||
|
|
||||||
|
@ -155,7 +147,8 @@ function usePanelDragZoneGestureHandlers(
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
|
|
||||||
tempTransaction = getStudio().tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio().tempTransaction(
|
||||||
|
({stateEditors}) => {
|
||||||
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
|
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
|
||||||
{
|
{
|
||||||
...sheet.address,
|
...sheet.address,
|
||||||
|
@ -163,7 +156,8 @@ function usePanelDragZoneGestureHandlers(
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (dragHappened && tempTransaction !== undefined) {
|
if (dragHappened && tempTransaction !== undefined) {
|
||||||
|
@ -171,27 +165,24 @@ function usePanelDragZoneGestureHandlers(
|
||||||
} else if (tempTransaction) {
|
} else if (tempTransaction) {
|
||||||
tempTransaction.discard()
|
tempTransaction.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
lockCursorTo: 'ew-resize',
|
lockCursorTo: 'ew-resize',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const panelMoveGestureHandlers = (): Parameters<typeof useDrag>[1] => {
|
const panelMoveGestureHandlers = (): Parameters<typeof useDrag>[1] => {
|
||||||
let stuffBeforeDrag = panelStuffRef.current
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let unlock: VoidFn | undefined
|
|
||||||
return {
|
return {
|
||||||
debugName: 'FocusRangeZone/panelMoveGestureHandlers',
|
debugName: 'FocusRangeZone/panelMoveGestureHandlers',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
stuffBeforeDrag = panelStuffRef.current
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
if (unlock) {
|
const stuffBeforeDrag = panelStuffRef.current
|
||||||
const u = unlock
|
|
||||||
unlock = undefined
|
const unlock = panelStuffRef.current.addBoundsHighlightLock()
|
||||||
u()
|
|
||||||
}
|
return {
|
||||||
unlock = panelStuffRef.current.addBoundsHighlightLock()
|
|
||||||
},
|
|
||||||
onDrag(dx, dy) {
|
onDrag(dx, dy) {
|
||||||
const newDims: typeof panelStuffRef.current['dims'] = {
|
const newDims: typeof panelStuffRef.current['dims'] = {
|
||||||
...stuffBeforeDrag.dims,
|
...stuffBeforeDrag.dims,
|
||||||
|
@ -204,50 +195,55 @@ function usePanelDragZoneGestureHandlers(
|
||||||
})
|
})
|
||||||
|
|
||||||
tempTransaction?.discard()
|
tempTransaction?.discard()
|
||||||
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
tempTransaction = getStudio()!.tempTransaction(
|
||||||
|
({stateEditors}) => {
|
||||||
stateEditors.studio.historic.panelPositions.setPanelPosition({
|
stateEditors.studio.historic.panelPositions.setPanelPosition({
|
||||||
position,
|
position,
|
||||||
panelId: stuffBeforeDrag.panelId,
|
panelId: stuffBeforeDrag.panelId,
|
||||||
})
|
})
|
||||||
})
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (unlock) {
|
unlock()
|
||||||
const u = unlock
|
|
||||||
unlock = undefined
|
|
||||||
u()
|
|
||||||
}
|
|
||||||
if (dragHappened) {
|
if (dragHappened) {
|
||||||
tempTransaction?.commit()
|
tempTransaction?.commit()
|
||||||
} else {
|
} else {
|
||||||
tempTransaction?.discard()
|
tempTransaction?.discard()
|
||||||
}
|
}
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
lockCursorTo: 'move',
|
lockCursorTo: 'move',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentGestureHandlers: undefined | Parameters<typeof useDrag>[1]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'FocusRangeZone',
|
debugName: 'FocusRangeZone',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
if (event.shiftKey) {
|
const [_mode, currentGestureHandlers] = event.shiftKey
|
||||||
setMode('creating')
|
? [
|
||||||
currentGestureHandlers = focusRangeCreationGestureHandlers()
|
'creating' as 'creating',
|
||||||
} else {
|
focusRangeCreationGestureHandlers().onDragStart(event),
|
||||||
setMode('moving-panel')
|
]
|
||||||
currentGestureHandlers = panelMoveGestureHandlers()
|
: [
|
||||||
}
|
'moving-panel' as 'moving-panel',
|
||||||
currentGestureHandlers.onDragStart!(event)
|
panelMoveGestureHandlers().onDragStart(event),
|
||||||
},
|
]
|
||||||
onDrag(dx, dy, event) {
|
|
||||||
currentGestureHandlers!.onDrag(dx, dy, event)
|
setMode(_mode)
|
||||||
|
|
||||||
|
if (currentGestureHandlers === false) return false
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDrag(dx, dy, event, ddx, ddy) {
|
||||||
|
currentGestureHandlers.onDrag(dx, dy, event, ddx, ddy)
|
||||||
},
|
},
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
setMode('none')
|
setMode('none')
|
||||||
currentGestureHandlers!.onDragEnd!(dragHappened)
|
currentGestureHandlers.onDragEnd?.(dragHappened)
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [layoutP, panelStuffRef])
|
}, [layoutP, panelStuffRef])
|
||||||
|
|
|
@ -247,17 +247,14 @@ function useDragMarker(
|
||||||
propsRef.current = props
|
propsRef.current = props
|
||||||
|
|
||||||
const useDragOpts = useMemo<UseDragOpts>(() => {
|
const useDragOpts = useMemo<UseDragOpts>(() => {
|
||||||
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
|
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
|
||||||
let markerAtStartOfDrag: StudioHistoricStateSequenceEditorMarker
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: `MarkerDot/useDragMarker (${props.marker.id})`,
|
debugName: `MarkerDot/useDragMarker (${props.marker.id})`,
|
||||||
|
|
||||||
onDragStart(_event) {
|
onDragStart(_event) {
|
||||||
markerAtStartOfDrag = propsRef.current.marker
|
const markerAtStartOfDrag = propsRef.current.marker
|
||||||
toUnitSpace = val(props.layoutP.scaledSpace.toUnitSpace)
|
const toUnitSpace = val(props.layoutP.scaledSpace.toUnitSpace)
|
||||||
},
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
|
return {
|
||||||
onDrag(dx, _dy, event) {
|
onDrag(dx, _dy, event) {
|
||||||
const original = markerAtStartOfDrag
|
const original = markerAtStartOfDrag
|
||||||
const newPosition = Math.max(
|
const newPosition = Math.max(
|
||||||
|
@ -286,7 +283,8 @@ function useDragMarker(
|
||||||
onDragEnd(dragHappened) {
|
onDragEnd(dragHappened) {
|
||||||
if (dragHappened) tempTransaction?.commit()
|
if (dragHappened) tempTransaction?.commit()
|
||||||
else tempTransaction?.discard()
|
else tempTransaction?.discard()
|
||||||
tempTransaction = undefined
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import type Sequence from '@theatre/core/sequences/Sequence'
|
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
import RoomToClick from '@theatre/studio/uiComponents/RoomToClick'
|
import RoomToClick from '@theatre/studio/uiComponents/RoomToClick'
|
||||||
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
|
@ -203,20 +202,17 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
||||||
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
|
|
||||||
|
|
||||||
let posBeforeSeek = 0
|
|
||||||
let sequence: Sequence
|
|
||||||
let scaledSpaceToUnitSpace: typeof layoutP.scaledSpace.toUnitSpace.$$__pointer_type
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debugName: 'Playhead',
|
debugName: 'Playhead',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
sequence = val(layoutP.sheet).getSequence()
|
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
|
||||||
posBeforeSeek = sequence.position
|
|
||||||
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
const sequence = val(layoutP.sheet).getSequence()
|
||||||
|
const posBeforeSeek = sequence.position
|
||||||
|
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
|
||||||
setIsSeeking(true)
|
setIsSeeking(true)
|
||||||
},
|
|
||||||
|
return {
|
||||||
onDrag(dx, _, event) {
|
onDrag(dx, _, event) {
|
||||||
const deltaPos = scaledSpaceToUnitSpace(dx)
|
const deltaPos = scaledSpaceToUnitSpace(dx)
|
||||||
|
|
||||||
|
@ -231,6 +227,8 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
|
||||||
setIsSeeking(false)
|
setIsSeeking(false)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const [isDragging] = useDrag(thumbNode, gestureHandlers)
|
const [isDragging] = useDrag(thumbNode, gestureHandlers)
|
||||||
|
|
|
@ -4,6 +4,28 @@ import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import {useCssCursorLock} from './PointerEventsHandler'
|
import {useCssCursorLock} from './PointerEventsHandler'
|
||||||
import type {CapturedPointer} from '@theatre/studio/UIRoot/PointerCapturing'
|
import type {CapturedPointer} from '@theatre/studio/UIRoot/PointerCapturing'
|
||||||
import {usePointerCapturing} from '@theatre/studio/UIRoot/PointerCapturing'
|
import {usePointerCapturing} from '@theatre/studio/UIRoot/PointerCapturing'
|
||||||
|
import noop from '@theatre/shared/utils/noop'
|
||||||
|
|
||||||
|
export enum MouseButton {
|
||||||
|
Left = 0,
|
||||||
|
Middle = 1,
|
||||||
|
// Not including Right because it _might_ interfere with chord clicking.
|
||||||
|
// So we'll wait for chord-clicking to land before exploring right-button gestures
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dx, dy: delta x/y from the start of the drag
|
||||||
|
*/
|
||||||
|
type OnDragCallback = (
|
||||||
|
// deltaX/Y are counted from the start of the drag
|
||||||
|
deltaX: number,
|
||||||
|
deltaY: number,
|
||||||
|
event: MouseEvent,
|
||||||
|
dxFromLastEvent: number,
|
||||||
|
dyFromLastEvent: number,
|
||||||
|
) => void
|
||||||
|
|
||||||
|
type OnDragEndCallback = (dragHappened: boolean) => void
|
||||||
|
|
||||||
export type UseDragOpts = {
|
export type UseDragOpts = {
|
||||||
/**
|
/**
|
||||||
|
@ -35,22 +57,23 @@ export type UseDragOpts = {
|
||||||
* onDragStart can be undefined, in which case, we always handle useDrag,
|
* onDragStart can be undefined, in which case, we always handle useDrag,
|
||||||
* but when defined, we can allow the handler to return false to indicate ignore this dragging
|
* but when defined, we can allow the handler to return false to indicate ignore this dragging
|
||||||
*/
|
*/
|
||||||
onDragStart?: (event: MouseEvent) => void | false
|
onDragStart: (event: MouseEvent) =>
|
||||||
|
| false
|
||||||
|
| {
|
||||||
/**
|
/**
|
||||||
* Called at the end of the drag gesture.
|
* Called at the end of the drag gesture.
|
||||||
* `dragHappened` will be `true` if the user actually moved the pointer
|
* `dragHappened` will be `true` if the user actually moved the pointer
|
||||||
* (if onDrag isn't called, then this will be false becuase the user hasn't moved the pointer)
|
* (if onDrag isn't called, then this will be false becuase the user hasn't moved the pointer)
|
||||||
*/
|
*/
|
||||||
onDragEnd?: (dragHappened: boolean) => void
|
onDragEnd?: OnDragEndCallback
|
||||||
/**
|
onDrag: OnDragCallback
|
||||||
* This will be called 0 times if the gesture ends up being a click,
|
}
|
||||||
* or 1 or more times if it ends up being a drag gesture.
|
|
||||||
*
|
// which mouse button to use the drag event
|
||||||
* `dx`: the delta x
|
buttons?:
|
||||||
* `dy`: the delta y
|
| [MouseButton]
|
||||||
* `event`: the mouse event
|
| [MouseButton, MouseButton]
|
||||||
*/
|
| [MouseButton | MouseButton | MouseButton]
|
||||||
onDrag: (dx: number, dy: number, event: MouseEvent) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useDrag(
|
export default function useDrag(
|
||||||
|
@ -80,9 +103,15 @@ export default function useDrag(
|
||||||
}
|
}
|
||||||
}>({dragHappened: false, startPos: {x: 0, y: 0}})
|
}>({dragHappened: false, startPos: {x: 0, y: 0}})
|
||||||
|
|
||||||
|
const callbacksRef = useRef<{
|
||||||
|
onDrag: OnDragCallback
|
||||||
|
onDragEnd: OnDragEndCallback
|
||||||
|
}>({onDrag: noop, onDragEnd: noop})
|
||||||
|
|
||||||
const capturedPointerRef = useRef<undefined | CapturedPointer>()
|
const capturedPointerRef = useRef<undefined | CapturedPointer>()
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!target) return
|
if (!target) return
|
||||||
|
let lastDeltas = [0, 0]
|
||||||
|
|
||||||
const getDistances = (event: MouseEvent): [number, number] => {
|
const getDistances = (event: MouseEvent): [number, number] => {
|
||||||
const {startPos} = stateRef.current
|
const {startPos} = stateRef.current
|
||||||
|
@ -94,14 +123,26 @@ export default function useDrag(
|
||||||
modeRef.current = 'dragging'
|
modeRef.current = 'dragging'
|
||||||
|
|
||||||
const deltas = getDistances(event)
|
const deltas = getDistances(event)
|
||||||
optsRef.current.onDrag(deltas[0], deltas[1], event)
|
const [deltaFromLastX, deltaFromLastY] = [
|
||||||
|
deltas[0] - lastDeltas[0],
|
||||||
|
deltas[1] - lastDeltas[1],
|
||||||
|
]
|
||||||
|
lastDeltas = deltas
|
||||||
|
|
||||||
|
callbacksRef.current.onDrag(
|
||||||
|
deltas[0],
|
||||||
|
deltas[1],
|
||||||
|
event,
|
||||||
|
deltaFromLastX,
|
||||||
|
deltaFromLastY,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const dragEndHandler = () => {
|
const dragEndHandler = () => {
|
||||||
removeDragListeners()
|
removeDragListeners()
|
||||||
modeRef.current = 'notDragging'
|
modeRef.current = 'notDragging'
|
||||||
|
|
||||||
optsRef.current.onDragEnd?.(stateRef.current.dragHappened)
|
callbacksRef.current.onDragEnd(stateRef.current.dragHappened)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addDragListeners = () => {
|
const addDragListeners = () => {
|
||||||
|
@ -136,13 +177,18 @@ export default function useDrag(
|
||||||
const opts = optsRef.current
|
const opts = optsRef.current
|
||||||
if (opts.disabled === true) return
|
if (opts.disabled === true) return
|
||||||
|
|
||||||
if (event.button !== 0) return
|
const acceptedButtons: MouseButton[] = opts.buttons ?? [MouseButton.Left]
|
||||||
|
|
||||||
// onDragStart can be undefined, in which case, we always handle useDrag,
|
if (!acceptedButtons.includes(event.button)) return
|
||||||
// but when defined, we can allow the handler to return false to indicate ignore this dragging
|
|
||||||
if (opts.onDragStart != null) {
|
const returnOfOnDragStart = opts.onDragStart(event)
|
||||||
const shouldIgnore = opts.onDragStart(event) === false
|
|
||||||
if (shouldIgnore) return
|
if (returnOfOnDragStart === false) {
|
||||||
|
// we should ignore the gesture
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
callbacksRef.current.onDrag = returnOfOnDragStart.onDrag
|
||||||
|
callbacksRef.current.onDragEnd = returnOfOnDragStart.onDragEnd ?? noop
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to capture pointer after we know the provided handler wants to handle drag start
|
// need to capture pointer after we know the provided handler wants to handle drag start
|
||||||
|
@ -175,7 +221,7 @@ export default function useDrag(
|
||||||
target.removeEventListener('click', preventUnwantedClick as $FixMe)
|
target.removeEventListener('click', preventUnwantedClick as $FixMe)
|
||||||
|
|
||||||
if (modeRef.current !== 'notDragging') {
|
if (modeRef.current !== 'notDragging') {
|
||||||
optsRef.current.onDragEnd?.(modeRef.current === 'dragging')
|
callbacksRef.current.onDragEnd?.(modeRef.current === 'dragging')
|
||||||
}
|
}
|
||||||
modeRef.current = 'notDragging'
|
modeRef.current = 'notDragging'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue