Wire Editor to Flipper
Summary: This diff implements the integration between a Litho Editor and Flipper. It does so by converting the Editor format to FlipperObject, and then converting it back from FlipperDynamic. This conversion works for both `State` and `Prop`. We already provide default implementations for primitive + wrapped types and String, so the functionality should match the existing one. Reviewed By: passy, Katalune Differential Revision: D22455220 fbshipit-source-id: f7f633765f3d997ce6de09d2c1277019e72c0802
This commit is contained in:
committed by
Facebook GitHub Bot
parent
1784eb78d9
commit
10f9a48540
@@ -21,6 +21,7 @@ android {
|
|||||||
compileOnly deps.lithoAnnotations
|
compileOnly deps.lithoAnnotations
|
||||||
implementation project(':android')
|
implementation project(':android')
|
||||||
implementation deps.lithoCore
|
implementation deps.lithoCore
|
||||||
|
implementation deps.lithoEditorCore
|
||||||
implementation deps.lithoSectionsDebug
|
implementation deps.lithoSectionsDebug
|
||||||
implementation deps.lithoSectionsCore
|
implementation deps.lithoSectionsCore
|
||||||
implementation deps.lithoWidget
|
implementation deps.lithoWidget
|
||||||
|
|||||||
@@ -73,11 +73,7 @@ public class DataUtils {
|
|||||||
props.put(f.getName(), InspectorValue.immutable(description));
|
props.put(f.getName(), InspectorValue.immutable(description));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isTypeMutable(f.getType())) {
|
props.put(f.getName(), FlipperEditor.makeFlipperField(node, f));
|
||||||
props.put(f.getName(), InspectorValue.mutable(f.get(node)));
|
|
||||||
} else {
|
|
||||||
props.put(f.getName(), InspectorValue.immutable(f.get(node)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -93,7 +89,7 @@ public class DataUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
static FlipperObject getStateData(Object node, StateContainer stateContainer) throws Exception {
|
static FlipperObject getStateData(StateContainer stateContainer) {
|
||||||
if (stateContainer == null) {
|
if (stateContainer == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -106,11 +102,7 @@ public class DataUtils {
|
|||||||
|
|
||||||
final State annotation = f.getAnnotation(State.class);
|
final State annotation = f.getAnnotation(State.class);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
if (DataUtils.isTypeMutable(f.getType())) {
|
state.put(f.getName(), FlipperEditor.makeFlipperField(stateContainer, f));
|
||||||
state.put(f.getName(), InspectorValue.mutable(f.get(stateContainer)));
|
|
||||||
} else {
|
|
||||||
state.put(f.getName(), InspectorValue.immutable(f.get(stateContainer)));
|
|
||||||
}
|
|
||||||
hasState = true;
|
hasState = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,23 +110,6 @@ public class DataUtils {
|
|||||||
return hasState ? state.build() : null;
|
return hasState ? state.build() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isTypeMutable(Class<?> type) {
|
|
||||||
if (type == int.class || type == Integer.class) {
|
|
||||||
return true;
|
|
||||||
} else if (type == long.class || type == Long.class) {
|
|
||||||
return true;
|
|
||||||
} else if (type == float.class || type == Float.class) {
|
|
||||||
return true;
|
|
||||||
} else if (type == double.class || type == Double.class) {
|
|
||||||
return true;
|
|
||||||
} else if (type == boolean.class || type == Boolean.class) {
|
|
||||||
return true;
|
|
||||||
} else if (type.isAssignableFrom(String.class)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static com.facebook.flipper.plugins.inspector.InspectorValue fromDrawable(Drawable d) {
|
static com.facebook.flipper.plugins.inspector.InspectorValue fromDrawable(Drawable d) {
|
||||||
int color = 0;
|
int color = 0;
|
||||||
if (d instanceof ColorDrawable) {
|
if (d instanceof ColorDrawable) {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
|
|||||||
|
|
||||||
for (Pair<String[], FlipperDynamic> override : overrides) {
|
for (Pair<String[], FlipperDynamic> override : overrides) {
|
||||||
if (override.first[0].equals("Props")) {
|
if (override.first[0].equals("Props")) {
|
||||||
applyReflectiveOverride(component, override.first[1], override.second);
|
applyReflectiveOverride(component, override.first, override.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
|
|||||||
|
|
||||||
for (Pair<String[], FlipperDynamic> override : overrides) {
|
for (Pair<String[], FlipperDynamic> override : overrides) {
|
||||||
if (override.first[0].equals("State")) {
|
if (override.first[0].equals("State")) {
|
||||||
applyReflectiveOverride(stateContainer, override.first[1], override.second);
|
applyReflectiveOverride(stateContainer, override.first, override.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,8 +278,8 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static FlipperObject getStateData(DebugComponent node) throws Exception {
|
private static FlipperObject getStateData(DebugComponent node) {
|
||||||
return DataUtils.getStateData(node, node.getStateContainer());
|
return DataUtils.getStateData(node.getStateContainer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -502,31 +502,13 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
|
|||||||
return YogaEdge.valueOf(s.toUpperCase());
|
return YogaEdge.valueOf(s.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyReflectiveOverride(Object o, String key, FlipperDynamic dynamic) {
|
// The path follows the pattern (Props|State)/field/(field|index)*
|
||||||
|
private static void applyReflectiveOverride(
|
||||||
|
Object o, final String[] path, final FlipperDynamic dynamic) {
|
||||||
try {
|
try {
|
||||||
final Field field = o.getClass().getDeclaredField(key);
|
final Field field = o.getClass().getDeclaredField(path[1]);
|
||||||
field.setAccessible(true);
|
FlipperEditor.updateComponent(path, field, o, dynamic);
|
||||||
|
|
||||||
final Class type = field.getType();
|
|
||||||
|
|
||||||
Object value = null;
|
|
||||||
if (type == int.class || type == Integer.class) {
|
|
||||||
value = dynamic.asInt();
|
|
||||||
} else if (type == long.class || type == Long.class) {
|
|
||||||
value = dynamic.asLong();
|
|
||||||
} else if (type == float.class || type == Float.class) {
|
|
||||||
value = dynamic.asFloat();
|
|
||||||
} else if (type == double.class || type == Double.class) {
|
|
||||||
value = dynamic.asDouble();
|
|
||||||
} else if (type == boolean.class || type == Boolean.class) {
|
|
||||||
value = dynamic.asBoolean();
|
|
||||||
} else if (type.isAssignableFrom(String.class)) {
|
|
||||||
value = dynamic.asString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null) {
|
|
||||||
field.set(o, value);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ public class DebugSectionDescriptor extends NodeDescriptor<DebugSection> {
|
|||||||
return DataUtils.getPropData(section);
|
return DataUtils.getPropData(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable FlipperObject getStateData(DebugSection node) throws Exception {
|
private static @Nullable FlipperObject getStateData(DebugSection node) {
|
||||||
return DataUtils.getStateData(node, node.getStateContainer());
|
return DataUtils.getStateData(node.getStateContainer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.flipper.plugins.litho;
|
||||||
|
|
||||||
|
import com.facebook.flipper.core.FlipperArray;
|
||||||
|
import com.facebook.flipper.core.FlipperDynamic;
|
||||||
|
import com.facebook.flipper.core.FlipperObject;
|
||||||
|
import com.facebook.flipper.core.FlipperValue;
|
||||||
|
import com.facebook.flipper.plugins.inspector.InspectorValue;
|
||||||
|
import com.facebook.litho.editor.EditorRegistry;
|
||||||
|
import com.facebook.litho.editor.model.EditorArray;
|
||||||
|
import com.facebook.litho.editor.model.EditorBool;
|
||||||
|
import com.facebook.litho.editor.model.EditorNumber;
|
||||||
|
import com.facebook.litho.editor.model.EditorShape;
|
||||||
|
import com.facebook.litho.editor.model.EditorString;
|
||||||
|
import com.facebook.litho.editor.model.EditorValue;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for making Litho Editor compatible with Flipper.
|
||||||
|
*
|
||||||
|
* <p>It provides methods to convert from FlipperDynamic, and to provide the description of a Prop
|
||||||
|
* or State as a FlipperObject, FlipperArray or FlipperValue.
|
||||||
|
*/
|
||||||
|
public class FlipperEditor {
|
||||||
|
/**
|
||||||
|
* Uses an editor to create a FlipperObject, FlipperArray or FlipperValue to describe it. If no
|
||||||
|
* editor is available then it returns the class name.
|
||||||
|
*/
|
||||||
|
public static Object makeFlipperField(Object node, Field f) {
|
||||||
|
Class<?> type = f.getType();
|
||||||
|
final EditorValue editorValue = EditorRegistry.read(type, f, node);
|
||||||
|
if (editorValue != null) {
|
||||||
|
return intoFlipper(editorValue);
|
||||||
|
} else {
|
||||||
|
return InspectorValue.immutable(type.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses an editor to update a field nested in an object path with the value of a FlipperDynamic.
|
||||||
|
* If no editor is available then it returns null.
|
||||||
|
*
|
||||||
|
* <p>The path as defined by Flipper starts with either "Props" or "State" followed by the call
|
||||||
|
* chain into the object. Fields retain their name, positions in an array use their index.
|
||||||
|
*/
|
||||||
|
public static @Nullable Boolean updateComponent(
|
||||||
|
String[] path, Field field, Object o, FlipperDynamic dynamic) {
|
||||||
|
Object raw = dynamic.raw();
|
||||||
|
EditorValue edit;
|
||||||
|
if (raw instanceof String) {
|
||||||
|
edit = EditorValue.string((String) raw);
|
||||||
|
} else if (raw instanceof Number) {
|
||||||
|
edit = EditorValue.number(((Number) raw));
|
||||||
|
} else if (raw instanceof Boolean) {
|
||||||
|
edit = EditorValue.bool((Boolean) raw);
|
||||||
|
} else {
|
||||||
|
edit = EditorValue.string(raw.toString());
|
||||||
|
}
|
||||||
|
for (int i = path.length - 1; i > 0; i--) {
|
||||||
|
HashMap<String, EditorValue> content = new HashMap<>();
|
||||||
|
content.put(path[i], edit);
|
||||||
|
edit = EditorValue.shape(content);
|
||||||
|
}
|
||||||
|
return EditorRegistry.write(field.getType(), field, o, edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts into one of FlipperValue, FlipperObject, or FlipperArray */
|
||||||
|
public static Object intoFlipper(EditorValue editorValue) {
|
||||||
|
return editorValue.when(
|
||||||
|
new EditorValue.EditorVisitor<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object isShape(EditorShape object) {
|
||||||
|
FlipperObject.Builder bb = new FlipperObject.Builder();
|
||||||
|
|
||||||
|
for (Map.Entry<String, EditorValue> entry : object.value.entrySet()) {
|
||||||
|
bb.put(entry.getKey(), intoFlipper(entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bb.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object isArray(EditorArray array) {
|
||||||
|
FlipperArray.Builder bb = new FlipperArray.Builder();
|
||||||
|
|
||||||
|
for (EditorValue entry : array.value) {
|
||||||
|
Object flipper = intoFlipper(entry);
|
||||||
|
if (flipper instanceof FlipperValue) {
|
||||||
|
bb.put((FlipperValue) flipper);
|
||||||
|
} else if (flipper instanceof FlipperObject) {
|
||||||
|
bb.put((FlipperObject) flipper);
|
||||||
|
} else if (flipper instanceof FlipperArray) {
|
||||||
|
bb.put((FlipperArray) flipper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bb.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object isNumber(EditorNumber number) {
|
||||||
|
return InspectorValue.mutable(number.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object isString(EditorString string) {
|
||||||
|
return InspectorValue.mutable(string.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object isBool(EditorBool bool) {
|
||||||
|
return InspectorValue.mutable(bool.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,10 @@ public class FlipperDynamic {
|
|||||||
mObject = object;
|
mObject = object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object raw() {
|
||||||
|
return mObject;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable String asString() {
|
public @Nullable String asString() {
|
||||||
if (mObject == null) {
|
if (mObject == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ ext.deps = [
|
|||||||
// Litho
|
// Litho
|
||||||
lithoAnnotations : "com.facebook.litho:litho-annotations:$LITHO_VERSION",
|
lithoAnnotations : "com.facebook.litho:litho-annotations:$LITHO_VERSION",
|
||||||
lithoCore : "com.facebook.litho:litho-core:$LITHO_VERSION",
|
lithoCore : "com.facebook.litho:litho-core:$LITHO_VERSION",
|
||||||
|
lithoEditorCore : "com.facebook.litho:litho-editor-core:$LITHO_VERSION",
|
||||||
lithoSectionsAnnotations: "com.facebook.litho:litho-sections-annotations:$LITHO_VERSION",
|
lithoSectionsAnnotations: "com.facebook.litho:litho-sections-annotations:$LITHO_VERSION",
|
||||||
lithoSectionsDebug : "com.facebook.litho:litho-sections-debug:$LITHO_VERSION",
|
lithoSectionsDebug : "com.facebook.litho:litho-sections-debug:$LITHO_VERSION",
|
||||||
lithoSectionsCore : "com.facebook.litho:litho-sections-core:$LITHO_VERSION",
|
lithoSectionsCore : "com.facebook.litho:litho-sections-core:$LITHO_VERSION",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ POM_DEVELOPER_NAME=facebook
|
|||||||
POM_ISSUES_URL = 'https://github.com/facebook/flipper/issues/'
|
POM_ISSUES_URL = 'https://github.com/facebook/flipper/issues/'
|
||||||
|
|
||||||
# Shared version numbers
|
# Shared version numbers
|
||||||
LITHO_VERSION=0.36.0
|
LITHO_VERSION=0.37.1
|
||||||
ANDROIDX_VERSION=1.1.0
|
ANDROIDX_VERSION=1.1.0
|
||||||
KOTLIN_VERSION=1.3.71
|
KOTLIN_VERSION=1.3.71
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user