Added inline tree attributes

Summary:
This is temporary solution to get to parity with the old plugin. In future would like to make this more flexible on the desktop side

Additionally getData was renamed to getAttributes for consistency

Reviewed By: lblasa

Differential Revision: D41845248

fbshipit-source-id: 50e94a7712f5d42938229134e212cef5d379475d
This commit is contained in:
Luke De Feo
2022-12-12 07:28:37 -08:00
committed by Facebook GitHub Bot
parent 97cca42822
commit 1a9724d790
22 changed files with 107 additions and 26 deletions

View File

@@ -72,7 +72,9 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto
private val StateId = private val StateId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Litho State") MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Litho State")
override fun getData(node: DebugComponent): MaybeDeferred<Map<MetadataId, InspectableObject>> { override fun getAttributes(
node: DebugComponent
): MaybeDeferred<Map<MetadataId, InspectableObject>> {
return Deferred { return Deferred {
val attributeSections = mutableMapOf<MetadataId, InspectableObject>() val attributeSections = mutableMapOf<MetadataId, InspectableObject>()
@@ -101,4 +103,17 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto
override fun getTags(node: DebugComponent): Set<String> = setOf(BaseTags.Declarative, LithoTag) override fun getTags(node: DebugComponent): Set<String> = setOf(BaseTags.Declarative, LithoTag)
override fun getSnapshot(node: DebugComponent, bitmap: Bitmap?): Bitmap? = null override fun getSnapshot(node: DebugComponent, bitmap: Bitmap?): Bitmap? = null
override fun getInlineAttributes(node: DebugComponent): Map<String, String> {
val attributes = mutableMapOf<String, String>()
val key = node.key
val testKey = node.testKey
if (key != null && key.trim { it <= ' ' }.length > 0) {
attributes["key"] = key
}
if (testKey != null && testKey.trim { it <= ' ' }.length > 0) {
attributes["testKey"] = testKey
}
return attributes
}
} }

View File

@@ -36,7 +36,7 @@ object LithoViewDescriptor : ChainedDescriptor<LithoView>() {
MetadataRegister.register( MetadataRegister.register(
MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "isIncrementalMountEnabled") MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "isIncrementalMountEnabled")
override fun onGetData( override fun onGetAttributes(
node: LithoView, node: LithoView,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -25,7 +25,7 @@ object TextDrawableDescriptor : ChainedDescriptor<TextDrawable>() {
private val TextAttributeId = private val TextAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "text") MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "text")
override fun onGetData( override fun onGetAttributes(
node: TextDrawable, node: TextDrawable,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -83,14 +83,14 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
open fun onGetChildren(node: T): List<Any>? = null open fun onGetChildren(node: T): List<Any>? = null
final override fun getData(node: T): MaybeDeferred<Map<MetadataId, InspectableObject>> { final override fun getAttributes(node: T): MaybeDeferred<Map<MetadataId, InspectableObject>> {
val builder = mutableMapOf<MetadataId, InspectableObject>() val builder = mutableMapOf<MetadataId, InspectableObject>()
onGetData(node, builder) onGetAttributes(node, builder)
var curDescriptor: ChainedDescriptor<T>? = mSuper var curDescriptor: ChainedDescriptor<T>? = mSuper
while (curDescriptor != null) { while (curDescriptor != null) {
curDescriptor.onGetData(node, builder) curDescriptor.onGetAttributes(node, builder)
curDescriptor = curDescriptor.mSuper curDescriptor = curDescriptor.mSuper
} }
@@ -101,7 +101,7 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
* Get the data to show for this node in the sidebar of the inspector. Each key will be a have its * Get the data to show for this node in the sidebar of the inspector. Each key will be a have its
* own section * own section
*/ */
open fun onGetData(node: T, attributeSections: MutableMap<MetadataId, InspectableObject>) {} open fun onGetAttributes(node: T, attributeSections: MutableMap<MetadataId, InspectableObject>) {}
/** Get a snapshot of the node. */ /** Get a snapshot of the node. */
final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? { final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
@@ -111,4 +111,21 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
open fun onGetSnapshot(node: T, bitmap: Bitmap?): Bitmap? { open fun onGetSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
return null return null
} }
final override fun getInlineAttributes(node: T): Map<String, String> {
val builder = mutableMapOf<String, String>()
onGetInlineAttributes(node, builder)
var curDescriptor: ChainedDescriptor<T>? = mSuper
while (curDescriptor != null) {
curDescriptor.onGetInlineAttributes(node, builder)
curDescriptor = curDescriptor.mSuper
}
return builder
}
open fun onGetInlineAttributes(node: T, attributes: MutableMap<String, String>) {}
} }

View File

@@ -20,7 +20,7 @@ object ColorDrawableDescriptor : ChainedDescriptor<ColorDrawable>() {
override fun onGetName(node: ColorDrawable): String = node.javaClass.simpleName override fun onGetName(node: ColorDrawable): String = node.javaClass.simpleName
override fun onGetData( override fun onGetAttributes(
node: ColorDrawable, node: ColorDrawable,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -26,7 +26,7 @@ object DrawableDescriptor : ChainedDescriptor<Drawable>() {
override fun onGetBounds(node: Drawable): Bounds = override fun onGetBounds(node: Drawable): Bounds =
Bounds(node.bounds.left, node.bounds.top, node.bounds.width(), node.bounds.height()) Bounds(node.bounds.left, node.bounds.top, node.bounds.width(), node.bounds.height())
override fun onGetData( override fun onGetAttributes(
node: Drawable, node: Drawable,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -32,7 +32,7 @@ class FragmentFrameworkDescriptor(val register: DescriptorRegister) :
override fun onGetChildren(node: android.app.Fragment): List<Any> = override fun onGetChildren(node: android.app.Fragment): List<Any> =
node.view?.let { view -> listOf(view) } ?: listOf() node.view?.let { view -> listOf(view) } ?: listOf()
override fun onGetData( override fun onGetAttributes(
node: android.app.Fragment, node: android.app.Fragment,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -58,7 +58,7 @@ class FragmentSupportDescriptor(val register: DescriptorRegister) :
} }
} }
override fun onGetData( override fun onGetAttributes(
node: androidx.fragment.app.Fragment, node: androidx.fragment.app.Fragment,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -32,7 +32,7 @@ object ImageViewDescriptor : ChainedDescriptor<ImageView>() {
override fun onGetName(node: ImageView): String = node.javaClass.simpleName override fun onGetName(node: ImageView): String = node.javaClass.simpleName
override fun onGetData( override fun onGetAttributes(
node: ImageView, node: ImageView,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -71,14 +71,20 @@ interface NodeDescriptor<T> {
fun getActiveChild(node: T): Any? fun getActiveChild(node: T): Any?
/** /**
* Get the data to show for this node in the sidebar of the inspector. The object will be shown in * Get the attribute to show for this node in the sidebar of the inspector. The object first level
* order and with a header matching the given name. * is a section and subsequent objects within are the first level of that section. Nested objects
* will nest in the sidebar
*/ */
fun getData(node: T): MaybeDeferred<Map<MetadataId, InspectableObject>> fun getAttributes(node: T): MaybeDeferred<Map<MetadataId, InspectableObject>>
/** /**
* Set of tags to describe this node in an abstract way for the UI Unfortunately this can't be an * Set of tags to describe this node in an abstract way for the UI Unfortunately this can't be an
* enum as we have to plugin 3rd party frameworks dynamically * enum as we have to plugin 3rd party frameworks dynamically
*/ */
fun getTags(node: T): Set<String> fun getTags(node: T): Set<String>
/**
* These are shown inline in the tree view on the desktop, will likely be removed in the future
*/
fun getInlineAttributes(node: T): Map<String, String> = mutableMapOf()
} }

View File

@@ -27,7 +27,7 @@ object ObjectDescriptor : NodeDescriptor<Any> {
override fun getChildren(node: Any) = listOf<Any>() override fun getChildren(node: Any) = listOf<Any>()
override fun getData(node: Any) = Immediate(mapOf<MetadataId, InspectableObject>()) override fun getAttributes(node: Any) = Immediate(mapOf<MetadataId, InspectableObject>())
override fun getBounds(node: Any): Bounds = Bounds(0, 0, 0, 0) override fun getBounds(node: Any): Bounds = Bounds(0, 0, 0, 0)

View File

@@ -38,8 +38,8 @@ object OffsetChildDescriptor : NodeDescriptor<OffsetChild> {
override fun getActiveChild(node: OffsetChild): Any? = node.descriptor.getActiveChild(node.child) override fun getActiveChild(node: OffsetChild): Any? = node.descriptor.getActiveChild(node.child)
override fun getData(node: OffsetChild): MaybeDeferred<Map<MetadataId, InspectableObject>> = override fun getAttributes(node: OffsetChild): MaybeDeferred<Map<MetadataId, InspectableObject>> =
node.descriptor.getData(node.child) node.descriptor.getAttributes(node.child)
override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(node.child) override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(node.child)
override fun getSnapshot(node: OffsetChild, bitmap: Bitmap?): Bitmap? = override fun getSnapshot(node: OffsetChild, bitmap: Bitmap?): Bitmap? =

View File

@@ -46,7 +46,7 @@ object TextViewDescriptor : ChainedDescriptor<TextView>() {
override fun onGetName(node: TextView): String = node.javaClass.simpleName override fun onGetName(node: TextView): String = node.javaClass.simpleName
override fun onGetData( override fun onGetAttributes(
node: TextView, node: TextView,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -273,7 +273,10 @@ object ViewDescriptor : ChainedDescriptor<View>() {
override fun onGetTags(node: View): Set<String> = BaseTags.NativeAndroid override fun onGetTags(node: View): Set<String> = BaseTags.NativeAndroid
override fun onGetData(node: View, attributeSections: MutableMap<MetadataId, InspectableObject>) { override fun onGetAttributes(
node: View,
attributeSections: MutableMap<MetadataId, InspectableObject>
) {
val props = mutableMapOf<Int, Inspectable>() val props = mutableMapOf<Int, Inspectable>()
@@ -365,6 +368,16 @@ object ViewDescriptor : ChainedDescriptor<View>() {
attributeSections[SectionId] = InspectableObject(props.toMap()) attributeSections[SectionId] = InspectableObject(props.toMap())
} }
override fun onGetInlineAttributes(node: View, attributes: MutableMap<String, String>) {
val id = node.id
if (id == View.NO_ID) {
return
}
val value = ResourcesUtil.getIdStringQuietly(node.getContext(), node.getResources(), id)
attributes["id"] = value
}
override fun onGetSnapshot(node: View, bitmap: Bitmap?): Bitmap? { override fun onGetSnapshot(node: View, bitmap: Bitmap?): Bitmap? {
if (node.width <= 0 || node.height <= 0) { if (node.width <= 0 || node.height <= 0) {
return null return null

View File

@@ -60,7 +60,7 @@ object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
private val ClipToPaddingAttributeId = private val ClipToPaddingAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "clipToPadding") MetadataRegister.register(MetadataRegister.TYPE_LAYOUT, NAMESPACE, "clipToPadding")
override fun onGetData( override fun onGetAttributes(
node: ViewGroup, node: ViewGroup,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -31,7 +31,7 @@ object ViewPagerDescriptor : ChainedDescriptor<ViewPager>() {
private val CurrentItemIndexAttributeId = private val CurrentItemIndexAttributeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "currentItemIndex") MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "currentItemIndex")
override fun onGetData( override fun onGetAttributes(
node: ViewPager, node: ViewPager,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -38,7 +38,7 @@ object WindowDescriptor : ChainedDescriptor<Window>() {
override fun onGetChildren(node: Window): List<Any> = listOf(node.decorView) override fun onGetChildren(node: Window): List<Any> = listOf(node.decorView)
@SuppressLint("PrivateApi") @SuppressLint("PrivateApi")
override fun onGetData( override fun onGetAttributes(
node: Window, node: Window,
attributeSections: MutableMap<MetadataId, InspectableObject> attributeSections: MutableMap<MetadataId, InspectableObject>
) { ) {

View File

@@ -15,6 +15,7 @@ data class Node(
val qualifiedName: String, val qualifiedName: String,
val name: String, val name: String,
val attributes: Map<MetadataId, InspectableObject>, val attributes: Map<MetadataId, InspectableObject>,
val inlineAttributes: Map<String, String>,
val bounds: Bounds, val bounds: Bounds,
val tags: Set<String>, val tags: Set<String>,
val children: List<Id>, val children: List<Id>,

View File

@@ -61,6 +61,7 @@ class PartialLayoutTraversal(
descriptor.getQualifiedName(node), descriptor.getQualifiedName(node),
descriptor.getName(node), descriptor.getName(node),
emptyMap(), emptyMap(),
emptyMap(),
descriptor.getBounds(node), descriptor.getBounds(node),
emptySet(), emptySet(),
emptyList(), emptyList(),
@@ -92,7 +93,7 @@ class PartialLayoutTraversal(
} }
} }
val attributes = descriptor.getData(node) val attributes = descriptor.getAttributes(node)
val bounds = descriptor.getBounds(node) val bounds = descriptor.getBounds(node)
val tags = descriptor.getTags(node) val tags = descriptor.getTags(node)
visited.add( visited.add(
@@ -102,6 +103,7 @@ class PartialLayoutTraversal(
descriptor.getQualifiedName(node), descriptor.getQualifiedName(node),
descriptor.getName(node), descriptor.getName(node),
attrs, attrs,
descriptor.getInlineAttributes(node),
bounds, bounds,
tags, tags,
childrenIds, childrenIds,

View File

@@ -273,6 +273,7 @@ const FakeNode: UINode = {
id: 'Fakeroot', id: 'Fakeroot',
qualifiedName: 'Fakeroot', qualifiedName: 'Fakeroot',
name: 'Fakeroot', name: 'Fakeroot',
inlineAttributes: {},
children: [], children: [],
attributes: {}, attributes: {},
bounds: {x: 0, y: 0, height: 0, width: 0}, bounds: {x: 0, y: 0, height: 0, width: 0},

View File

@@ -30,9 +30,11 @@ import {plugin} from '../index';
import {Glyph} from 'flipper'; import {Glyph} from 'flipper';
import {head} from 'lodash'; import {head} from 'lodash';
import {reverse} from 'lodash/fp'; import {reverse} from 'lodash/fp';
import {Dropdown, Menu} from 'antd'; import {Dropdown, Menu, Typography} from 'antd';
import {UIDebuggerMenuItem} from './util/UIDebuggerMenuItem'; import {UIDebuggerMenuItem} from './util/UIDebuggerMenuItem';
const {Text} = Typography;
export function Tree2({ export function Tree2({
nodes, nodes,
rootId, rootId,
@@ -168,11 +170,34 @@ function TreeItemContainer({
/> />
{nodeIcon(treeNode)} {nodeIcon(treeNode)}
<HighlightedText text={treeNode.name} /> <HighlightedText text={treeNode.name} />
<InlineAttributes attributes={treeNode.inlineAttributes} />
</TreeItem> </TreeItem>
</ContextMenu> </ContextMenu>
); );
} }
const TreeAttributeContainer = styled(Text)({
color: theme.textColorSecondary,
fontWeight: 300,
marginLeft: 5,
fontSize: 12,
});
function InlineAttributes({attributes}: {attributes: Record<string, string>}) {
return (
<>
{Object.entries(attributes ?? {}).map(([key, value]) => (
<>
<TreeAttributeContainer key={key}>
<span style={{color: theme.warningColor}}>{key}</span>
<span>={value}</span>
</TreeAttributeContainer>
</>
))}
</>
);
}
function useIsHovered(nodeId: Id) { function useIsHovered(nodeId: Id) {
const instance = usePlugin(plugin); const instance = usePlugin(plugin);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
@@ -200,7 +225,7 @@ const TreeItem = styled.li<{
isSelected: boolean; isSelected: boolean;
}>(({item, isHovered, isSelected}) => ({ }>(({item, isHovered, isSelected}) => ({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'baseline',
height: '26px', height: '26px',
paddingLeft: `${(item.depth + 1) * renderDepthOffset}px`, paddingLeft: `${(item.depth + 1) * renderDepthOffset}px`,
borderWidth: '1px', borderWidth: '1px',

View File

@@ -67,6 +67,7 @@ export type UINode = {
qualifiedName: string; qualifiedName: string;
name: string; name: string;
attributes: Record<MetadataId, Inspectable>; attributes: Record<MetadataId, Inspectable>;
inlineAttributes: Record<string, string>;
children: Id[]; children: Id[];
bounds: Bounds; bounds: Bounds;
tags: Tag[]; tags: Tag[];