From f3b75523381db7b35f62e2b4d3e8acebd30b5796 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 21 Sep 2022 07:02:48 -0700 Subject: [PATCH] 2D wire frame highlight from tree, select from wireframe Summary: Introduced some basic bidirectional link between tree and wireframe, the specific interaction will need some tweaking but this should get us started. When hovering over the tree we halt the rendering of the wireframe up to that point, this allows us to explore parent views that layout child views. When clicking a view in the wireframe it is 'seleceted' as if it was clicked in the tree. This set the tree selection so you can identify it in the tree as well as opens the side bar Reviewed By: lblasa Differential Revision: D39539277 fbshipit-source-id: 3beb1ad4cb56b398c640ac3e7fac2cc97f3f1a18 --- .../public/ui-debugger/components/Tree.tsx | 23 +++++- .../components/Visualization2D.tsx | 76 ++++++++++++------- .../public/ui-debugger/components/main.tsx | 12 ++- 3 files changed, 80 insertions(+), 31 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/components/Tree.tsx b/desktop/plugins/public/ui-debugger/components/Tree.tsx index 04a801eee..d5e70366a 100644 --- a/desktop/plugins/public/ui-debugger/components/Tree.tsx +++ b/desktop/plugins/public/ui-debugger/components/Tree.tsx @@ -9,23 +9,40 @@ import {Id, UINode} from '../types'; import {DataNode} from 'antd/es/tree'; -import {Tree as AntTree, Typography} from 'antd'; +import {Tree as AntTree} from 'antd'; import {DownOutlined} from '@ant-design/icons'; import React from 'react'; export function Tree(props: { rootId: Id; nodes: Map; - setSelectedNode: (id: Id) => void; + selectedNode?: Id; + onSelectNode: (id: Id) => void; + onHoveredNode: (id?: Id) => void; }) { const [antTree, inactive] = nodesToAntTree(props.rootId, props.nodes); return ( { + //when mouse exits the entire tree then unhover + props.onHoveredNode(undefined); + }} showIcon showLine + titleRender={(node) => { + return ( +
{ + props.onHoveredNode(node.key as Id); + }}> + {node.title} +
+ ); + }} + selectedKeys={[props.selectedNode ?? '']} onSelect={(selected) => { - props.setSelectedNode(selected[0] as Id); + props.onSelectNode(selected[0] as Id); }} defaultExpandAll expandedKeys={[...props.nodes.keys()].filter( diff --git a/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx b/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx index 799265d14..2b404caff 100644 --- a/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx +++ b/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx @@ -9,48 +9,52 @@ import React from 'react'; import {Bounds, Id, Tag, UINode} from '../types'; -import {styled, Layout} from 'flipper-plugin'; +import {styled, Layout, theme} from 'flipper-plugin'; import {Typography} from 'antd'; export const Visualization2D: React.FC< - {root: Id; nodes: Map} & React.HTMLAttributes -> = ({root, nodes}) => { - // - const bounds = nodes.get(root)?.bounds; - const rootBorderStyle = bounds - ? { - borderWidth: '3px', - margin: '-3px', - borderStyle: 'solid', - borderColor: 'black', - width: bounds.width / 2, - height: bounds.height / 2, - } - : {}; + { + root: Id; + nodes: Map; + hoveredNode?: Id; + onSelectNode: (id: Id) => void; + } & React.HTMLAttributes +> = ({root, nodes, hoveredNode, onSelectNode}) => { return ( Visualizer +
- ; + + ;
); }; -function VisualizationNode({ +function Visualization2DNode({ nodeId, nodes, isRoot, + hoveredNode, + onSelectNode, }: { isRoot: boolean; nodeId: Id; nodes: Map; + hoveredNode?: Id; + onSelectNode: (id: Id) => void; }) { const node = nodes.get(nodeId); @@ -58,19 +62,28 @@ function VisualizationNode({ return null; } - let childrenIds = node.children; + const isHovered = hoveredNode === nodeId; - //if there is an active child dont draw the other children - //this means we don't draw overlapping activities / tabs - if (node.activeChild) { - childrenIds = [node.activeChild]; + let childrenIds: Id[] = []; + + if (!isHovered) { + //if there is an active child don't draw the other children + //this means we don't draw overlapping activities / tabs etc + if (node.activeChild) { + childrenIds = [node.activeChild]; + } else { + childrenIds = node.children; + } } + const children = childrenIds.map((childId) => ( - )); @@ -81,7 +94,15 @@ function VisualizationNode({ const isZeroWidthOrHeight = node.bounds?.height === 0 || node.bounds?.width === 0; return ( - + { + e.stopPropagation(); + onSelectNode(nodeId); + }} + bounds={node.bounds} + isRoot={isRoot} + tags={node.tags} + isHovered={isHovered}> {/* Dirty hack to avoid showing highly overlapping text */} {!hasOverlappingChild && !isZeroWidthOrHeight && node.bounds ? node.name @@ -94,11 +115,13 @@ function VisualizationNode({ const BoundsBox = styled.div<{ bounds?: Bounds; isRoot: boolean; + isHovered: boolean; tags: Tag[]; }>((props) => { const bounds = props.bounds ?? {x: 0, y: 0, width: 0, height: 0}; return { // borderWidth: props.isRoot ? '5px' : '1px', + cursor: 'pointer', borderWidth: '1px', //to offset the border margin: '-1px', @@ -109,6 +132,7 @@ const BoundsBox = styled.div<{ : 'black', borderStyle: 'solid', position: 'absolute', + backgroundColor: props.isHovered ? theme.selectionBackgroundColor : 'white', //todo need to understand why its so big and needs halving left: bounds.x / 2, top: bounds.y / 2, diff --git a/desktop/plugins/public/ui-debugger/components/main.tsx b/desktop/plugins/public/ui-debugger/components/main.tsx index 3000e5d7a..a299cbd8c 100644 --- a/desktop/plugins/public/ui-debugger/components/main.tsx +++ b/desktop/plugins/public/ui-debugger/components/main.tsx @@ -31,6 +31,7 @@ export function Component() { const [showPerfStats, setShowPerfStats] = useState(false); const [selectedNode, setSelectedNode] = useState(undefined); + const [hoveredNode, setHoveredNode] = useState(undefined); useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show)); @@ -58,11 +59,18 @@ export function Component() { - + {selectedNode && renderAttributesInspector(nodes.get(selectedNode))}