AX tree expands with main tree on search (cannot search ax tree yet)
Summary: AX tree will now stay in sync with the main tree when searching. Also allows user to completely erase search (previously had left one letter highlighted in the tree even if entire query was erased). Reviewed By: danielbuechele Differential Revision: D9276721 fbshipit-source-id: 5272bb9cf3400ad3eb9d16bf438b0e5d4b551c6a
This commit is contained in:
committed by
Facebook Github Bot
parent
00847365ef
commit
656044ce69
@@ -372,7 +372,9 @@ public class InspectorSonarPlugin implements SonarPlugin {
|
||||
public void onReceiveOnMainThread(SonarObject params, SonarResponder responder)
|
||||
throws Exception {
|
||||
final String query = params.getString("query");
|
||||
final SearchResultNode matchTree = searchTree(query.toLowerCase(), mApplication);
|
||||
final boolean axEnabled = params.getBoolean("axEnabled");
|
||||
|
||||
final SearchResultNode matchTree = searchTree(query.toLowerCase(), mApplication, axEnabled);
|
||||
final SonarObject results = matchTree == null ? null : matchTree.toSonarObject();
|
||||
final SonarObject response =
|
||||
new SonarObject.Builder().put("results", results).put("query", query).build();
|
||||
@@ -503,14 +505,19 @@ public class InspectorSonarPlugin implements SonarPlugin {
|
||||
descriptor.setHighlighted(obj, highlighted, isAlignmentMode);
|
||||
}
|
||||
|
||||
public SearchResultNode searchTree(String query, Object obj) throws Exception {
|
||||
private boolean hasAXNode(SonarObject node) {
|
||||
SonarObject extraInfo = node.getObject("extraInfo");
|
||||
return extraInfo != null && extraInfo.getBoolean("hasAXNode");
|
||||
}
|
||||
|
||||
public SearchResultNode searchTree(String query, Object obj, boolean axEnabled) throws Exception {
|
||||
final NodeDescriptor descriptor = descriptorForObject(obj);
|
||||
List<SearchResultNode> childTrees = null;
|
||||
boolean isMatch = descriptor.matches(query, obj);
|
||||
|
||||
for (int i = 0; i < descriptor.getChildCount(obj); i++) {
|
||||
Object child = descriptor.getChildAt(obj, i);
|
||||
SearchResultNode childNode = searchTree(query, child);
|
||||
SearchResultNode childNode = searchTree(query, child, axEnabled);
|
||||
if (childNode != null) {
|
||||
if (childTrees == null) {
|
||||
childTrees = new ArrayList<>();
|
||||
@@ -521,7 +528,8 @@ public class InspectorSonarPlugin implements SonarPlugin {
|
||||
|
||||
if (isMatch || childTrees != null) {
|
||||
final String id = trackObject(obj);
|
||||
return new SearchResultNode(id, isMatch, getNode(id), childTrees);
|
||||
SonarObject node = getNode(id);
|
||||
return new SearchResultNode(id, isMatch, node, childTrees, axEnabled && hasAXNode(node) ? getAXNode(id) : null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -18,15 +18,17 @@ public class SearchResultNode {
|
||||
private final String id;
|
||||
private final boolean isMatch;
|
||||
private final SonarObject element;
|
||||
private final SonarObject axElement;
|
||||
@Nullable
|
||||
private final List<SearchResultNode> children;
|
||||
|
||||
SearchResultNode(
|
||||
String id, boolean isMatch, SonarObject element, @Nullable List<SearchResultNode> children) {
|
||||
String id, boolean isMatch, SonarObject element, List<SearchResultNode> children, SonarObject axElement) {
|
||||
this.id = id;
|
||||
this.isMatch = isMatch;
|
||||
this.element = element;
|
||||
this.children = children;
|
||||
this.axElement = axElement;
|
||||
}
|
||||
|
||||
SonarObject toSonarObject() {
|
||||
@@ -44,6 +46,7 @@ public class SearchResultNode {
|
||||
return new SonarObject.Builder()
|
||||
.put("id", this.id)
|
||||
.put("isMatch", this.isMatch)
|
||||
.put("axElement", this.axElement)
|
||||
.put("element", this.element)
|
||||
.put("children", childArray)
|
||||
.build();
|
||||
|
||||
@@ -206,6 +206,11 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SonarObject getExtraInfo(ApplicationWrapper node) {
|
||||
return new SonarObject.Builder().put("hasAXNode", true).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlighted(ApplicationWrapper node, boolean selected, boolean isAlignmentMode) throws Exception {
|
||||
final int childCount = getChildCount(node);
|
||||
|
||||
@@ -463,7 +463,10 @@ public class ViewDescriptor extends NodeDescriptor<View> {
|
||||
|
||||
@Override
|
||||
public SonarObject getExtraInfo(View node) {
|
||||
return new SonarObject.Builder().put("focused", AccessibilityUtil.isAXFocused(node)).build();
|
||||
return new SonarObject.Builder()
|
||||
.put("focused", AccessibilityUtil.isAXFocused(node))
|
||||
.put("hasAXNode", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -86,6 +86,9 @@ public class AccessibilityRoleUtil {
|
||||
}
|
||||
|
||||
public static AccessibilityRole getRole(View view) {
|
||||
if (view == null) {
|
||||
return AccessibilityRole.NONE;
|
||||
}
|
||||
AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain();
|
||||
ViewCompat.onInitializeAccessibilityNodeInfo(view, nodeInfo);
|
||||
AccessibilityRole role = getRole(nodeInfo);
|
||||
|
||||
@@ -107,6 +107,7 @@ type SearchResultTree = {|
|
||||
isMatch: Boolean,
|
||||
children: ?Array<SearchResultTree>,
|
||||
element: Element,
|
||||
axElement: Element,
|
||||
|};
|
||||
|
||||
const LoadingSpinner = LoadingIndicator.extends({
|
||||
@@ -333,15 +334,17 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
||||
};
|
||||
|
||||
search(query: string) {
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
outstandingSearchQuery: query,
|
||||
});
|
||||
this.client
|
||||
.call('getSearchResults', {query: query})
|
||||
.then(response => this.displaySearchResults(response));
|
||||
|
||||
if (!query) {
|
||||
this.displaySearchResults({query: '', results: null});
|
||||
} else {
|
||||
this.client
|
||||
.call('getSearchResults', {query: query, axEnabled: this.axEnabled()})
|
||||
.then(response => this.displaySearchResults(response));
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand(command: string) {
|
||||
@@ -391,7 +394,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
||||
results,
|
||||
query,
|
||||
}: {
|
||||
results: SearchResultTree,
|
||||
results: ?SearchResultTree,
|
||||
query: string,
|
||||
}) {
|
||||
const elements = this.getElementsFromSearchResultTree(results);
|
||||
@@ -405,10 +408,29 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
||||
elements: elements.map(x => x.element),
|
||||
type: 'UpdateElements',
|
||||
});
|
||||
|
||||
this.dispatchAction({
|
||||
elements: idsToExpand,
|
||||
type: 'ExpandElements',
|
||||
});
|
||||
|
||||
if (this.axEnabled()) {
|
||||
const AXelements = elements.filter(x => x.axElement);
|
||||
const AXidsToExpand = AXelements.filter(x => x.hasChildren).map(
|
||||
x => x.axElement.id,
|
||||
);
|
||||
|
||||
this.dispatchAction({
|
||||
elements: AXelements.map(x => x.axElement),
|
||||
type: 'UpdateAXElements',
|
||||
});
|
||||
|
||||
this.dispatchAction({
|
||||
elements: AXidsToExpand,
|
||||
type: 'ExpandAXElements',
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
searchResults: {
|
||||
matches: new Set(
|
||||
@@ -422,7 +444,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
||||
});
|
||||
}
|
||||
|
||||
getElementsFromSearchResultTree(tree: SearchResultTree) {
|
||||
getElementsFromSearchResultTree(tree: ?SearchResultTree) {
|
||||
if (!tree) {
|
||||
return [];
|
||||
}
|
||||
@@ -432,6 +454,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
||||
isMatch: tree.isMatch,
|
||||
hasChildren: Boolean(tree.children),
|
||||
element: tree.element,
|
||||
axElement: tree.axElement,
|
||||
},
|
||||
];
|
||||
if (tree.children) {
|
||||
@@ -842,12 +865,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
||||
|
||||
onElementSelected = debounce((selectedKey: ElementID) => {
|
||||
const {key, AXkey} = this.getKeysFromSelected(selectedKey);
|
||||
|
||||
this.dispatchAction({
|
||||
key: key,
|
||||
AXkey: AXkey,
|
||||
type: 'SelectElement',
|
||||
});
|
||||
this.dispatchAction({key, AXkey, type: 'SelectElement'});
|
||||
|
||||
this.client.send('setHighlighted', {
|
||||
id: selectedKey,
|
||||
|
||||
Reference in New Issue
Block a user