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:
@ -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 |
@ -26,6 +26,7 @@ export const AppHeader = ({
|
||||
|
||||
return (
|
||||
<header
|
||||
id="app-header"
|
||||
className={
|
||||
'w-full grid ' +
|
||||
styles.header +
|
||||
|
@ -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,
|
||||
|
@ -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 ' +
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
Reference in New Issue
Block a user