TODO: don't let clicks get swallowed, don't rerender all the time

This commit is contained in:
Frank Noirot
2024-05-14 12:02:08 -04:00
parent eba79867d8
commit a1e654d0f8
7 changed files with 74 additions and 55 deletions

View File

@ -5,12 +5,6 @@ import { useModelingContext } from 'hooks/useModelingContext'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton'
import { isSingleCursorInPipe } from 'lang/queryAst'
import { useKclContext } from 'lang/KclProvider'
import {
NetworkHealthState,
useNetworkStatus,
} from 'components/NetworkHealthIndicator'
import { useStore } from 'useStore'
import { useShouldDisableModelingActions } from 'hooks/useShouldDisableModelingActions'
import { useInteractionMap } from 'hooks/useInteractionMap'
@ -20,10 +14,18 @@ export const Toolbar = () => {
const shouldDisableModelingActions = useShouldDisableModelingActions()
useInteractionMap(
[
{
name: 'sketch',
title: 'Start Sketch',
sequence: 'shift+s',
action: () =>
send({ type: 'Enter sketch', data: { forceNewSketch: true } }),
guard: () => !shouldDisableModelingActions && state.matches('idle'),
},
{
name: 'extrude',
title: 'Extrude',
sequence: 'shift+e',
sequence: 'ctrl+c shift+e',
action: () =>
commandBarSend({
type: 'Find and select command',
@ -50,13 +52,6 @@ export const Toolbar = () => {
context.selectionRanges
)
}, [engineCommandManager.artifactMap, context.selectionRanges])
const { overallState } = useNetworkStatus()
const { isExecuting } = useKclContext()
const { isStreamReady } = useStore((s) => ({
isStreamReady: s.isStreamReady,
}))
const disableAllButtons =
overallState !== NetworkHealthState.Ok || isExecuting || !isStreamReady
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
const span = toolbarButtonsRef.current
@ -95,7 +90,7 @@ export const Toolbar = () => {
iconClassName,
bgClassName,
}}
disabled={disableAllButtons}
disabled={shouldDisableModelingActions}
>
<span data-testid="start-sketch">Start Sketch</span>
</ActionButton>
@ -112,7 +107,7 @@ export const Toolbar = () => {
iconClassName,
bgClassName,
}}
disabled={disableAllButtons}
disabled={shouldDisableModelingActions}
>
Edit Sketch
</ActionButton>
@ -129,7 +124,7 @@ export const Toolbar = () => {
iconClassName,
bgClassName,
}}
disabled={disableAllButtons}
disabled={shouldDisableModelingActions}
>
Exit Sketch
</ActionButton>
@ -152,7 +147,7 @@ export const Toolbar = () => {
iconClassName,
bgClassName,
}}
disabled={disableAllButtons}
disabled={shouldDisableModelingActions}
>
Line
</ActionButton>
@ -175,7 +170,7 @@ export const Toolbar = () => {
disabled={
(!state.can('Equip tangential arc to') &&
!state.matches('Sketch.Tangential arc to')) ||
disableAllButtons
shouldDisableModelingActions
}
>
Tangential Arc
@ -199,7 +194,7 @@ export const Toolbar = () => {
disabled={
(!state.can('Equip rectangle tool') &&
!state.matches('Sketch.Rectangle tool')) ||
disableAllButtons
shouldDisableModelingActions
}
title={
state.can('Equip rectangle tool')
@ -244,7 +239,7 @@ export const Toolbar = () => {
disabled={
!state.nextEvents
.filter((event) => state.can(event as any))
.includes(eventName) || disableAllButtons
.includes(eventName) || shouldDisableModelingActions
}
title={eventName}
icon={{
@ -270,7 +265,7 @@ export const Toolbar = () => {
data: { name: 'Extrude', ownerMachine: 'modeling' },
})
}
disabled={!state.can('Extrude') || disableAllButtons}
disabled={!state.can('Extrude') || shouldDisableModelingActions}
title={
state.can('Extrude')
? 'extrude'

View File

@ -36,7 +36,6 @@ export const CommandBar = () => {
})
},
guard: () => true,
ownerId: 'commandBar',
},
{
name: 'close',
@ -46,10 +45,10 @@ export const CommandBar = () => {
commandBarSend({ type: 'Close' })
},
guard: () => !commandBarState.matches('Closed'),
ownerId: 'commandBar',
},
],
[commandBarState, commandBarSend]
[commandBarState, commandBarSend],
'Command Bar'
)
function stepBack() {

View File

@ -59,6 +59,10 @@ function CommandBarSelectionInput({
return () => kclManager.enterEditMode()
}, [])
useEffect(() => {
console.log('selectionsByType', selectionsByType)
}, [selectionsByType])
// Fast-forward through this arg if it's marked as skippable
// and we have a valid selection already
useEffect(() => {

View File

@ -173,7 +173,18 @@ export function InteractionMapMachineProvider({
}
const fireEvent = (event: MouseEvent | KeyboardEvent) => {
send({ type: 'Fire event', data: event })
// Don't fire click events on interactable elements,
// and make sure these fire last in the bubbling phase
if (
event.BUBBLING_PHASE &&
!(
event instanceof MouseEvent &&
event.target instanceof HTMLElement &&
['INPUT', 'BUTTON', 'ANCHOR'].includes(event.target.tagName)
)
) {
send({ type: 'Fire event', data: event })
}
}
window.addEventListener('keydown', fireEvent)

View File

@ -6,7 +6,7 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useModelingContext } from 'hooks/useModelingContext'
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
import { butName } from 'lib/cameraControls'
import { buttonName } from 'lib/cameraControls'
import { sendSelectEventToEngine } from 'lib/selections'
export const Stream = ({ className = '' }: { className?: string }) => {
@ -63,7 +63,7 @@ export const Stream = ({ className = '' }: { className?: string }) => {
if (state.matches('Sketch')) return
if (state.matches('Sketch no face')) return
if (!didDragInStream && butName(e).left) {
if (!didDragInStream && buttonName(e).left) {
sendSelectEventToEngine(e, videoRef.current, streamDimensions)
}

View File

@ -8,6 +8,12 @@ import { settingsMachine } from 'machines/settingsMachine'
import { homeMachine } from 'machines/homeMachine'
import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
import { useShouldDisableModelingActions } from './useShouldDisableModelingActions'
import { useKclContext } from 'lang/KclProvider'
import {
NetworkHealthState,
useNetworkStatus,
} from 'components/NetworkHealthIndicator'
import { useStore } from 'useStore'
// This might not be necessary, AnyStateMachine from xstate is working
export type AllMachines =
@ -42,13 +48,17 @@ export default function useStateMachineCommands<
onCancel,
}: UseStateMachineCommandsArgs<T, S>) {
const { commandBarSend } = useCommandsContext()
const shouldDisableModelingActions = useShouldDisableModelingActions()
const { overallState } = useNetworkStatus()
const { isExecuting } = useKclContext()
const { isStreamReady } = useStore((s) => ({
isStreamReady: s.isStreamReady,
}))
useEffect(() => {
const disableAllButtons =
overallState !== NetworkHealthState.Ok || isExecuting || !isStreamReady
const newCommands = state.nextEvents
.filter(
(_) => !allCommandsRequireNetwork || !shouldDisableModelingActions
)
.filter((_) => !allCommandsRequireNetwork || !disableAllButtons)
.filter((e) => !['done.', 'error.'].some((n) => e.includes(n)))
.map((type) =>
createMachineCommand<T, S>({
@ -71,5 +81,5 @@ export default function useStateMachineCommands<
data: { commands: newCommands },
})
}
}, [state, shouldDisableModelingActions])
}, [state, overallState, isExecuting, isStreamReady])
}

View File

@ -64,7 +64,7 @@ export interface MouseGuard {
rotate: MouseGuardHandler
}
export const butName = (e: React.MouseEvent) => ({
export const buttonName = (e: React.MouseEvent) => ({
middle: !!(e.buttons & 4) || e.button === 1,
right: !!(e.buttons & 2) || e.button === 2,
left: !!(e.buttons & 1) || e.button === 0,
@ -75,8 +75,8 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
pan: {
description: 'Right click + Shift + drag or middle click + drag',
callback: (e) =>
(butName(e).middle && noModifiersPressed(e)) ||
(butName(e).right && e.shiftKey),
(buttonName(e).middle && noModifiersPressed(e)) ||
(buttonName(e).right && e.shiftKey),
},
zoom: {
description: 'Scroll wheel or Right click + Ctrl + drag',
@ -85,15 +85,15 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
},
rotate: {
description: 'Right click + drag',
callback: (e) => butName(e).right && noModifiersPressed(e),
callback: (e) => buttonName(e).right && noModifiersPressed(e),
},
},
OnShape: {
pan: {
description: 'Right click + Ctrl + drag or middle click + drag',
callback: (e) =>
(butName(e).right && e.ctrlKey) ||
(butName(e).middle && noModifiersPressed(e)),
(buttonName(e).right && e.ctrlKey) ||
(buttonName(e).middle && noModifiersPressed(e)),
},
zoom: {
description: 'Scroll wheel',
@ -102,77 +102,77 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
},
rotate: {
description: 'Right click + drag',
callback: (e) => butName(e).right && noModifiersPressed(e),
callback: (e) => buttonName(e).right && noModifiersPressed(e),
},
},
'Trackpad Friendly': {
pan: {
description: 'Left click + Alt + Shift + drag or middle click + drag',
callback: (e) =>
(butName(e).left && e.altKey && e.shiftKey && !e.metaKey) ||
(butName(e).middle && noModifiersPressed(e)),
(buttonName(e).left && e.altKey && e.shiftKey && !e.metaKey) ||
(buttonName(e).middle && noModifiersPressed(e)),
},
zoom: {
description: 'Scroll wheel or Left click + Alt + OS + drag',
dragCallback: (e) => butName(e).left && e.altKey && e.metaKey,
dragCallback: (e) => buttonName(e).left && e.altKey && e.metaKey,
scrollCallback: () => true,
},
rotate: {
description: 'Left click + Alt + drag',
callback: (e) => butName(e).left && e.altKey && !e.shiftKey && !e.metaKey,
callback: (e) => buttonName(e).left && e.altKey && !e.shiftKey && !e.metaKey,
lenientDragStartButton: 0,
},
},
Solidworks: {
pan: {
description: 'Right click + Ctrl + drag',
callback: (e) => butName(e).right && e.ctrlKey,
callback: (e) => buttonName(e).right && e.ctrlKey,
lenientDragStartButton: 2,
},
zoom: {
description: 'Scroll wheel or Middle click + Shift + drag',
dragCallback: (e) => butName(e).middle && e.shiftKey,
dragCallback: (e) => buttonName(e).middle && e.shiftKey,
scrollCallback: () => true,
},
rotate: {
description: 'Middle click + drag',
callback: (e) => butName(e).middle && noModifiersPressed(e),
callback: (e) => buttonName(e).middle && noModifiersPressed(e),
},
},
NX: {
pan: {
description: 'Middle click + Shift + drag',
callback: (e) => butName(e).middle && e.shiftKey,
callback: (e) => buttonName(e).middle && e.shiftKey,
},
zoom: {
description: 'Scroll wheel or Middle click + Ctrl + drag',
dragCallback: (e) => butName(e).middle && e.ctrlKey,
dragCallback: (e) => buttonName(e).middle && e.ctrlKey,
scrollCallback: () => true,
},
rotate: {
description: 'Middle click + drag',
callback: (e) => butName(e).middle && noModifiersPressed(e),
callback: (e) => buttonName(e).middle && noModifiersPressed(e),
},
},
Creo: {
pan: {
description: 'Middle click + Shift + drag',
callback: (e) => butName(e).middle && e.shiftKey,
callback: (e) => buttonName(e).middle && e.shiftKey,
},
zoom: {
description: 'Scroll wheel or Middle click + Ctrl + drag',
dragCallback: (e) => butName(e).middle && e.ctrlKey,
dragCallback: (e) => buttonName(e).middle && e.ctrlKey,
scrollCallback: () => true,
},
rotate: {
description: 'Middle click + drag',
callback: (e) => butName(e).middle && noModifiersPressed(e),
callback: (e) => buttonName(e).middle && noModifiersPressed(e),
},
},
AutoCAD: {
pan: {
description: 'Middle click + drag',
callback: (e) => butName(e).middle && noModifiersPressed(e),
callback: (e) => buttonName(e).middle && noModifiersPressed(e),
},
zoom: {
description: 'Scroll wheel',
@ -181,7 +181,7 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
},
rotate: {
description: 'Middle click + Shift + drag',
callback: (e) => butName(e).middle && e.shiftKey,
callback: (e) => buttonName(e).middle && e.shiftKey,
},
},
}