Nudging behavior on number props can now be customized

This commit is contained in:
Aria Minaei 2021-08-22 12:58:38 +02:00
parent 74724d7ec1
commit 36017c6a73
7 changed files with 81 additions and 14 deletions

View file

@ -13,7 +13,7 @@ const editorSheetObjectConfig = types.compound({
showAxes: types.boolean(true, {label: 'Axes'}), showAxes: types.boolean(true, {label: 'Axes'}),
showGrid: types.boolean(true, {label: 'Grid'}), showGrid: types.boolean(true, {label: 'Grid'}),
showOverlayIcons: types.boolean(false, {label: 'Overlay Icons'}), showOverlayIcons: types.boolean(false, {label: 'Overlay Icons'}),
resolution: types.number(1440, { resolution: types.number(500, {
label: 'Resolution', label: 'Resolution',
range: [0, 1000], range: [0, 1000],
}), }),

View file

@ -18,21 +18,25 @@ export type TransformControlsMode = 'translate' | 'rotate' | 'scale'
export type TransformControlsSpace = 'world' | 'local' export type TransformControlsSpace = 'world' | 'local'
export type ViewportShading = 'wireframe' | 'flat' | 'solid' | 'rendered' export type ViewportShading = 'wireframe' | 'flat' | 'solid' | 'rendered'
const positionComp = types.number(1, {nudgeMultiplier: 0.1})
const rotationComp = types.number(1, {nudgeMultiplier: 0.02})
const scaleComp = types.number(1, {nudgeMultiplier: 0.1})
export const baseSheetObjectType = types.compound({ export const baseSheetObjectType = types.compound({
position: types.compound({ position: types.compound({
x: types.number(0), x: positionComp,
y: types.number(0), y: positionComp,
z: types.number(0), z: positionComp,
}), }),
rotation: types.compound({ rotation: types.compound({
x: types.number(0), x: rotationComp,
y: types.number(0), y: rotationComp,
z: types.number(0), z: rotationComp,
}), }),
scale: types.compound({ scale: types.compound({
x: types.number(1), x: scaleComp,
y: types.number(1), y: scaleComp,
z: types.number(1), z: scaleComp,
}), }),
}) })

View file

@ -13,11 +13,40 @@ export interface PropTypeConfig_Number extends IBasePropType<number> {
type: 'number' type: 'number'
default: number default: number
range?: [min: number, max: number] range?: [min: number, max: number]
nudgeFn: NumberNudgeFn
nudgeMultiplier: number
}
export type NumberNudgeFn = (p: {
deltaX: number
deltaFraction: number
magnitude: number
config: PropTypeConfig_Number
}) => number
const defaultNumberNudgeFn: NumberNudgeFn = ({
config,
deltaX,
deltaFraction,
magnitude,
}) => {
const {range} = config
if (range) {
return (
deltaFraction * (range[1] - range[0]) * magnitude * config.nudgeMultiplier
)
}
return deltaX * magnitude * config.nudgeMultiplier
} }
export const number = ( export const number = (
defaultValue: number, defaultValue: number,
opts?: Pick<PropTypeConfig_Number, 'range'> & PropTypeConfigExtras, opts?: {
nudgeFn?: PropTypeConfig_Number['nudgeFn']
range?: PropTypeConfig_Number['range']
nudgeMultiplier?: number
} & PropTypeConfigExtras,
): PropTypeConfig_Number => { ): PropTypeConfig_Number => {
return { return {
type: 'number', type: 'number',
@ -26,6 +55,9 @@ export const number = (
[s]: 'TheatrePropType', [s]: 'TheatrePropType',
...(opts ? opts : {}), ...(opts ? opts : {}),
label: opts?.label, label: opts?.label,
nudgeFn: opts?.nudgeFn ?? defaultNumberNudgeFn,
nudgeMultiplier:
typeof opts?.nudgeMultiplier === 'number' ? opts.nudgeMultiplier : 1,
} }
} }

View file

@ -1,7 +1,7 @@
import type {PropTypeConfig_Number} from '@theatre/core/propTypes' import type {PropTypeConfig_Number} from '@theatre/core/propTypes'
import type SheetObject from '@theatre/core/sheetObjects/SheetObject' import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import BasicNumberInput from '@theatre/studio/uiComponents/form/BasicNumberInput' import BasicNumberInput from '@theatre/studio/uiComponents/form/BasicNumberInput'
import React from 'react' import React, {useCallback} from 'react'
import {useEditingToolsForPrimitiveProp} from './utils/useEditingToolsForPrimitiveProp' import {useEditingToolsForPrimitiveProp} from './utils/useEditingToolsForPrimitiveProp'
import {SingleRowPropEditor} from './utils/SingleRowPropEditor' import {SingleRowPropEditor} from './utils/SingleRowPropEditor'
@ -12,6 +12,13 @@ const NumberPropEditor: React.FC<{
}> = ({propConfig, pointerToProp, obj}) => { }> = ({propConfig, pointerToProp, obj}) => {
const stuff = useEditingToolsForPrimitiveProp<number>(pointerToProp, obj) const stuff = useEditingToolsForPrimitiveProp<number>(pointerToProp, obj)
const nudge = useCallback(
(params: {deltaX: number; deltaFraction: number; magnitude: number}) => {
return propConfig.nudgeFn({...params, config: propConfig})
},
[propConfig],
)
return ( return (
<SingleRowPropEditor {...{stuff, propConfig, pointerToProp}}> <SingleRowPropEditor {...{stuff, propConfig, pointerToProp}}>
<BasicNumberInput <BasicNumberInput
@ -20,6 +27,7 @@ const NumberPropEditor: React.FC<{
discardTemporaryValue={stuff.discardTemporaryValue} discardTemporaryValue={stuff.discardTemporaryValue}
permenantlySetValue={stuff.permenantlySetValue} permenantlySetValue={stuff.permenantlySetValue}
range={propConfig.range} range={propConfig.range}
nudge={nudge}
/> />
</SingleRowPropEditor> </SingleRowPropEditor>
) )

View file

@ -4,6 +4,7 @@ import styled from 'styled-components'
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout' import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import {usePrism, useVal} from '@theatre/dataverse-react' import {usePrism, useVal} from '@theatre/dataverse-react'
import getStudio from '@theatre/studio/getStudio' import getStudio from '@theatre/studio/getStudio'
import type {BasicNumberInputNudgeFn} from '@theatre/studio/uiComponents/form/BasicNumberInput'
import BasicNumberInput from '@theatre/studio/uiComponents/form/BasicNumberInput' import BasicNumberInput from '@theatre/studio/uiComponents/form/BasicNumberInput'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore' import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import {propNameText} from '@theatre/studio/panels/DetailPanel/propEditors/utils/SingleRowPropEditor' import {propNameText} from '@theatre/studio/panels/DetailPanel/propEditors/utils/SingleRowPropEditor'
@ -23,6 +24,8 @@ const Label = styled.div`
white-space: nowrap; white-space: nowrap;
` `
const nudge: BasicNumberInputNudgeFn = ({deltaX}) => deltaX * 0.25
const LengthEditorPopover: React.FC<{ const LengthEditorPopover: React.FC<{
layoutP: Pointer<SequenceEditorPanelLayout> layoutP: Pointer<SequenceEditorPanelLayout>
/** /**
@ -87,6 +90,7 @@ const LengthEditorPopover: React.FC<{
isValid={greaterThanZero} isValid={greaterThanZero}
inputRef={inputRef} inputRef={inputRef}
onBlur={onRequestClose} onBlur={onRequestClose}
nudge={nudge}
/> />
</Container> </Container>
) )

View file

@ -73,6 +73,7 @@ const FillIndicator = styled.div`
background-color: #2d5561; background-color: #2d5561;
z-index: -1; z-index: -1;
border-radius: 2px; border-radius: 2px;
pointer-events: none;
${Container}.dragging &, ${Container}.noFocus:hover & { ${Container}.dragging &, ${Container}.noFocus:hover & {
background-color: #338198; background-color: #338198;
@ -103,6 +104,12 @@ type IState = IState_NoFocus | IState_EditingViaKeyboard | IState_Dragging
const alwaysValid = (v: number) => true const alwaysValid = (v: number) => true
export type BasicNumberInputNudgeFn = (params: {
deltaX: number
deltaFraction: number
magnitude: number
}) => number
const BasicNumberInput: React.FC<{ const BasicNumberInput: React.FC<{
value: number value: number
temporarilySetValue: (v: number) => void temporarilySetValue: (v: number) => void
@ -117,6 +124,7 @@ const BasicNumberInput: React.FC<{
* before this, so use this for UI purposes such as closing a popover. * before this, so use this for UI purposes such as closing a popover.
*/ */
onBlur?: () => void onBlur?: () => void
nudge: BasicNumberInputNudgeFn
}> = (propsA) => { }> = (propsA) => {
const [stateA, setState] = useState<IState>({mode: 'noFocus'}) const [stateA, setState] = useState<IState>({mode: 'noFocus'})
const isValid = propsA.isValid ?? alwaysValid const isValid = propsA.isValid ?? alwaysValid
@ -207,8 +215,11 @@ const BasicNumberInput: React.FC<{
}) })
} }
let inputWidth: number
const transitionToDraggingMode = () => { const transitionToDraggingMode = () => {
const curValue = refs.current.props.value const curValue = refs.current.props.value
inputWidth = inputRef.current?.getBoundingClientRect().width!
setState({ setState({
mode: 'dragging', mode: 'dragging',
@ -238,9 +249,16 @@ const BasicNumberInput: React.FC<{
} }
} }
const onDrag = (dx: number, _dy: number) => { const onDrag = (deltaX: number, _dy: number) => {
const curState = refs.current.state as IState_Dragging const curState = refs.current.state as IState_Dragging
const newValue = curState.valueBeforeDragging + dx
const newValue =
curState.valueBeforeDragging +
propsA.nudge({
deltaX,
deltaFraction: deltaX / inputWidth,
magnitude: 1,
})
setState({ setState({
...curState, ...curState,

View file

@ -23,6 +23,7 @@ const Container = styled.ul`
min-width: ${minWidth}px; min-width: ${minWidth}px;
z-index: 10000; z-index: 10000;
background: ${transparentize(0.2, '#111')}; background: ${transparentize(0.2, '#111')};
backdrop-filter: blur(2px);
color: white; color: white;
list-style-type: none; list-style-type: none;
padding: 2px 0; padding: 2px 0;