Skip to content

Commit 2354849

Browse files
georginahalpernGeorginaryantrem
authored
[Fluent/InspectorV2] Add OutlineAndOverlay section to InspectorV2; Add ColorPicker component; Various cleanup (#16783)
- Add outline/overlay mesh property section - First iteration of colorpicker - Update primitives to take in value/onChange and not export the fluent props - Remove the property indexing in syncedslider so that all components use the simple value / onchange API. That way we can bring back boundproperties all together --------- Co-authored-by: Georgina <[email protected]> Co-authored-by: Ryan Tremblay <[email protected]>
1 parent 6c5e718 commit 2354849

24 files changed

+734
-178
lines changed

packages/dev/inspector-v2/src/components/properties/meshAdvancedProperties.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ export const MeshAdvancedProperties: FunctionComponent<{ mesh: AbstractMesh }> =
2121
<SwitchPropertyLine
2222
label="Compute bones using shaders"
2323
description="Whether to compute bones using shaders."
24-
checked={computeBonesUsingShaders}
25-
onChange={(_, data) => (mesh.computeBonesUsingShaders = data.checked)}
24+
value={computeBonesUsingShaders}
25+
onChange={(checked) => (mesh.computeBonesUsingShaders = checked)}
2626
/>
2727
)}
2828
<SwitchPropertyLine
2929
label="Check collisions"
3030
description="Whether to check for collisions."
31-
checked={checkCollisions}
32-
onChange={(_, data) => (mesh.checkCollisions = data.checked)}
31+
value={checkCollisions}
32+
onChange={(checked) => (mesh.checkCollisions = checked)}
3333
/>
3434
</>
3535
);

packages/dev/inspector-v2/src/components/properties/meshGeneralProperties.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export const MeshGeneralProperties: FunctionComponent<{ mesh: AbstractMesh; sele
2424
key="MeshIsEnabled"
2525
label="Is enabled"
2626
description="Whether the mesh is enabled or not."
27-
checked={isEnabled}
28-
onChange={(_, data) => mesh.setEnabled(data.checked)}
27+
value={isEnabled}
28+
onChange={(checked) => mesh.setEnabled(checked)}
2929
/>
3030
{material && (!material.reservedDataStore || !material.reservedDataStore.hidden) && (
3131
<LinkPropertyLine
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// eslint-disable-next-line import/no-internal-modules
2+
import type { Color3, Mesh } from "core/index";
3+
4+
import type { FunctionComponent } from "react";
5+
6+
import { Collapse } from "@fluentui/react-motion-components-preview";
7+
8+
import { Color3PropertyLine } from "shared-ui-components/fluent/hoc/colorPropertyLine";
9+
import { SwitchPropertyLine } from "shared-ui-components/fluent/hoc/switchPropertyLine";
10+
import { useInterceptObservable } from "../../hooks/instrumentationHooks";
11+
import { useObservableState } from "../../hooks/observableHooks";
12+
13+
type Color3Keys<T> = { [P in keyof T]: T[P] extends Color3 ? P : never }[keyof T];
14+
15+
// eslint-disable-next-line @typescript-eslint/naming-convention
16+
function useColor3Property<T extends object, K extends Color3Keys<T>>(target: T, propertyKey: K): Color3 {
17+
const color = useObservableState(() => target[propertyKey] as Color3, useInterceptObservable("property", target, propertyKey));
18+
useObservableState(() => color.r, useInterceptObservable("property", color, "r"));
19+
useObservableState(() => color.g, useInterceptObservable("property", color, "g"));
20+
useObservableState(() => color.b, useInterceptObservable("property", color, "b"));
21+
return color;
22+
}
23+
24+
export const MeshOutlineOverlayProperties: FunctionComponent<{ mesh: Mesh }> = (props) => {
25+
const { mesh } = props;
26+
27+
// There is no observable for colors, so we use an interceptor to listen for changes.
28+
const renderOverlay = useObservableState(() => mesh.renderOverlay, useInterceptObservable("property", mesh, "renderOverlay"));
29+
const overlayColor = useColor3Property(mesh, "overlayColor");
30+
31+
const renderOutline = useObservableState(() => mesh.renderOutline, useInterceptObservable("property", mesh, "renderOutline"));
32+
const outlineColor = useColor3Property(mesh, "outlineColor");
33+
34+
return (
35+
<>
36+
<SwitchPropertyLine key="RenderOverlay" label="Render Overlay" value={renderOverlay} onChange={(checked) => (mesh.renderOverlay = checked)} />
37+
<Collapse visible={renderOverlay}>
38+
<Color3PropertyLine
39+
key="OverlayColor"
40+
label="Overlay Color"
41+
value={overlayColor}
42+
onChange={(color) => {
43+
mesh.overlayColor = color;
44+
}}
45+
/>
46+
</Collapse>
47+
<SwitchPropertyLine key="RenderOutline" label="Render Outline" value={renderOutline} onChange={(checked) => (mesh.renderOutline = checked)} />
48+
<Collapse visible={renderOutline}>
49+
<Color3PropertyLine
50+
key="OutlineColor"
51+
label="Outline Color"
52+
value={outlineColor}
53+
onChange={(color) => {
54+
mesh.outlineColor = color;
55+
}}
56+
/>
57+
</Collapse>
58+
</>
59+
);
60+
};

packages/dev/inspector-v2/src/components/properties/transformNodeTransformProperties.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TransformNode, Vector3 } from "core/index";
33

44
import type { FunctionComponent } from "react";
55

6-
import { VectorPropertyLine } from "shared-ui-components/fluent/hoc/vectorPropertyLine";
6+
import { Vector3PropertyLine } from "shared-ui-components/fluent/hoc/vectorPropertyLine";
77

88
import { useInterceptObservable } from "../../hooks/instrumentationHooks";
99
import { useObservableState } from "../../hooks/observableHooks";
@@ -30,9 +30,9 @@ export const TransformNodeTransformProperties: FunctionComponent<{ node: Transfo
3030

3131
return (
3232
<>
33-
<VectorPropertyLine key="PositionTransform" label="Position" description="The position of the transform node." vector={position} />
34-
<VectorPropertyLine key="RotationTransform" label="Rotation" description="The rotation of the transform node." vector={rotation} />
35-
<VectorPropertyLine key="ScalingTransform" label="Scaling" description="The scaling of the transform node." vector={scaling} />
33+
<Vector3PropertyLine key="PositionTransform" label="Position" value={position} onChange={(val) => (transformNode.position = val)} />
34+
<Vector3PropertyLine key="RotationTransform" label="Rotation" value={rotation} onChange={(val) => (transformNode.scaling = val)} />
35+
<Vector3PropertyLine key="ScalingTransform" label="Scaling" value={scaling} onChange={(val) => (transformNode.scaling = val)} />
3636
</>
3737
);
3838
};

packages/dev/inspector-v2/src/services/panes/properties/meshPropertiesService.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,30 @@ import type { IPropertiesService } from "./propertiesService";
33
import type { ISelectionService } from "../../selectionService";
44

55
import { AbstractMesh } from "core/Meshes/abstractMesh";
6+
import { Mesh } from "core/Meshes";
67

78
import { GeneralPropertiesSectionIdentity } from "./commonPropertiesService";
89
import { PropertiesServiceIdentity } from "./propertiesService";
910
import { SelectionServiceIdentity } from "../../selectionService";
1011
import { MeshAdvancedProperties } from "../../../components/properties/meshAdvancedProperties";
1112
import { MeshGeneralProperties } from "../../../components/properties/meshGeneralProperties";
13+
import { MeshOutlineOverlayProperties } from "../../../components/properties/meshOutlineOverlayProperties";
1214

1315
export const AdvancedPropertiesSectionIdentity = Symbol("Advanced");
16+
export const OutlineOverlayPropertiesSectionItentity = Symbol("Outline & Overlay");
1417

1518
export const MeshPropertiesServiceDefinition: ServiceDefinition<[], [IPropertiesService, ISelectionService]> = {
1619
friendlyName: "Mesh Properties",
1720
consumes: [PropertiesServiceIdentity, SelectionServiceIdentity],
1821
factory: (propertiesService, selectionService) => {
22+
// Abstract Mesh
1923
const advancedSectionRegistration = propertiesService.addSection({
2024
order: 2,
2125
identity: AdvancedPropertiesSectionIdentity,
2226
});
2327

24-
const contentRegistration = propertiesService.addSectionContent({
25-
key: "Mesh Properties",
28+
const abstractMeshContentRegistration = propertiesService.addSectionContent({
29+
key: "Abstract Mesh Properties",
2630
// Meshes without vertices are effectively TransformNodes, so don't add mesh properties for them.
2731
predicate: (entity: unknown): entity is AbstractMesh => entity instanceof AbstractMesh && entity.getTotalVertices() > 0,
2832
content: [
@@ -42,10 +46,30 @@ export const MeshPropertiesServiceDefinition: ServiceDefinition<[], [IProperties
4246
],
4347
});
4448

49+
const outlineOverlaySectionRegistration = propertiesService.addSection({
50+
order: 0,
51+
identity: OutlineOverlayPropertiesSectionItentity,
52+
});
53+
54+
const meshPropertiesContentRegistration = propertiesService.addSectionContent({
55+
key: "Mesh Properties",
56+
predicate: (entity: unknown): entity is Mesh => entity instanceof Mesh,
57+
content: [
58+
// "OUTLINES & OVERLAYS" section.
59+
{
60+
section: OutlineOverlayPropertiesSectionItentity,
61+
order: 0,
62+
component: ({ context }) => <MeshOutlineOverlayProperties mesh={context} />,
63+
},
64+
],
65+
});
66+
4567
return {
4668
dispose: () => {
47-
contentRegistration.dispose();
69+
abstractMeshContentRegistration.dispose();
70+
meshPropertiesContentRegistration.dispose();
4871
advancedSectionRegistration.dispose();
72+
outlineOverlaySectionRegistration.dispose();
4973
},
5074
};
5175
},

packages/dev/inspector-v2/test/app/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { registerBuiltInLoaders } from "loaders/dynamic";
99
import { ShowInspector } from "../../src/inspector";
1010

1111
import "core/Helpers/sceneHelpers";
12+
// For testing the Outline & Overlay section
13+
import "core/Rendering/outlineRenderer";
1214

1315
// Register scene loader plugins.
1416
registerBuiltInLoaders();
Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,60 @@
11
import type { FunctionComponent } from "react";
2+
import { forwardRef, useState } from "react";
23

3-
import type { PropertyLineProps } from "./propertyLine";
4+
import type { BaseComponentProps, PropertyLineProps } from "./propertyLine";
45
import { PropertyLine } from "./propertyLine";
56
import { SyncedSliderLine } from "./syncedSliderLine";
67

78
import type { Color3 } from "core/Maths/math.color";
89
import { Color4 } from "core/Maths/math.color";
10+
import { ColorPickerPopup } from "../primitives/colorPicker";
11+
import type { ColorPickerProps } from "../primitives/colorPicker";
912

10-
type ColorSliderProps = {
11-
color: Color3 | Color4;
12-
};
13-
14-
const ColorSliders: FunctionComponent<ColorSliderProps> = (props) => {
15-
return (
16-
<>
17-
<SyncedSliderLine label="R" propertyKey="r" target={props.color} min={0} max={255} />
18-
<SyncedSliderLine label="G" propertyKey="g" target={props.color} min={0} max={255} />
19-
<SyncedSliderLine label="B" propertyKey="b" target={props.color} min={0} max={255} />
20-
{props.color instanceof Color4 && <SyncedSliderLine label="A" propertyKey="a" target={props.color} min={0} max={1} />}
21-
</>
22-
);
23-
};
13+
export type ColorPropertyLineProps = ColorPickerProps<Color3 | Color4> & PropertyLineProps;
2414

2515
/**
2616
* Reusable component which renders a color property line containing a label, colorPicker popout, and expandable RGBA values
2717
* The expandable RGBA values are synced sliders that allow the user to modify the color's RGBA values directly
2818
* @param props - PropertyLine props, replacing children with a color object so that we can properly display the color
2919
* @returns Component wrapping a colorPicker component (coming soon) with a property line
3020
*/
31-
export const ColorPropertyLine: FunctionComponent<ColorSliderProps & PropertyLineProps> = (props) => {
21+
const ColorPropertyLine = forwardRef<HTMLDivElement, ColorPropertyLineProps>((props, ref) => {
22+
const [color, setColor] = useState(props.value);
23+
const onChange = (value: Color3 | Color4) => {
24+
setColor(value);
25+
props.onChange(value);
26+
};
3227
return (
33-
<PropertyLine {...props} expandedContent={<ColorSliders {...props} />}>
34-
{
35-
props.color.toString()
36-
// Will replace with colorPicker in future PR
37-
}
28+
<PropertyLine ref={ref} {...props} expandedContent={<ColorSliders {...props} value={color} onChange={onChange} />}>
29+
<ColorPickerPopup {...props} value={color} />
3830
</PropertyLine>
3931
);
32+
});
33+
34+
const ColorSliders: FunctionComponent<BaseComponentProps<Color3 | Color4>> = (props) => {
35+
const [color, setColor] = useState(props.value);
36+
const onChange = (value: number, key: "r" | "g" | "b" | "a") => {
37+
let newColor;
38+
if (key === "a") {
39+
newColor = Color4.FromColor3(color, value);
40+
} else {
41+
newColor = color.clone();
42+
newColor[key] = value / 255.0;
43+
}
44+
45+
setColor(newColor); // Create a new object to trigger re-render
46+
props.onChange(newColor);
47+
};
48+
49+
return (
50+
<>
51+
<SyncedSliderLine label="R" value={color.r * 255.0} min={0} max={255} step={1} onChange={(value) => onChange(value, "r")} />
52+
<SyncedSliderLine label="G" value={color.g * 255.0} min={0} max={255} step={1} onChange={(value) => onChange(value, "g")} />
53+
<SyncedSliderLine label="B" value={color.b * 255.0} min={0} max={255} step={1} onChange={(value) => onChange(value, "b")} />
54+
{color instanceof Color4 && <SyncedSliderLine label="A" value={color.a} min={0} max={1} step={0.01} onChange={(value) => onChange(value, "a")} />}
55+
</>
56+
);
4057
};
58+
59+
export const Color3PropertyLine = ColorPropertyLine as FunctionComponent<ColorPickerProps<Color3> & PropertyLineProps>;
60+
export const Color4PropertyLine = ColorPropertyLine as FunctionComponent<ColorPickerProps<Color4> & PropertyLineProps>;

packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Body1Strong, Button, InfoLabel, ToggleButton, makeStyles, tokens } from "@fluentui/react-components";
1+
import { Body1, Body1Strong, Button, InfoLabel, ToggleButton, makeStyles, tokens } from "@fluentui/react-components";
22
import { Collapse } from "@fluentui/react-motion-components-preview";
33
import { AddFilled, CopyRegular, SubtractFilled } from "@fluentui/react-icons";
44
import type { FunctionComponent, PropsWithChildren } from "react";
5-
import { useContext, useState } from "react";
5+
import { useContext, useState, forwardRef } from "react";
66
import { copyCommandToClipboard } from "../../copyCommandToClipboard";
77
import { ToolContext } from "./fluentToolWrapper";
88

@@ -76,9 +76,32 @@ export type PropertyLineProps = {
7676
expandedContent?: JSX.Element;
7777
};
7878

79-
export const LineContainer: FunctionComponent<PropsWithChildren> = (props) => {
79+
export const LineContainer = forwardRef<HTMLDivElement, PropsWithChildren>((props, ref) => {
8080
const classes = usePropertyLineStyles();
81-
return <div className={classes.container}>{props.children}</div>;
81+
return (
82+
<div ref={ref} className={classes.container}>
83+
{props.children}
84+
</div>
85+
);
86+
});
87+
88+
export type BaseComponentProps<T> = {
89+
/**
90+
* The value of the property to be displayed and modified.
91+
*/
92+
value: T;
93+
/**
94+
* Callback function to handle changes to the value
95+
*/
96+
onChange: (value: T) => void;
97+
/**
98+
* Optional flag to disable the component, preventing any interaction.
99+
*/
100+
disabled?: boolean;
101+
/**
102+
* Optional class name to apply custom styles to the component.
103+
*/
104+
className?: string;
82105
};
83106

84107
/**
@@ -88,7 +111,7 @@ export const LineContainer: FunctionComponent<PropsWithChildren> = (props) => {
88111
* @returns A React element representing the property line.
89112
*
90113
*/
91-
export const PropertyLine: FunctionComponent<PropsWithChildren<PropertyLineProps>> = (props) => {
114+
export const PropertyLine = forwardRef<HTMLDivElement, PropsWithChildren<PropertyLineProps>>((props, ref) => {
92115
const classes = usePropertyLineStyles();
93116
const [expanded, setExpanded] = useState(false);
94117

@@ -97,7 +120,7 @@ export const PropertyLine: FunctionComponent<PropsWithChildren<PropertyLineProps
97120
const { disableCopy } = useContext(ToolContext);
98121

99122
return (
100-
<LineContainer>
123+
<LineContainer ref={ref}>
101124
<div className={classes.line}>
102125
<InfoLabel className={classes.label} info={description}>
103126
<Body1Strong className={classes.labelText}>{label}</Body1Strong>
@@ -128,4 +151,12 @@ export const PropertyLine: FunctionComponent<PropsWithChildren<PropertyLineProps
128151
</Collapse>
129152
</LineContainer>
130153
);
154+
});
155+
156+
export const PlaceholderPropertyLine: FunctionComponent<BaseComponentProps<any> & PropertyLineProps> = (props) => {
157+
return (
158+
<PropertyLine {...props}>
159+
<Body1>{props.value}</Body1>
160+
</PropertyLine>
161+
);
131162
};

packages/dev/sharedUiComponents/src/fluent/hoc/switchPropertyLine.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import type { SwitchProps } from "@fluentui/react-components";
21
import { PropertyLine } from "./propertyLine";
32
import type { PropertyLineProps } from "./propertyLine";
43
import type { FunctionComponent } from "react";
54
import { Switch } from "../primitives/switch";
6-
5+
import type { SwitchProps } from "../primitives/switch";
76
/**
87
* Wraps a switch in a property line
98
* @param props - The properties for the switch and property line

0 commit comments

Comments
 (0)