WIP aggregate editing popover working but !clean

This commit is contained in:
vezwork 2022-06-30 10:46:24 -04:00 committed by Elliot
parent 069902e054
commit 38e6a4ba36
7 changed files with 194 additions and 89 deletions

View file

@ -17,18 +17,86 @@ import type {KeyframeWithPathToPropFromCommonRoot} from '@theatre/studio/store/t
import {commonRootOfPathsToProps} from '@theatre/shared/utils/addresses' import {commonRootOfPathsToProps} from '@theatre/shared/utils/addresses'
import type {ILogger} from '@theatre/shared/logger' import type {ILogger} from '@theatre/shared/logger'
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
import type {EditingOptionsTree} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover'
import {useKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover'
import type {
SequenceEditorTree_PrimitiveProp,
SequenceEditorTree_PropWithChildren,
SequenceEditorTree_SheetObject,
} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree'
import type {KeyframeWithTrack} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes'
type IAggregateKeyframeDotProps = { type IAggregateKeyframeDotProps = {
editorProps: IAggregateKeyframeEditorProps editorProps: IAggregateKeyframeEditorProps
utils: IAggregateKeyframeEditorUtils utils: IAggregateKeyframeEditorUtils
} }
function sheetObjectBuild(
viewModel: SequenceEditorTree_SheetObject,
keyframes: KeyframeWithTrack[],
): EditingOptionsTree | null {
const children = viewModel.children
.map((a) =>
a.type === 'propWithChildren'
? propWithChildrenBuild(a, keyframes)
: primitivePropBuild(a, keyframes),
)
.filter((a): a is EditingOptionsTree => a !== null)
if (children.length === 0) return null
return {
type: 'sheetObject',
sheetObject: viewModel.sheetObject,
children,
}
}
function propWithChildrenBuild(
viewModel: SequenceEditorTree_PropWithChildren,
keyframes: KeyframeWithTrack[],
): EditingOptionsTree | null {
const children = viewModel.children
.map((a) =>
a.type === 'propWithChildren'
? propWithChildrenBuild(a, keyframes)
: primitivePropBuild(a, keyframes),
)
.filter((a): a is EditingOptionsTree => a !== null)
if (children.length === 0) return null
return {
type: 'propWithChildren',
pathToProp: viewModel.pathToProp,
propConfig: viewModel.propConf,
children,
}
}
function primitivePropBuild(
viewModelLeaf: SequenceEditorTree_PrimitiveProp,
keyframes: KeyframeWithTrack[],
): EditingOptionsTree | null {
const keyframe = keyframes.find((kf) => kf.track.id === viewModelLeaf.trackId)
if (!keyframe) return null
return {
type: 'primitiveProp',
keyframe: keyframe.kf,
pathToProp: viewModelLeaf.pathToProp,
propConfig: viewModelLeaf.propConf,
sheetObject: viewModelLeaf.sheetObject,
trackId: viewModelLeaf.trackId,
}
}
export function AggregateKeyframeDot( export function AggregateKeyframeDot(
props: React.PropsWithChildren<IAggregateKeyframeDotProps>, props: React.PropsWithChildren<IAggregateKeyframeDotProps>,
) { ) {
const logger = useLogger('AggregateKeyframeDot') const logger = useLogger('AggregateKeyframeDot')
const {cur} = props.utils const {cur} = props.utils
const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] =
useKeyframeInlineEditorPopover(
props.editorProps.viewModel.type === 'sheetObject'
? sheetObjectBuild(props.editorProps.viewModel, cur.keyframes)
: propWithChildrenBuild(props.editorProps.viewModel, cur.keyframes),
)
const presence = usePresence(props.utils.itemKey) const presence = usePresence(props.utils.itemKey)
presence.useRelations( presence.useRelations(
() => () =>
@ -57,6 +125,7 @@ export function AggregateKeyframeDot(
// Need this for the dragging logic to be able to get the keyframe props // Need this for the dragging logic to be able to get the keyframe props
// based on the position. // based on the position.
{...DopeSnap.includePositionSnapAttrs(cur.position)} {...DopeSnap.includePositionSnapAttrs(cur.position)}
onClick={(e) => openEditor(e, ref.current!)}
/> />
<AggregateKeyframeVisualDot <AggregateKeyframeVisualDot
flag={presence.flag} flag={presence.flag}
@ -64,6 +133,7 @@ export function AggregateKeyframeDot(
isSelected={cur.selected} isSelected={cur.selected}
/> />
{contextMenu} {contextMenu}
{inlineEditorPopover}
</> </>
) )
} }

View file

@ -1,28 +1,19 @@
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import type { import type {PropTypeConfig_AllSimples} from '@theatre/core/propTypes'
PropTypeConfig,
PropTypeConfig_AllSimples,
} from '@theatre/core/propTypes'
import type {IEditingTools} from '@theatre/studio/propEditors/utils/IEditingTools'
import type {PropConfigForType} from '@theatre/studio/propEditors/utils/PropConfigForType'
import type {ISimplePropEditorReactProps} from '@theatre/studio/propEditors/simpleEditors/ISimplePropEditorReactProps' import type {ISimplePropEditorReactProps} from '@theatre/studio/propEditors/simpleEditors/ISimplePropEditorReactProps'
import {simplePropEditorByPropType} from '@theatre/studio/propEditors/simpleEditors/simplePropEditorByPropType' import {simplePropEditorByPropType} from '@theatre/studio/propEditors/simpleEditors/simplePropEditorByPropType'
import SingleKeyframeSimplePropEditor from './DeterminePropEditorForSingleKeyframe/SingleKeyframeSimplePropEditor' import SingleKeyframeSimplePropEditor from './DeterminePropEditorForSingleKeyframe/SingleKeyframeSimplePropEditor'
import type {
type IDeterminePropEditorForSingleKeyframeProps< EditingOptionsTree,
K extends PropTypeConfig['type'], PrimitivePropEditingOptions,
> = { } from './useSingleKeyframeInlineEditorPopover'
editingTools: IEditingTools<PropConfigForType<K>['valueType']> import last from 'lodash-es/last'
propConfig: PropConfigForType<K> import {useTempTransactionEditingTools} from './useTempTransactionEditingTools'
keyframeValue: PropConfigForType<K>['valueType']
displayLabel?: string
}
const SingleKeyframePropEditorContainer = styled.div` const SingleKeyframePropEditorContainer = styled.div`
padding: 2px;
display: flex; display: flex;
align-items: stretch; align-items: stretch;
@ -30,7 +21,7 @@ const SingleKeyframePropEditorContainer = styled.div`
min-width: 100px; min-width: 100px;
} }
` `
const SingleKeyframePropLabel = styled.span` const SingleKeyframePropLabel = styled.div`
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 11px; font-size: 11px;
@ -42,6 +33,10 @@ const SingleKeyframePropLabel = styled.span`
color: #919191; color: #919191;
` `
const IndentedThing = styled.div`
margin-left: 24px;
`
/** /**
* Given a propConfig, this function gives the corresponding prop editor for * Given a propConfig, this function gives the corresponding prop editor for
* use in the dope sheet inline prop editor on a keyframe. * use in the dope sheet inline prop editor on a keyframe.
@ -52,35 +47,73 @@ const SingleKeyframePropLabel = styled.span`
* *
* @param p - propConfig object for any type of prop. * @param p - propConfig object for any type of prop.
*/ */
export function DeterminePropEditorForSingleKeyframe( export function DeterminePropEditorForKeyframeTree(p: EditingOptionsTree) {
p: IDeterminePropEditorForSingleKeyframeProps<PropTypeConfig['type']>, if (p.type === 'sheetObject') {
) { return (
const propConfig = p.propConfig <>
<SingleKeyframePropLabel>
if (propConfig.type === 'compound') { {p.sheetObject.address.objectKey}
throw new Error( </SingleKeyframePropLabel>
'We do not yet support editing compound props for a keyframe', <IndentedThing>
{p.children.map((c, i) => (
<DeterminePropEditorForKeyframeTree key={i} {...c} />
))}
</IndentedThing>
</>
) )
} else if (propConfig.type === 'enum') { } else if (p.type === 'propWithChildren') {
const label = p.propConfig.label ?? last(p.pathToProp)
return (
<>
<SingleKeyframePropLabel>{label}</SingleKeyframePropLabel>
<IndentedThing>
{p.children.map((c, i) => (
<DeterminePropEditorForKeyframeTree key={i} {...c} />
))}
</IndentedThing>
</>
)
} else {
return <BeepBoop {...p} />
}
}
function BeepBoop(p: PrimitivePropEditingOptions) {
const label = p.propConfig.label ?? last(p.pathToProp)
const editingTools = useEditingToolsForKeyframeEditorPopover(p)
if (p.propConfig.type === 'enum') {
// notice: enums are not implemented, yet. // notice: enums are not implemented, yet.
return <></> return <></>
} else { } else {
const PropEditor = simplePropEditorByPropType[propConfig.type] const PropEditor = simplePropEditorByPropType[
p.propConfig.type
] as React.VFC<ISimplePropEditorReactProps<PropTypeConfig_AllSimples>>
return ( return (
<SingleKeyframePropEditorContainer> <SingleKeyframePropEditorContainer>
<SingleKeyframePropLabel>{p.displayLabel}</SingleKeyframePropLabel> <SingleKeyframePropLabel>{label}</SingleKeyframePropLabel>
<SingleKeyframeSimplePropEditor <SingleKeyframeSimplePropEditor
SimpleEditorComponent={ SimpleEditorComponent={PropEditor}
PropEditor as React.VFC< propConfig={p.propConfig}
ISimplePropEditorReactProps<PropTypeConfig_AllSimples> editingTools={editingTools}
> keyframeValue={p.keyframe.value}
}
propConfig={propConfig}
editingTools={p.editingTools}
keyframeValue={p.keyframeValue}
/> />
</SingleKeyframePropEditorContainer> </SingleKeyframePropEditorContainer>
) )
} }
} }
function useEditingToolsForKeyframeEditorPopover(
props: PrimitivePropEditingOptions,
) {
const obj = props.sheetObject
return useTempTransactionEditingTools(({stateEditors}, value) => {
const newKeyframe = {...props.keyframe, value}
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes({
...obj.address,
trackId: props.trackId,
keyframes: [newKeyframe],
snappingFunction: obj.sheet.getSequence().closestGridPosition,
})
})
}

View file

@ -23,7 +23,7 @@ import {
snapToNone, snapToNone,
snapToSome, snapToSome,
} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' } from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget'
import {useSingleKeyframeInlineEditorPopover} from './useSingleKeyframeInlineEditorPopover' import {useKeyframeInlineEditorPopover} from './useSingleKeyframeInlineEditorPopover'
import usePresence, { import usePresence, {
PresenceFlag, PresenceFlag,
} from '@theatre/studio/uiComponents/usePresence' } from '@theatre/studio/uiComponents/usePresence'
@ -95,22 +95,21 @@ const HitZone = styled.div<{isInlineEditorPopoverOpen: boolean}>`
type ISingleKeyframeDotProps = ISingleKeyframeEditorProps type ISingleKeyframeDotProps = ISingleKeyframeEditorProps
/** The ◆ you can grab onto in "keyframe editor" (aka "dope sheet" in other programs) */ /** The ◆ you can grab onto in "keyframe editor" (aka "dope sheet" in other programs) */
const SingleKeyframeDot: React.VFC<ISingleKeyframeDotProps> = React.memo( const SingleKeyframeDot: React.VFC<ISingleKeyframeDotProps> = (props) => {
(props) => {
const logger = useLogger('SingleKeyframeDot', props.keyframe.id) const logger = useLogger('SingleKeyframeDot', props.keyframe.id)
const presence = usePresence(props.itemKey) const presence = usePresence(props.itemKey)
const [ref, node] = useRefAndState<HTMLDivElement | null>(null) const [ref, node] = useRefAndState<HTMLDivElement | null>(null)
const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props) const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props)
const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] = const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] =
useSingleKeyframeInlineEditorPopover({ useKeyframeInlineEditorPopover({
type: 'primitiveProp',
keyframe: props.keyframe, keyframe: props.keyframe,
pathToProp: props.leaf.pathToProp, pathToProp: props.leaf.pathToProp,
propConf: props.leaf.propConf, propConfig: props.leaf.propConf,
sheetObject: props.leaf.sheetObject, sheetObject: props.leaf.sheetObject,
trackId: props.leaf.trackId, trackId: props.leaf.trackId,
}) })
const [isDragging] = useDragForSingleKeyframeDot(node, props, { const [isDragging] = useDragForSingleKeyframeDot(node, props, {
onClickFromDrag(dragStartEvent) { onClickFromDrag(dragStartEvent) {
openEditor(dragStartEvent, ref.current!) openEditor(dragStartEvent, ref.current!)

View file

@ -1,53 +1,51 @@
import React from 'react' import React from 'react'
import last from 'lodash-es/last'
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 {useTempTransactionEditingTools} from './useTempTransactionEditingTools' import {DeterminePropEditorForKeyframeTree} from './DeterminePropEditorForSingleKeyframe'
import {DeterminePropEditorForSingleKeyframe} from './DeterminePropEditorForSingleKeyframe'
import type {SequenceTrackId} from '@theatre/shared/utils/ids' import type {SequenceTrackId} from '@theatre/shared/utils/ids'
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic' import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
import type SheetObject from '@theatre/core/sheetObjects/SheetObject' import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import type {PropTypeConfig} from '@theatre/core/propTypes' import type {
PropTypeConfig_AllSimples,
PropTypeConfig_Compound,
PropTypeConfig_Enum,
} from '@theatre/core/propTypes'
import type {PathToProp} from '@theatre/shared/utils/addresses' import type {PathToProp} from '@theatre/shared/utils/addresses'
import type {UnknownValidCompoundProps} from '@theatre/core/propTypes/internals'
/** The editor that pops up when directly clicking a Keyframe. */ /** The editor that pops up when directly clicking a Keyframe. */
export function useSingleKeyframeInlineEditorPopover( export function useKeyframeInlineEditorPopover(
props: SingleKeyframeEditingOptions, props: EditingOptionsTree | null,
) { ) {
const editingTools = useEditingToolsForKeyframeEditorPopover(props)
const label = props.propConf.label ?? last(props.pathToProp)
return usePopover({debugName: 'useKeyframeInlineEditorPopover'}, () => ( return usePopover({debugName: 'useKeyframeInlineEditorPopover'}, () => (
<BasicPopover showPopoverEdgeTriangle> <BasicPopover showPopoverEdgeTriangle>
<DeterminePropEditorForSingleKeyframe {props === null ? undefined : (
propConfig={props.propConf} <DeterminePropEditorForKeyframeTree {...props} />
editingTools={editingTools} )}
keyframeValue={props.keyframe.value}
displayLabel={label != null ? String(label) : undefined}
/>
</BasicPopover> </BasicPopover>
)) ))
} }
type SingleKeyframeEditingOptions = { export type EditingOptionsTree =
| SheetObjectEditingOptionsTree
| PropWithChildrenEditingOptionsTree
| PrimitivePropEditingOptions
type SheetObjectEditingOptionsTree = {
type: 'sheetObject'
sheetObject: SheetObject
children: EditingOptionsTree[]
}
type PropWithChildrenEditingOptionsTree = {
type: 'propWithChildren'
propConfig: PropTypeConfig_Compound<UnknownValidCompoundProps>
pathToProp: PathToProp
children: EditingOptionsTree[]
}
export type PrimitivePropEditingOptions = {
type: 'primitiveProp'
keyframe: Keyframe keyframe: Keyframe
propConf: PropTypeConfig propConfig: PropTypeConfig_AllSimples | PropTypeConfig_Enum // note: enums are not implemented yet
sheetObject: SheetObject sheetObject: SheetObject
trackId: SequenceTrackId trackId: SequenceTrackId
pathToProp: PathToProp pathToProp: PathToProp
} }
function useEditingToolsForKeyframeEditorPopover(
props: SingleKeyframeEditingOptions,
) {
const obj = props.sheetObject
return useTempTransactionEditingTools(({stateEditors}, value) => {
const newKeyframe = {...props.keyframe, value}
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes({
...obj.address,
trackId: props.trackId,
keyframes: [newKeyframe],
snappingFunction: obj.sheet.getSequence().closestGridPosition,
})
})
}

View file

@ -16,7 +16,7 @@ import {
useCssCursorLock, useCssCursorLock,
} from '@theatre/studio/uiComponents/PointerEventsHandler' } from '@theatre/studio/uiComponents/PointerEventsHandler'
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
import {useSingleKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' import {useKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover'
import usePresence, { import usePresence, {
PresenceFlag, PresenceFlag,
} from '@theatre/studio/uiComponents/usePresence' } from '@theatre/studio/uiComponents/usePresence'
@ -71,7 +71,7 @@ const GraphEditorDotNonScalar: React.VFC<IProps> = (props) => {
const curValue = props.which === 'left' ? 0 : 1 const curValue = props.which === 'left' ? 0 : 1
const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] = const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] =
useSingleKeyframeInlineEditorPopover({ useKeyframeInlineEditorPopover({
keyframe: props.keyframe, keyframe: props.keyframe,
pathToProp: props.pathToProp, pathToProp: props.pathToProp,
propConf: props.propConfig, propConf: props.propConfig,

View file

@ -16,7 +16,7 @@ import {
useCssCursorLock, useCssCursorLock,
} from '@theatre/studio/uiComponents/PointerEventsHandler' } from '@theatre/studio/uiComponents/PointerEventsHandler'
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
import {useSingleKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' import {useKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover'
import usePresence, { import usePresence, {
PresenceFlag, PresenceFlag,
} from '@theatre/studio/uiComponents/usePresence' } from '@theatre/studio/uiComponents/usePresence'
@ -71,7 +71,7 @@ const GraphEditorDotScalar: React.VFC<IProps> = (props) => {
const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue) const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue)
const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] = const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] =
useSingleKeyframeInlineEditorPopover({ useKeyframeInlineEditorPopover({
keyframe: props.keyframe, keyframe: props.keyframe,
pathToProp: props.pathToProp, pathToProp: props.pathToProp,
propConf: props.propConfig, propConf: props.propConfig,

View file

@ -18,6 +18,7 @@ import {prism, val, valueDerivation} from '@theatre/dataverse'
import logger from '@theatre/shared/logger' import logger from '@theatre/shared/logger'
import {titleBarHeight} from '@theatre/studio/panels/BasePanel/common' import {titleBarHeight} from '@theatre/studio/panels/BasePanel/common'
import type {Studio} from '@theatre/studio/Studio' import type {Studio} from '@theatre/studio/Studio'
import type {UnknownValidCompoundProps} from '@theatre/core/propTypes/internals'
/** /**
* Base "view model" for each row with common * Base "view model" for each row with common
@ -73,6 +74,7 @@ export type SequenceEditorTree_PropWithChildren =
SequenceEditorTree_Row<'propWithChildren'> & { SequenceEditorTree_Row<'propWithChildren'> & {
isCollapsed: boolean isCollapsed: boolean
sheetObject: SheetObject sheetObject: SheetObject
propConf: PropTypeConfig_Compound<UnknownValidCompoundProps>
pathToProp: PathToProp pathToProp: PathToProp
children: Array< children: Array<
SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp
@ -233,6 +235,7 @@ export const calculateSequenceEditorTree = (
addProp_compound( addProp_compound(
sheetObject, sheetObject,
trackMapping, trackMapping,
conf,
pathToProp, pathToProp,
conf, conf,
arrayOfChildren, arrayOfChildren,
@ -259,6 +262,7 @@ export const calculateSequenceEditorTree = (
function addProp_compound( function addProp_compound(
sheetObject: SheetObject, sheetObject: SheetObject,
trackMapping: IPropPathToTrackIdTree, trackMapping: IPropPathToTrackIdTree,
propConf: PropTypeConfig_Compound<UnknownValidCompoundProps>,
pathToProp: PathToProp, pathToProp: PathToProp,
conf: PropTypeConfig_Compound<$FixMe>, conf: PropTypeConfig_Compound<$FixMe>,
arrayOfChildren: Array< arrayOfChildren: Array<
@ -276,6 +280,7 @@ export const calculateSequenceEditorTree = (
const row: SequenceEditorTree_PropWithChildren = { const row: SequenceEditorTree_PropWithChildren = {
type: 'propWithChildren', type: 'propWithChildren',
isCollapsed, isCollapsed,
propConf,
pathToProp, pathToProp,
sheetItemKey: createStudioSheetItemKey.forSheetObjectProp( sheetItemKey: createStudioSheetItemKey.forSheetObjectProp(
sheetObject, sheetObject,