feat(289): Labeled markers (#362)
This commit is contained in:
parent
1fb69126fd
commit
4b4fe858f9
6 changed files with 137 additions and 3 deletions
|
@ -19,13 +19,16 @@ import type {UseDragOpts} from '@theatre/studio/uiComponents/useDrag'
|
||||||
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
import type {StudioHistoricStateSequenceEditorMarker} from '@theatre/studio/store/types'
|
import type {StudioHistoricStateSequenceEditorMarker} from '@theatre/studio/store/types'
|
||||||
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
||||||
import DopeSnap from './DopeSnap'
|
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
|
||||||
import {absoluteDims} from '@theatre/studio/utils/absoluteDims'
|
import {absoluteDims} from '@theatre/studio/utils/absoluteDims'
|
||||||
import {DopeSnapHitZoneUI} from './DopeSnapHitZoneUI'
|
import {DopeSnapHitZoneUI} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnapHitZoneUI'
|
||||||
import {
|
import {
|
||||||
snapToAll,
|
snapToAll,
|
||||||
snapToNone,
|
snapToNone,
|
||||||
} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget'
|
} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget'
|
||||||
|
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
|
||||||
|
import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover'
|
||||||
|
import MarkerEditorPopover from './MarkerEditorPopover'
|
||||||
|
|
||||||
const MARKER_SIZE_W_PX = 12
|
const MARKER_SIZE_W_PX = 12
|
||||||
const MARKER_SIZE_H_PX = 12
|
const MARKER_SIZE_H_PX = 12
|
||||||
|
@ -164,11 +167,32 @@ const MarkerDotVisible: React.VFC<IMarkerDotVisibleProps> = ({
|
||||||
marker,
|
marker,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
node: popoverNode,
|
||||||
|
toggle: togglePopover,
|
||||||
|
close: closePopover,
|
||||||
|
} = usePopover({debugName: 'MarkerPopover'}, () => {
|
||||||
|
return (
|
||||||
|
<BasicPopover>
|
||||||
|
<MarkerEditorPopover
|
||||||
|
marker={marker}
|
||||||
|
layoutP={layoutP}
|
||||||
|
onRequestClose={closePopover}
|
||||||
|
/>
|
||||||
|
</BasicPopover>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{contextMenu}
|
{contextMenu}
|
||||||
|
{popoverNode}
|
||||||
<HitZone
|
<HitZone
|
||||||
|
title={marker.label ? `Marker: ${marker.label}` : 'Marker'}
|
||||||
ref={markRef}
|
ref={markRef}
|
||||||
|
onClick={(e) => {
|
||||||
|
togglePopover(e, markRef.current!)
|
||||||
|
}}
|
||||||
{...DopeSnapHitZoneUI.reactProps({
|
{...DopeSnapHitZoneUI.reactProps({
|
||||||
isDragging,
|
isDragging,
|
||||||
position: marker.position,
|
position: marker.position,
|
|
@ -0,0 +1,99 @@
|
||||||
|
import type {Pointer} from '@theatre/dataverse'
|
||||||
|
import React, {useLayoutEffect, useMemo, useRef} from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
|
import {useVal} from '@theatre/react'
|
||||||
|
import getStudio from '@theatre/studio/getStudio'
|
||||||
|
import type {BasicNumberInputNudgeFn} from '@theatre/studio/uiComponents/form/BasicNumberInput'
|
||||||
|
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
|
||||||
|
import {propNameTextCSS} from '@theatre/studio/propEditors/utils/propNameTextCSS'
|
||||||
|
import type {StudioHistoricStateSequenceEditorMarker} from '@theatre/studio/store/types/historic'
|
||||||
|
import BasicStringInput from '@theatre/studio/uiComponents/form/BasicStringInput'
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
/* padding: 4px 8px; */
|
||||||
|
height: 28px;
|
||||||
|
align-items: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Label = styled.div`
|
||||||
|
${propNameTextCSS};
|
||||||
|
white-space: nowrap;
|
||||||
|
`
|
||||||
|
|
||||||
|
const nudge: BasicNumberInputNudgeFn = ({deltaX}) => deltaX * 0.25
|
||||||
|
|
||||||
|
const MarkerEditorPopover: React.FC<{
|
||||||
|
layoutP: Pointer<SequenceEditorPanelLayout>
|
||||||
|
marker: StudioHistoricStateSequenceEditorMarker
|
||||||
|
/**
|
||||||
|
* Called when user hits enter/escape
|
||||||
|
*/
|
||||||
|
onRequestClose: (reason: string) => void
|
||||||
|
}> = ({layoutP, marker}) => {
|
||||||
|
const sheet = useVal(layoutP.sheet)
|
||||||
|
|
||||||
|
const fns = useMemo(() => {
|
||||||
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
temporarilySetValue(newLabel: string): void {
|
||||||
|
if (tempTransaction) {
|
||||||
|
tempTransaction.discard()
|
||||||
|
tempTransaction = undefined
|
||||||
|
}
|
||||||
|
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
|
||||||
|
stateEditors.studio.historic.projects.stateByProjectId.stateBySheetId.sequenceEditor.updateMarker(
|
||||||
|
{
|
||||||
|
sheetAddress: sheet.address,
|
||||||
|
markerId: marker.id,
|
||||||
|
label: newLabel,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
discardTemporaryValue(): void {
|
||||||
|
if (tempTransaction) {
|
||||||
|
tempTransaction.discard()
|
||||||
|
tempTransaction = undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
permanentlySetValue(newLabel: string): void {
|
||||||
|
if (tempTransaction) {
|
||||||
|
tempTransaction.discard()
|
||||||
|
tempTransaction = undefined
|
||||||
|
}
|
||||||
|
getStudio()!.transaction(({stateEditors}) => {
|
||||||
|
stateEditors.studio.historic.projects.stateByProjectId.stateBySheetId.sequenceEditor.updateMarker(
|
||||||
|
{
|
||||||
|
sheetAddress: sheet.address,
|
||||||
|
markerId: marker.id,
|
||||||
|
label: newLabel,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, [layoutP, sheet])
|
||||||
|
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
inputRef.current!.focus()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
{/* <Label>Marker</Label> */}
|
||||||
|
<BasicStringInput
|
||||||
|
value={marker.label ?? ''}
|
||||||
|
{...fns}
|
||||||
|
isValid={() => true}
|
||||||
|
inputRef={inputRef}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MarkerEditorPopover
|
|
@ -11,7 +11,7 @@ import HorizontalScrollbar from './HorizontalScrollbar'
|
||||||
import Playhead from './Playhead'
|
import Playhead from './Playhead'
|
||||||
import TopStrip from './TopStrip'
|
import TopStrip from './TopStrip'
|
||||||
import FocusRangeCurtains from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/FocusRangeCurtains'
|
import FocusRangeCurtains from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/FocusRangeCurtains'
|
||||||
import Markers from './Markers'
|
import Markers from './Markers/Markers'
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -338,6 +338,16 @@ namespace stateEditors {
|
||||||
pointableSetUtil.remove(currentMarkerSet, options.markerId),
|
pointableSetUtil.remove(currentMarkerSet, options.markerId),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateMarker(options: {
|
||||||
|
sheetAddress: SheetAddress
|
||||||
|
markerId: SequenceMarkerId
|
||||||
|
label: string
|
||||||
|
}) {
|
||||||
|
const currentMarkerSet = _ensureMarkers(options.sheetAddress)
|
||||||
|
const marker = currentMarkerSet.byId[options.markerId]
|
||||||
|
if (marker !== undefined) marker.label = options.label
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ export type PaneInstanceDescriptor = {
|
||||||
*/
|
*/
|
||||||
export type StudioHistoricStateSequenceEditorMarker = {
|
export type StudioHistoricStateSequenceEditorMarker = {
|
||||||
id: SequenceMarkerId
|
id: SequenceMarkerId
|
||||||
|
label?: string
|
||||||
/**
|
/**
|
||||||
* The position this marker takes in the sequence.
|
* The position this marker takes in the sequence.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue