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
|
||||
implementation project(':android')
|
||||
implementation deps.lithoCore
|
||||
implementation deps.lithoEditorCore
|
||||
implementation deps.lithoSectionsDebug
|
||||
implementation deps.lithoSectionsCore
|
||||
implementation deps.lithoWidget
|
||||
|
||||
@@ -73,11 +73,7 @@ public class DataUtils {
|
||||
props.put(f.getName(), InspectorValue.immutable(description));
|
||||
}
|
||||
} else {
|
||||
if (isTypeMutable(f.getType())) {
|
||||
props.put(f.getName(), InspectorValue.mutable(f.get(node)));
|
||||
} else {
|
||||
props.put(f.getName(), InspectorValue.immutable(f.get(node)));
|
||||
}
|
||||
props.put(f.getName(), FlipperEditor.makeFlipperField(node, f));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -93,7 +89,7 @@ public class DataUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static FlipperObject getStateData(Object node, StateContainer stateContainer) throws Exception {
|
||||
static FlipperObject getStateData(StateContainer stateContainer) {
|
||||
if (stateContainer == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -106,11 +102,7 @@ public class DataUtils {
|
||||
|
||||
final State annotation = f.getAnnotation(State.class);
|
||||
if (annotation != null) {
|
||||
if (DataUtils.isTypeMutable(f.getType())) {
|
||||
state.put(f.getName(), InspectorValue.mutable(f.get(stateContainer)));
|
||||
} else {
|
||||
state.put(f.getName(), InspectorValue.immutable(f.get(stateContainer)));
|
||||
}
|
||||
state.put(f.getName(), FlipperEditor.makeFlipperField(stateContainer, f));
|
||||
hasState = true;
|
||||
}
|
||||
}
|
||||
@@ -118,23 +110,6 @@ public class DataUtils {
|
||||
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) {
|
||||
int color = 0;
|
||||
if (d instanceof ColorDrawable) {
|
||||
|
||||
@@ -58,7 +58,7 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
|
||||
|
||||
for (Pair<String[], FlipperDynamic> override : overrides) {
|
||||
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) {
|
||||
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
|
||||
private static FlipperObject getStateData(DebugComponent node) throws Exception {
|
||||
return DataUtils.getStateData(node, node.getStateContainer());
|
||||
private static FlipperObject getStateData(DebugComponent node) {
|
||||
return DataUtils.getStateData(node.getStateContainer());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -502,31 +502,13 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
|
||||
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 {
|
||||
final Field field = o.getClass().getDeclaredField(key);
|
||||
field.setAccessible(true);
|
||||
final Field field = o.getClass().getDeclaredField(path[1]);
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ public class DebugSectionDescriptor extends NodeDescriptor<DebugSection> {
|
||||
return DataUtils.getPropData(section);
|
||||
}
|
||||
|
||||
private static @Nullable FlipperObject getStateData(DebugSection node) throws Exception {
|
||||
return DataUtils.getStateData(node, node.getStateContainer());
|
||||
private static @Nullable FlipperObject getStateData(DebugSection node) {
|
||||
return DataUtils.getStateData(node.getStateContainer());
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user