diff --git a/desktop/plugins/public/ui-debugger/ClientTypes.tsx b/desktop/plugins/public/ui-debugger/ClientTypes.tsx index 8883d176d..05b6dcd8c 100644 --- a/desktop/plugins/public/ui-debugger/ClientTypes.tsx +++ b/desktop/plugins/public/ui-debugger/ClientTypes.tsx @@ -7,6 +7,8 @@ * @format */ +import {TraversalMode} from './DesktopTypes'; + export type Events = { init: InitEvent; subtreeUpdate: SubtreeUpdateEvent; @@ -14,6 +16,11 @@ export type Events = { perfStats: PerfStatsEvent; performanceStats: PerformanceStatsEvent; metadataUpdate: UpdateMetadataEvent; + setTraversalMode: SetTraversalModeEvent; +}; + +export type SetTraversalModeEvent = { + mode: TraversalMode; }; export type FrameScanEvent = { @@ -69,6 +76,8 @@ export type FrameworkEvent = { export type InitEvent = { rootId: Id; frameworkEventMetadata?: FrameworkEventMetadata[]; + supportedTraversalModes?: TraversalMode[]; + currentTraversalMode?: TraversalMode; }; /** @@ -110,6 +119,10 @@ export type UpdateMetadataEvent = { attributeMetadata: Record; }; +export type UpdateAvailableTraversalModeEvent = { + modes: TraversalMode[]; +}; + export type ClientNode = { id: Id; parent?: Id; diff --git a/desktop/plugins/public/ui-debugger/DesktopTypes.tsx b/desktop/plugins/public/ui-debugger/DesktopTypes.tsx index 7b51cfe48..7bd2d3022 100644 --- a/desktop/plugins/public/ui-debugger/DesktopTypes.tsx +++ b/desktop/plugins/public/ui-debugger/DesktopTypes.tsx @@ -42,6 +42,9 @@ export type UIState = { visualiserWidth: Atom; frameworkEventMonitoring: Atom>; filterMainThreadMonitoring: Atom; + + supportedTraversalModes: Atom; + currentTraversalMode: Atom; }; //enumerates the keys of input type and casts each to ReadOnlyAtom, this is so we only expose read only atoms to the UI @@ -66,6 +69,8 @@ export type NestedNode = { activeChildIdx?: number; }; +export type TraversalMode = 'view-hierarchy' | 'accessibility-hierarchy'; + export type ViewMode = | {mode: 'default'} | {mode: 'frameworkEventsTable'; nodeId: Id; isTree: boolean}; @@ -106,6 +111,7 @@ export type UIActions = { onCollapseAllNonAncestors: (nodeId: Id) => void; onCollapseAllRecursively: (nodeId: Id) => void; ensureAncestorsExpanded: (nodeId: Id) => void; + setCurrentTraversalMode: (mode: TraversalMode) => void; }; export type SelectionSource = diff --git a/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx b/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx index cd7d65b1b..1cbcebb8a 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx @@ -18,10 +18,12 @@ import { Space, Switch, Badge, + Dropdown, } from 'antd'; import { EyeOutlined, PauseCircleOutlined, + AppstoreOutlined, PlayCircleOutlined, SearchOutlined, } from '@ant-design/icons'; @@ -31,6 +33,7 @@ import { buildTreeSelectData, FrameworkEventsTreeSelect, } from '../shared/FrameworkEventsTreeSelect'; +import {TraversalMode} from '../../DesktopTypes'; export const TreeControls: React.FC = () => { const instance = usePlugin(plugin); @@ -44,13 +47,50 @@ export const TreeControls: React.FC = () => { instance.uiState.frameworkEventMonitoring, ); - const frameworkEventMetadata = useValue(instance.frameworkEventMetadata); - const [showFrameworkEventsModal, setShowFrameworkEventsModal] = useState(false); + const frameworkEventMetadata = useValue(instance.frameworkEventMetadata); + + const currentTraversalMode = useValue(instance.uiState.currentTraversalMode); + const supportedTraversalModes = useValue( + instance.uiState.supportedTraversalModes, + ); + const labelForMode = (mode: TraversalMode) => { + switch (mode) { + case 'view-hierarchy': + return 'UI Hierarchy'; + case 'accessibility-hierarchy': + return 'Accessibility Hierarchy'; + } + }; + + const menus = supportedTraversalModes.map((mode) => ({ + key: mode, + label: labelForMode(mode), + })); + return ( + {supportedTraversalModes.length > 1 ? ( + { + const mode = event.selectedKeys[0] as TraversalMode; + instance.uiActions.setCurrentTraversalMode(mode); // update UI + await instance.onTraversalModeChange(mode); // update mobile client + }, + }}> + + + + + ) : null} { diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index c294c208c..1bdbaecd2 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -23,6 +23,7 @@ import { import { UIState, NodeSelection, + TraversalMode, StreamState, ReadOnlyUIState, LiveClientState, @@ -38,7 +39,15 @@ import {uiActions} from './plugin/uiActions'; import {first} from 'lodash'; import {getNode} from './utils/map'; -export function plugin(client: PluginClient) { +type TraversalModeChangeEvent = { + mode: TraversalMode; +}; + +export type Methods = { + onTraversalModeChange(params: TraversalModeChangeEvent): Promise; +}; + +export function plugin(client: PluginClient) { const rootId = createState(undefined); const metadata = createState>(new Map()); @@ -87,6 +96,22 @@ export function plugin(client: PluginClient) { draft.set(frameworkEventMeta.type, false); }); }); + if ( + event.supportedTraversalModes && + event.supportedTraversalModes.length > 1 + ) { + uiState.supportedTraversalModes.set(event.supportedTraversalModes); + } + if ( + event.currentTraversalMode && + uiState.supportedTraversalModes.get().includes(event.currentTraversalMode) + ) { + uiState.currentTraversalMode.set(event.currentTraversalMode); + console.log( + `[ui-debugger] Unsupported debugger mode ${event.currentTraversalMode}.`, + ); + } + frameworkEventMetadata.update((draft) => { event.frameworkEventMetadata?.forEach((frameworkEventMeta) => { draft.set(frameworkEventMeta.type, frameworkEventMeta); @@ -256,6 +281,9 @@ export function plugin(client: PluginClient) { }); client.onMessage('frameScan', processFrame); + const onTraversalModeChange = async (mode: TraversalMode) => + client.send('onTraversalModeChange', {mode}); + return { rootId, uiState: uiState as ReadOnlyUIState, @@ -268,6 +296,7 @@ export function plugin(client: PluginClient) { metadata, perfEvents, os: client.device.os, + onTraversalModeChange, }; } @@ -308,5 +337,9 @@ function createUIState(): UIState { focusedNode: createState(undefined), expandedNodes: createState>(new Set()), wireFrameMode: createState('All'), + + // view-hierarchy is the default state so we start with it until we fetch supported modes from the client + supportedTraversalModes: createState(['view-hierarchy']), + currentTraversalMode: createState('view-hierarchy'), }; } diff --git a/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx b/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx index 42ed7910a..2307053f5 100644 --- a/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx +++ b/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx @@ -11,6 +11,7 @@ import {Atom} from 'flipper-plugin'; import {debounce} from 'lodash'; import {ClientNode, FrameworkEventType, Id, SnapshotInfo} from '../ClientTypes'; import { + TraversalMode, LiveClientState, SelectionSource, UIActions, @@ -189,6 +190,11 @@ export function uiActions( searchTermUpdatedDebounced(searchTerm); }; + const setCurrentTraversalMode = (mode: TraversalMode) => { + tracker.track('traversal-mode-updated', {mode}); + uiState.currentTraversalMode.set(mode); + }; + return { onExpandNode, onCollapseNode, @@ -207,5 +213,6 @@ export function uiActions( onExpandAllRecursively, onCollapseAllRecursively, ensureAncestorsExpanded, + setCurrentTraversalMode, }; } diff --git a/desktop/plugins/public/ui-debugger/utils/tracker.tsx b/desktop/plugins/public/ui-debugger/utils/tracker.tsx index b0aa1850a..bb1a68d40 100644 --- a/desktop/plugins/public/ui-debugger/utils/tracker.tsx +++ b/desktop/plugins/public/ui-debugger/utils/tracker.tsx @@ -10,7 +10,7 @@ import {getFlipperLib} from 'flipper-plugin'; import {FrameworkEventType, Tag} from '../ClientTypes'; -import {SelectionSource} from '../DesktopTypes'; +import {TraversalMode, SelectionSource} from '../DesktopTypes'; const UI_DEBUGGER_IDENTIFIER = 'ui-debugger'; @@ -67,6 +67,7 @@ type TrackerEvents = { 'context-menu-expand-recursive': {}; 'context-menu-collapse-recursive': {}; 'context-menu-collapse-non-ancestors': {}; + 'traversal-mode-updated': {mode: TraversalMode}; }; export interface Tracker {