Add setting to toggle scale grid visibility (#2838)

* Add a setting for showScaleGrid

* Fix up setting persistence, move under modeling

* Make the setting actually do something

* the lamest fmt I've seen in a while

* Fix clippy warnings

* Add snapshot tests for grid (first time using Playwright masks)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI after new screenshots added

---------

Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Frank Noirot
2024-07-01 15:31:42 -04:00
committed by GitHub
parent 0c5b13ade5
commit 8972f53256
14 changed files with 152 additions and 14 deletions

View File

@ -796,3 +796,83 @@ test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
maxDiffPixels: 100,
})
})
test.describe('Grid visibility', () => {
test('Grid turned off', async ({ page }) => {
const u = await getUtils(page)
const stream = page.getByTestId('stream')
const mask = [
page.locator('#app-header'),
page.locator('#sidebar-top-ribbon'),
page.locator('#sidebar-bottom-ribbon'),
]
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
await u.closeDebugPanel()
await u.closeKclCodePanel()
// TODO: Find a way to truly know that the objects have finished
// rendering, because an execution-done message is not sufficient.
await page.waitForTimeout(1000)
await expect(stream).toHaveScreenshot({
maxDiffPixels: 100,
mask,
})
})
test('Grid turned on', async ({ page }) => {
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: {
...TEST_SETTINGS,
modeling: {
...TEST_SETTINGS.modeling,
showScaleGrid: true,
},
},
}),
}
)
const u = await getUtils(page)
const stream = page.getByTestId('stream')
const mask = [
page.locator('#app-header'),
page.locator('#sidebar-top-ribbon'),
page.locator('#sidebar-bottom-ribbon'),
]
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
await u.closeDebugPanel()
await u.closeKclCodePanel()
// TODO: Find a way to truly know that the objects have finished
// rendering, because an execution-done message is not sufficient.
await page.waitForTimeout(1000)
await expect(stream).toHaveScreenshot({
maxDiffPixels: 100,
mask,
})
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -26,6 +26,7 @@ export const AppHeader = ({
return (
<header
id="app-header"
className={
'w-full grid ' +
styles.header +

View File

@ -102,7 +102,7 @@ export const ModelingMachineProvider = ({
settings: {
context: {
app: { theme, enableSSAO },
modeling: { defaultUnit, highlightEdges },
modeling: { defaultUnit, highlightEdges, showScaleGrid },
},
},
} = useSettingsAuthContext()
@ -117,6 +117,7 @@ export const ModelingMachineProvider = ({
theme: theme.current,
highlightEdges: highlightEdges.current,
enableSSAO: enableSSAO.current,
showScaleGrid: showScaleGrid.current,
})
const { htmlRef } = useStore((s) => ({
htmlRef: s.htmlRef,

View File

@ -1,6 +1,6 @@
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Resizable } from 're-resizable'
import { useCallback, useEffect, useState } from 'react'
import { HTMLAttributes, useCallback, useEffect, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useStore } from 'useStore'
import { Tab } from '@headlessui/react'
@ -56,15 +56,19 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
bottomRight: 'hidden',
}}
>
<div className={styles.grid + ' flex-1'}>
<ModelingSidebarSection panes={topPanes} />
<ModelingSidebarSection panes={bottomPanes} alignButtons="end" />
<div id="app-sidebar" className={styles.grid + ' flex-1'}>
<ModelingSidebarSection id="sidebar-top" panes={topPanes} />
<ModelingSidebarSection
id="sidebar-bottom"
panes={bottomPanes}
alignButtons="end"
/>
</div>
</Resizable>
)
}
interface ModelingSidebarSectionProps {
interface ModelingSidebarSectionProps extends HTMLAttributes<HTMLDivElement> {
panes: SidebarPane[]
alignButtons?: 'start' | 'end'
}
@ -72,6 +76,8 @@ interface ModelingSidebarSectionProps {
function ModelingSidebarSection({
panes,
alignButtons = 'start',
className,
...props
}: ModelingSidebarSectionProps) {
const { settings } = useSettingsAuthContext()
const showDebugPanel = settings.context.modeling.showDebugPanel
@ -123,7 +129,7 @@ function ModelingSidebarSection({
}, [showDebugPanel.current, togglePane, openPanes])
return (
<div className="group contents">
<div className={'group contents ' + className} {...props}>
<Tab.Group
vertical
selectedIndex={
@ -135,6 +141,7 @@ function ModelingSidebarSection({
}}
>
<Tab.List
id={`${props.id}-ribbon`}
className={
'pointer-events-auto ' +
(alignButtons === 'start'
@ -161,6 +168,7 @@ function ModelingSidebarSection({
))}
</Tab.List>
<Tab.Panels
id={`${props.id}-pane`}
as="article"
className={
'col-start-2 col-span-1 ' +

View File

@ -134,6 +134,11 @@ export const SettingsAuthProviderBase = ({
},
})
},
setEngineScaleGridVisibility: (context) => {
engineCommandManager.setScaleGridVisibility(
context.modeling.showScaleGrid.current
)
},
setClientTheme: (context) => {
const opposingTheme = getOppositeTheme(context.app.theme.current)
sceneInfra.theme = opposingTheme

View File

@ -13,11 +13,13 @@ export function useSetupEngineManager(
theme: Themes.System,
highlightEdges: true,
enableSSAO: true,
showScaleGrid: false,
} as {
pool: string | null
theme: Themes
highlightEdges: boolean
enableSSAO: boolean
showScaleGrid: boolean
}
) {
const {

View File

@ -1158,6 +1158,7 @@ export class EngineCommandManager extends EventTarget {
theme: Themes.Dark,
highlightEdges: true,
enableSSAO: true,
showScaleGrid: false,
},
}: {
setMediaStream: (stream: MediaStream) => void
@ -1172,6 +1173,7 @@ export class EngineCommandManager extends EventTarget {
theme: Themes
highlightEdges: boolean
enableSSAO: boolean
showScaleGrid: boolean
}
}) {
this.makeDefaultPlanes = makeDefaultPlanes
@ -1254,7 +1256,7 @@ export class EngineCommandManager extends EventTarget {
// We want modify the grid first because we don't want it to flash.
// Ideally these would already be default hidden in engine (TODO do
// that) https://github.com/KittyCAD/engine/issues/2282
this.modifyGrid(true)?.then(async () => {
this.modifyGrid(!settings.showScaleGrid)?.then(async () => {
await this.initPlanes()
this.resolveReady()
setIsStreamReady(true)
@ -2074,4 +2076,12 @@ export class EngineCommandManager extends EventTarget {
},
})
}
/**
* Set the visibility of the scale grid in the engine scene.
* @param visible - whether to show or hide the scale grid
*/
setScaleGridVisibility(visible: boolean) {
this.modifyGrid(!visible)
}
}

View File

@ -325,6 +325,18 @@ export function createSettings() {
},
hideOnLevel: 'project',
}),
/**
* Whether to show a scale grid in the 3D modeling view
*/
showScaleGrid: new Setting<boolean>({
defaultValue: false,
description: 'Whether to show a scale grid in the 3D modeling view',
validate: (v) => typeof v === 'boolean',
commandConfig: {
inputType: 'boolean',
},
hideOnLevel: 'project',
}),
/**
* Whether to show the debug panel, which lets you see
* various states of the app to aid in development

View File

@ -48,6 +48,7 @@ function configurationToSettingsPayload(
),
highlightEdges: configuration?.settings?.modeling?.highlight_edges,
showDebugPanel: configuration?.settings?.modeling?.show_debug_panel,
showScaleGrid: configuration?.settings?.modeling?.show_scale_grid,
},
textEditor: {
textWrapping: configuration?.settings?.text_editor?.text_wrapping,

View File

@ -11,7 +11,7 @@ import {
export const settingsMachine = createMachine(
{
/** @xstate-layout N4IgpgJg5mDOIC5QGUwBc0EsB2VYDpMIAbMAYlnXwEMAHW-Ae2wCNHqAnCHKZNatAFdYAbQAMAXUShajWJizNpIAB6IALAFYAnPgBMARgDsBsQDY969QGYjmzQBoQAT0SnrADnwePY61r0PAwNtMyMAX3CnVAweAiJSCio6BjQACzAAWzAAYUZiRg5xKSQQWXlFbGU1BD1PfFtfE3UzTUNNaydXBCD1b209PTEPTTMtdQNNSOj0LFx4knJKNHxMxggwYh58DYAzakFiNABVbAVi5XKFTCVSmsGxfCMPM08PQaDNU0cXRG1tLwedTaKxif7+UJTKIgGJzPCERZJFYpfDpLJgC6lK6VaqIEx6fBmCw2Do2IJ6MxdRDvTT4MRDdRGEzWbQ6ELTGGzOIIxLLVbrTbbNKYKBpLaitAAUWgcExMjk11uoBqVgM3jMYhsAIMrVs6ipPWChOeYhC9KMFhGHNh3IS5AASnB0AACZZw0SSS4KnF3PFafADTV1YZ2IxiH7dNpGfCaIzAgE+IzWMzBa1c+Y88gxZ3UYjEV3pvBysrem5VX0IFq0y3aTXWOp6JmU34IKMxuz0joGEYWsxp2IZu1kABUxexZdxtRG+EmQMZmne3dNBs0jKewLBsbCwI81n77vwtDAHHksDhBYHeDIEGYYEI2AAbowANZ3o8nzBnm3zMelpWqRAAFp62sJ4jEsZ4AT0UJGwjPFzH6cwNW0AwWXpbRImhbABXgUpvzwL0KgnCtgJMMCII8KCYLsA11EGOkXneDxmXMCk92hfCFlIQjFXLZUgLjddWhaFkgRCaxOhbEYzBnXwXkmOjAjjfduXfU9zzdOIeJ9fiEEA6ckwMClQ2BFpmJXMF9DjYI6hZfxmMw8IgA */
/** @xstate-layout N4IgpgJg5mDOIC5QGUwBc0EsB2VYDpMIAbMAYlnXwEMAHW-Ae2wCNHqAnCHKZNatAFdYAbQAMAXUShajWJizNpIAB6IALAFYAnPgBMARgDsBsQDY969QGYjmzQBoQAT0SnrADnwePY61r0PAwNtMyMAX3CnVAweAiJSCio6BjQACzAAWzAAYUZiRg5xKSQQWXlFbGU1BD1PfFtfE3UzTUNNaydXBCD1b209PTEPTTMtdQNNSOj0LFx4knJKNHxMxggwYh58DYAzakFiNABVbAVi5XKFTCVSmusxPXx7bRt1DzMxI3UjD3UutwhAz4MyeHxiV5+AYRKIgGJzPCERZJFYpfDpLJgC6lK6VaqIExPMwWGwdGxBPRmAE9PSafCPMQ-EzWbQ6ELTOGzOJIxLLVbrTbbNKYKBpLaitAAUWgcGxMjk11uoBqVmBH0ZLKCrVs-xciCCwLCvhCjyMFhGHPh3IS5AASnB0AACZYI0SSS4KvF3AlafADRl1YZ2IxiRx6hBtIzPb7abQ+DxGaxmYKWrnzHnkGKO6jEYjOtN4OVlT03KrehAtOnm7Qaup6Ixm6mR6OaR4dAwjM1mVOxdM2lH8jZbXD4WBpRgAd2QAGMc2AAOIcIhF3Gl-EIRPA6yGcyh4whSnU0xGJ5GAat0OfFowma9xH9gBUK5LStUiECdMmfx+mg8hmNTY-PgMYQpoZoxh41g9q6+C0GAHDyLACL5nesBkBAzBgIQ2AAG6MAA1lhcEIZgSFWvMz4VGu5YALTbtYwEnj8HhxnooT1mG3QhmY-TmJ82gGCyjzaJEsLYAK8ClOReAelRr41HRJiMZYvysexdjUuohh+poBiGDuXzGKy0HWossmKmWyqIDR3zAZWLSahM2jWJ04YjDxHbDMmmhaYE3wmemxGIchLpxOZXpWQgNEjMB1h6WEYHqK8ZgJk2EL6N8wR1Cy-gJqJ4RAA */
id: 'Settings',
predictableActionArguments: true,
context: {} as ReturnType<typeof createSettings>,
@ -32,6 +32,7 @@ export const settingsMachine = createMachine(
// No toast
actions: ['setSettingAtLevel'],
},
'set.app.themeColor': {
target: 'persisting settings',
@ -93,6 +94,15 @@ export const settingsMachine = createMachine(
'setClientTheme',
],
},
'set.modeling.showScaleGrid': {
target: 'persisting settings',
actions: [
'setSettingAtLevel',
'toastSuccess',
'setEngineScaleGridVisibility',
],
},
},
},

View File

@ -379,6 +379,9 @@ pub struct ModelingSettings {
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
#[serde(default, skip_serializing_if = "is_default")]
pub enable_ssao: DefaultTrue,
/// Whether or not to show a scale grid in the 3D modeling view
#[serde(default, alias = "showScaleGrid", skip_serializing_if = "is_default")]
pub show_scale_grid: bool,
}
#[derive(Debug, Copy, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq)]
@ -654,7 +657,8 @@ textWrapping = true
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
enable_ssao: false.into()
enable_ssao: false.into(),
show_scale_grid: false,
},
text_editor: TextEditorSettings {
text_wrapping: true.into(),
@ -712,7 +716,8 @@ includeSettings = false
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
enable_ssao: true.into()
enable_ssao: true.into(),
show_scale_grid: false,
},
text_editor: TextEditorSettings {
text_wrapping: false.into(),
@ -775,7 +780,8 @@ defaultProjectName = "projects-$nnn"
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
enable_ssao: true.into()
enable_ssao: true.into(),
show_scale_grid: false,
},
text_editor: TextEditorSettings {
text_wrapping: false.into(),
@ -850,7 +856,8 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
mouse_controls: Default::default(),
highlight_edges: true.into(),
show_debug_panel: false,
enable_ssao: true.into()
enable_ssao: true.into(),
show_scale_grid: false,
},
text_editor: TextEditorSettings {
text_wrapping: true.into(),

View File

@ -129,7 +129,8 @@ includeSettings = false
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
enable_ssao: true.into()
enable_ssao: true.into(),
show_scale_grid: false,
},
text_editor: TextEditorSettings {
text_wrapping: false.into(),