Basic mutual highlighting for Litho components

Summary: Shows basic relationship between the AX and nonAX tree litho nodes. When a litho component is selected from the nonAX tree, it's corresponding hostView or lithoView (root of the component tree) is highlighted in the AX tree giving priority to the hostView if it exists. If a hostView is selected in the AX tree, it's corresponding component is selected in the non-AX tree. If a lithoView is selected from the AX tree, it's corresponding lithoView is highlighted in the non-AX tree. This means that each hostView has a one-to-one highlighting between the two trees but lithoViews will have many nodes in the main tree that map to one node in the AX tree (which is accurate to litho components rendering but we may need to change in the future if it is not clear).

Reviewed By: jknoxville

Differential Revision: D8972205

fbshipit-source-id: d136f5b594d0ac1b66a82b35dc7b085186829fc4
This commit is contained in:
Sara Valderrama
2018-07-25 10:12:01 -07:00
committed by Facebook Github Bot
parent c57e6e4396
commit 41f4478a74
4 changed files with 70 additions and 10 deletions

View File

@@ -541,9 +541,12 @@ public class InspectorSonarPlugin implements SonarPlugin {
} }
}.run(); }.run();
String name = descriptor.getAXName(obj);
name = name.substring(name.lastIndexOf('.') + 1);
return new SonarObject.Builder() return new SonarObject.Builder()
.put("id", descriptor.getId(obj)) .put("id", descriptor.getId(obj))
.put("name", descriptor.getAXName(obj)) .put("name", name)
.put("data", data) .put("data", data)
.put("children", children) .put("children", children)
.put("attributes", attributes) .put("attributes", attributes)

View File

@@ -558,6 +558,29 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
return attributes; return attributes;
} }
@Override
public SonarObject getExtraInfo(DebugComponent node) {
SonarObject.Builder extraInfo = new SonarObject.Builder();
final NodeDescriptor descriptor = descriptorForClass(View.class);
final View hostView = node.getComponentHost();
final View lithoView = node.getLithoView();
if (hostView != null) {
try {
extraInfo.put("linkedAXNode", descriptor.getId(hostView));
} catch (Exception ignored) {
// doesn't have linked node descriptor
}
} else if (lithoView != null) {
try {
extraInfo.put("linkedAXNode", descriptor.getId(lithoView));
} catch (Exception ignored) {
// doesn't add linked node descriptor
}
}
return extraInfo.build();
}
@Override @Override
public void setHighlighted(DebugComponent node, boolean selected) { public void setHighlighted(DebugComponent node, boolean selected) {
final LithoView lithoView = node.getLithoView(); final LithoView lithoView = node.getLithoView();

View File

@@ -46,6 +46,7 @@ export type InspectorState = {|
inAXMode: boolean, inAXMode: boolean,
searchResults: ?ElementSearchResultSet, searchResults: ?ElementSearchResultSet,
outstandingSearchQuery: ?string, outstandingSearchQuery: ?string,
AXtoNonAXMapping: {[key: ElementID]: ElementID},
|}; |};
type SelectElementArgs = {| type SelectElementArgs = {|
@@ -160,14 +161,39 @@ export default class Layout extends SonarPlugin<InspectorState> {
AXselected: null, AXselected: null,
searchResults: null, searchResults: null,
outstandingSearchQuery: null, outstandingSearchQuery: null,
AXtoNonAXMapping: {},
}; };
reducers = { reducers = {
SelectElement(state: InspectorState, {key}: SelectElementArgs) { SelectElement(state: InspectorState, {key}: SelectElementArgs) {
const linkedAXNode =
state.elements[key] && state.elements[key].extraInfo.linkedAXNode;
// element only in main tree with linkedAXNode selected
if (linkedAXNode) {
return {
selected: key,
AXselected: linkedAXNode,
};
// element only in AX tree with linked nonAX element selected
} else if (
(!state.elements[key] ||
state.elements[key].name === 'ComponentHost') &&
state.AXtoNonAXMapping[key]
) {
return {
selected: state.AXtoNonAXMapping[key],
AXselected: key,
};
// keys are same for both trees or 'linked' element does not exist
} else {
return { return {
selected: key, selected: key,
AXselected: key, AXselected: key,
}; };
}
}, },
ExpandElement(state: InspectorState, {expand, key}: ExpandElementArgs) { ExpandElement(state: InspectorState, {expand, key}: ExpandElementArgs) {
@@ -235,6 +261,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
UpdateElements(state: InspectorState, {elements}: UpdateElementsArgs) { UpdateElements(state: InspectorState, {elements}: UpdateElementsArgs) {
const updatedElements = state.elements; const updatedElements = state.elements;
const updatedMapping = state.AXtoNonAXMapping;
for (const element of elements) { for (const element of elements) {
const current = updatedElements[element.id] || {}; const current = updatedElements[element.id] || {};
@@ -242,9 +269,13 @@ export default class Layout extends SonarPlugin<InspectorState> {
...current, ...current,
...element, ...element,
}; };
const linked = element.extraInfo.linkedAXNode;
if (linked && !updatedMapping[linked]) {
updatedMapping[linked] = element.id;
}
} }
return {elements: updatedElements}; return {elements: updatedElements, AXtoNonAXMapping: updatedMapping};
}, },
UpdateAXElements(state: InspectorState, {elements}: UpdateElementsArgs) { UpdateAXElements(state: InspectorState, {elements}: UpdateElementsArgs) {
@@ -627,9 +658,11 @@ export default class Layout extends SonarPlugin<InspectorState> {
this.getNodes([key], true, false).then((elements: Array<Element>) => { this.getNodes([key], true, false).then((elements: Array<Element>) => {
this.dispatchAction({elements, type: 'UpdateElements'}); this.dispatchAction({elements, type: 'UpdateElements'});
}); });
if (AXToggleButtonEnabled) {
this.getNodes([key], true, true).then((elements: Array<Element>) => { this.getNodes([key], true, true).then((elements: Array<Element>) => {
this.dispatchAction({elements, type: 'UpdateAXElements'}); this.dispatchAction({elements, type: 'UpdateAXElements'});
}); });
}
}); });
onElementHovered = debounce((key: ?ElementID) => { onElementHovered = debounce((key: ?ElementID) => {
@@ -693,7 +726,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
<FlexColumn fill={true}> <FlexColumn fill={true}>
<Toolbar> <Toolbar>
<SearchIconContainer <SearchIconContainer
onClick={inAXMode ? null : this.onFindClick} onClick={this.onFindClick}
role="button" role="button"
tabIndex={-1} tabIndex={-1}
title="Select an element on the device to inspect it"> title="Select an element on the device to inspect it">

View File

@@ -36,6 +36,7 @@ export type ElementAttribute = {|
export type ElementExtraInfo = {| export type ElementExtraInfo = {|
nonAXWithAXChild?: boolean, nonAXWithAXChild?: boolean,
linkedAXNode?: string,
|}; |};
export type Element = {| export type Element = {|