Skip to content

Commit f338005

Browse files
authored
Inspector v2: Side pane collapsing and undocking (#17341)
With this PR the whole "side panes" (all tabs, top and bottom, plus toolbars) can be: 1. Collapsed/expanded (small expand buttons will overlay the canvas in the top corners) 2. "Undocked" to child windows Styling in the child windows is easy to manage. No need to copy styles or anything like Inspector v1, it just requires a little bit of Fluent setup in the child window, and then only the used styles will be injected into that window. In the Inspector v2 implementation, when a child window is closed, it is just re-docked in the main window. Also, child windows will automatically close if Inspector itself is closed, or if the main window is closed. Unfortunately, the approach I initially used with portals to keep the panes mounted all the time (so we don't lose their state when redocking) *does not* work with child windows. Simply re-parenting a raw html element into a child window basically bypasses all the fluent theming and also seems to break other things. For this reason, I rolled that part of the change back, which means when you redock a side pane, it is unmounted/remounted and the state is lost. However, I did two things to improve this: 1. Panes are not unmounted/remounted when the selected tab changes. 2. Scene explorer selects the currently selected entity when it first mounts, so if you re-dock to a new location (including a child window), it at least selects and scrolls to the same selected entity. This is still an improvement over the v1 behavior! <img width="1146" height="753" alt="image" src="https://github.com/user-attachments/assets/8fff2e9f-aa38-44ed-ae63-71b01ae017c1" /> <img width="1149" height="751" alt="image" src="https://github.com/user-attachments/assets/36e81e0c-26ed-4bad-a9ee-26762e295473" /> <img width="436" height="306" alt="image" src="https://github.com/user-attachments/assets/b4ca7528-d805-4d2c-9bd0-eeb95286858d" /> <img width="1146" height="754" alt="image" src="https://github.com/user-attachments/assets/79c533ee-67ca-4b9d-87b5-2fb30381d69c" />
1 parent 58d6e06 commit f338005

File tree

4 files changed

+317
-166
lines changed

4 files changed

+317
-166
lines changed

packages/dev/inspector-v2/src/components/gizmoToolbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type GizmoMode = "translate" | "rotate" | "scale" | "boundingBox";
2525

2626
const useStyles = makeStyles({
2727
coordinatesModeButton: {
28-
margin: `0 ${tokens.spacingVerticalXS}`,
28+
margin: `0 0 0 ${tokens.spacingHorizontalXS}`,
2929
},
3030
coordinatesModeMenu: {
3131
minWidth: 0,

packages/dev/inspector-v2/src/components/scene/sceneExplorer.tsx

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -722,17 +722,17 @@ export const SceneExplorer: FunctionComponent<{
722722
for (let treeItem = allTreeItems.get(entity.uniqueId); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
723723
parentStack.push(treeItem.type === "entity" ? treeItem.entity.uniqueId : treeItem.sectionName);
724724
}
725+
// The first item will be the entity itself, so just remove it.
726+
parentStack.shift();
725727
return parentStack;
726728
},
727729
[allTreeItems]
728730
);
729731

730-
const [isScrollToPending, setIsScrollToPending] = useState(false);
731-
732-
useEffect(() => {
733-
if (selectedEntity && selectedEntity !== previousSelectedEntity.current) {
732+
const selectEntity = useCallback(
733+
(selectedEntity: unknown) => {
734734
const entity = selectedEntity as EntityBase;
735-
if (entity.uniqueId != undefined) {
735+
if (entity && entity.uniqueId != undefined) {
736736
const parentStack = getParentStack(entity);
737737
if (parentStack.length > 0) {
738738
const newOpenItems = new Set<TreeItemValue>(openItems);
@@ -743,10 +743,26 @@ export const SceneExplorer: FunctionComponent<{
743743
setIsScrollToPending(true);
744744
}
745745
}
746+
747+
previousSelectedEntity.current = selectedEntity;
748+
},
749+
[getParentStack, openItems]
750+
);
751+
752+
const [isScrollToPending, setIsScrollToPending] = useState(false);
753+
754+
useEffect(() => {
755+
if (selectedEntity && selectedEntity !== previousSelectedEntity.current) {
756+
selectEntity(selectedEntity);
746757
}
758+
}, [selectedEntity, selectEntity]);
747759

748-
previousSelectedEntity.current = selectedEntity;
749-
}, [selectedEntity]);
760+
useEffect(() => {
761+
// If there are no open items, then it should mean scene explorer has only just mounted, so we should select the already selected entity.
762+
if (openItems.size === 0) {
763+
selectEntity(selectedEntity);
764+
}
765+
}, [openItems, selectEntity, selectedEntity]);
750766

751767
// We need to wait for a render to complete before we can scroll to the item, hence the isScrollToPending.
752768
useEffect(() => {

0 commit comments

Comments
 (0)