diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx index 43703df..c718942 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx @@ -1,7 +1,6 @@ import {val} from '@theatre/dataverse' import React, {useMemo, useRef} from 'react' import {AggregateKeyframePositionIsSelected} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregatedKeyframeTrack' -import {DopeSnapHitZoneUI} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnapHitZoneUI' import useRefAndState from '@theatre/studio/utils/useRefAndState' import type {UseDragOpts} from '@theatre/studio/uiComponents/useDrag' import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore' @@ -21,6 +20,13 @@ import { } from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/selections' import type {KeyframeWithPathToPropFromCommonRoot} from '@theatre/studio/store/types/ahistoric' import {commonRootOfPathsToProps} from '@theatre/shared/utils/addresses' +import type {ILogger} from '@theatre/shared/logger' +import { + collectKeyframeSnapPositions, + snapToNone, + snapToSome, +} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' +import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic' type IAggregateKeyframeDotProps = { editorProps: IAggregateKeyframeEditorProps @@ -41,17 +47,11 @@ export function AggregateKeyframeDot( }, }) - const [contextMenu] = useAggregateKeyframeContextMenu(props, node) + const [contextMenu] = useAggregateKeyframeContextMenu(props, logger, node) return ( <> - + keyframe.id !== kfWithTrack.kf.id, + ) && + !( + // if all of the children of the current aggregate keyframe are in a selection, + ( + props.selection && + // then we exclude them and all other keyframes in the selection from being snap targets + props.selection.byObjectKey[objectKey]?.byTrackId[trackId] + ?.byKeyframeId[keyframe.id] + ) + ) + ) + }, + ) + + snapToSome(snapPositions) + if ( props.selection && props.aggregateKeyframes[props.index].selected === @@ -172,7 +208,7 @@ function useDragForAggregateKeyframeDot( ) { const {selection, viewModel} = props const {sheetObject} = viewModel - const hanlders = selection + const handlers = selection .getDragHandlers({ ...sheetObject.address, domNode: node!, @@ -180,7 +216,16 @@ function useDragForAggregateKeyframeDot( }) .onDragStart(event) - return hanlders && {...hanlders, onClick: options.onClickFromDrag} + return ( + handlers && { + ...handlers, + onClick: options.onClickFromDrag, + onDragEnd: (...args) => { + handlers.onDragEnd?.(...args) + snapToNone() + }, + } + ) } const propsAtStartOfDrag = props @@ -227,6 +272,8 @@ function useDragForAggregateKeyframeDot( } else { tempTransaction?.discard() } + + snapToNone() }, onClick(ev) { options.onClickFromDrag(ev) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeVisualDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeVisualDot.tsx index 1278745..19153c5 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeVisualDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeVisualDot.tsx @@ -2,7 +2,7 @@ import React from 'react' import {AggregateKeyframePositionIsSelected} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregatedKeyframeTrack' import styled from 'styled-components' import {absoluteDims} from '@theatre/studio/utils/absoluteDims' -import {DopeSnapHitZoneUI} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnapHitZoneUI' +import {pointerEventsAutoInNormalMode} from '@theatre/studio/css' const DOT_SIZE_PX = 16 const DOT_HOVER_SIZE_PX = DOT_SIZE_PX + 5 @@ -20,16 +20,15 @@ export const HitZone = styled.div` z-index: 2; cursor: ew-resize; - ${DopeSnapHitZoneUI.CSS} + position: absolute; + ${absoluteDims(12)}; + ${pointerEventsAutoInNormalMode}; - #pointer-root.draggingPositionInSequenceEditor & { - ${DopeSnapHitZoneUI.CSS_WHEN_SOMETHING_DRAGGING} - } - - &:hover + ${DotContainer}, - #pointer-root.draggingPositionInSequenceEditor &:hover + ${DotContainer}, - // notice "," css "or" - &.${DopeSnapHitZoneUI.BEING_DRAGGED_CLASS} + ${DotContainer} { + &:hover + + ${DotContainer}, + #pointer-root.draggingPositionInSequenceEditor + &:hover + + ${DotContainer} { ${absoluteDims(DOT_HOVER_SIZE_PX)} } ` diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregatedKeyframeTrack.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregatedKeyframeTrack.tsx index 2026e6c..94072a4 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregatedKeyframeTrack.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregatedKeyframeTrack.tsx @@ -7,11 +7,11 @@ import type { SequenceEditorTree_SheetObject, } from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic' -import {usePrism} from '@theatre/react' +import {usePrism, useVal} from '@theatre/react' import type {Pointer} from '@theatre/dataverse' import {valueDerivation} from '@theatre/dataverse' import {val} from '@theatre/dataverse' -import React from 'react' +import React, {Fragment, useMemo} from 'react' import styled from 'styled-components' import type {IContextMenuItem} from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu' import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu' @@ -21,16 +21,20 @@ import AggregateKeyframeEditor from './AggregateKeyframeEditor/AggregateKeyframe import type {AggregatedKeyframes} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes' import {useLogger} from '@theatre/studio/uiComponents/useLogger' import getStudio from '@theatre/studio/getStudio' -import type { - SheetObjectAddress} from '@theatre/shared/utils/addresses'; +import type {SheetObjectAddress} from '@theatre/shared/utils/addresses' import { decodePathToProp, doesPathStartWith, - encodePathToProp + encodePathToProp, } from '@theatre/shared/utils/addresses' import type {SequenceTrackId} from '@theatre/shared/utils/ids' import type Sequence from '@theatre/core/sequences/Sequence' import type {KeyframeWithPathToPropFromCommonRoot} from '@theatre/studio/store/types' +import {collectAggregateSnapPositions} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes' +import KeyframeSnapTarget, { + snapPositionsStateD, +} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' +import {emptyObject} from '@theatre/shared/utils' const AggregatedKeyframeTrackContainer = styled.div` position: relative; @@ -88,21 +92,50 @@ function AggregatedKeyframeTrack_memo(props: IAggregatedKeyframeTracksProps) { }), ) - const keyframeEditors = posKfs.map(({position, keyframes}, index) => ( - collectAggregateSnapPositions(viewModel, snapPositions), + [snapPositions], + ) + + const snapTargets = aggregateSnapPositions.map((position) => ( + )) + const keyframeEditors = posKfs.map(({position, keyframes}, index) => ( + + {snapToAllKeyframes && ( + + )} + + + )) + return ( {keyframeEditors} + {snapTargets} {contextMenu} ) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/BasicKeyframedTrack.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/BasicKeyframedTrack.tsx index fbf4411..7bf6fc7 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/BasicKeyframedTrack.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/BasicKeyframedTrack.tsx @@ -2,10 +2,10 @@ import type {TrackData} from '@theatre/core/projects/store/types/SheetState_Hist import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout' import type {SequenceEditorTree_PrimitiveProp} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic' -import {usePrism} from '@theatre/react' +import {usePrism, useVal} from '@theatre/react' import type {Pointer} from '@theatre/dataverse' import {val} from '@theatre/dataverse' -import React from 'react' +import React, {Fragment} from 'react' import styled from 'styled-components' import SingleKeyframeEditor from './KeyframeEditor/SingleKeyframeEditor' import type {IContextMenuItem} from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu' @@ -14,6 +14,9 @@ import useRefAndState from '@theatre/studio/utils/useRefAndState' import getStudio from '@theatre/studio/getStudio' import {arePathsEqual} from '@theatre/shared/utils/addresses' import type {KeyframeWithPathToPropFromCommonRoot} from '@theatre/studio/store/types' +import KeyframeSnapTarget, { + snapPositionsStateD, +} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' const Container = styled.div` position: relative; @@ -58,15 +61,45 @@ const BasicKeyframedTrack: React.VFC = React.memo( props, ) + const snapPositionsState = useVal(snapPositionsStateD) + + const snapPositions = + snapPositionsState.mode === 'snapToSome' + ? snapPositionsState.positions[leaf.sheetObject.address.objectKey]?.[ + leaf.trackId + ] + : [] ?? [] + + const snapToAllKeyframes = snapPositionsState.mode === 'snapToAll' + const keyframeEditors = trackData.keyframes.map((kf, index) => ( - + {snapToAllKeyframes && ( + + )} + + + )) + + const snapTargets = snapPositions.map((position) => ( + )) @@ -78,6 +111,7 @@ const BasicKeyframedTrack: React.VFC = React.memo( }} > {keyframeEditors} + {snapTargets} {contextMenu} ) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx index e472e08..557df7e 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx @@ -5,8 +5,8 @@ import last from 'lodash-es/last' 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 type {UseDragOpts} from '@theatre/studio/uiComponents/useDrag' +import useDrag from '@theatre/studio/uiComponents/useDrag' import useRefAndState from '@theatre/studio/utils/useRefAndState' import {val} from '@theatre/dataverse' import {useLockFrameStampPosition} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider' @@ -19,10 +19,15 @@ import {useTempTransactionEditingTools} from './useTempTransactionEditingTools' import {DeterminePropEditorForSingleKeyframe} from './DeterminePropEditorForSingleKeyframe' import type {ISingleKeyframeEditorProps} from './SingleKeyframeEditor' import {absoluteDims} from '@theatre/studio/utils/absoluteDims' -import {DopeSnapHitZoneUI} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnapHitZoneUI' import {useLogger} from '@theatre/studio/uiComponents/useLogger' import type {ILogger} from '@theatre/shared/logger' import {copyableKeyframesFromSelection} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/selections' +import {pointerEventsAutoInNormalMode} from '@theatre/studio/css' +import { + collectKeyframeSnapPositions, + snapToNone, + snapToSome, +} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' export const DOT_SIZE_PX = 6 const DOT_HOVER_SIZE_PX = DOT_SIZE_PX + 5 @@ -49,17 +54,11 @@ const HitZone = styled.div` z-index: 1; cursor: ew-resize; - ${DopeSnapHitZoneUI.CSS} + position: absolute; + ${absoluteDims(12)}; + ${pointerEventsAutoInNormalMode}; - #pointer-root.draggingPositionInSequenceEditor & { - ${DopeSnapHitZoneUI.CSS_WHEN_SOMETHING_DRAGGING} - } - - &:hover - + ${Diamond}, - // notice , "or" in CSS - &.${DopeSnapHitZoneUI.BEING_DRAGGED_CLASS} - + ${Diamond} { + &:hover + ${Diamond} { ${absoluteDims(DOT_HOVER_SIZE_PX)} } ` @@ -82,13 +81,7 @@ const SingleKeyframeDot: React.VFC = (props) => { return ( <> - + {inlineEditorPopover} {contextMenu} @@ -210,6 +203,37 @@ function useDragForSingleKeyframeDot( debugName: 'KeyframeDot/useDragKeyframe', onDragStart(event) { const props = propsRef.current + + const tracksByObject = val( + getStudio()!.atomP.historic.coreByProject[ + props.leaf.sheetObject.address.projectId + ].sheetsById[props.leaf.sheetObject.address.sheetId].sequence + .tracksByObject, + )! + + const snapPositions = collectKeyframeSnapPositions( + tracksByObject, + // Calculate all the valid snap positions in the sequence editor, + // excluding this keyframe, and any selection it is part of. + function shouldIncludeKeyfram(keyframe, {trackId, objectKey}) { + return ( + // we exclude this keyframe from being a snap target + keyframe.id !== props.keyframe.id && + !( + // if the current dragged keyframe is in the selection, + ( + props.selection && + // then we exclude it and all other keyframes in the selection from being snap targets + props.selection.byObjectKey[objectKey]?.byTrackId[trackId] + ?.byKeyframeId[keyframe.id] + ) + ) + ) + }, + ) + + snapToSome(snapPositions) + if (props.selection) { const {selection, leaf} = props const {sheetObject} = leaf @@ -226,7 +250,16 @@ function useDragForSingleKeyframeDot( // in the future, we may want to show an multi-editor, like in the // single tween editor, so that selected keyframes' values can be changed // together - return handlers && {...handlers, onClick: options.onClickFromDrag} + return ( + handlers && { + ...handlers, + onClick: options.onClickFromDrag, + onDragEnd: (...args) => { + handlers.onDragEnd?.(...args) + snapToNone() + }, + } + ) } const propsAtStartOfDrag = props @@ -272,6 +305,8 @@ function useDragForSingleKeyframeDot( } else { tempTransaction?.discard() } + + snapToNone() }, onClick(ev) { options.onClickFromDrag(ev) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx index feed734..5168fc7 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx @@ -12,6 +12,7 @@ import {pointerEventsAutoInNormalMode} from '@theatre/studio/css' import {useCssCursorLock} from '@theatre/studio/uiComponents/PointerEventsHandler' import type {IRange} from '@theatre/shared/utils/types' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' +import {snapToAll, snapToNone} from './KeyframeSnapTarget' const Container = styled.div` position: absolute; @@ -115,6 +116,8 @@ function useDragPlayheadHandlers( const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace) setIsSeeking(true) + snapToAll() + return { onDrag(dx: number, _, event) { const deltaPos = scaledSpaceToUnitSpace(dx) @@ -135,6 +138,7 @@ function useDragPlayheadHandlers( }, onDragEnd() { setIsSeeking(false) + snapToNone() }, } }, diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget.tsx new file mode 100644 index 0000000..fad2c44 --- /dev/null +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget.tsx @@ -0,0 +1,129 @@ +import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout' +import type {Pointer} from '@theatre/dataverse' +import {Box, val} from '@theatre/dataverse' +import React from 'react' +import styled from 'styled-components' +import {DopeSnapHitZoneUI} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnapHitZoneUI' +import type {ObjectAddressKey, SequenceTrackId} from '@theatre/shared/utils/ids' +import type { + BasicKeyframedTrack, + HistoricPositionalSequence, + Keyframe, +} from '@theatre/core/projects/store/types/SheetState_Historic' + +const HitZone = styled.div` + z-index: 1; + cursor: ew-resize; + + ${DopeSnapHitZoneUI.CSS} + + #pointer-root.draggingPositionInSequenceEditor & { + ${DopeSnapHitZoneUI.CSS_WHEN_SOMETHING_DRAGGING} + } +` + +const Container = styled.div` + position: absolute; +` + +export type ISnapTargetPRops = { + layoutP: Pointer + leaf: {nodeHeight: number} + position: number +} + +const KeyframeSnapTarget: React.VFC = (props) => { + return ( + + + + ) +} + +export default KeyframeSnapTarget + +export type KeyframeSnapPositions = { + [objectKey: ObjectAddressKey]: { + [trackId: SequenceTrackId]: number[] + } +} + +const stateB = new Box< + | { + // all keyframes must be snap targets + mode: 'snapToAll' + } + | { + // only these keyframes must be snap targets + mode: 'snapToSome' + positions: KeyframeSnapPositions + } + | { + // no keyframe should be a snap target + mode: 'snapToNone' + } +>({mode: 'snapToNone'}) + +export const snapPositionsStateD = stateB.derivation + +export function snapToAll() { + stateB.set({mode: 'snapToAll'}) +} + +export function snapToNone() { + stateB.set({mode: 'snapToNone'}) +} + +export function snapToSome(positions: KeyframeSnapPositions) { + stateB.set({mode: 'snapToSome', positions}) +} + +export function collectKeyframeSnapPositions( + tracksByObject: HistoricPositionalSequence['tracksByObject'], + shouldIncludeKeyframe: ( + kf: Keyframe, + track: { + trackId: SequenceTrackId + trackData: BasicKeyframedTrack + objectKey: ObjectAddressKey + }, + ) => boolean, +): KeyframeSnapPositions { + return Object.fromEntries( + Object.entries(tracksByObject).map( + ([objectKey, trackDataAndTrackIdByPropPath]) => [ + objectKey, + Object.fromEntries( + Object.entries(trackDataAndTrackIdByPropPath!.trackData).map( + ([trackId, track]) => [ + trackId, + track!.keyframes + .filter((kf) => + shouldIncludeKeyframe(kf, { + trackId, + trackData: track!, + objectKey, + }), + ) + .map((keyframe) => keyframe.position), + ], + ), + ), + ], + ), + ) +} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes.tsx index 5ca3e89..a9e1af5 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes.tsx @@ -11,6 +11,7 @@ import type { } from '@theatre/core/projects/store/types/SheetState_Historic' import type {IUtilLogger} from '@theatre/shared/logger' import {encodePathToProp} from '@theatre/shared/utils/addresses' +import {uniq} from 'lodash-es' /** * An index over a series of keyframes that have been collected from different tracks. @@ -128,3 +129,45 @@ export function collectAggregateKeyframesInPrism( tracks, } } + +/** + * Collects all the snap positions for an aggregate track. + */ +export function collectAggregateSnapPositions( + leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_SheetObject, + snapTargetPositions: {[key: string]: {[key: string]: number[]}}, +): number[] { + const sheetObject = leaf.sheetObject + + const projectId = sheetObject.address.projectId + + const sheetObjectTracksP = + getStudio().atomP.historic.coreByProject[projectId].sheetsById[ + sheetObject.address.sheetId + ].sequence.tracksByObject[sheetObject.address.objectKey] + + const positions: number[] = [] + for (const childLeaf of leaf.children) { + if (childLeaf.type === 'primitiveProp') { + const trackId = val( + sheetObjectTracksP.trackIdByPropPath[ + encodePathToProp(childLeaf.pathToProp) + ], + ) + if (!trackId) { + continue + } + + positions.push( + ...(snapTargetPositions[sheetObject.address.objectKey]?.[trackId] ?? + []), + ) + } else if (childLeaf.type === 'propWithChildren') { + positions.push( + ...collectAggregateSnapPositions(childLeaf, snapTargetPositions), + ) + } + } + + return uniq(positions) +} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx index bd39883..55fb237 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx @@ -22,6 +22,10 @@ import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEdito import DopeSnap from './DopeSnap' import {absoluteDims} from '@theatre/studio/utils/absoluteDims' import {DopeSnapHitZoneUI} from './DopeSnapHitZoneUI' +import { + snapToAll, + snapToNone, +} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' const MARKER_SIZE_W_PX = 12 const MARKER_SIZE_H_PX = 12 @@ -221,6 +225,8 @@ function useDragMarker( const toUnitSpace = val(props.layoutP.scaledSpace.toUnitSpace) let tempTransaction: CommitOrDiscard | undefined + snapToAll() + return { onDrag(dx, _dy, event) { const original = markerAtStartOfDrag @@ -250,6 +256,8 @@ function useDragMarker( onDragEnd(dragHappened) { if (dragHappened) tempTransaction?.commit() else tempTransaction?.discard() + + snapToNone() }, } }, diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx index f1b271e..6af70b5 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx @@ -27,6 +27,10 @@ import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useCo import getStudio from '@theatre/studio/getStudio' import {generateSequenceMarkerId} from '@theatre/shared/utils/ids' import DopeSnap from './DopeSnap' +import { + snapToAll, + snapToNone, +} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' const Container = styled.div<{isVisible: boolean}>` --thumbColor: #00e0ff; @@ -214,6 +218,8 @@ const Playhead: React.FC<{layoutP: Pointer}> = ({ const setIsSeeking = val(layoutP.seeker.setIsSeeking) setIsSeeking(true) + snapToAll() + return { onDrag(dx, _, event) { const deltaPos = scaledSpaceToUnitSpace(dx) @@ -227,6 +233,7 @@ const Playhead: React.FC<{layoutP: Pointer}> = ({ }, onDragEnd(dragHappened) { setIsSeeking(false) + snapToNone() }, onClick(e) { openPopover(e, thumbRef.current!)