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