Merge branch 'feature/listview' into 'master'
Feature/listview See merge request !10
This commit is contained in:
commit
19c700705b
@ -22,15 +22,16 @@ import android.widget.FrameLayout;
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import pub.doric.dev.DevPanel;
|
||||||
import pub.doric.DoricContext;
|
import pub.doric.DoricContext;
|
||||||
import pub.doric.DoricDriver;
|
import pub.doric.DoricDriver;
|
||||||
import pub.doric.dev.DevPanel;
|
|
||||||
import pub.doric.dev.LocalServer;
|
import pub.doric.dev.LocalServer;
|
||||||
import pub.doric.dev.event.EnterDebugEvent;
|
import pub.doric.dev.event.EnterDebugEvent;
|
||||||
import pub.doric.dev.event.QuitDebugEvent;
|
import pub.doric.dev.event.QuitDebugEvent;
|
||||||
@ -49,9 +50,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
doricContext = DoricContext.create(this, DoricUtils.readAssetFile("demo/Snake.js"), "test");
|
doricContext = DoricContext.create(this, DoricUtils.readAssetFile("demo/ListDemo.js"), "test");
|
||||||
doricContext.init(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
doricContext.init(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
// doricContext.callEntity("log");
|
|
||||||
doricContext.getRootNode().setRootView((FrameLayout) findViewById(R.id.root));
|
doricContext.getRootNode().setRootView((FrameLayout) findViewById(R.id.root));
|
||||||
|
|
||||||
LocalServer localServer = new LocalServer(getApplicationContext(), 8910);
|
LocalServer localServer = new LocalServer(getApplicationContext(), 8910);
|
||||||
|
@ -20,6 +20,8 @@ import android.text.TextUtils;
|
|||||||
import pub.doric.plugin.ShaderPlugin;
|
import pub.doric.plugin.ShaderPlugin;
|
||||||
import pub.doric.shader.HLayoutNode;
|
import pub.doric.shader.HLayoutNode;
|
||||||
import pub.doric.shader.ImageNode;
|
import pub.doric.shader.ImageNode;
|
||||||
|
import pub.doric.shader.list.ListItemNode;
|
||||||
|
import pub.doric.shader.list.ListNode;
|
||||||
import pub.doric.shader.RootNode;
|
import pub.doric.shader.RootNode;
|
||||||
import pub.doric.shader.StackNode;
|
import pub.doric.shader.StackNode;
|
||||||
import pub.doric.shader.TextNode;
|
import pub.doric.shader.TextNode;
|
||||||
@ -66,6 +68,8 @@ public class DoricRegistry {
|
|||||||
this.registerViewNode(StackNode.class);
|
this.registerViewNode(StackNode.class);
|
||||||
this.registerViewNode(VLayoutNode.class);
|
this.registerViewNode(VLayoutNode.class);
|
||||||
this.registerViewNode(HLayoutNode.class);
|
this.registerViewNode(HLayoutNode.class);
|
||||||
|
this.registerViewNode(ListNode.class);
|
||||||
|
this.registerViewNode(ListItemNode.class);
|
||||||
initRegistry(this);
|
initRegistry(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,15 @@ public class SettableFuture<T> {
|
|||||||
return mResult;
|
return mResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
try {
|
||||||
|
mReadyLatch.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
public static class TimeoutException extends RuntimeException {
|
public static class TimeoutException extends RuntimeException {
|
||||||
|
|
||||||
public TimeoutException() {
|
public TimeoutException() {
|
||||||
|
@ -197,7 +197,7 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
|
|||||||
values.add(DoricUtils.toJavaValue(arg));
|
values.add(DoricUtils.toJavaValue(arg));
|
||||||
}
|
}
|
||||||
return mDoricJSE.invokeMethod(DoricConstant.GLOBAL_DORIC, method,
|
return mDoricJSE.invokeMethod(DoricConstant.GLOBAL_DORIC, method,
|
||||||
values.toArray(new JavaValue[values.size()]), true);
|
values.toArray(new JavaValue[values.size()]), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
package pub.doric.engine.remote;
|
package pub.doric.engine.remote;
|
||||||
|
|
||||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||||
|
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||||
import com.github.pengfeizhou.jscore.JavaFunction;
|
import com.github.pengfeizhou.jscore.JavaFunction;
|
||||||
import com.github.pengfeizhou.jscore.JavaValue;
|
import com.github.pengfeizhou.jscore.JavaValue;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonPrimitive;
|
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
@ -25,14 +24,10 @@ import okhttp3.Response;
|
|||||||
import okhttp3.WebSocket;
|
import okhttp3.WebSocket;
|
||||||
import okhttp3.WebSocketListener;
|
import okhttp3.WebSocketListener;
|
||||||
import pub.doric.dev.event.QuitDebugEvent;
|
import pub.doric.dev.event.QuitDebugEvent;
|
||||||
import pub.doric.utils.DoricUtils;
|
|
||||||
|
|
||||||
public class RemoteJSExecutor {
|
public class RemoteJSExecutor {
|
||||||
|
|
||||||
private final WebSocket webSocket;
|
private final WebSocket webSocket;
|
||||||
|
|
||||||
private final Map<String, JavaFunction> globalFunctions = new HashMap<>();
|
private final Map<String, JavaFunction> globalFunctions = new HashMap<>();
|
||||||
|
|
||||||
private JSDecoder temp;
|
private JSDecoder temp;
|
||||||
|
|
||||||
public RemoteJSExecutor() {
|
public RemoteJSExecutor() {
|
||||||
@ -41,7 +36,7 @@ public class RemoteJSExecutor {
|
|||||||
.readTimeout(10, TimeUnit.SECONDS)
|
.readTimeout(10, TimeUnit.SECONDS)
|
||||||
.writeTimeout(10, TimeUnit.SECONDS)
|
.writeTimeout(10, TimeUnit.SECONDS)
|
||||||
.build();
|
.build();
|
||||||
final Request request = new Request.Builder().url("ws://192.168.25.175:2080").build();
|
final Request request = new Request.Builder().url("ws://192.168.24.79:2080").build();
|
||||||
|
|
||||||
final Thread current = Thread.currentThread();
|
final Thread current = Thread.currentThread();
|
||||||
webSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
|
webSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
|
||||||
@ -67,55 +62,28 @@ public class RemoteJSExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
|
public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
|
||||||
JsonElement je = DoricUtils.gson.fromJson(text, JsonElement.class);
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject(text);
|
||||||
if (je instanceof JsonObject) {
|
String cmd = jsonObject.optString("cmd");
|
||||||
JsonObject jo = ((JsonObject) je);
|
|
||||||
String cmd = jo.get("cmd").getAsString();
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case "injectGlobalJSFunction": {
|
case "injectGlobalJSFunction": {
|
||||||
String name = jo.get("name").getAsString();
|
String name = jsonObject.optString("name");
|
||||||
JsonArray arguments = jo.getAsJsonArray("arguments");
|
JSONArray arguments = jsonObject.optJSONArray("arguments");
|
||||||
JSDecoder[] decoders = new JSDecoder[arguments.size()];
|
assert arguments != null;
|
||||||
for (int i = 0; i < arguments.size(); i++) {
|
JSDecoder[] decoders = new JSDecoder[arguments.length()];
|
||||||
if (arguments.get(i).isJsonPrimitive()) {
|
for (int i = 0; i < arguments.length(); i++) {
|
||||||
JsonPrimitive jp = ((JsonPrimitive) arguments.get(i));
|
Object o = arguments.get(i);
|
||||||
if (jp.isNumber()) {
|
decoders[i] = new JSDecoder(new ValueBuilder(o).build());
|
||||||
ValueBuilder vb = new ValueBuilder(jp.getAsNumber());
|
|
||||||
JSDecoder decoder = new JSDecoder(vb.build());
|
|
||||||
decoders[i] = decoder;
|
|
||||||
} else if (jp.isBoolean()) {
|
|
||||||
ValueBuilder vb = new ValueBuilder(jp.getAsBoolean());
|
|
||||||
JSDecoder decoder = new JSDecoder(vb.build());
|
|
||||||
decoders[i] = decoder;
|
|
||||||
} else if (jp.isString()) {
|
|
||||||
ValueBuilder vb = new ValueBuilder(jp.getAsString());
|
|
||||||
JSDecoder decoder = new JSDecoder(vb.build());
|
|
||||||
decoders[i] = decoder;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
ValueBuilder vb = new ValueBuilder(new JSONObject(DoricUtils.gson.toJson(arguments.get(i))));
|
|
||||||
JSDecoder decoder = new JSDecoder(vb.build());
|
|
||||||
decoders[i] = decoder;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
globalFunctions.get(name).exec(decoders);
|
globalFunctions.get(name).exec(decoders);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "invokeMethod": {
|
case "invokeMethod": {
|
||||||
try {
|
try {
|
||||||
Object result = jo.get("result");
|
Object result = jsonObject.opt("result");
|
||||||
ValueBuilder vb = new ValueBuilder(result);
|
ValueBuilder vb = new ValueBuilder(result);
|
||||||
JSDecoder decoder = new JSDecoder(vb.build());
|
temp = new JSDecoder(vb.build());
|
||||||
temp = decoder;
|
|
||||||
System.out.println(result);
|
System.out.println(result);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
@ -125,6 +93,8 @@ public class RemoteJSExecutor {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -141,33 +111,29 @@ public class RemoteJSExecutor {
|
|||||||
|
|
||||||
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
|
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
|
||||||
globalFunctions.put(name, javaFunction);
|
globalFunctions.put(name, javaFunction);
|
||||||
|
webSocket.send(new JSONBuilder().put("cmd", "injectGlobalJSFunction")
|
||||||
JsonObject jo = new JsonObject();
|
.put("name", name).toString()
|
||||||
jo.addProperty("cmd", "injectGlobalJSFunction");
|
);
|
||||||
jo.addProperty("name", name);
|
|
||||||
webSocket.send(DoricUtils.gson.toJson(jo));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void injectGlobalJSObject(String name, JavaValue javaValue) {
|
public void injectGlobalJSObject(String name, JavaValue javaValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) {
|
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) {
|
||||||
JsonObject jo = new JsonObject();
|
JSONArray jsonArray = new JSONArray();
|
||||||
jo.addProperty("cmd", "invokeMethod");
|
|
||||||
jo.addProperty("objectName", objectName);
|
|
||||||
jo.addProperty("functionName", functionName);
|
|
||||||
|
|
||||||
JsonArray ja = new JsonArray();
|
|
||||||
for (JavaValue javaValue : javaValues) {
|
for (JavaValue javaValue : javaValues) {
|
||||||
JsonObject argument = new JsonObject();
|
jsonArray.put(new JSONBuilder()
|
||||||
argument.addProperty("type", javaValue.getType());
|
.put("type", javaValue.getType())
|
||||||
argument.addProperty("value", javaValue.getValue());
|
.put("value", javaValue.getValue())
|
||||||
ja.add(argument);
|
.toJSONObject());
|
||||||
}
|
}
|
||||||
jo.add("javaValues", ja);
|
webSocket.send(new JSONBuilder()
|
||||||
|
.put("cmd", "invokeMethod")
|
||||||
jo.addProperty("hashKey", hashKey);
|
.put("objectName", objectName)
|
||||||
webSocket.send(DoricUtils.gson.toJson(jo));
|
.put("functionName", functionName)
|
||||||
|
.put("javaValues", jsonArray)
|
||||||
|
.put("hashKey", hashKey)
|
||||||
|
.toString());
|
||||||
|
|
||||||
LockSupport.park(Thread.currentThread());
|
LockSupport.park(Thread.currentThread());
|
||||||
return temp;
|
return temp;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package pub.doric.plugin;
|
package pub.doric.plugin;
|
||||||
|
|
||||||
import pub.doric.DoricContext;
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.async.AsyncResult;
|
||||||
import pub.doric.extension.bridge.DoricMethod;
|
import pub.doric.extension.bridge.DoricMethod;
|
||||||
import pub.doric.extension.bridge.DoricPlugin;
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
import pub.doric.utils.DoricLog;
|
import pub.doric.utils.DoricLog;
|
||||||
@ -49,7 +50,23 @@ public class ShaderPlugin extends DoricJavaPlugin {
|
|||||||
rootNode.render(jsObject.getProperty("props").asObject());
|
rootNode.render(jsObject.getProperty("props").asObject());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, ThreadMode.UI);
|
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Object result) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
DoricLog.e("Shader.render:error%s", t.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinish() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
DoricLog.e("Shader.render:error%s", e.getLocalizedMessage());
|
DoricLog.e("Shader.render:error%s", e.getLocalizedMessage());
|
||||||
|
@ -15,160 +15,141 @@
|
|||||||
*/
|
*/
|
||||||
package pub.doric.shader;
|
package pub.doric.shader;
|
||||||
|
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import pub.doric.DoricContext;
|
import pub.doric.DoricContext;
|
||||||
import pub.doric.utils.DoricUtils;
|
|
||||||
|
|
||||||
import com.github.pengfeizhou.jscore.JSArray;
|
import com.github.pengfeizhou.jscore.JSArray;
|
||||||
import com.github.pengfeizhou.jscore.JSObject;
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
import com.github.pengfeizhou.jscore.JSValue;
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Description: com.github.penfeizhou.doric.widget
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
* @Author: pengfei.zhou
|
* @Author: pengfei.zhou
|
||||||
* @CreateDate: 2019-07-20
|
* @CreateDate: 2019-07-20
|
||||||
*/
|
*/
|
||||||
public abstract class GroupNode<F extends ViewGroup> extends ViewNode<F> {
|
public abstract class GroupNode<F extends ViewGroup> extends SuperNode<F> {
|
||||||
private Map<String, ViewNode> mChildrenNode = new HashMap<>();
|
private ArrayList<ViewNode> mChildNodes = new ArrayList<>();
|
||||||
private SparseArray<ViewNode> mIndexInfo = new SparseArray<>();
|
private ArrayList<String> mChildViewIds = new ArrayList<>();
|
||||||
|
|
||||||
|
protected boolean mReusable = false;
|
||||||
|
|
||||||
public GroupNode(DoricContext doricContext) {
|
public GroupNode(DoricContext doricContext) {
|
||||||
super(doricContext);
|
super(doricContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blend(F view, ViewGroup.LayoutParams layoutParams, String name, JSValue prop) {
|
protected void blend(F view, String name, JSValue prop) {
|
||||||
super.blend(view, layoutParams, name, prop);
|
if ("children".equals(name)) {
|
||||||
switch (name) {
|
JSArray ids = prop.asArray();
|
||||||
case "children":
|
mChildViewIds.clear();
|
||||||
JSArray jsArray = prop.asArray();
|
for (int i = 0; i < ids.size(); i++) {
|
||||||
int i;
|
mChildViewIds.add(ids.get(i).asString().value());
|
||||||
List<ViewNode> tobeRemoved = new ArrayList<>();
|
|
||||||
for (i = 0; i < jsArray.size(); i++) {
|
|
||||||
JSValue jsValue = jsArray.get(i);
|
|
||||||
if (!jsValue.isObject()) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
JSObject childObj = jsValue.asObject();
|
|
||||||
String type = childObj.getProperty("type").asString().value();
|
|
||||||
String id = childObj.getProperty("id").asString().value();
|
|
||||||
ViewNode child = mChildrenNode.get(id);
|
|
||||||
if (child == null) {
|
|
||||||
child = ViewNode.create(getDoricContext(), type);
|
|
||||||
child.index = i;
|
|
||||||
child.mParent = this;
|
|
||||||
child.mId = id;
|
|
||||||
mChildrenNode.put(id, child);
|
|
||||||
} else {
|
} else {
|
||||||
if (i != child.index) {
|
super.blend(view, name, prop);
|
||||||
mIndexInfo.remove(i);
|
|
||||||
child.index = i;
|
|
||||||
mView.removeView(child.getView());
|
|
||||||
}
|
}
|
||||||
tobeRemoved.remove(child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewNode node = mIndexInfo.get(i);
|
@Override
|
||||||
|
public void blend(JSObject jsObject) {
|
||||||
if (node != null && node != child) {
|
super.blend(jsObject);
|
||||||
mView.removeViewAt(i);
|
configChildNode();
|
||||||
mIndexInfo.remove(i);
|
|
||||||
tobeRemoved.add(node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewGroup.LayoutParams params = child.getLayoutParams();
|
private void configChildNode() {
|
||||||
if (params == null) {
|
for (int idx = 0; idx < mChildViewIds.size(); idx++) {
|
||||||
params = generateDefaultLayoutParams();
|
String id = mChildViewIds.get(idx);
|
||||||
|
JSObject model = getSubModel(id);
|
||||||
|
String type = model.getProperty("type").asString().value();
|
||||||
|
if (idx < mChildNodes.size()) {
|
||||||
|
ViewNode oldNode = mChildNodes.get(idx);
|
||||||
|
if (id.equals(oldNode.getId())) {
|
||||||
|
//The same,skip
|
||||||
|
} else {
|
||||||
|
if (mReusable) {
|
||||||
|
if (oldNode.getType().equals(type)) {
|
||||||
|
//Same type,can be reused
|
||||||
|
oldNode.setId(id);
|
||||||
|
oldNode.blend(model.getProperty("props").asObject());
|
||||||
|
} else {
|
||||||
|
//Replace this view
|
||||||
|
mChildNodes.remove(idx);
|
||||||
|
mView.removeView(oldNode.getDoricLayer());
|
||||||
|
ViewNode newNode = ViewNode.create(getDoricContext(), type);
|
||||||
|
newNode.setId(id);
|
||||||
|
if (newNode instanceof GroupNode) {
|
||||||
|
((GroupNode) newNode).mReusable = this.mReusable;
|
||||||
}
|
}
|
||||||
child.blend(childObj.getProperty("props").asObject(), params);
|
newNode.init(this);
|
||||||
if (mIndexInfo.get(i) == null) {
|
newNode.blend(model.getProperty("props").asObject());
|
||||||
mView.addView(child.getView(), i, child.getLayoutParams());
|
mChildNodes.add(idx, newNode);
|
||||||
mIndexInfo.put(i, child);
|
mView.addView(newNode.getDoricLayer(), idx, newNode.getLayoutParams());
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
int count = mView.getChildCount();
|
//Find in remain nodes
|
||||||
while (i < count) {
|
int position = -1;
|
||||||
ViewNode node = mIndexInfo.get(i);
|
for (int start = idx + 1; start < mChildNodes.size(); start++) {
|
||||||
if (node != null) {
|
ViewNode node = mChildNodes.get(start);
|
||||||
mChildrenNode.remove(node.getId());
|
if (id.equals(node.getId())) {
|
||||||
mIndexInfo.remove(i);
|
//Found
|
||||||
tobeRemoved.remove(node);
|
position = start;
|
||||||
mView.removeView(node.getView());
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ViewNode node : tobeRemoved) {
|
|
||||||
mChildrenNode.remove(node.getId());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
super.blend(view, layoutParams, name, prop);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (position >= 0) {
|
||||||
|
//Found swap idx,position
|
||||||
|
ViewNode reused = mChildNodes.remove(position);
|
||||||
|
ViewNode abandoned = mChildNodes.remove(idx);
|
||||||
|
mChildNodes.set(idx, reused);
|
||||||
|
mChildNodes.set(position, abandoned);
|
||||||
|
//View swap index
|
||||||
|
mView.removeView(reused.getDoricLayer());
|
||||||
|
mView.addView(reused.getDoricLayer(), idx);
|
||||||
|
mView.removeView(abandoned.getDoricLayer());
|
||||||
|
mView.addView(abandoned.getDoricLayer(), position);
|
||||||
|
} else {
|
||||||
|
//Not found,insert
|
||||||
|
ViewNode newNode = ViewNode.create(getDoricContext(), type);
|
||||||
|
newNode.setId(id);
|
||||||
|
newNode.init(this);
|
||||||
|
newNode.blend(model.getProperty("props").asObject());
|
||||||
|
|
||||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
mChildNodes.add(idx, newNode);
|
||||||
return new ViewGroup.LayoutParams(0, 0);
|
mView.addView(newNode.getDoricLayer(), idx, newNode.getLayoutParams());
|
||||||
}
|
|
||||||
|
|
||||||
protected void blendChild(ViewNode viewNode, JSObject jsObject) {
|
|
||||||
|
|
||||||
|
|
||||||
JSValue jsValue = jsObject.getProperty("margin");
|
|
||||||
JSValue widthSpec = jsObject.getProperty("widthSpec");
|
|
||||||
JSValue heightSpec = jsObject.getProperty("widthSpec");
|
|
||||||
|
|
||||||
ViewGroup.LayoutParams layoutParams = viewNode.getLayoutParams();
|
|
||||||
if (widthSpec.isNumber()) {
|
|
||||||
switch (widthSpec.asNumber().toInt()) {
|
|
||||||
case 1:
|
|
||||||
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (heightSpec.isNumber()) {
|
}
|
||||||
switch (heightSpec.asNumber().toInt()) {
|
} else {
|
||||||
case 1:
|
//Insert
|
||||||
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
ViewNode newNode = ViewNode.create(getDoricContext(), type);
|
||||||
break;
|
newNode.setId(id);
|
||||||
case 2:
|
if (newNode instanceof GroupNode) {
|
||||||
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
((GroupNode) newNode).mReusable = this.mReusable;
|
||||||
break;
|
}
|
||||||
default:
|
newNode.init(this);
|
||||||
|
newNode.blend(model.getProperty("props").asObject());
|
||||||
|
mChildNodes.add(newNode);
|
||||||
|
mView.addView(newNode.getDoricLayer(), idx, newNode.getLayoutParams());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = mChildNodes.size();
|
||||||
|
for (int idx = mChildViewIds.size(); idx < size; idx++) {
|
||||||
|
ViewNode viewNode = mChildNodes.remove(mChildViewIds.size());
|
||||||
|
mView.removeView(viewNode.getDoricLayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blendSubNode(JSObject subProp) {
|
||||||
|
String subNodeId = subProp.getProperty("id").asString().value();
|
||||||
|
for (ViewNode node : mChildNodes) {
|
||||||
|
if (subNodeId.equals(node.getId())) {
|
||||||
|
node.blend(subProp.getProperty("props").asObject());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (jsValue.isObject() && layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
||||||
JSValue topVal = jsValue.asObject().getProperty("top");
|
|
||||||
if (topVal.isNumber()) {
|
|
||||||
((ViewGroup.MarginLayoutParams) layoutParams).topMargin = DoricUtils.dp2px(topVal.asNumber().toFloat());
|
|
||||||
}
|
|
||||||
JSValue leftVal = jsValue.asObject().getProperty("left");
|
|
||||||
if (leftVal.isNumber()) {
|
|
||||||
((ViewGroup.MarginLayoutParams) layoutParams).leftMargin = DoricUtils.dp2px(leftVal.asNumber().toFloat());
|
|
||||||
}
|
|
||||||
JSValue rightVal = jsValue.asObject().getProperty("right");
|
|
||||||
if (rightVal.isNumber()) {
|
|
||||||
((ViewGroup.MarginLayoutParams) layoutParams).rightMargin = DoricUtils.dp2px(rightVal.asNumber().toFloat());
|
|
||||||
}
|
|
||||||
JSValue bottomVal = jsValue.asObject().getProperty("bottom");
|
|
||||||
if (bottomVal.isNumber()) {
|
|
||||||
((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = DoricUtils.dp2px(bottomVal.asNumber().toFloat());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,8 @@ public class HLayoutNode extends LinearNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LinearLayout build(JSObject jsObject) {
|
protected LinearLayout build() {
|
||||||
LinearLayout linearLayout = super.build(jsObject);
|
LinearLayout linearLayout = super.build();
|
||||||
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
return linearLayout;
|
return linearLayout;
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,12 @@ public class ImageNode extends ViewNode<ImageView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ImageView build(JSObject jsObject) {
|
protected ImageView build() {
|
||||||
return new ImageView(getContext());
|
return new ImageView(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blend(ImageView view, ViewGroup.LayoutParams layoutParams, String name, JSValue prop) {
|
protected void blend(ImageView view, String name, JSValue prop) {
|
||||||
if ("imageUrl".equals(name)) {
|
if ("imageUrl".equals(name)) {
|
||||||
Glide.with(getContext()).load(prop.asString().value())
|
Glide.with(getContext()).load(prop.asString().value())
|
||||||
.listener(new RequestListener<Drawable>() {
|
.listener(new RequestListener<Drawable>() {
|
||||||
@ -67,7 +67,7 @@ public class ImageNode extends ViewNode<ImageView> {
|
|||||||
})
|
})
|
||||||
.into(view);
|
.into(view);
|
||||||
} else {
|
} else {
|
||||||
super.blend(view, layoutParams, name, prop);
|
super.blend(view, name, prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ public class LinearNode extends GroupNode<LinearLayout> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blendChild(ViewNode viewNode, JSObject layoutConfig) {
|
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject layoutConfig) {
|
||||||
super.blendChild(viewNode, layoutConfig);
|
super.blendSubLayoutConfig(viewNode, layoutConfig);
|
||||||
JSValue jsValue = layoutConfig.getProperty("alignment");
|
JSValue jsValue = layoutConfig.getProperty("alignment");
|
||||||
if (jsValue.isNumber()) {
|
if (jsValue.isNumber()) {
|
||||||
((LinearLayout.LayoutParams) viewNode.getLayoutParams()).gravity = jsValue.asNumber().toInt();
|
((LinearLayout.LayoutParams) viewNode.getLayoutParams()).gravity = jsValue.asNumber().toInt();
|
||||||
@ -54,12 +54,12 @@ public class LinearNode extends GroupNode<LinearLayout> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LinearLayout build(JSObject jsObject) {
|
protected LinearLayout build() {
|
||||||
return new LinearLayout(getContext());
|
return new LinearLayout(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blend(LinearLayout view, ViewGroup.LayoutParams params, String name, JSValue prop) {
|
protected void blend(LinearLayout view, String name, JSValue prop) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "space":
|
case "space":
|
||||||
ShapeDrawable shapeDrawable;
|
ShapeDrawable shapeDrawable;
|
||||||
@ -82,7 +82,7 @@ public class LinearNode extends GroupNode<LinearLayout> {
|
|||||||
view.setGravity(prop.asNumber().toInt());
|
view.setGravity(prop.asNumber().toInt());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.blend(view, params, name, prop);
|
super.blend(view, name, prop);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,12 +36,17 @@ public class RootNode extends StackNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView() {
|
public View getDoricLayer() {
|
||||||
return mView;
|
return mView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRootView(FrameLayout rootView) {
|
public void setRootView(FrameLayout rootView) {
|
||||||
this.mView = rootView;
|
this.mView = rootView;
|
||||||
|
mLayoutParams = rootView.getLayoutParams();
|
||||||
|
if (mLayoutParams == null) {
|
||||||
|
mLayoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
rootView.setLayoutParams(mLayoutParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -50,6 +55,6 @@ public class RootNode extends StackNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void render(JSObject props) {
|
public void render(JSObject props) {
|
||||||
blend(props, getLayoutParams());
|
blend(props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ public class StackNode extends GroupNode<FrameLayout> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blendChild(ViewNode viewNode, JSObject jsObject) {
|
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
|
||||||
super.blendChild(viewNode, jsObject);
|
super.blendSubLayoutConfig(viewNode, jsObject);
|
||||||
JSValue jsValue = jsObject.getProperty("alignment");
|
JSValue jsValue = jsObject.getProperty("alignment");
|
||||||
if (jsValue.isNumber()) {
|
if (jsValue.isNumber()) {
|
||||||
((FrameLayout.LayoutParams) viewNode.getLayoutParams()).gravity = jsValue.asNumber().toInt();
|
((FrameLayout.LayoutParams) viewNode.getLayoutParams()).gravity = jsValue.asNumber().toInt();
|
||||||
@ -45,18 +45,18 @@ public class StackNode extends GroupNode<FrameLayout> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FrameLayout build(JSObject jsObject) {
|
protected FrameLayout build() {
|
||||||
return new FrameLayout(getContext());
|
return new FrameLayout(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blend(FrameLayout view, ViewGroup.LayoutParams layoutParams, String name, JSValue prop) {
|
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "gravity":
|
case "gravity":
|
||||||
view.setForegroundGravity(prop.asNumber().toInt());
|
view.setForegroundGravity(prop.asNumber().toInt());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.blend(view, layoutParams, name, prop);
|
super.blend(view, name, prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
148
Android/doric/src/main/java/pub/doric/shader/SuperNode.java
Normal file
148
Android/doric/src/main/java/pub/doric/shader/SuperNode.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSArray;
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.utils.DoricUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: pub.doric.shader
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-13
|
||||||
|
*/
|
||||||
|
public abstract class SuperNode<V extends View> extends ViewNode<V> {
|
||||||
|
private Map<String, JSObject> subNodes = new HashMap<>();
|
||||||
|
|
||||||
|
public SuperNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||||
|
return new ViewGroup.LayoutParams(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(V view, String name, JSValue prop) {
|
||||||
|
if (name.equals("subviews")) {
|
||||||
|
if (prop.isArray()) {
|
||||||
|
JSArray subviews = prop.asArray();
|
||||||
|
for (int i = 0; i < subviews.size(); i++) {
|
||||||
|
JSObject subNode = subviews.get(i).asObject();
|
||||||
|
mixinSubNode(subNode);
|
||||||
|
blendSubNode(subNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mixinSubNode(JSObject subNode) {
|
||||||
|
String id = subNode.getProperty("id").asString().value();
|
||||||
|
JSObject targetNode = subNodes.get(id);
|
||||||
|
if (targetNode == null) {
|
||||||
|
subNodes.put(id, subNode);
|
||||||
|
} else {
|
||||||
|
mixin(subNode, targetNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSObject getSubModel(String id) {
|
||||||
|
return subNodes.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubModel(String id, JSObject model) {
|
||||||
|
subNodes.put(id, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSubModel() {
|
||||||
|
subNodes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void blendSubNode(JSObject subProperties);
|
||||||
|
|
||||||
|
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
|
||||||
|
JSValue margin = jsObject.getProperty("margin");
|
||||||
|
JSValue widthSpec = jsObject.getProperty("widthSpec");
|
||||||
|
JSValue heightSpec = jsObject.getProperty("heightSpec");
|
||||||
|
ViewGroup.LayoutParams layoutParams = viewNode.getLayoutParams();
|
||||||
|
if (widthSpec.isNumber()) {
|
||||||
|
switch (widthSpec.asNumber().toInt()) {
|
||||||
|
case 1:
|
||||||
|
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (heightSpec.isNumber()) {
|
||||||
|
switch (heightSpec.asNumber().toInt()) {
|
||||||
|
case 1:
|
||||||
|
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (margin.isObject() && layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||||
|
JSValue topVal = margin.asObject().getProperty("top");
|
||||||
|
if (topVal.isNumber()) {
|
||||||
|
((ViewGroup.MarginLayoutParams) layoutParams).topMargin = DoricUtils.dp2px(topVal.asNumber().toFloat());
|
||||||
|
}
|
||||||
|
JSValue leftVal = margin.asObject().getProperty("left");
|
||||||
|
if (leftVal.isNumber()) {
|
||||||
|
((ViewGroup.MarginLayoutParams) layoutParams).leftMargin = DoricUtils.dp2px(leftVal.asNumber().toFloat());
|
||||||
|
}
|
||||||
|
JSValue rightVal = margin.asObject().getProperty("right");
|
||||||
|
if (rightVal.isNumber()) {
|
||||||
|
((ViewGroup.MarginLayoutParams) layoutParams).rightMargin = DoricUtils.dp2px(rightVal.asNumber().toFloat());
|
||||||
|
}
|
||||||
|
JSValue bottomVal = margin.asObject().getProperty("bottom");
|
||||||
|
if (bottomVal.isNumber()) {
|
||||||
|
((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = DoricUtils.dp2px(bottomVal.asNumber().toFloat());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mixin(JSObject src, JSObject target) {
|
||||||
|
JSObject srcProps = src.getProperty("props").asObject();
|
||||||
|
JSObject targetProps = target.getProperty("props").asObject();
|
||||||
|
for (String key : srcProps.propertySet()) {
|
||||||
|
JSValue jsValue = srcProps.getProperty(key);
|
||||||
|
if ("subviews".equals(key) && jsValue.isArray()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
targetProps.asObject().setProperty(key, jsValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,13 +16,11 @@
|
|||||||
package pub.doric.shader;
|
package pub.doric.shader;
|
||||||
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import pub.doric.DoricContext;
|
import pub.doric.DoricContext;
|
||||||
import pub.doric.extension.bridge.DoricPlugin;
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
|
||||||
import com.github.pengfeizhou.jscore.JSObject;
|
|
||||||
import com.github.pengfeizhou.jscore.JSValue;
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,12 +35,12 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TextView build(JSObject jsObject) {
|
protected TextView build() {
|
||||||
return new TextView(getContext());
|
return new TextView(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blend(TextView view, ViewGroup.LayoutParams params, String name, JSValue prop) {
|
protected void blend(TextView view, String name, JSValue prop) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "text":
|
case "text":
|
||||||
view.setText(prop.asString().toString());
|
view.setText(prop.asString().toString());
|
||||||
@ -57,7 +55,7 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
view.setGravity(prop.asNumber().toInt());
|
view.setGravity(prop.asNumber().toInt());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.blend(view, params, name, prop);
|
super.blend(view, name, prop);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@ import android.widget.LinearLayout;
|
|||||||
import pub.doric.DoricContext;
|
import pub.doric.DoricContext;
|
||||||
import pub.doric.extension.bridge.DoricPlugin;
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
|
||||||
import com.github.pengfeizhou.jscore.JSObject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Description: com.github.penfeizhou.doric.shader
|
* @Description: com.github.penfeizhou.doric.shader
|
||||||
* @Author: pengfei.zhou
|
* @Author: pengfei.zhou
|
||||||
@ -34,8 +32,8 @@ public class VLayoutNode extends LinearNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LinearLayout build(JSObject jsObject) {
|
protected LinearLayout build() {
|
||||||
LinearLayout linearLayout = super.build(jsObject);
|
LinearLayout linearLayout = super.build();
|
||||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
return linearLayout;
|
return linearLayout;
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,13 @@ import android.widget.FrameLayout;
|
|||||||
|
|
||||||
import pub.doric.DoricContext;
|
import pub.doric.DoricContext;
|
||||||
import pub.doric.DoricRegistry;
|
import pub.doric.DoricRegistry;
|
||||||
|
import pub.doric.async.AsyncResult;
|
||||||
import pub.doric.utils.DoricContextHolder;
|
import pub.doric.utils.DoricContextHolder;
|
||||||
import pub.doric.utils.DoricConstant;
|
import pub.doric.utils.DoricConstant;
|
||||||
import pub.doric.utils.DoricMetaInfo;
|
import pub.doric.utils.DoricMetaInfo;
|
||||||
import pub.doric.utils.DoricUtils;
|
import pub.doric.utils.DoricUtils;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||||
import com.github.pengfeizhou.jscore.JSObject;
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
import com.github.pengfeizhou.jscore.JSValue;
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
@ -39,10 +41,10 @@ import java.util.LinkedList;
|
|||||||
*/
|
*/
|
||||||
public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
||||||
protected T mView;
|
protected T mView;
|
||||||
int index;
|
SuperNode mSuperNode;
|
||||||
GroupNode mParent;
|
|
||||||
String mId;
|
String mId;
|
||||||
private ViewGroup.LayoutParams mLayoutParams;
|
protected ViewGroup.LayoutParams mLayoutParams;
|
||||||
|
private String mType;
|
||||||
|
|
||||||
public ViewNode(DoricContext doricContext) {
|
public ViewNode(DoricContext doricContext) {
|
||||||
super(doricContext);
|
super(doricContext);
|
||||||
@ -50,7 +52,25 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
|||||||
|
|
||||||
private DoricLayer doricLayer;
|
private DoricLayer doricLayer;
|
||||||
|
|
||||||
public View getView() {
|
public void init(SuperNode superNode) {
|
||||||
|
this.mSuperNode = superNode;
|
||||||
|
this.mLayoutParams = superNode.generateDefaultLayoutParams();
|
||||||
|
this.doricLayer = new DoricLayer(getContext());
|
||||||
|
this.doricLayer.setLayoutParams(mLayoutParams);
|
||||||
|
this.mView = build();
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mLayoutParams.width, mLayoutParams.height);
|
||||||
|
doricLayer.addView(mView, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.mId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType(){
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getDoricLayer() {
|
||||||
return doricLayer;
|
return doricLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,53 +78,46 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
|||||||
return getDoricContext().getContext();
|
return getDoricContext().getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract T build(JSObject jsObject);
|
protected abstract T build();
|
||||||
|
|
||||||
void blend(JSObject jsObject, ViewGroup.LayoutParams layoutParams) {
|
public void blend(JSObject jsObject) {
|
||||||
mLayoutParams = layoutParams;
|
if (jsObject != null) {
|
||||||
if (mView == null) {
|
|
||||||
mView = build(jsObject);
|
|
||||||
}
|
|
||||||
if (getView() == null) {
|
|
||||||
doricLayer = new DoricLayer(getContext());
|
|
||||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(layoutParams.width, layoutParams.height);
|
|
||||||
doricLayer.addView(mView, params);
|
|
||||||
}
|
|
||||||
for (String prop : jsObject.propertySet()) {
|
for (String prop : jsObject.propertySet()) {
|
||||||
blend(mView, layoutParams, prop, jsObject.getProperty(prop));
|
blend(mView, prop, jsObject.getProperty(prop));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ViewGroup.LayoutParams params = mView.getLayoutParams();
|
ViewGroup.LayoutParams params = mView.getLayoutParams();
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
params.width = layoutParams.width;
|
params.width = mLayoutParams.width;
|
||||||
params.height = layoutParams.height;
|
params.height = mLayoutParams.height;
|
||||||
} else {
|
} else {
|
||||||
params = layoutParams;
|
params = mLayoutParams;
|
||||||
}
|
}
|
||||||
mView.setLayoutParams(params);
|
mView.setLayoutParams(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void blend(T view, ViewGroup.LayoutParams layoutParams, String name, JSValue prop) {
|
protected void blend(T view, String name, JSValue prop) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "width":
|
case "width":
|
||||||
if (layoutParams.width >= 0) {
|
if (mLayoutParams.width >= 0) {
|
||||||
layoutParams.width = DoricUtils.dp2px(prop.asNumber().toFloat());
|
mLayoutParams.width = DoricUtils.dp2px(prop.asNumber().toFloat());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "height":
|
case "height":
|
||||||
if (layoutParams.height >= 0) {
|
if (mLayoutParams.height >= 0) {
|
||||||
layoutParams.height = DoricUtils.dp2px(prop.asNumber().toFloat());
|
mLayoutParams.height = DoricUtils.dp2px(prop.asNumber().toFloat());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "x":
|
case "x":
|
||||||
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||||
float x = prop.asNumber().toFloat();
|
float x = prop.asNumber().toFloat();
|
||||||
((ViewGroup.MarginLayoutParams) layoutParams).leftMargin = DoricUtils.dp2px(x);
|
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "y":
|
case "y":
|
||||||
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||||
float y = prop.asNumber().toFloat();
|
float y = prop.asNumber().toFloat();
|
||||||
((ViewGroup.MarginLayoutParams) layoutParams).topMargin = DoricUtils.dp2px(y);
|
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "bgColor":
|
case "bgColor":
|
||||||
@ -120,8 +133,8 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "layoutConfig":
|
case "layoutConfig":
|
||||||
if (prop.isObject() && mParent != null) {
|
if (prop.isObject() && mSuperNode != null) {
|
||||||
mParent.blendChild(this, prop.asObject());
|
mSuperNode.blendSubLayoutConfig(this, prop.asObject());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "border":
|
case "border":
|
||||||
@ -172,26 +185,28 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
|||||||
ViewNode viewNode = this;
|
ViewNode viewNode = this;
|
||||||
do {
|
do {
|
||||||
ids.push(viewNode.mId);
|
ids.push(viewNode.mId);
|
||||||
viewNode = viewNode.mParent;
|
viewNode = viewNode.mSuperNode;
|
||||||
} while (viewNode != null && !(viewNode instanceof RootNode));
|
} while (viewNode != null && !(viewNode instanceof RootNode));
|
||||||
|
|
||||||
return ids.toArray(new String[0]);
|
return ids.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void callJSResponse(String funcId, Object... args) {
|
public AsyncResult<JSDecoder> callJSResponse(String funcId, Object... args) {
|
||||||
final Object[] nArgs = new Object[args.length + 2];
|
final Object[] nArgs = new Object[args.length + 2];
|
||||||
nArgs[0] = getIdList();
|
nArgs[0] = getIdList();
|
||||||
nArgs[1] = funcId;
|
nArgs[1] = funcId;
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
System.arraycopy(args, 0, nArgs, 2, args.length);
|
System.arraycopy(args, 0, nArgs, 2, args.length);
|
||||||
}
|
}
|
||||||
getDoricContext().callEntity(DoricConstant.DORIC_ENTITY_RESPONSE, nArgs);
|
return getDoricContext().callEntity(DoricConstant.DORIC_ENTITY_RESPONSE, nArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ViewNode create(DoricContext doricContext, String type) {
|
public static ViewNode create(DoricContext doricContext, String type) {
|
||||||
DoricRegistry registry = doricContext.getDriver().getRegistry();
|
DoricRegistry registry = doricContext.getDriver().getRegistry();
|
||||||
DoricMetaInfo<ViewNode> clz = registry.acquireViewNodeInfo(type);
|
DoricMetaInfo<ViewNode> clz = registry.acquireViewNodeInfo(type);
|
||||||
return clz.createInstance(doricContext);
|
ViewNode ret = clz.createInstance(doricContext);
|
||||||
|
ret.mType = type;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ViewGroup.LayoutParams getLayoutParams() {
|
public ViewGroup.LayoutParams getLayoutParams() {
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.list;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSArray;
|
||||||
|
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||||
|
import com.github.pengfeizhou.jscore.JSNull;
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.async.AsyncResult;
|
||||||
|
import pub.doric.shader.ViewNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
|
||||||
|
|
||||||
|
private final ListNode listNode;
|
||||||
|
String renderItemFuncId;
|
||||||
|
int itemCount = 0;
|
||||||
|
int batchCount = 15;
|
||||||
|
SparseArray<String> itemValues = new SparseArray<>();
|
||||||
|
|
||||||
|
ListAdapter(ListNode listNode) {
|
||||||
|
this.listNode = listNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ListItemNode node = (ListItemNode) ViewNode.create(listNode.getDoricContext(), "ListItem");
|
||||||
|
node.init(listNode);
|
||||||
|
return new DoricViewHolder(node, node.getDoricLayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
|
||||||
|
JSValue jsValue = getItemModel(position);
|
||||||
|
if (jsValue.isObject()) {
|
||||||
|
JSObject jsObject = jsValue.asObject();
|
||||||
|
holder.listItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||||
|
holder.listItemNode.blend(jsObject.getProperty("props").asObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
JSValue value = getItemModel(position);
|
||||||
|
if (value.isObject()) {
|
||||||
|
if (value.asObject().getProperty("identifier").isString()) {
|
||||||
|
return value.asObject().getProperty("identifier").asString().value().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.getItemViewType(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSValue getItemModel(final int position) {
|
||||||
|
String id = itemValues.get(position);
|
||||||
|
if (TextUtils.isEmpty(id)) {
|
||||||
|
AsyncResult<JSDecoder> asyncResult = listNode.callJSResponse(
|
||||||
|
"renderBunchedItems",
|
||||||
|
position,
|
||||||
|
batchCount);
|
||||||
|
try {
|
||||||
|
JSDecoder jsDecoder = asyncResult.synchronous().get();
|
||||||
|
JSValue result = jsDecoder.decode();
|
||||||
|
if (result.isArray()) {
|
||||||
|
JSArray jsArray = result.asArray();
|
||||||
|
for (int i = 0; i < jsArray.size(); i++) {
|
||||||
|
JSObject itemModel = jsArray.get(i).asObject();
|
||||||
|
String itemId = itemModel.getProperty("id").asString().value();
|
||||||
|
itemValues.put(i + position, itemId);
|
||||||
|
listNode.setSubModel(itemId, itemModel);
|
||||||
|
}
|
||||||
|
return listNode.getSubModel(itemValues.get(position));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new JSNull();
|
||||||
|
} else {
|
||||||
|
JSObject childModel = listNode.getSubModel(id);
|
||||||
|
if (childModel == null) {
|
||||||
|
return new JSNull();
|
||||||
|
} else {
|
||||||
|
return childModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void blendSubNode(JSObject subProperties) {
|
||||||
|
for (int i = 0; i < itemValues.size(); i++) {
|
||||||
|
if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) {
|
||||||
|
notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DoricViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ListItemNode listItemNode;
|
||||||
|
|
||||||
|
DoricViewHolder(ListItemNode node, @NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
listItemNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.list;
|
||||||
|
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
import pub.doric.shader.StackNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
@DoricPlugin(name = "ListItem")
|
||||||
|
public class ListItemNode extends StackNode {
|
||||||
|
public String identifier = "";
|
||||||
|
|
||||||
|
public ListItemNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
this.mReusable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||||
|
if ("identifier".equals(name)) {
|
||||||
|
this.identifier = prop.asString().value();
|
||||||
|
} else {
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.list;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
import pub.doric.shader.SuperNode;
|
||||||
|
import pub.doric.shader.ViewNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
@DoricPlugin(name = "List")
|
||||||
|
public class ListNode extends SuperNode<RecyclerView> {
|
||||||
|
private final ListAdapter listAdapter;
|
||||||
|
|
||||||
|
public ListNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
this.listAdapter = new ListAdapter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blendSubNode(JSObject subProperties) {
|
||||||
|
listAdapter.blendSubNode(subProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView build() {
|
||||||
|
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(this.listAdapter);
|
||||||
|
return recyclerView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blend(JSObject jsObject) {
|
||||||
|
super.blend(jsObject);
|
||||||
|
if (mView != null) {
|
||||||
|
mView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(RecyclerView view, String name, JSValue prop) {
|
||||||
|
switch (name) {
|
||||||
|
case "itemCount":
|
||||||
|
this.listAdapter.itemCount = prop.asNumber().toInt();
|
||||||
|
break;
|
||||||
|
case "renderItem":
|
||||||
|
this.listAdapter.renderItemFuncId = prop.asString().value();
|
||||||
|
// If reset renderItem,should reset native cache.
|
||||||
|
this.listAdapter.itemValues.clear();
|
||||||
|
clearSubModel();
|
||||||
|
break;
|
||||||
|
case "batchCount":
|
||||||
|
this.listAdapter.batchCount = 15;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
|
||||||
|
super.blendSubLayoutConfig(viewNode, jsObject);
|
||||||
|
}
|
||||||
|
}
|
@ -14,4 +14,9 @@ org.gradle.jvmargs=-Xmx1536m
|
|||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
# org.gradle.parallel=true
|
# org.gradle.parallel=true
|
||||||
|
|
||||||
|
# To re-enable the build cache, either delete the following
|
||||||
|
# line or set the property to 'true'.
|
||||||
|
android.enableBuildCache=false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export default [
|
export default [
|
||||||
'src/Counter',
|
'src/Counter',
|
||||||
'src/Snake',
|
'src/Snake',
|
||||||
|
'src/ListDemo',
|
||||||
]
|
]
|
@ -13,7 +13,9 @@ class CounterView extends ViewHolder<CountModel> {
|
|||||||
text({
|
text({
|
||||||
textSize: 40,
|
textSize: 40,
|
||||||
layoutConfig: {
|
layoutConfig: {
|
||||||
alignment: new Gravity().center()
|
alignment: new Gravity().center(),
|
||||||
|
widthSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
},
|
},
|
||||||
}).also(it => { this.number = it }),
|
}).also(it => { this.number = it }),
|
||||||
text({
|
text({
|
||||||
@ -25,7 +27,9 @@ class CounterView extends ViewHolder<CountModel> {
|
|||||||
},
|
},
|
||||||
corners: 5,
|
corners: 5,
|
||||||
layoutConfig: {
|
layoutConfig: {
|
||||||
alignment: new Gravity().center()
|
alignment: new Gravity().center(),
|
||||||
|
widthSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
},
|
},
|
||||||
shadow: {
|
shadow: {
|
||||||
color: Color.parse("#00ff00"),
|
color: Color.parse("#00ff00"),
|
||||||
|
71
demo/src/ListDemo.ts
Normal file
71
demo/src/ListDemo.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout } from "doric";
|
||||||
|
const colors = [
|
||||||
|
"#f0932b",
|
||||||
|
"#eb4d4b",
|
||||||
|
"#6ab04c",
|
||||||
|
"#e056fd",
|
||||||
|
"#686de0",
|
||||||
|
"#30336b",
|
||||||
|
]
|
||||||
|
@Entry
|
||||||
|
class ListPanel extends Panel {
|
||||||
|
build(rootView: Group): void {
|
||||||
|
rootView.addChild(vlayout([
|
||||||
|
text({
|
||||||
|
text: "ListDemo",
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.EXACTLY,
|
||||||
|
},
|
||||||
|
textSize: 30,
|
||||||
|
textColor: Color.parse("#535c68"),
|
||||||
|
bgColor: Color.parse("#dff9fb"),
|
||||||
|
textAlignment: gravity().center(),
|
||||||
|
height: 50,
|
||||||
|
}),
|
||||||
|
list({
|
||||||
|
itemCount: 1000,
|
||||||
|
renderItem: (idx: number) => {
|
||||||
|
return listItem(text({
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
margin: {
|
||||||
|
left: 10,
|
||||||
|
right: 50,
|
||||||
|
top: 50,
|
||||||
|
bottom: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text: `Cell At Line ${idx}`,
|
||||||
|
textAlignment: gravity().center(),
|
||||||
|
textColor: Color.parse("#ffffff"),
|
||||||
|
textSize: 20,
|
||||||
|
})).also(it => {
|
||||||
|
it.gravity = gravity().center()
|
||||||
|
it.bgColor = Color.parse(colors[idx % colors.length])
|
||||||
|
it.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.EXACTLY,
|
||||||
|
}
|
||||||
|
it.height = 100
|
||||||
|
it.onClick = () => {
|
||||||
|
log(`Click item at ${idx}`)
|
||||||
|
it.bgColor = Color.parse('#000000')
|
||||||
|
log(`bgcolor is ${Color.parse('#000000').toModel()}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.AT_MOST,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]).also(it => {
|
||||||
|
it.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.AT_MOST,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
E2334AFE22E9D2070098A085 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334AFD22E9D2070098A085 /* main.m */; };
|
E2334AFE22E9D2070098A085 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334AFD22E9D2070098A085 /* main.m */; };
|
||||||
E2334B0822E9D2070098A085 /* ExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334B0722E9D2070098A085 /* ExampleTests.m */; };
|
E2334B0822E9D2070098A085 /* ExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334B0722E9D2070098A085 /* ExampleTests.m */; };
|
||||||
E2334B1322E9D2070098A085 /* ExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334B1222E9D2070098A085 /* ExampleUITests.m */; };
|
E2334B1322E9D2070098A085 /* ExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334B1222E9D2070098A085 /* ExampleUITests.m */; };
|
||||||
|
E2BF7BF7237E8E9F001B0EDC /* ListDemo.js in Resources */ = {isa = PBXBuildFile; fileRef = E2BF7BF6237E8E9F001B0EDC /* ListDemo.js */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -67,6 +68,7 @@
|
|||||||
E2334B0E22E9D2070098A085 /* ExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
E2334B0E22E9D2070098A085 /* ExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
E2334B1222E9D2070098A085 /* ExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleUITests.m; sourceTree = "<group>"; };
|
E2334B1222E9D2070098A085 /* ExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleUITests.m; sourceTree = "<group>"; };
|
||||||
E2334B1422E9D2070098A085 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
E2334B1422E9D2070098A085 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
E2BF7BF6237E8E9F001B0EDC /* ListDemo.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = ListDemo.js; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -123,6 +125,7 @@
|
|||||||
E21DC9D12302865E00660C5C /* src */ = {
|
E21DC9D12302865E00660C5C /* src */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E2BF7BF6237E8E9F001B0EDC /* ListDemo.js */,
|
||||||
E21DC9D22302865E00660C5C /* Snake.js */,
|
E21DC9D22302865E00660C5C /* Snake.js */,
|
||||||
E21DC9D32302865E00660C5C /* Counter.js */,
|
E21DC9D32302865E00660C5C /* Counter.js */,
|
||||||
);
|
);
|
||||||
@ -295,6 +298,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
E2BF7BF7237E8E9F001B0EDC /* ListDemo.js in Resources */,
|
||||||
E21DC9D42302870000660C5C /* Snake.js in Resources */,
|
E21DC9D42302870000660C5C /* Snake.js in Resources */,
|
||||||
E21DC9D52302870000660C5C /* Counter.js in Resources */,
|
E21DC9D52302870000660C5C /* Counter.js in Resources */,
|
||||||
E2334AFB22E9D2070098A085 /* LaunchScreen.storyboard in Resources */,
|
E2334AFB22E9D2070098A085 /* LaunchScreen.storyboard in Resources */,
|
||||||
|
@ -26,11 +26,11 @@ @implementation ViewController
|
|||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"Snake" ofType:@"js"];
|
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"ListDemo" ofType:@"js"];
|
||||||
NSString *jsContent = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
|
NSString *jsContent = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
|
||||||
self.doricContext = [[DoricContext alloc] initWithScript:jsContent source:@"test.js"];
|
self.doricContext = [[DoricContext alloc] initWithScript:jsContent source:@"test.js"];
|
||||||
[self.doricContext.rootNode setupRootView:[[DoricStackView new] also:^(DoricStackView *it) {
|
[self.doricContext.rootNode setupRootView:[[DoricStackView new] also:^(DoricStackView *it) {
|
||||||
it.layoutConfig = [[DoricStackConfig alloc] initWithWidth:DoricLayoutAtMost height:DoricLayoutAtMost];
|
it.layoutConfig = [[DoricLayoutConfig alloc] initWithWidth:DoricLayoutAtMost height:DoricLayoutAtMost];
|
||||||
[self.view addSubview:it];
|
[self.view addSubview:it];
|
||||||
}]];
|
}]];
|
||||||
[self.doricContext initContextWithWidth:self.view.width height:self.view.height];
|
[self.doricContext initContextWithWidth:self.view.width height:self.view.height];
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
@implementation DoricContextHolder
|
@implementation DoricContextHolder
|
||||||
|
|
||||||
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
if (self = [super init]) {
|
if (self = [self init]) {
|
||||||
_doricContext = doricContext;
|
_doricContext = doricContext;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#import "DoricHLayoutNode.h"
|
#import "DoricHLayoutNode.h"
|
||||||
#import "DoricTextNode.h"
|
#import "DoricTextNode.h"
|
||||||
#import "DoricImageNode.h"
|
#import "DoricImageNode.h"
|
||||||
|
#import "DoricListNode.h"
|
||||||
|
#import "DoricListItemNode.h"
|
||||||
|
|
||||||
@interface DoricRegistry ()
|
@interface DoricRegistry ()
|
||||||
|
|
||||||
@ -58,6 +60,8 @@ - (void)innerRegister {
|
|||||||
[self registerViewNode:DoricHLayoutNode.class withName:@"HLayout"];
|
[self registerViewNode:DoricHLayoutNode.class withName:@"HLayout"];
|
||||||
[self registerViewNode:DoricTextNode.class withName:@"Text"];
|
[self registerViewNode:DoricTextNode.class withName:@"Text"];
|
||||||
[self registerViewNode:DoricImageNode.class withName:@"Image"];
|
[self registerViewNode:DoricImageNode.class withName:@"Image"];
|
||||||
|
[self registerViewNode:DoricListNode.class withName:@"List"];
|
||||||
|
[self registerViewNode:DoricListItemNode.class withName:@"ListItem"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)registerJSBundle:(NSString *)bundle withName:(NSString *)name {
|
- (void)registerJSBundle:(NSString *)bundle withName:(NSString *)name {
|
||||||
|
@ -20,19 +20,12 @@
|
|||||||
// Created by pengfei.zhou on 2019/7/30.
|
// Created by pengfei.zhou on 2019/7/30.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "DoricViewNode.h"
|
#import "DoricSuperNode.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@interface DoricGroupNode <V:UIView *, P:DoricLayoutConfig *> : DoricViewNode<V>
|
@interface DoricGroupNode <V:UIView *> : DoricSuperNode<V>
|
||||||
|
@property(nonatomic, assign) BOOL reusable;
|
||||||
@property(nonatomic, strong) NSMutableDictionary *children;
|
|
||||||
@property(nonatomic, strong) NSMutableArray *indexedChildren;
|
|
||||||
|
|
||||||
|
|
||||||
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig;
|
|
||||||
|
|
||||||
- (P)generateDefaultLayoutParams;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
@ -23,124 +23,140 @@
|
|||||||
#import <Doric/DoricExtensions.h>
|
#import <Doric/DoricExtensions.h>
|
||||||
#import "DoricGroupNode.h"
|
#import "DoricGroupNode.h"
|
||||||
|
|
||||||
|
@interface DoricGroupNode ()
|
||||||
|
@property(nonatomic, copy) NSArray<DoricViewNode *> *childNodes;
|
||||||
|
@property(nonatomic, copy) NSArray <NSString *> *childViewIds;
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation DoricGroupNode
|
@implementation DoricGroupNode
|
||||||
|
|
||||||
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
if (self = [super initWithContext:doricContext]) {
|
if (self = [super initWithContext:doricContext]) {
|
||||||
_children = [[NSMutableDictionary alloc] init];
|
_childNodes = @[];
|
||||||
_indexedChildren = [[NSMutableArray alloc] init];
|
_childViewIds = @[];
|
||||||
|
_reusable = NO;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIView *)build:(NSDictionary *)props {
|
- (UIView *)build {
|
||||||
UIView *ret = [[UIView alloc] init];
|
UIView *ret = [[UIView alloc] init];
|
||||||
ret.clipsToBounds = YES;
|
ret.clipsToBounds = YES;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop {
|
- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop {
|
||||||
if ([name isEqualToString:@"children"]) {
|
if ([@"children" isEqualToString:name]) {
|
||||||
NSArray *array = prop;
|
self.childViewIds = prop;
|
||||||
NSInteger i;
|
|
||||||
NSMutableArray *tobeRemoved = [[NSMutableArray alloc] init];
|
|
||||||
for (i = 0; i < array.count; i++) {
|
|
||||||
NSDictionary *val = array[i];
|
|
||||||
if (!val || (NSNull *) val == [NSNull null]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
NSString *type = val[@"type"];
|
|
||||||
NSString *viewId = val[@"id"];
|
|
||||||
DoricViewNode *node = self.children[viewId];
|
|
||||||
if (node == nil) {
|
|
||||||
node = [DoricViewNode create:self.doricContext withType:type];
|
|
||||||
node.index = i;
|
|
||||||
node.parent = self;
|
|
||||||
node.viewId = viewId;
|
|
||||||
self.children[viewId] = node;
|
|
||||||
} else {
|
|
||||||
if (i != node.index) {
|
|
||||||
[self.indexedChildren removeObjectAtIndex:i];
|
|
||||||
node.index = i;
|
|
||||||
[node.view removeFromSuperview];
|
|
||||||
}
|
|
||||||
[tobeRemoved removeObject:node];
|
|
||||||
}
|
|
||||||
DoricViewNode *old = i >= self.indexedChildren.count ? nil : self.indexedChildren[i];
|
|
||||||
if (old && old != node) {
|
|
||||||
[old.view removeFromSuperview];
|
|
||||||
self.indexedChildren[i] = [NSNull null];
|
|
||||||
[tobeRemoved addObject:old];
|
|
||||||
}
|
|
||||||
|
|
||||||
DoricLayoutConfig *params = node.layoutConfig;
|
|
||||||
if (params == nil) {
|
|
||||||
params = [self generateDefaultLayoutParams];
|
|
||||||
node.layoutConfig = params;
|
|
||||||
}
|
|
||||||
[node blend:val[@"props"]];
|
|
||||||
if (self.indexedChildren.count <= i) {
|
|
||||||
[self.view addSubview:node.view];
|
|
||||||
[self.indexedChildren addObject:node];
|
|
||||||
} else if (self.indexedChildren[i] == [NSNull null]) {
|
|
||||||
self.indexedChildren[i] = node;
|
|
||||||
[self.view insertSubview:node.view atIndex:i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NSInteger start = i;
|
|
||||||
while (start < self.indexedChildren.count) {
|
|
||||||
DoricViewNode *node = self.indexedChildren[(NSUInteger) start];
|
|
||||||
if (node) {
|
|
||||||
[self.children removeObjectForKey:node.viewId];
|
|
||||||
[node.view removeFromSuperview];
|
|
||||||
[tobeRemoved removeObject:node];
|
|
||||||
}
|
|
||||||
start++;
|
|
||||||
}
|
|
||||||
if (i < self.indexedChildren.count) {
|
|
||||||
[self.indexedChildren removeObjectsInRange:NSMakeRange((NSUInteger) i, self.indexedChildren.count - i)];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DoricViewNode *node in tobeRemoved) {
|
|
||||||
[self.children removeObjectForKey:node.viewId];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
[super blendView:view forPropName:name propValue:prop];
|
[super blendView:view forPropName:name propValue:prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)blend:(NSDictionary *)props {
|
||||||
|
[super blend:props];
|
||||||
|
[self configChildNodes];
|
||||||
|
}
|
||||||
|
|
||||||
- (DoricLayoutConfig *)generateDefaultLayoutParams {
|
- (DoricLayoutConfig *)generateDefaultLayoutParams {
|
||||||
DoricLayoutConfig *params = [[DoricLayoutConfig alloc] init];
|
DoricLayoutConfig *params = [[DoricLayoutConfig alloc] init];
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig {
|
- (void)configChildNodes {
|
||||||
DoricLayoutConfig *params = child.layoutConfig;
|
NSMutableArray *childNodes = [self.childNodes mutableCopy];
|
||||||
|
for (NSUInteger idx = 0; idx < self.childViewIds.count; idx++) {
|
||||||
|
NSString *viewId = self.childViewIds[idx];
|
||||||
|
NSDictionary *model = [self subModelOf:viewId];
|
||||||
|
NSString *type = model[@"type"];
|
||||||
|
if (idx < self.childNodes.count) {
|
||||||
|
DoricViewNode *oldNode = childNodes[idx];
|
||||||
|
if ([viewId isEqualToString:oldNode.viewId]) {
|
||||||
|
///Same,skip
|
||||||
|
} else {
|
||||||
|
if (self.reusable) {
|
||||||
|
if ([oldNode.type isEqualToString:type]) {
|
||||||
|
///Same type,can be reused
|
||||||
|
oldNode.viewId = viewId;
|
||||||
|
[oldNode blend:model[@"props"]];
|
||||||
|
} else {
|
||||||
|
///Replace this view
|
||||||
|
[childNodes removeObjectAtIndex:idx];
|
||||||
|
[oldNode.view removeFromSuperview];
|
||||||
|
DoricViewNode *viewNode = [DoricViewNode create:self.doricContext withType:type];
|
||||||
|
if ([viewNode isKindOfClass:[DoricGroupNode class]]) {
|
||||||
|
((DoricGroupNode *) viewNode).reusable = self.reusable;
|
||||||
|
}
|
||||||
|
viewNode.viewId = viewId;
|
||||||
|
[viewNode initWithSuperNode:self];
|
||||||
|
[viewNode blend:model[@"props"]];
|
||||||
|
[childNodes insertObject:viewNode atIndex:idx];
|
||||||
|
[self.view insertSubview:viewNode.view atIndex:idx];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
///Find in remain nodes
|
||||||
|
NSInteger position = -1;
|
||||||
|
for (NSUInteger start = idx + 1; start < childNodes.count; start++) {
|
||||||
|
DoricViewNode *node = childNodes[start];
|
||||||
|
if ([viewId isEqualToString:node.viewId]) {
|
||||||
|
position = start;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (position >= 0) {
|
||||||
|
///Found ,swap idx,position
|
||||||
|
DoricViewNode *reused = childNodes[(NSUInteger) position];
|
||||||
|
[childNodes removeObjectAtIndex:(NSUInteger) position];
|
||||||
|
[childNodes removeObjectAtIndex:idx];
|
||||||
|
[childNodes insertObject:reused atIndex:idx];
|
||||||
|
[childNodes insertObject:oldNode atIndex:(NSUInteger) position];
|
||||||
|
|
||||||
[layoutConfig[@"widthSpec"] also:^(NSNumber *it) {
|
///View swap index
|
||||||
if (it) {
|
[reused.view removeFromSuperview];
|
||||||
params.widthSpec = (DoricLayoutSpec) [it integerValue];
|
[oldNode.view removeFromSuperview];
|
||||||
|
[self.view insertSubview:reused.view atIndex:idx];
|
||||||
|
[self.view insertSubview:oldNode.view atIndex:position];
|
||||||
|
} else {
|
||||||
|
///Not found,insert
|
||||||
|
DoricViewNode *viewNode = [DoricViewNode create:self.doricContext withType:type];
|
||||||
|
viewNode.viewId = viewId;
|
||||||
|
[viewNode initWithSuperNode:self];
|
||||||
|
[viewNode blend:model[@"props"]];
|
||||||
|
[childNodes insertObject:viewNode atIndex:idx];
|
||||||
|
[self.view insertSubview:viewNode.view atIndex:idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/// Insert
|
||||||
|
DoricViewNode *viewNode = [DoricViewNode create:self.doricContext withType:type];
|
||||||
|
if ([viewNode isKindOfClass:[DoricGroupNode class]]) {
|
||||||
|
((DoricGroupNode *) viewNode).reusable = self.reusable;
|
||||||
|
}
|
||||||
|
viewNode.viewId = viewId;
|
||||||
|
[viewNode initWithSuperNode:self];
|
||||||
|
[viewNode blend:model[@"props"]];
|
||||||
|
[childNodes addObject:viewNode];
|
||||||
|
[self.view addSubview:viewNode.view];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUInteger count = childNodes.count;
|
||||||
|
for (NSUInteger idx = self.childViewIds.count; idx < count; idx++) {
|
||||||
|
DoricViewNode *viewNode = childNodes.lastObject;
|
||||||
|
[childNodes removeLastObject];
|
||||||
|
[viewNode.view removeFromSuperview];
|
||||||
|
}
|
||||||
|
self.childNodes = [childNodes copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendSubNode:(NSDictionary *)subModel {
|
||||||
|
NSString *viewId = subModel[@"id"];
|
||||||
|
[self.childNodes enumerateObjectsUsingBlock:^(DoricViewNode *obj, NSUInteger idx, BOOL *stop) {
|
||||||
|
if ([viewId isEqualToString:obj.viewId]) {
|
||||||
|
[obj blend:subModel[@"props"]];
|
||||||
|
*stop = YES;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[layoutConfig[@"heightSpec"] also:^(NSNumber *it) {
|
|
||||||
if (it) {
|
|
||||||
params.heightSpec = (DoricLayoutSpec) [it integerValue];
|
|
||||||
}
|
}
|
||||||
}];
|
|
||||||
|
|
||||||
if ([params isKindOfClass:DoricMarginConfig.class]) {
|
|
||||||
DoricMarginConfig *marginParams = (DoricMarginConfig *) params;
|
|
||||||
NSDictionary *margin = layoutConfig[@"margin"];
|
|
||||||
if (margin) {
|
|
||||||
marginParams.margin = DoricMarginMake(
|
|
||||||
[(NSNumber *) margin[@"left"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"top"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"right"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"bottom"] floatValue]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -22,5 +22,5 @@
|
|||||||
|
|
||||||
#import "DoricGroupNode.h"
|
#import "DoricGroupNode.h"
|
||||||
|
|
||||||
@interface DoricHLayoutNode : DoricGroupNode<DoricHLayoutView *, DoricLinearConfig *>
|
@interface DoricHLayoutNode : DoricGroupNode<DoricHLayoutView *>
|
||||||
@end
|
@end
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#import "DoricUtil.h"
|
#import "DoricUtil.h"
|
||||||
|
|
||||||
@implementation DoricHLayoutNode
|
@implementation DoricHLayoutNode
|
||||||
- (DoricHLayoutView *)build:(NSDictionary *)props {
|
- (DoricHLayoutView *)build {
|
||||||
return [DoricHLayoutView new];
|
return [DoricHLayoutView new];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,29 +37,4 @@ - (void)blendView:(DoricHLayoutView *)view forPropName:(NSString *)name propValu
|
|||||||
[super blendView:view forPropName:name propValue:prop];
|
[super blendView:view forPropName:name propValue:prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig {
|
|
||||||
[super blendChild:child layoutConfig:layoutConfig];
|
|
||||||
if (![child.layoutConfig isKindOfClass:DoricLinearConfig.class]) {
|
|
||||||
DoricLog(@"blend DoricHLayoutView child error,layout params not match");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DoricLinearConfig *params = (DoricLinearConfig *) child.layoutConfig;
|
|
||||||
NSDictionary *margin = layoutConfig[@"margin"];
|
|
||||||
if (margin) {
|
|
||||||
params.margin = DoricMarginMake(
|
|
||||||
[(NSNumber *) margin[@"left"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"top"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"right"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"bottom"] floatValue]);
|
|
||||||
}
|
|
||||||
NSNumber *alignment = layoutConfig[@"alignment"];
|
|
||||||
if (alignment) {
|
|
||||||
params.alignment = (DoricGravity) [alignment integerValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (DoricLinearConfig *)generateDefaultLayoutParams {
|
|
||||||
return [[DoricLinearConfig alloc] init];
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
@implementation DoricImageNode
|
@implementation DoricImageNode
|
||||||
|
|
||||||
- (UIImageView *)build:(NSDictionary *)props {
|
- (UIImageView *)build {
|
||||||
return [[UIImageView alloc] init];
|
return [[UIImageView alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@ typedef struct DoricMargin DoricMargin;
|
|||||||
DoricMargin DoricMarginMake(CGFloat left, CGFloat top, CGFloat right, CGFloat bottom);
|
DoricMargin DoricMarginMake(CGFloat left, CGFloat top, CGFloat right, CGFloat bottom);
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, DoricLayoutSpec) {
|
typedef NS_ENUM(NSInteger, DoricLayoutSpec) {
|
||||||
DoricLayoutExact,
|
DoricLayoutExact = 0,
|
||||||
DoricLayoutWrapContent,
|
DoricLayoutWrapContent = 1,
|
||||||
DoricLayoutAtMost,
|
DoricLayoutAtMost = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, DoricGravity) {
|
typedef NS_ENUM(NSInteger, DoricGravity) {
|
||||||
@ -54,43 +54,31 @@ typedef NS_ENUM(NSInteger, DoricGravity) {
|
|||||||
@interface DoricLayoutConfig : NSObject
|
@interface DoricLayoutConfig : NSObject
|
||||||
@property(nonatomic, assign) DoricLayoutSpec widthSpec;
|
@property(nonatomic, assign) DoricLayoutSpec widthSpec;
|
||||||
@property(nonatomic, assign) DoricLayoutSpec heightSpec;
|
@property(nonatomic, assign) DoricLayoutSpec heightSpec;
|
||||||
|
@property(nonatomic) DoricMargin margin;
|
||||||
@property(nonatomic, assign) DoricGravity alignment;
|
@property(nonatomic, assign) DoricGravity alignment;
|
||||||
|
@property(nonatomic, assign) NSUInteger weight;
|
||||||
|
|
||||||
- (instancetype)init;
|
- (instancetype)init;
|
||||||
|
|
||||||
- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height;
|
- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height;
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface DoricStackConfig : DoricLayoutConfig
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface DoricMarginConfig : DoricLayoutConfig
|
|
||||||
@property(nonatomic) DoricMargin margin;
|
|
||||||
|
|
||||||
- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height margin:(DoricMargin)margin;
|
- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height margin:(DoricMargin)margin;
|
||||||
@end
|
|
||||||
|
|
||||||
@interface DoricLinearConfig : DoricMarginConfig
|
|
||||||
@property(nonatomic, assign) NSUInteger weight;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface DoricLayoutContainer <T :DoricLayoutConfig *> : UIView
|
@interface DoricLayoutContainer : UIView
|
||||||
|
|
||||||
- (T)configForChild:(__kindof UIView *)child;
|
|
||||||
|
|
||||||
- (void)layout;
|
|
||||||
|
|
||||||
- (void)requestLayout;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface DoricStackView : DoricLayoutContainer<DoricStackConfig *>
|
|
||||||
@property(nonatomic, assign) DoricGravity gravity;
|
@property(nonatomic, assign) DoricGravity gravity;
|
||||||
|
|
||||||
|
- (void)layout:(CGSize)targetSize;
|
||||||
|
|
||||||
|
- (CGSize)sizeContent:(CGSize)size;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface DoricLinearView : DoricLayoutContainer<DoricLinearConfig *>
|
@interface DoricStackView : DoricLayoutContainer
|
||||||
@property(nonatomic, assign) DoricGravity gravity;
|
@end
|
||||||
|
|
||||||
|
@interface DoricLinearView : DoricLayoutContainer
|
||||||
@property(nonatomic, assign) CGFloat space;
|
@property(nonatomic, assign) CGFloat space;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -46,107 +46,70 @@ - (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)hei
|
|||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DoricMarginConfig
|
|
||||||
- (instancetype)init {
|
|
||||||
if (self = [super init]) {
|
|
||||||
_margin = DoricMarginMake(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height margin:(DoricMargin)margin {
|
- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height margin:(DoricMargin)margin {
|
||||||
if (self = [super initWithWidth:width height:height]) {
|
if (self = [super init]) {
|
||||||
|
_widthSpec = width;
|
||||||
|
_heightSpec = height;
|
||||||
_margin = margin;
|
_margin = margin;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DoricStackConfig
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DoricLinearConfig
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface DoricLayoutContainer ()
|
@interface DoricLayoutContainer ()
|
||||||
@property(nonatomic, assign) BOOL waitingLayout;
|
@property(nonatomic, assign) CGFloat contentWidth;
|
||||||
|
@property(nonatomic, assign) CGFloat contentHeight;
|
||||||
|
@property(nonatomic, assign) NSUInteger contentWeight;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DoricLayoutContainer
|
@implementation DoricLayoutContainer
|
||||||
- (instancetype)init {
|
|
||||||
if (self = [super init]) {
|
|
||||||
_waitingLayout = NO;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(CGRect)frame {
|
- (void)layoutSubviews {
|
||||||
if (self = [super initWithFrame:frame]) {
|
|
||||||
_waitingLayout = NO;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
|
||||||
if (self = [super initWithCoder:coder]) {
|
|
||||||
_waitingLayout = NO;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (DoricLayoutConfig *)configForChild:(UIView *)child {
|
|
||||||
DoricLayoutConfig *config = child.layoutConfig;
|
|
||||||
if (!config) {
|
|
||||||
config = [[DoricLayoutConfig alloc] init];
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)requestLayout {
|
|
||||||
if ([self.superview isKindOfClass:[DoricLinearView class]]) {
|
|
||||||
[(DoricLinearView *) self.superview requestLayout];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (self.waitingLayout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.waitingLayout = YES;
|
|
||||||
__weak typeof(self) _self = self;
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
__strong typeof(_self) self = _self;
|
|
||||||
[self sizeToFit];
|
|
||||||
[self layout];
|
|
||||||
self.waitingLayout = NO;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setNeedsLayout {
|
|
||||||
[super setNeedsLayout];
|
|
||||||
if (self.waitingLayout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[self requestLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)waitingLayout {
|
|
||||||
if ([self.superview isKindOfClass:[DoricLayoutContainer class]]) {
|
if ([self.superview isKindOfClass:[DoricLayoutContainer class]]) {
|
||||||
return [(DoricLayoutContainer *) self.superview waitingLayout];
|
[self.superview layoutSubviews];
|
||||||
|
} else {
|
||||||
|
CGSize size = [self sizeThatFits:CGSizeMake(self.superview.width, self.superview.height)];
|
||||||
|
[self layout:size];
|
||||||
}
|
}
|
||||||
return _waitingLayout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layout {
|
- (CGSize)sizeThatFits:(CGSize)size {
|
||||||
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView *child, NSUInteger idx, BOOL *stop) {
|
CGFloat width = self.width;
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
CGFloat height = self.height;
|
||||||
[(DoricLayoutContainer *) child layout];
|
|
||||||
|
DoricLayoutConfig *config = self.layoutConfig;
|
||||||
|
if (!config) {
|
||||||
|
config = [DoricLayoutConfig new];
|
||||||
}
|
}
|
||||||
}];
|
if (config.widthSpec == DoricLayoutAtMost
|
||||||
|
|| config.widthSpec == DoricLayoutWrapContent) {
|
||||||
|
width = size.width - config.margin.left - config.margin.right;
|
||||||
|
}
|
||||||
|
if (config.heightSpec == DoricLayoutAtMost
|
||||||
|
|| config.heightSpec == DoricLayoutWrapContent) {
|
||||||
|
height = size.height - config.margin.top - config.margin.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CGSize contentSize = [self sizeContent:CGSizeMake(width, height)];
|
||||||
|
if (config.widthSpec == DoricLayoutWrapContent) {
|
||||||
|
width = contentSize.width;
|
||||||
|
}
|
||||||
|
if (config.heightSpec == DoricLayoutWrapContent) {
|
||||||
|
height = contentSize.height;
|
||||||
|
}
|
||||||
|
return CGSizeMake(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGSize)sizeContent:(CGSize)size {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layout:(CGSize)targetSize {
|
||||||
|
self.width = targetSize.width;
|
||||||
|
self.height = targetSize.height;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@ -157,175 +120,223 @@ @interface DoricStackView ()
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DoricStackView
|
@implementation DoricStackView
|
||||||
- (DoricStackConfig *)configForChild:(UIView *)child {
|
|
||||||
DoricStackConfig *config = (DoricStackConfig *) child.layoutConfig;
|
|
||||||
if (!config) {
|
|
||||||
config = [[DoricStackConfig alloc] init];
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
- (CGSize)sizeContent:(CGSize)size {
|
||||||
- (void)sizeToFit {
|
CGFloat contentWidth = 0;
|
||||||
DoricLayoutConfig *config = self.layoutConfig;
|
CGFloat contentHeight = 0;
|
||||||
self.contentWidth = 0;
|
|
||||||
self.contentHeight = 0;
|
|
||||||
for (UIView *child in self.subviews) {
|
for (UIView *child in self.subviews) {
|
||||||
if (child.isHidden) {
|
if (child.isHidden) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DoricStackConfig *childConfig = [self configForChild:child];
|
DoricLayoutConfig *childConfig = child.layoutConfig;
|
||||||
|
if (!childConfig) {
|
||||||
|
childConfig = [DoricLayoutConfig new];
|
||||||
|
}
|
||||||
|
CGSize childSize = CGSizeMake(child.width, child.height);
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]
|
if ([child isKindOfClass:[DoricLayoutContainer class]]
|
||||||
|| childConfig.widthSpec == DoricLayoutWrapContent
|
|| childConfig.widthSpec == DoricLayoutWrapContent
|
||||||
|| childConfig.heightSpec == DoricLayoutWrapContent) {
|
|| childConfig.heightSpec == DoricLayoutWrapContent) {
|
||||||
[child sizeToFit];
|
childSize = [child sizeThatFits:CGSizeMake(size.width, size.height - contentHeight)];
|
||||||
}
|
}
|
||||||
self.contentWidth = MAX(self.contentWidth, child.width);
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
self.contentHeight = MAX(self.contentHeight, child.height);
|
childSize.width = child.width;
|
||||||
|
} else if (childConfig.widthSpec == DoricLayoutAtMost) {
|
||||||
|
childSize.width = size.width;
|
||||||
}
|
}
|
||||||
if (config.widthSpec == DoricLayoutWrapContent) {
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
self.width = self.contentWidth;
|
childSize.height = child.height;
|
||||||
} else if (config.widthSpec == DoricLayoutAtMost) {
|
} else if (childConfig.heightSpec == DoricLayoutAtMost) {
|
||||||
self.width = self.superview.width;
|
childSize.height = size.height - contentHeight;
|
||||||
}
|
}
|
||||||
if (config.heightSpec == DoricLayoutWrapContent) {
|
|
||||||
self.height = self.contentHeight;
|
|
||||||
} else if (config.heightSpec == DoricLayoutAtMost) {
|
|
||||||
self.height = self.superview.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layout {
|
|
||||||
for (UIView *child in self.subviews) {
|
|
||||||
if (child.isHidden) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DoricStackConfig *childConfig = [self configForChild:child];
|
|
||||||
DoricGravity gravity = childConfig.alignment | self.gravity;
|
|
||||||
if ((gravity & LEFT) == LEFT) {
|
|
||||||
child.left = 0;
|
|
||||||
} else if ((gravity & RIGHT) == RIGHT) {
|
|
||||||
child.right = self.width;
|
|
||||||
} else if ((gravity & CENTER_X) == CENTER_X) {
|
|
||||||
child.centerX = self.width / 2;
|
|
||||||
}
|
|
||||||
if ((gravity & TOP) == TOP) {
|
|
||||||
child.top = 0;
|
|
||||||
} else if ((gravity & BOTTOM) == BOTTOM) {
|
|
||||||
child.bottom = self.height;
|
|
||||||
} else if ((gravity & CENTER_Y) == CENTER_Y) {
|
|
||||||
child.centerY = self.height / 2;
|
|
||||||
}
|
|
||||||
if (childConfig.widthSpec == DoricLayoutAtMost) {
|
|
||||||
child.width = self.width;
|
|
||||||
}
|
|
||||||
if (childConfig.heightSpec == DoricLayoutAtMost) {
|
|
||||||
child.height = self.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
|
||||||
[(DoricLayoutContainer *) child layout];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface DoricLinearView ()
|
|
||||||
@property(nonatomic, assign) CGFloat contentWidth;
|
|
||||||
@property(nonatomic, assign) CGFloat contentHeight;
|
|
||||||
@property(nonatomic, assign) NSUInteger contentWeight;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DoricLinearView
|
|
||||||
- (DoricLinearConfig *)configForChild:(UIView *)child {
|
|
||||||
DoricLinearConfig *config = (DoricLinearConfig *) child.layoutConfig;
|
|
||||||
if (!config) {
|
|
||||||
config = [[DoricLinearConfig alloc] init];
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DoricVLayoutView
|
|
||||||
|
|
||||||
- (void)sizeToFit {
|
|
||||||
DoricLayoutConfig *config = self.layoutConfig;
|
|
||||||
self.contentWidth = 0;
|
|
||||||
self.contentHeight = 0;
|
|
||||||
self.contentWeight = 0;
|
|
||||||
for (UIView *child in self.subviews) {
|
|
||||||
if (child.isHidden) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DoricLinearConfig *childConfig = [self configForChild:child];
|
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]
|
|
||||||
|| childConfig.widthSpec == DoricLayoutWrapContent
|
|
||||||
|| childConfig.heightSpec == DoricLayoutWrapContent) {
|
|
||||||
[child sizeToFit];
|
|
||||||
}
|
|
||||||
self.contentWidth = MAX(self.contentWidth, child.width + childConfig.margin.left + childConfig.margin.right);
|
|
||||||
self.contentHeight += child.height + self.space + childConfig.margin.top + childConfig.margin.bottom;
|
|
||||||
self.contentWeight += childConfig.weight;
|
|
||||||
}
|
|
||||||
self.contentHeight -= self.space;
|
|
||||||
if (config.widthSpec == DoricLayoutWrapContent) {
|
|
||||||
self.width = self.contentWidth;
|
|
||||||
} else if (config.widthSpec == DoricLayoutAtMost) {
|
|
||||||
self.width = self.superview.width;
|
|
||||||
}
|
|
||||||
if (config.heightSpec == DoricLayoutWrapContent) {
|
|
||||||
self.height = self.contentHeight;
|
|
||||||
} else if (config.heightSpec == DoricLayoutAtMost) {
|
|
||||||
self.height = self.superview.height;
|
|
||||||
}
|
|
||||||
if (self.contentWeight) {
|
|
||||||
CGFloat remain = self.height - self.contentHeight;
|
|
||||||
for (UIView *child in self.subviews) {
|
|
||||||
if (child.isHidden) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DoricLinearConfig *childConfig = [self configForChild:child];
|
|
||||||
if (childConfig.weight) {
|
if (childConfig.weight) {
|
||||||
child.height += remain / self.contentWeight * childConfig.weight;
|
childSize.height = child.height;
|
||||||
}
|
}
|
||||||
|
contentWidth = MAX(contentWidth, childSize.width + childConfig.margin.left + childConfig.margin.right);
|
||||||
|
contentHeight = MAX(contentHeight, childSize.height + childConfig.margin.top + childConfig.margin.bottom);
|
||||||
}
|
}
|
||||||
self.contentHeight = self.height;
|
self.contentWidth = contentWidth;
|
||||||
}
|
self.contentHeight = contentHeight;
|
||||||
|
return CGSizeMake(contentWidth, contentHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layout {
|
- (void)layout:(CGSize)targetSize {
|
||||||
CGFloat yStart = 0;
|
|
||||||
if ((self.gravity & TOP) == TOP) {
|
|
||||||
yStart = 0;
|
|
||||||
} else if ((self.gravity & BOTTOM) == BOTTOM) {
|
|
||||||
yStart = self.height - self.contentHeight;
|
|
||||||
} else if ((self.gravity & CENTER_Y) == CENTER_Y) {
|
|
||||||
yStart = (self.height - self.contentHeight) / 2;
|
|
||||||
}
|
|
||||||
for (UIView *child in self.subviews) {
|
for (UIView *child in self.subviews) {
|
||||||
if (child.isHidden) {
|
if (child.isHidden) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DoricLinearConfig *childConfig = [self configForChild:child];
|
DoricLayoutConfig *childConfig = child.layoutConfig;
|
||||||
|
if (!childConfig) {
|
||||||
|
childConfig = [DoricLayoutConfig new];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGSize size = [child sizeThatFits:CGSizeMake(targetSize.width, targetSize.height)];
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
size.width = child.width;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
size.height = child.height;
|
||||||
|
}
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
size.width = child.width;
|
||||||
|
} else if (childConfig.widthSpec == DoricLayoutAtMost) {
|
||||||
|
size.width = targetSize.width;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
size.height = child.height;
|
||||||
|
} else if (childConfig.heightSpec == DoricLayoutAtMost) {
|
||||||
|
size.height = targetSize.height;
|
||||||
|
}
|
||||||
|
child.width = size.width;
|
||||||
|
child.height = size.height;
|
||||||
|
|
||||||
DoricGravity gravity = childConfig.alignment | self.gravity;
|
DoricGravity gravity = childConfig.alignment | self.gravity;
|
||||||
|
|
||||||
if ((gravity & LEFT) == LEFT) {
|
if ((gravity & LEFT) == LEFT) {
|
||||||
child.left = 0;
|
child.left = 0;
|
||||||
} else if ((gravity & RIGHT) == RIGHT) {
|
} else if ((gravity & RIGHT) == RIGHT) {
|
||||||
child.right = self.width;
|
child.right = targetSize.width;
|
||||||
} else if ((gravity & CENTER_X) == CENTER_X) {
|
} else if ((gravity & CENTER_X) == CENTER_X) {
|
||||||
child.centerX = self.width / 2;
|
child.centerX = targetSize.width / 2;
|
||||||
} else {
|
} else {
|
||||||
if (childConfig.margin.left) {
|
if (childConfig.margin.left) {
|
||||||
child.left = childConfig.margin.left;
|
child.left = childConfig.margin.left;
|
||||||
} else if (childConfig.margin.right) {
|
} else if (childConfig.margin.right) {
|
||||||
child.right = self.width - childConfig.margin.right;
|
child.right = targetSize.width - childConfig.margin.right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (childConfig.widthSpec == DoricLayoutAtMost) {
|
|
||||||
child.width = self.width;
|
if ((gravity & TOP) == TOP) {
|
||||||
|
child.top = 0;
|
||||||
|
} else if ((gravity & BOTTOM) == BOTTOM) {
|
||||||
|
child.bottom = targetSize.height;
|
||||||
|
} else if ((gravity & CENTER_Y) == CENTER_Y) {
|
||||||
|
child.centerY = targetSize.height / 2;
|
||||||
|
} else {
|
||||||
|
if (childConfig.margin.top) {
|
||||||
|
child.top = childConfig.margin.top;
|
||||||
|
} else if (childConfig.margin.bottom) {
|
||||||
|
child.bottom = targetSize.height - childConfig.margin.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
||||||
|
[(DoricLayoutContainer *) child layout:size];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.width = targetSize.width;
|
||||||
|
self.height = targetSize.height;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricLinearView
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricVLayoutView
|
||||||
|
- (CGSize)sizeContent:(CGSize)size {
|
||||||
|
CGFloat contentWidth = 0;
|
||||||
|
CGFloat contentHeight = 0;
|
||||||
|
NSUInteger contentWeight = 0;
|
||||||
|
for (UIView *child in self.subviews) {
|
||||||
|
if (child.isHidden) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DoricLayoutConfig *childConfig = child.layoutConfig;
|
||||||
|
if (!childConfig) {
|
||||||
|
childConfig = [DoricLayoutConfig new];
|
||||||
|
}
|
||||||
|
CGSize childSize = CGSizeMake(child.width, child.height);
|
||||||
|
if ([child isKindOfClass:[DoricLayoutContainer class]]
|
||||||
|
|| childConfig.widthSpec == DoricLayoutWrapContent
|
||||||
|
|| childConfig.heightSpec == DoricLayoutWrapContent) {
|
||||||
|
childSize = [child sizeThatFits:CGSizeMake(size.width, size.height - contentHeight)];
|
||||||
|
}
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
childSize.width = child.width;
|
||||||
|
} else if (childConfig.widthSpec == DoricLayoutAtMost) {
|
||||||
|
childSize.width = size.width;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
childSize.height = child.height;
|
||||||
|
} else if (childConfig.heightSpec == DoricLayoutAtMost) {
|
||||||
|
childSize.height = size.height - contentHeight;
|
||||||
|
}
|
||||||
|
if (childConfig.weight) {
|
||||||
|
childSize.height = child.height;
|
||||||
|
}
|
||||||
|
contentWidth = MAX(contentWidth, childSize.width + childConfig.margin.left + childConfig.margin.right);
|
||||||
|
contentHeight += childSize.height + self.space + childConfig.margin.top + childConfig.margin.bottom;
|
||||||
|
contentWeight += childConfig.weight;
|
||||||
|
}
|
||||||
|
contentHeight -= self.space;
|
||||||
|
self.contentWidth = contentWidth;
|
||||||
|
self.contentHeight = contentHeight;
|
||||||
|
self.contentWeight = contentWeight;
|
||||||
|
if (contentWeight) {
|
||||||
|
contentHeight = size.height;
|
||||||
|
}
|
||||||
|
return CGSizeMake(contentWidth, contentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layout:(CGSize)targetSize {
|
||||||
|
CGFloat yStart = 0;
|
||||||
|
if ((self.gravity & TOP) == TOP) {
|
||||||
|
yStart = 0;
|
||||||
|
} else if ((self.gravity & BOTTOM) == BOTTOM) {
|
||||||
|
yStart = targetSize.height - self.contentHeight;
|
||||||
|
} else if ((self.gravity & CENTER_Y) == CENTER_Y) {
|
||||||
|
yStart = (targetSize.height - self.contentHeight) / 2;
|
||||||
|
}
|
||||||
|
CGFloat remain = targetSize.height - self.contentHeight;
|
||||||
|
for (UIView *child in self.subviews) {
|
||||||
|
if (child.isHidden) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DoricLayoutConfig *childConfig = child.layoutConfig;
|
||||||
|
if (!childConfig) {
|
||||||
|
childConfig = [DoricLayoutConfig new];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGSize size = [child sizeThatFits:CGSizeMake(targetSize.width, targetSize.height - yStart)];
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
size.width = child.width;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
size.height = child.height;
|
||||||
|
}
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
size.width = child.width;
|
||||||
|
} else if (childConfig.widthSpec == DoricLayoutAtMost) {
|
||||||
|
size.width = targetSize.width;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
size.height = child.height;
|
||||||
|
} else if (childConfig.heightSpec == DoricLayoutAtMost) {
|
||||||
|
size.height = targetSize.height - yStart;
|
||||||
|
}
|
||||||
|
if (childConfig.weight) {
|
||||||
|
size.height = child.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childConfig.weight) {
|
||||||
|
size.height += remain / self.contentWeight * childConfig.weight;
|
||||||
|
}
|
||||||
|
child.width = size.width;
|
||||||
|
child.height = size.height;
|
||||||
|
|
||||||
|
DoricGravity gravity = childConfig.alignment | self.gravity;
|
||||||
|
|
||||||
|
if ((gravity & LEFT) == LEFT) {
|
||||||
|
child.left = 0;
|
||||||
|
} else if ((gravity & RIGHT) == RIGHT) {
|
||||||
|
child.right = self.width;
|
||||||
|
} else if ((gravity & CENTER_X) == CENTER_X) {
|
||||||
|
child.centerX = targetSize.width / 2;
|
||||||
|
} else {
|
||||||
|
if (childConfig.margin.left) {
|
||||||
|
child.left = childConfig.margin.left;
|
||||||
|
} else if (childConfig.margin.right) {
|
||||||
|
child.right = targetSize.width - childConfig.margin.right;
|
||||||
}
|
}
|
||||||
if (childConfig.heightSpec == DoricLayoutAtMost) {
|
|
||||||
child.height = self.height - yStart - childConfig.margin.top - childConfig.margin.bottom - self.space;
|
|
||||||
}
|
}
|
||||||
if (childConfig.margin.top) {
|
if (childConfig.margin.top) {
|
||||||
yStart += childConfig.margin.top;
|
yStart += childConfig.margin.top;
|
||||||
@ -336,102 +347,124 @@ - (void)layout {
|
|||||||
yStart += childConfig.margin.bottom;
|
yStart += childConfig.margin.bottom;
|
||||||
}
|
}
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
||||||
[(DoricLayoutContainer *) child layout];
|
[(DoricLayoutContainer *) child layout:size];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.width = targetSize.width;
|
||||||
|
self.height = targetSize.height;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation DoricHLayoutView
|
@implementation DoricHLayoutView
|
||||||
- (void)sizeToFit {
|
|
||||||
DoricLinearConfig *config;
|
- (CGSize)sizeContent:(CGSize)size {
|
||||||
if ([self.superview isKindOfClass:[DoricLinearView class]]) {
|
CGFloat contentWidth = 0;
|
||||||
config = [(DoricLinearView *) self.superview configForChild:self];
|
CGFloat contentHeight = 0;
|
||||||
} else {
|
NSUInteger contentWeight = 0;
|
||||||
config = (DoricLinearConfig *) self.layoutConfig;
|
|
||||||
if (!config) {
|
|
||||||
config = [[DoricLinearConfig alloc] init];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.contentWidth = 0;
|
|
||||||
self.contentHeight = 0;
|
|
||||||
self.contentWeight = 0;
|
|
||||||
for (UIView *child in self.subviews) {
|
for (UIView *child in self.subviews) {
|
||||||
if (child.isHidden) {
|
if (child.isHidden) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DoricLinearConfig *childConfig = [self configForChild:child];
|
DoricLayoutConfig *childConfig = child.layoutConfig;
|
||||||
|
if (!childConfig) {
|
||||||
|
childConfig = [DoricLayoutConfig new];
|
||||||
|
}
|
||||||
|
CGSize childSize = CGSizeMake(child.width, child.height);
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]
|
if ([child isKindOfClass:[DoricLayoutContainer class]]
|
||||||
|| childConfig.widthSpec == DoricLayoutWrapContent
|
|| childConfig.widthSpec == DoricLayoutWrapContent
|
||||||
|| childConfig.heightSpec == DoricLayoutWrapContent) {
|
|| childConfig.heightSpec == DoricLayoutWrapContent) {
|
||||||
[child sizeToFit];
|
childSize = [child sizeThatFits:CGSizeMake(size.width - contentWidth, size.height)];
|
||||||
}
|
}
|
||||||
self.contentHeight = MAX(self.contentHeight, child.height + childConfig.margin.top + childConfig.margin.bottom);
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
self.contentWidth += child.width + self.space + childConfig.margin.left + childConfig.margin.right;
|
childSize.width = child.width;
|
||||||
self.contentWeight += childConfig.weight;
|
} else if (childConfig.widthSpec == DoricLayoutAtMost) {
|
||||||
|
childSize.width = size.width - contentWidth;
|
||||||
}
|
}
|
||||||
self.contentWidth -= self.space;
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
if (config.widthSpec == DoricLayoutWrapContent) {
|
childSize.height = child.height;
|
||||||
self.width = self.contentWidth;
|
} else if (childConfig.heightSpec == DoricLayoutAtMost) {
|
||||||
} else if (config.widthSpec == DoricLayoutAtMost) {
|
childSize.height = size.height;
|
||||||
self.width = self.superview.width;
|
|
||||||
}
|
}
|
||||||
if (config.heightSpec == DoricLayoutWrapContent) {
|
|
||||||
self.height = self.contentHeight;
|
|
||||||
} else if (config.heightSpec == DoricLayoutAtMost) {
|
|
||||||
self.height = self.superview.height;
|
|
||||||
}
|
|
||||||
if (self.contentWeight) {
|
|
||||||
CGFloat remain = self.width - self.contentWidth;
|
|
||||||
for (UIView *child in self.subviews) {
|
|
||||||
if (child.isHidden) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DoricLinearConfig *childConfig = [self configForChild:child];
|
|
||||||
if (childConfig.weight) {
|
if (childConfig.weight) {
|
||||||
child.width += remain / self.contentWeight * childConfig.weight;
|
childSize.width = child.width;
|
||||||
}
|
}
|
||||||
|
contentWidth += childSize.width + self.space + childConfig.margin.left + childConfig.margin.right;
|
||||||
|
contentHeight = MAX(contentHeight, childSize.height + childConfig.margin.top + childConfig.margin.bottom);
|
||||||
|
contentWeight += childConfig.weight;
|
||||||
}
|
}
|
||||||
self.contentWidth = self.width;
|
contentWidth -= self.space;
|
||||||
|
self.contentWidth = contentWidth;
|
||||||
|
self.contentHeight = contentHeight;
|
||||||
|
self.contentWeight = contentWeight;
|
||||||
|
if (contentWeight) {
|
||||||
|
contentWidth = size.width;
|
||||||
}
|
}
|
||||||
|
return CGSizeMake(contentWidth, contentHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layout {
|
- (void)layout:(CGSize)targetSize {
|
||||||
CGFloat xStart = 0;
|
CGFloat xStart = 0;
|
||||||
if ((self.gravity & LEFT) == LEFT) {
|
if (self.contentWeight) {
|
||||||
|
xStart = 0;
|
||||||
|
} else if ((self.gravity & LEFT) == LEFT) {
|
||||||
xStart = 0;
|
xStart = 0;
|
||||||
} else if ((self.gravity & RIGHT) == RIGHT) {
|
} else if ((self.gravity & RIGHT) == RIGHT) {
|
||||||
xStart = self.width - self.contentWidth;
|
xStart = targetSize.width - self.contentWidth;
|
||||||
} else if ((self.gravity & CENTER_X) == CENTER_X) {
|
} else if ((self.gravity & CENTER_X) == CENTER_X) {
|
||||||
xStart = (self.width - self.contentWidth) / 2;
|
xStart = (targetSize.width - self.contentWidth) / 2;
|
||||||
}
|
}
|
||||||
|
CGFloat remain = targetSize.width - self.contentWidth;
|
||||||
for (UIView *child in self.subviews) {
|
for (UIView *child in self.subviews) {
|
||||||
if (child.isHidden) {
|
if (child.isHidden) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DoricLinearConfig *childConfig = [self configForChild:child];
|
DoricLayoutConfig *childConfig = child.layoutConfig;
|
||||||
|
if (!childConfig) {
|
||||||
|
childConfig = [DoricLayoutConfig new];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGSize size = [child sizeThatFits:CGSizeMake(targetSize.width - xStart, targetSize.height)];
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
size.width = child.width;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
size.height = child.height;
|
||||||
|
}
|
||||||
|
if (childConfig.widthSpec == DoricLayoutExact) {
|
||||||
|
size.width = child.width;
|
||||||
|
} else if (childConfig.widthSpec == DoricLayoutAtMost) {
|
||||||
|
size.width = targetSize.width - xStart;
|
||||||
|
}
|
||||||
|
if (childConfig.heightSpec == DoricLayoutExact) {
|
||||||
|
size.height = child.height;
|
||||||
|
} else if (childConfig.heightSpec == DoricLayoutAtMost) {
|
||||||
|
size.height = targetSize.height;
|
||||||
|
}
|
||||||
|
if (childConfig.weight) {
|
||||||
|
size.width = child.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childConfig.weight) {
|
||||||
|
size.width += remain / self.contentWeight * childConfig.weight;
|
||||||
|
}
|
||||||
|
child.width = size.width;
|
||||||
|
child.height = size.height;
|
||||||
|
|
||||||
DoricGravity gravity = childConfig.alignment | self.gravity;
|
DoricGravity gravity = childConfig.alignment | self.gravity;
|
||||||
if ((gravity & TOP) == TOP) {
|
if ((gravity & TOP) == TOP) {
|
||||||
child.top = 0;
|
child.top = 0;
|
||||||
} else if ((gravity & BOTTOM) == BOTTOM) {
|
} else if ((gravity & BOTTOM) == BOTTOM) {
|
||||||
child.bottom = self.height;
|
child.bottom = targetSize.height;
|
||||||
} else if ((gravity & CENTER_Y) == CENTER_Y) {
|
} else if ((gravity & CENTER_Y) == CENTER_Y) {
|
||||||
child.centerY = self.height / 2;
|
child.centerY = targetSize.height / 2;
|
||||||
} else {
|
} else {
|
||||||
if (childConfig.margin.top) {
|
if (childConfig.margin.top) {
|
||||||
child.top = childConfig.margin.top;
|
child.top = childConfig.margin.top;
|
||||||
} else if (childConfig.margin.bottom) {
|
} else if (childConfig.margin.bottom) {
|
||||||
child.bottom = self.height - childConfig.margin.bottom;
|
child.bottom = targetSize.height - childConfig.margin.bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childConfig.heightSpec == DoricLayoutAtMost) {
|
|
||||||
child.height = self.height;
|
|
||||||
}
|
|
||||||
if (childConfig.widthSpec == DoricLayoutAtMost) {
|
|
||||||
child.width = self.width - xStart - childConfig.margin.right - childConfig.margin.left - self.space;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childConfig.margin.left) {
|
if (childConfig.margin.left) {
|
||||||
xStart += childConfig.margin.left;
|
xStart += childConfig.margin.left;
|
||||||
}
|
}
|
||||||
@ -441,9 +474,11 @@ - (void)layout {
|
|||||||
xStart += childConfig.margin.right;
|
xStart += childConfig.margin.right;
|
||||||
}
|
}
|
||||||
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
if ([child isKindOfClass:[DoricLayoutContainer class]]) {
|
||||||
[(DoricLayoutContainer *) child layout];
|
[(DoricLayoutContainer *) child layout:size];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.width = targetSize.width;
|
||||||
|
self.height = targetSize.height;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
25
iOS/Pod/Classes/Shader/DoricListItemNode.h
Normal file
25
iOS/Pod/Classes/Shader/DoricListItemNode.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "DoricStackNode.h"
|
||||||
|
|
||||||
|
@interface DoricListItemNode : DoricStackNode
|
||||||
|
@end
|
44
iOS/Pod/Classes/Shader/DoricListItemNode.m
Normal file
44
iOS/Pod/Classes/Shader/DoricListItemNode.m
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "DoricListItemNode.h"
|
||||||
|
#import "DoricExtensions.h"
|
||||||
|
|
||||||
|
@interface DoricListItemNode ()
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface DoricListItemView : DoricStackView
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricListItemView
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation DoricListItemNode
|
||||||
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
|
if (self = [super initWithContext:doricContext]) {
|
||||||
|
self.reusable = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DoricStackView *)build {
|
||||||
|
return [DoricListItemView new];
|
||||||
|
}
|
||||||
|
@end
|
24
iOS/Pod/Classes/Shader/DoricListNode.h
Normal file
24
iOS/Pod/Classes/Shader/DoricListNode.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "DoricSuperNode.h"
|
||||||
|
|
||||||
|
@interface DoricListNode : DoricSuperNode<UITableView *>
|
||||||
|
@end
|
153
iOS/Pod/Classes/Shader/DoricListNode.m
Normal file
153
iOS/Pod/Classes/Shader/DoricListNode.m
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <JavaScriptCore/JavaScriptCore.h>
|
||||||
|
#import "DoricListNode.h"
|
||||||
|
#import "DoricExtensions.h"
|
||||||
|
#import "DoricListItemNode.h"
|
||||||
|
|
||||||
|
@interface DoricTableViewCell : UITableViewCell
|
||||||
|
@property(nonatomic, strong) DoricListItemNode *doricListItemNode;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricTableViewCell
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface DoricListNode () <UITableViewDataSource, UITableViewDelegate>
|
||||||
|
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSString *> *itemViewIds;
|
||||||
|
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSNumber *> *itemHeights;
|
||||||
|
@property(nonatomic, assign) NSUInteger itemCount;
|
||||||
|
@property(nonatomic, assign) NSUInteger batchCount;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricListNode
|
||||||
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
|
if (self = [super initWithContext:doricContext]) {
|
||||||
|
_itemViewIds = [NSMutableDictionary new];
|
||||||
|
_itemHeights = [NSMutableDictionary new];
|
||||||
|
_batchCount = 15;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UITableView *)build {
|
||||||
|
return [[UITableView new] also:^(UITableView *it) {
|
||||||
|
it.dataSource = self;
|
||||||
|
it.delegate = self;
|
||||||
|
it.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendView:(UITableView *)view forPropName:(NSString *)name propValue:(id)prop {
|
||||||
|
if ([@"itemCount" isEqualToString:name]) {
|
||||||
|
self.itemCount = [prop unsignedIntegerValue];
|
||||||
|
} else if ([@"renderItem" isEqualToString:name]) {
|
||||||
|
[self.itemViewIds removeAllObjects];
|
||||||
|
[self clearSubModel];
|
||||||
|
} else if ([@"batchCount" isEqualToString:name]) {
|
||||||
|
self.batchCount = [prop unsignedIntegerValue];
|
||||||
|
} else {
|
||||||
|
[super blendView:view forPropName:name propValue:prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blend:(NSDictionary *)props {
|
||||||
|
[super blend:props];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||||
|
return self.itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
NSUInteger position = (NSUInteger) indexPath.row;
|
||||||
|
NSDictionary *model = [self itemModelAt:position];
|
||||||
|
NSDictionary *props = model[@"props"];
|
||||||
|
NSString *reuseId = props[@"identifier"];
|
||||||
|
|
||||||
|
DoricTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId ?: @"doriccell"];
|
||||||
|
if (!cell) {
|
||||||
|
cell = [[DoricTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseId ?: @"doriccell"];
|
||||||
|
DoricListItemNode *listItemNode = [[DoricListItemNode alloc] initWithContext:self.doricContext];
|
||||||
|
[listItemNode initWithSuperNode:self];
|
||||||
|
cell.doricListItemNode = listItemNode;
|
||||||
|
[cell.contentView addSubview:listItemNode.view];
|
||||||
|
}
|
||||||
|
DoricListItemNode *node = cell.doricListItemNode;
|
||||||
|
node.viewId = model[@"id"];
|
||||||
|
[node blend:props];
|
||||||
|
CGSize size = [node.view sizeThatFits:CGSizeMake(cell.width, cell.height)];
|
||||||
|
[self callItem:position height:size.height];
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
NSUInteger position = (NSUInteger) indexPath.row;
|
||||||
|
NSNumber *heightNumber = self.itemHeights[@(position)];
|
||||||
|
if (heightNumber) {
|
||||||
|
return [heightNumber floatValue];
|
||||||
|
} else {
|
||||||
|
return 44.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)itemModelAt:(NSUInteger)position {
|
||||||
|
NSString *viewId = self.itemViewIds[@(position)];
|
||||||
|
if (viewId && viewId.length > 0) {
|
||||||
|
return [self subModelOf:viewId];
|
||||||
|
} else {
|
||||||
|
DoricAsyncResult *result = [self callJSResponse:@"renderBunchedItems", @(position), @(self.batchCount), nil];
|
||||||
|
JSValue *models = [result waitUntilResult];
|
||||||
|
NSArray *array = [models toArray];
|
||||||
|
[array enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
|
||||||
|
NSString *thisViewId = obj[@"id"];
|
||||||
|
[self setSubModel:obj in:thisViewId];
|
||||||
|
NSUInteger pos = position + idx;
|
||||||
|
self.itemViewIds[@(pos)] = thisViewId;
|
||||||
|
}];
|
||||||
|
return array[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendSubNode:(NSDictionary *)subModel {
|
||||||
|
NSString *viewId = subModel[@"id"];
|
||||||
|
[self.itemViewIds enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull key, NSString *_Nonnull obj, BOOL *_Nonnull stop) {
|
||||||
|
if ([viewId isEqualToString:obj]) {
|
||||||
|
*stop = YES;
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[key integerValue] inSection:0];
|
||||||
|
[UIView performWithoutAnimation:^{
|
||||||
|
[self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)callItem:(NSUInteger)position height:(CGFloat)height {
|
||||||
|
NSNumber *old = self.itemHeights[@(position)];
|
||||||
|
if (old && old.floatValue == height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.itemHeights[@(position)] = @(height);
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:position inSection:0];
|
||||||
|
[UIView performWithoutAnimation:^{
|
||||||
|
[self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
@end
|
@ -25,13 +25,12 @@
|
|||||||
@implementation DoricRootNode
|
@implementation DoricRootNode
|
||||||
- (void)setupRootView:(DoricStackView *)view {
|
- (void)setupRootView:(DoricStackView *)view {
|
||||||
self.view = view;
|
self.view = view;
|
||||||
self.layoutConfig = view.layoutConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)render:(NSDictionary *)props {
|
- (void)render:(NSDictionary *)props {
|
||||||
[self blend:props];
|
[self blend:props];
|
||||||
}
|
}
|
||||||
- (void)requestLayout {
|
- (void)requestLayout {
|
||||||
[self.view requestLayout];
|
[self.view setNeedsLayout];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -22,5 +22,5 @@
|
|||||||
|
|
||||||
#import "DoricGroupNode.h"
|
#import "DoricGroupNode.h"
|
||||||
|
|
||||||
@interface DoricStackNode : DoricGroupNode<DoricStackView *, DoricStackConfig *>
|
@interface DoricStackNode : DoricGroupNode<DoricStackView *>
|
||||||
@end
|
@end
|
||||||
|
@ -21,11 +21,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "DoricStackNode.h"
|
#import "DoricStackNode.h"
|
||||||
#import "DoricUtil.h"
|
|
||||||
|
|
||||||
@implementation DoricStackNode
|
@implementation DoricStackNode
|
||||||
|
|
||||||
- (DoricStackView *)build:(NSDictionary *)props {
|
- (DoricStackView *)build {
|
||||||
return [DoricStackView new];
|
return [DoricStackView new];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,22 +35,4 @@ - (void)blendView:(DoricStackView *)view forPropName:(NSString *)name propValue:
|
|||||||
[super blendView:view forPropName:name propValue:prop];
|
[super blendView:view forPropName:name propValue:prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (DoricStackConfig *)generateDefaultLayoutParams {
|
|
||||||
return [[DoricStackConfig alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig {
|
|
||||||
[super blendChild:child layoutConfig:layoutConfig];
|
|
||||||
if (![child.layoutConfig isKindOfClass:DoricStackConfig.class]) {
|
|
||||||
DoricLog(@"blend DoricHLayoutView child error,layout params not match");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DoricStackConfig *params = (DoricStackConfig *) child.layoutConfig;
|
|
||||||
NSNumber *alignment = layoutConfig[@"alignment"];
|
|
||||||
if (alignment) {
|
|
||||||
params.alignment = (DoricGravity) [alignment integerValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
35
iOS/Pod/Classes/Shader/DoricSuperNode.h
Normal file
35
iOS/Pod/Classes/Shader/DoricSuperNode.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "DoricViewNode.h"
|
||||||
|
|
||||||
|
@interface DoricSuperNode<V:UIView *> : DoricViewNode<V>
|
||||||
|
- (DoricLayoutConfig *)generateDefaultLayoutParams;
|
||||||
|
|
||||||
|
- (void)blendSubNode:(DoricViewNode *)subNode layoutConfig:(NSDictionary *)layoutConfig;
|
||||||
|
|
||||||
|
- (void)blendSubNode:(NSDictionary *)subModel;
|
||||||
|
|
||||||
|
- (NSDictionary *)subModelOf:(NSString *)viewId;
|
||||||
|
|
||||||
|
- (void)setSubModel:(NSDictionary *)model in:(NSString *)viewId;
|
||||||
|
|
||||||
|
- (void)clearSubModel;
|
||||||
|
@end
|
122
iOS/Pod/Classes/Shader/DoricSuperNode.m
Normal file
122
iOS/Pod/Classes/Shader/DoricSuperNode.m
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2019/11/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "DoricSuperNode.h"
|
||||||
|
#import "DoricExtensions.h"
|
||||||
|
|
||||||
|
@interface DoricSuperNode ()
|
||||||
|
@property(nonatomic, strong) NSMutableDictionary <NSString *, NSMutableDictionary *> *subNodes;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DoricSuperNode
|
||||||
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
||||||
|
if (self = [super initWithContext:doricContext]) {
|
||||||
|
_subNodes = [NSMutableDictionary new];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop {
|
||||||
|
if ([@"subviews" isEqualToString:name]) {
|
||||||
|
NSArray *subviews = prop;
|
||||||
|
for (NSDictionary *subModel in subviews) {
|
||||||
|
[self mixinSubNode:subModel];
|
||||||
|
[self blendSubNode:subModel];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[super blendView:view forPropName:name propValue:prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)mixinSubNode:(NSDictionary *)dictionary {
|
||||||
|
NSString *viewId = dictionary[@"id"];
|
||||||
|
NSMutableDictionary *oldModel = self.subNodes[viewId];
|
||||||
|
if (oldModel) {
|
||||||
|
[self mixin:dictionary to:oldModel];
|
||||||
|
} else {
|
||||||
|
self.subNodes[viewId] = [dictionary mutableCopy];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)mixin:(NSDictionary *)srcModel to:(NSMutableDictionary *)targetModel {
|
||||||
|
NSDictionary *srcProp = srcModel[@"props"];
|
||||||
|
NSMutableDictionary *targetProp = [targetModel[@"props"] mutableCopy];
|
||||||
|
[srcProp enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||||
|
if (![@"subviews" isEqualToString:key]) {
|
||||||
|
targetProp[key] = obj;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
targetModel[@"props"] = [targetProp copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendSubNode:(DoricViewNode *)subNode layoutConfig:(NSDictionary *)layoutConfig {
|
||||||
|
DoricLayoutConfig *params = subNode.layoutConfig;
|
||||||
|
|
||||||
|
[layoutConfig[@"widthSpec"] also:^(NSNumber *it) {
|
||||||
|
if (it) {
|
||||||
|
params.widthSpec = (DoricLayoutSpec) [it integerValue];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
[layoutConfig[@"heightSpec"] also:^(NSNumber *it) {
|
||||||
|
if (it) {
|
||||||
|
params.heightSpec = (DoricLayoutSpec) [it integerValue];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
NSDictionary *margin = layoutConfig[@"margin"];
|
||||||
|
if (margin) {
|
||||||
|
params.margin = DoricMarginMake(
|
||||||
|
[(NSNumber *) margin[@"left"] floatValue],
|
||||||
|
[(NSNumber *) margin[@"top"] floatValue],
|
||||||
|
[(NSNumber *) margin[@"right"] floatValue],
|
||||||
|
[(NSNumber *) margin[@"bottom"] floatValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
NSNumber *alignment = layoutConfig[@"alignment"];
|
||||||
|
if (alignment) {
|
||||||
|
params.alignment = (DoricGravity) [alignment integerValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)blendSubNode:(NSDictionary *)subModel {
|
||||||
|
NSAssert(NO, @"Should override class:%@ ,method:%@.", NSStringFromClass([self class]),
|
||||||
|
NSStringFromSelector(_cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DoricLayoutConfig *)generateDefaultLayoutParams {
|
||||||
|
DoricLayoutConfig *params = [[DoricLayoutConfig alloc] init];
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (NSDictionary *)subModelOf:(NSString *)viewId {
|
||||||
|
return self.subNodes[viewId];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setSubModel:(NSDictionary *)model in:(NSString *)viewId {
|
||||||
|
self.subNodes[viewId] = [model mutableCopy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearSubModel {
|
||||||
|
[self.subNodes removeAllObjects];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -25,7 +25,7 @@
|
|||||||
#import "DoricGroupNode.h"
|
#import "DoricGroupNode.h"
|
||||||
|
|
||||||
@implementation DoricTextNode
|
@implementation DoricTextNode
|
||||||
- (id)build:(NSDictionary *)props {
|
- (UILabel *)build {
|
||||||
return [[UILabel alloc] init];
|
return [[UILabel alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,5 +22,5 @@
|
|||||||
|
|
||||||
#import "DoricGroupNode.h"
|
#import "DoricGroupNode.h"
|
||||||
|
|
||||||
@interface DoricVLayoutNode : DoricGroupNode<DoricVLayoutView *, DoricLinearConfig *>
|
@interface DoricVLayoutNode : DoricGroupNode<DoricVLayoutView *>
|
||||||
@end
|
@end
|
||||||
|
@ -21,11 +21,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "DoricVLayoutNode.h"
|
#import "DoricVLayoutNode.h"
|
||||||
#import "DoricUtil.h"
|
|
||||||
|
|
||||||
@implementation DoricVLayoutNode
|
@implementation DoricVLayoutNode
|
||||||
|
|
||||||
- (DoricVLayoutView *)build:(NSDictionary *)props {
|
- (DoricVLayoutView *)build {
|
||||||
return [DoricVLayoutView new];
|
return [DoricVLayoutView new];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,28 +38,4 @@ - (void)blendView:(DoricVLayoutView *)view forPropName:(NSString *)name propValu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig {
|
|
||||||
[super blendChild:child layoutConfig:layoutconfig];
|
|
||||||
if (![child.layoutConfig isKindOfClass:DoricLinearConfig.class]) {
|
|
||||||
DoricLog(@"blend DoricVLayoutView child error,layout params not match");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DoricLinearConfig *params = (DoricLinearConfig *) child.layoutConfig;
|
|
||||||
NSDictionary *margin = layoutconfig[@"margin"];
|
|
||||||
if (margin) {
|
|
||||||
params.margin = DoricMarginMake(
|
|
||||||
[(NSNumber *) margin[@"left"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"top"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"right"] floatValue],
|
|
||||||
[(NSNumber *) margin[@"bottom"] floatValue]);
|
|
||||||
}
|
|
||||||
NSNumber *alignment = layoutconfig[@"alignment"];
|
|
||||||
if (alignment) {
|
|
||||||
params.alignment = (DoricGravity) [alignment integerValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (DoricLinearConfig *)generateDefaultLayoutParams {
|
|
||||||
return [[DoricLinearConfig alloc] init];
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
@ -25,30 +25,33 @@
|
|||||||
#import "UIView+Doric.h"
|
#import "UIView+Doric.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
@class DoricGroupNode;
|
@class DoricSuperNode;
|
||||||
|
|
||||||
@interface DoricViewNode <V:UIView *> : DoricContextHolder
|
@interface DoricViewNode <V:UIView *> : DoricContextHolder
|
||||||
|
|
||||||
@property(nonatomic, strong) V view;
|
@property(nonatomic, strong) V view;
|
||||||
|
@property(nonatomic, weak) DoricSuperNode *superNode;
|
||||||
@property(nonatomic, weak) DoricGroupNode *parent;
|
|
||||||
@property(nonatomic) NSInteger index;
|
@property(nonatomic) NSInteger index;
|
||||||
|
|
||||||
@property(nonatomic, strong) NSString *viewId;
|
@property(nonatomic, copy) NSString *viewId;
|
||||||
|
|
||||||
@property(nonatomic, strong) DoricLayoutConfig *layoutConfig;
|
@property(nonatomic, copy) NSString *type;
|
||||||
|
|
||||||
@property(nonatomic, strong, readonly) NSArray<NSString *> *idList;
|
@property(nonatomic, readonly) DoricLayoutConfig *layoutConfig;
|
||||||
|
|
||||||
- (V)build:(NSDictionary *)props;
|
@property(nonatomic, readonly) NSArray<NSString *> *idList;
|
||||||
|
|
||||||
|
- (void)initWithSuperNode:(DoricSuperNode *)superNode;
|
||||||
|
|
||||||
|
- (V)build;
|
||||||
|
|
||||||
- (void)blend:(NSDictionary *)props;
|
- (void)blend:(NSDictionary *)props;
|
||||||
|
|
||||||
- (void)blendView:(V)view forPropName:(NSString *)name propValue:(id)prop;
|
- (void)blendView:(V)view forPropName:(NSString *)name propValue:(id)prop;
|
||||||
|
|
||||||
- (void)callJSResponse:(NSString *)funcId, ...;
|
- (DoricAsyncResult *)callJSResponse:(NSString *)funcId, ...;
|
||||||
|
|
||||||
+ (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type;
|
+ (__kindof DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type;
|
||||||
|
|
||||||
- (void)requestLayout;
|
- (void)requestLayout;
|
||||||
@end
|
@end
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#import "DoricGroupNode.h"
|
#import "DoricGroupNode.h"
|
||||||
#import "DoricRootNode.h"
|
#import "DoricRootNode.h"
|
||||||
#import "DoricConstant.h"
|
#import "DoricConstant.h"
|
||||||
|
#import "DoricSuperNode.h"
|
||||||
|
#import "DoricExtensions.h"
|
||||||
|
|
||||||
void DoricAddEllipticArcPath(CGMutablePathRef path,
|
void DoricAddEllipticArcPath(CGMutablePathRef path,
|
||||||
CGPoint origin,
|
CGPoint origin,
|
||||||
@ -77,14 +79,23 @@ - (instancetype)initWithContext:(DoricContext *)doricContext {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIView *)build:(NSDictionary *)props {
|
|
||||||
|
- (void)initWithSuperNode:(DoricSuperNode *)superNode {
|
||||||
|
self.superNode = superNode;
|
||||||
|
self.view = [[self build] also:^(UIView *it) {
|
||||||
|
it.layoutConfig = [superNode generateDefaultLayoutParams];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DoricLayoutConfig *)layoutConfig {
|
||||||
|
return self.view.layoutConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView *)build {
|
||||||
return [[UIView alloc] init];
|
return [[UIView alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)blend:(NSDictionary *)props {
|
- (void)blend:(NSDictionary *)props {
|
||||||
if (self.view == nil) {
|
|
||||||
self.view = [self build:props];
|
|
||||||
}
|
|
||||||
self.view.layoutConfig = self.layoutConfig;
|
self.view.layoutConfig = self.layoutConfig;
|
||||||
for (NSString *key in props) {
|
for (NSString *key in props) {
|
||||||
id value = props[key];
|
id value = props[key];
|
||||||
@ -110,8 +121,8 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
|
|||||||
} else if ([name isEqualToString:@"bgColor"]) {
|
} else if ([name isEqualToString:@"bgColor"]) {
|
||||||
view.backgroundColor = DoricColor(prop);
|
view.backgroundColor = DoricColor(prop);
|
||||||
} else if ([name isEqualToString:@"layoutConfig"]) {
|
} else if ([name isEqualToString:@"layoutConfig"]) {
|
||||||
if (self.parent && [prop isKindOfClass:[NSDictionary class]]) {
|
if (self.superNode && [prop isKindOfClass:[NSDictionary class]]) {
|
||||||
[self.parent blendChild:self layoutConfig:prop];
|
[self.superNode blendSubNode:self layoutConfig:prop];
|
||||||
}
|
}
|
||||||
} else if ([name isEqualToString:@"onClick"]) {
|
} else if ([name isEqualToString:@"onClick"]) {
|
||||||
self.callbackIds[@"onClick"] = prop;
|
self.callbackIds[@"onClick"] = prop;
|
||||||
@ -176,13 +187,13 @@ - (void)onClick:(UIView *)view {
|
|||||||
DoricViewNode *node = self;
|
DoricViewNode *node = self;
|
||||||
do {
|
do {
|
||||||
[ret addObject:node.viewId];
|
[ret addObject:node.viewId];
|
||||||
node = node.parent;
|
node = node.superNode;
|
||||||
} while (node && ![node isKindOfClass:[DoricRootNode class]]);
|
} while (node && ![node isKindOfClass:[DoricRootNode class]]);
|
||||||
|
|
||||||
return [[ret reverseObjectEnumerator] allObjects];
|
return [[ret reverseObjectEnumerator] allObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)callJSResponse:(NSString *)funcId, ... {
|
- (DoricAsyncResult *)callJSResponse:(NSString *)funcId, ... {
|
||||||
NSMutableArray *array = [[NSMutableArray alloc] init];
|
NSMutableArray *array = [[NSMutableArray alloc] init];
|
||||||
[array addObject:self.idList];
|
[array addObject:self.idList];
|
||||||
[array addObject:funcId];
|
[array addObject:funcId];
|
||||||
@ -192,18 +203,21 @@ - (void)callJSResponse:(NSString *)funcId, ... {
|
|||||||
while ((arg = va_arg(args, id)) != nil) {
|
while ((arg = va_arg(args, id)) != nil) {
|
||||||
[array addObject:arg];
|
[array addObject:arg];
|
||||||
}
|
}
|
||||||
[self.doricContext callEntity:DORIC_ENTITY_RESPONSE withArgumentsArray:array];
|
DoricAsyncResult *ret = [self.doricContext callEntity:DORIC_ENTITY_RESPONSE withArgumentsArray:array];
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type {
|
+ (__kindof DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type {
|
||||||
DoricRegistry *registry = context.driver.registry;
|
DoricRegistry *registry = context.driver.registry;
|
||||||
Class clz = [registry acquireViewNode:type];
|
Class clz = [registry acquireViewNode:type];
|
||||||
return [(DoricViewNode *) [clz alloc] initWithContext:context];
|
DoricViewNode *viewNode = [(DoricViewNode *) [clz alloc] initWithContext:context];
|
||||||
|
viewNode.type = type;
|
||||||
|
return viewNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)requestLayout {
|
- (void)requestLayout {
|
||||||
[self.parent requestLayout];
|
[self.superNode requestLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -43,6 +43,8 @@ typedef void(^DoricFinishCallback)(void);
|
|||||||
- (BOOL)hasResult;
|
- (BOOL)hasResult;
|
||||||
|
|
||||||
- (R)getResult;
|
- (R)getResult;
|
||||||
|
|
||||||
|
- (R)waitUntilResult;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
@ -47,6 +47,7 @@ - (void)setupError:(NSException *)exception {
|
|||||||
self.finishCallback();
|
self.finishCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasResult {
|
- (BOOL)hasResult {
|
||||||
return self.result;
|
return self.result;
|
||||||
}
|
}
|
||||||
@ -75,4 +76,17 @@ - (void)setFinishCallback:(DoricFinishCallback)callback {
|
|||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id)waitUntilResult {
|
||||||
|
if (self.result) {
|
||||||
|
return self.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||||
|
self.resultCallback = ^(id r) {
|
||||||
|
dispatch_semaphore_signal(semaphore);
|
||||||
|
};
|
||||||
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||||
|
return self.result;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -26,7 +26,7 @@ void DoricLog(NSString *_Nonnull format, ...);
|
|||||||
|
|
||||||
UIColor *_Nonnull DoricColor(NSNumber *_Nonnull number);
|
UIColor *_Nonnull DoricColor(NSNumber *_Nonnull number);
|
||||||
|
|
||||||
NSBundle *DoricBundle();
|
NSBundle *_Nonnull DoricBundle(void);
|
||||||
|
|
||||||
#ifndef DC_LOCK
|
#ifndef DC_LOCK
|
||||||
#define DC_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
|
#define DC_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
export * from "./src/ui/view"
|
export * from "./src/ui/view"
|
||||||
|
export * from "./src/ui/layout"
|
||||||
|
export * from "./src/ui/listview"
|
||||||
|
export * from "./src/ui/widgets"
|
||||||
export * from "./src/ui/panel"
|
export * from "./src/ui/panel"
|
||||||
export * from "./src/ui/declarative"
|
export * from "./src/ui/declarative"
|
||||||
export * from "./src/util/color"
|
export * from "./src/util/color"
|
||||||
|
@ -1,51 +1,23 @@
|
|||||||
import { Text, Image, HLayout, VLayout, Stack, LayoutConfig, View } from './view'
|
/*
|
||||||
import { Color, GradientColor } from '../util/color'
|
* Copyright [2019] [Doric.Pub]
|
||||||
import { Gravity } from '../util/gravity'
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { View, LayoutSpec } from './view'
|
||||||
|
import { Stack, HLayout, VLayout } from './layout'
|
||||||
|
import { IText, IImage, Text, Image } from './widgets'
|
||||||
|
import { IList, List } from './listview'
|
||||||
|
|
||||||
export interface IView {
|
|
||||||
width?: number
|
|
||||||
height?: number
|
|
||||||
bgColor?: Color | GradientColor
|
|
||||||
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
|
|
||||||
border?: { width: number; color: Color; }
|
|
||||||
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number }
|
|
||||||
alpha?: number
|
|
||||||
hidden?: boolean
|
|
||||||
padding?: {
|
|
||||||
left?: number,
|
|
||||||
right?: number,
|
|
||||||
top?: number,
|
|
||||||
bottom?: number,
|
|
||||||
}
|
|
||||||
layoutConfig?: LayoutConfig
|
|
||||||
onClick?: Function
|
|
||||||
identifier?: string
|
|
||||||
}
|
|
||||||
export interface IText extends IView {
|
|
||||||
text?: string
|
|
||||||
textColor?: Color
|
|
||||||
textSize?: number
|
|
||||||
maxLines?: number
|
|
||||||
textAlignment?: Gravity
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IImage extends IView {
|
|
||||||
imageUrl?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IStack extends IView {
|
|
||||||
gravity?: Gravity
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IVLayout extends IView {
|
|
||||||
space?: number
|
|
||||||
gravity?: Gravity
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IHLayout extends IView {
|
|
||||||
space?: number
|
|
||||||
gravity?: Gravity
|
|
||||||
}
|
|
||||||
export function text(config: IText) {
|
export function text(config: IText) {
|
||||||
const ret = new Text
|
const ret = new Text
|
||||||
for (let key in config) {
|
for (let key in config) {
|
||||||
@ -64,6 +36,10 @@ export function image(config: IImage) {
|
|||||||
|
|
||||||
export function stack(views: View[]) {
|
export function stack(views: View[]) {
|
||||||
const ret = new Stack
|
const ret = new Stack
|
||||||
|
ret.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
}
|
||||||
for (let v of views) {
|
for (let v of views) {
|
||||||
ret.addChild(v)
|
ret.addChild(v)
|
||||||
}
|
}
|
||||||
@ -72,6 +48,10 @@ export function stack(views: View[]) {
|
|||||||
|
|
||||||
export function hlayout(views: View[]) {
|
export function hlayout(views: View[]) {
|
||||||
const ret = new HLayout
|
const ret = new HLayout
|
||||||
|
ret.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
}
|
||||||
for (let v of views) {
|
for (let v of views) {
|
||||||
ret.addChild(v)
|
ret.addChild(v)
|
||||||
}
|
}
|
||||||
@ -80,8 +60,20 @@ export function hlayout(views: View[]) {
|
|||||||
|
|
||||||
export function vlayout(views: View[]) {
|
export function vlayout(views: View[]) {
|
||||||
const ret = new VLayout
|
const ret = new VLayout
|
||||||
|
ret.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
}
|
||||||
for (let v of views) {
|
for (let v of views) {
|
||||||
ret.addChild(v)
|
ret.addChild(v)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function list(config: IList) {
|
||||||
|
const ret = new List
|
||||||
|
for (let key in config) {
|
||||||
|
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
56
js-framework/src/ui/layout.ts
Normal file
56
js-framework/src/ui/layout.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { LayoutConfig, Group, Property, IView } from "./view";
|
||||||
|
import { Gravity } from "../util/gravity";
|
||||||
|
|
||||||
|
export interface IStack extends IView {
|
||||||
|
gravity?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Stack extends Group implements IStack {
|
||||||
|
@Property
|
||||||
|
gravity?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class Root extends Stack {
|
||||||
|
|
||||||
|
}
|
||||||
|
class LinearLayout extends Group {
|
||||||
|
@Property
|
||||||
|
space?: number
|
||||||
|
|
||||||
|
@Property
|
||||||
|
gravity?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVLayout extends IView {
|
||||||
|
space?: number
|
||||||
|
gravity?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VLayout extends LinearLayout implements VLayout {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface IHLayout extends IView {
|
||||||
|
space?: number
|
||||||
|
gravity?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HLayout extends LinearLayout implements IHLayout {
|
||||||
|
}
|
86
js-framework/src/ui/listview.ts
Normal file
86
js-framework/src/ui/listview.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { View, Property, LayoutSpec, Superview, IView } from "./view";
|
||||||
|
import { Stack } from "./layout";
|
||||||
|
|
||||||
|
export function listItem(item: View) {
|
||||||
|
return (new ListItem).also((it) => {
|
||||||
|
it.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
}
|
||||||
|
it.addChild(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListItem extends Stack {
|
||||||
|
/**
|
||||||
|
* Set to reuse native view
|
||||||
|
*/
|
||||||
|
@Property
|
||||||
|
identifier?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IList extends IView {
|
||||||
|
renderItem: (index: number) => ListItem
|
||||||
|
itemCount: number
|
||||||
|
batchCount?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class List extends Superview implements IList {
|
||||||
|
private cachedViews: Map<string, ListItem> = new Map
|
||||||
|
private ignoreDirtyCallOnce = false
|
||||||
|
allSubviews() {
|
||||||
|
return this.cachedViews.values()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Property
|
||||||
|
itemCount = 0
|
||||||
|
|
||||||
|
@Property
|
||||||
|
renderItem!: (index: number) => ListItem
|
||||||
|
|
||||||
|
@Property
|
||||||
|
batchCount = 15
|
||||||
|
|
||||||
|
private getItem(itemIdx: number) {
|
||||||
|
let view = this.cachedViews.get(`${itemIdx}`)
|
||||||
|
if (view === undefined) {
|
||||||
|
view = this.renderItem(itemIdx)
|
||||||
|
view.superview = this
|
||||||
|
this.cachedViews.set(`${itemIdx}`, view)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty() {
|
||||||
|
if (this.ignoreDirtyCallOnce) {
|
||||||
|
this.ignoreDirtyCallOnce = false
|
||||||
|
//Ignore the dirty call once.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return super.isDirty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderBunchedItems(start: number, length: number) {
|
||||||
|
this.ignoreDirtyCallOnce = true;
|
||||||
|
return new Array(Math.min(length, this.itemCount - start)).fill(0).map((_, idx) => {
|
||||||
|
const listItem = this.getItem(start + idx)
|
||||||
|
return listItem.toModel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -14,9 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import './../runtime/global'
|
import './../runtime/global'
|
||||||
import { View, Group, Root } from "./view";
|
import { View, Group } from "./view";
|
||||||
import { loge, log } from '../util/log';
|
import { loge } from '../util/log';
|
||||||
import { Model } from '../util/types';
|
import { Model } from '../util/types';
|
||||||
|
import { Root } from './layout';
|
||||||
|
|
||||||
|
|
||||||
export function NativeCall(target: Panel, propertyKey: string, descriptor: PropertyDescriptor) {
|
export function NativeCall(target: Panel, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||||
@ -93,13 +94,13 @@ export abstract class Panel {
|
|||||||
for (let i = 2; i < arguments.length; i++) {
|
for (let i = 2; i < arguments.length; i++) {
|
||||||
argumentsList.push(arguments[i])
|
argumentsList.push(arguments[i])
|
||||||
}
|
}
|
||||||
Reflect.apply(v.responseCallback, v, argumentsList)
|
return Reflect.apply(v.responseCallback, v, argumentsList)
|
||||||
}
|
}
|
||||||
|
|
||||||
private retrospectView(ids: string[]): View {
|
private retrospectView(ids: string[]): View {
|
||||||
return ids.reduce((acc: View, cur) => {
|
return ids.reduce((acc: View, cur) => {
|
||||||
if (Reflect.has(acc, "subViewById")) {
|
if (Reflect.has(acc, "subviewById")) {
|
||||||
return Reflect.apply(Reflect.get(acc, "subViewById"), acc, [cur])
|
return Reflect.apply(Reflect.get(acc, "subviewById"), acc, [cur])
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
}, this.__root__)
|
}, this.__root__)
|
||||||
@ -112,13 +113,13 @@ export abstract class Panel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private hookBeforeNativeCall() {
|
private hookBeforeNativeCall() {
|
||||||
|
this.__root__.clean()
|
||||||
}
|
}
|
||||||
|
|
||||||
private hookAfterNativeCall() {
|
private hookAfterNativeCall() {
|
||||||
if (this.__root__.isDirty()) {
|
if (this.__root__.isDirty()) {
|
||||||
const model = this.__root__.toModel()
|
const model = this.__root__.toModel()
|
||||||
this.nativeRender(model)
|
this.nativeRender(model)
|
||||||
this.__root__.clean()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,13 +35,36 @@ export interface LayoutConfig {
|
|||||||
bottom?: number,
|
bottom?: number,
|
||||||
}
|
}
|
||||||
alignment?: Gravity
|
alignment?: Gravity
|
||||||
|
//Only affective in VLayout or HLayout
|
||||||
|
weight?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Property(target: Object, propKey: string) {
|
export function Property(target: Object, propKey: string) {
|
||||||
Reflect.defineMetadata(propKey, true, target)
|
Reflect.defineMetadata(propKey, true, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class View implements Modeling {
|
export interface IView {
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
bgColor?: Color | GradientColor
|
||||||
|
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
|
||||||
|
border?: { width: number; color: Color; }
|
||||||
|
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number }
|
||||||
|
alpha?: number
|
||||||
|
hidden?: boolean
|
||||||
|
padding?: {
|
||||||
|
left?: number,
|
||||||
|
right?: number,
|
||||||
|
top?: number,
|
||||||
|
bottom?: number,
|
||||||
|
}
|
||||||
|
layoutConfig?: LayoutConfig
|
||||||
|
onClick?: Function
|
||||||
|
identifier?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export abstract class View implements Modeling, IView {
|
||||||
@Property
|
@Property
|
||||||
width: number = 0
|
width: number = 0
|
||||||
|
|
||||||
@ -89,13 +112,7 @@ export abstract class View implements Modeling {
|
|||||||
@Property
|
@Property
|
||||||
onClick?: Function
|
onClick?: Function
|
||||||
|
|
||||||
/**
|
superview?: Superview
|
||||||
* Set to reuse native view
|
|
||||||
*/
|
|
||||||
@Property
|
|
||||||
identifier?: string
|
|
||||||
|
|
||||||
parent?: Group
|
|
||||||
|
|
||||||
callbacks: Map<String, Function> = new Map
|
callbacks: Map<String, Function> = new Map
|
||||||
|
|
||||||
@ -106,7 +123,10 @@ export abstract class View implements Modeling {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private id2Callback(id: string) {
|
private id2Callback(id: string) {
|
||||||
const f = this.callbacks.get(id)
|
let f = this.callbacks.get(id)
|
||||||
|
if (f === undefined) {
|
||||||
|
f = Reflect.get(this, id) as Function
|
||||||
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +193,11 @@ export abstract class View implements Modeling {
|
|||||||
}
|
}
|
||||||
/** Anchor end*/
|
/** Anchor end*/
|
||||||
|
|
||||||
__dirty_props__: { [index: string]: Model | undefined } = {}
|
private __dirty_props__: { [index: string]: Model | undefined } = {}
|
||||||
|
|
||||||
|
get dirtyProps() {
|
||||||
|
return this.__dirty_props__
|
||||||
|
}
|
||||||
|
|
||||||
nativeViewModel = {
|
nativeViewModel = {
|
||||||
id: this.viewId,
|
id: this.viewId,
|
||||||
@ -188,9 +212,6 @@ export abstract class View implements Modeling {
|
|||||||
newV = obj2Model(newV)
|
newV = obj2Model(newV)
|
||||||
}
|
}
|
||||||
this.__dirty_props__[propKey] = newV
|
this.__dirty_props__[propKey] = newV
|
||||||
if (this.parent instanceof Group) {
|
|
||||||
this.parent.onChildPropertyChanged(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clean() {
|
clean() {
|
||||||
@ -212,7 +233,7 @@ export abstract class View implements Modeling {
|
|||||||
for (let i = 1; i < arguments.length; i++) {
|
for (let i = 1; i < arguments.length; i++) {
|
||||||
argumentsList.push(arguments[i])
|
argumentsList.push(arguments[i])
|
||||||
}
|
}
|
||||||
Reflect.apply(f, this, argumentsList)
|
return Reflect.apply(f, this, argumentsList)
|
||||||
} else {
|
} else {
|
||||||
loge(`Cannot find callback:${id} for ${JSON.stringify(this.toModel())}`)
|
loge(`Cannot find callback:${id} for ${JSON.stringify(this.toModel())}`)
|
||||||
}
|
}
|
||||||
@ -221,249 +242,80 @@ export abstract class View implements Modeling {
|
|||||||
toModel() {
|
toModel() {
|
||||||
return this.nativeViewModel
|
return this.nativeViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
let(block: (it: this) => void) {
|
let(block: (it: this) => void) {
|
||||||
block(this)
|
block(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
also(block: (it: this) => void) {
|
also(block: (it: this) => void) {
|
||||||
block(this)
|
block(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
in(group: Group) {
|
in(group: Group) {
|
||||||
group.addChild(this)
|
group.addChild(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StackConfig extends LayoutConfig {
|
export abstract class Superview extends View {
|
||||||
|
subviewById(id: string): View | undefined {
|
||||||
|
for (let v of this.allSubviews()) {
|
||||||
|
if (v.viewId === id) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abstract allSubviews(): Iterable<View>
|
||||||
|
|
||||||
}
|
isDirty() {
|
||||||
|
if (super.isDirty()) {
|
||||||
export interface LinearConfig extends LayoutConfig {
|
return true
|
||||||
weight?: number
|
} else {
|
||||||
}
|
for (const v of this.allSubviews()) {
|
||||||
|
if (v.isDirty()) {
|
||||||
export interface SuperView {
|
return true
|
||||||
subViewById(id: string): View | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class Group extends View implements SuperView {
|
|
||||||
@Property
|
|
||||||
readonly children: View[] = new Proxy([], {
|
|
||||||
set: (target, index, value) => {
|
|
||||||
if (index === 'length') {
|
|
||||||
this.getDirtyChildrenModel().length = value as number
|
|
||||||
} else if (typeof index === 'string'
|
|
||||||
&& parseInt(index) >= 0
|
|
||||||
&& value instanceof View) {
|
|
||||||
value.parent = this
|
|
||||||
const childrenModel = this.getDirtyChildrenModel()
|
|
||||||
childrenModel[parseInt(index)] = value.nativeViewModel
|
|
||||||
}
|
|
||||||
if (this.parent) {
|
|
||||||
this.parent.onChildPropertyChanged(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.set(target, index, value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
subViewById(id: string): View | undefined {
|
|
||||||
for (let view of this.children) {
|
|
||||||
if (view.viewId === id) {
|
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined
|
|
||||||
}
|
}
|
||||||
addChild(view: View) {
|
return false
|
||||||
this.children.push(view)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clean() {
|
clean() {
|
||||||
this.children.forEach(e => { e.clean() })
|
for (let v of this.allSubviews()) {
|
||||||
|
v.clean()
|
||||||
|
}
|
||||||
super.clean()
|
super.clean()
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirtyChildrenModel(): Model[] {
|
|
||||||
if (this.__dirty_props__.children === undefined) {
|
|
||||||
this.__dirty_props__.children = []
|
|
||||||
}
|
|
||||||
return this.__dirty_props__.children as Model[]
|
|
||||||
}
|
|
||||||
|
|
||||||
toModel() {
|
toModel() {
|
||||||
if (this.__dirty_props__.children != undefined) {
|
const subviews = []
|
||||||
(this.__dirty_props__.children as Model[]).length = this.children.length
|
for (let v of this.allSubviews()) {
|
||||||
|
if (v.isDirty()) {
|
||||||
|
subviews.push(v.toModel())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
this.dirtyProps.subviews = subviews
|
||||||
return super.toModel()
|
return super.toModel()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onChildPropertyChanged(child: View) {
|
export abstract class Group extends Superview {
|
||||||
this.getDirtyChildrenModel()[this.children.indexOf(child)] = child.nativeViewModel
|
|
||||||
this.getDirtyChildrenModel().length = this.children.length
|
readonly children: View[] = new Proxy([], {
|
||||||
if (this.parent) {
|
set: (target, index, value) => {
|
||||||
this.parent.onChildPropertyChanged(this)
|
const ret = Reflect.set(target, index, value)
|
||||||
|
// Let getDirty return true
|
||||||
|
this.dirtyProps.children = this.children.map(e => e.viewId)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
allSubviews() {
|
||||||
|
return this.children
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(view: View) {
|
||||||
|
this.children.push(view)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirty() {
|
|
||||||
return super.isDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Stack extends Group {
|
|
||||||
@Property
|
|
||||||
gravity?: Gravity
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Scroller extends View implements SuperView {
|
|
||||||
@Property
|
|
||||||
contentView?: View
|
|
||||||
|
|
||||||
subViewById(id: string): View | undefined {
|
|
||||||
return this.contentView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Root extends Stack {
|
|
||||||
|
|
||||||
}
|
|
||||||
class LinearLayout extends Group {
|
|
||||||
@Property
|
|
||||||
space?: number
|
|
||||||
|
|
||||||
@Property
|
|
||||||
gravity?: Gravity
|
|
||||||
}
|
|
||||||
|
|
||||||
export class VLayout extends LinearLayout {
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HLayout extends LinearLayout {
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Text extends View {
|
|
||||||
@Property
|
|
||||||
text?: string
|
|
||||||
|
|
||||||
@Property
|
|
||||||
textColor?: Color
|
|
||||||
|
|
||||||
@Property
|
|
||||||
textSize?: number
|
|
||||||
|
|
||||||
@Property
|
|
||||||
maxLines?: number
|
|
||||||
|
|
||||||
@Property
|
|
||||||
textAlignment?: Gravity
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Image extends View {
|
|
||||||
@Property
|
|
||||||
imageUrl?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class List extends View implements SuperView {
|
|
||||||
private cachedViews: Map<string, View> = new Map
|
|
||||||
|
|
||||||
subViewById(id: string): View | undefined {
|
|
||||||
return this.cachedViews.get(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Property
|
|
||||||
itemCount = 0
|
|
||||||
|
|
||||||
@Property
|
|
||||||
renderItem!: (index: number) => View
|
|
||||||
|
|
||||||
|
|
||||||
private getItem(itemIdx: number) {
|
|
||||||
let view = this.cachedViews.get(`${itemIdx}`)
|
|
||||||
if (view === undefined) {
|
|
||||||
view = this.renderItem(itemIdx)
|
|
||||||
this.cachedViews.set(`${itemIdx}`, view)
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
@Property
|
|
||||||
private renderBunchedItems(items: number[]): View[] {
|
|
||||||
return items.map(e => this.getItem(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SectionList extends View implements SuperView {
|
|
||||||
private cachedViews: Map<string, View> = new Map
|
|
||||||
|
|
||||||
subViewById(id: string): View | undefined {
|
|
||||||
return this.cachedViews.get(id)
|
|
||||||
}
|
|
||||||
@Property
|
|
||||||
sectionRowsCount: number[] = []
|
|
||||||
|
|
||||||
@Property
|
|
||||||
renderSectionHeader!: (sectionIdx: number) => View
|
|
||||||
|
|
||||||
@Property
|
|
||||||
renderItem!: (sectionIdx: number, itemIdx: number) => View
|
|
||||||
|
|
||||||
@Property
|
|
||||||
sectionHeaderSticky = true
|
|
||||||
|
|
||||||
setupSectionRows(sectionCount: number, numberOfSection: (section: number) => number) {
|
|
||||||
this.sectionRowsCount = [...Array(sectionCount).keys()].map(e => numberOfSection(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
private getItem(sectionIdx: number, itemIdx: number) {
|
|
||||||
let view = this.cachedViews.get(`${sectionIdx}:${itemIdx}`)
|
|
||||||
if (view === undefined) {
|
|
||||||
view = this.renderItem(sectionIdx, itemIdx)
|
|
||||||
this.cachedViews.set(`${sectionIdx}:${itemIdx}`, view)
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSectionHeader(sectionIdx: number) {
|
|
||||||
let view = this.cachedViews.get(`${sectionIdx}:`)
|
|
||||||
if (view === undefined) {
|
|
||||||
view = this.renderSectionHeader(sectionIdx)
|
|
||||||
this.cachedViews.set(`${sectionIdx}:`, view)
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
@Property
|
|
||||||
private renderBunchedItems(items: Array<{ itemIdx: number, sectionIdx: number }>,
|
|
||||||
headers: number[]): { items: View[], headers: View[] } {
|
|
||||||
return {
|
|
||||||
items: items.map(e => this.getItem(e.sectionIdx, e.itemIdx)),
|
|
||||||
headers: headers.map(e => this.getSectionHeader(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Slide extends View implements SuperView {
|
|
||||||
@Property
|
|
||||||
pageCount = 0
|
|
||||||
|
|
||||||
@Property
|
|
||||||
renderPage!: (pageIdx: number) => View
|
|
||||||
|
|
||||||
private cachedViews: Map<string, View> = new Map
|
|
||||||
subViewById(id: string): View | undefined {
|
|
||||||
return this.cachedViews.get(id)
|
|
||||||
}
|
|
||||||
private getPage(pageIdx: number) {
|
|
||||||
let view = this.cachedViews.get(`${pageIdx}`)
|
|
||||||
if (view === undefined) {
|
|
||||||
view = this.renderPage(pageIdx)
|
|
||||||
this.cachedViews.set(`${pageIdx}`, view)
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
@Property
|
|
||||||
private renderBunchedPages(pages: number[]): View[] {
|
|
||||||
return pages.map(e => this.getPage(e))
|
|
||||||
}
|
|
||||||
}
|
|
52
js-framework/src/ui/widgets.ts
Normal file
52
js-framework/src/ui/widgets.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { IView, View, Property } from "./view"
|
||||||
|
import { Color } from "../util/color"
|
||||||
|
import { Gravity } from "../util/gravity"
|
||||||
|
|
||||||
|
export interface IText extends IView {
|
||||||
|
text?: string
|
||||||
|
textColor?: Color
|
||||||
|
textSize?: number
|
||||||
|
maxLines?: number
|
||||||
|
textAlignment?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Text extends View implements IText {
|
||||||
|
@Property
|
||||||
|
text?: string
|
||||||
|
|
||||||
|
@Property
|
||||||
|
textColor?: Color
|
||||||
|
|
||||||
|
@Property
|
||||||
|
textSize?: number
|
||||||
|
|
||||||
|
@Property
|
||||||
|
maxLines?: number
|
||||||
|
|
||||||
|
@Property
|
||||||
|
textAlignment?: Gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IImage extends IView {
|
||||||
|
imageUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Image extends View implements IImage {
|
||||||
|
@Property
|
||||||
|
imageUrl?: string
|
||||||
|
}
|
@ -77,3 +77,6 @@ export class Gravity implements Modeling {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
export function gravity() {
|
||||||
|
return new Gravity
|
||||||
|
}
|
@ -35,7 +35,7 @@ export function obj2Model(obj: Model): Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type _M = string | number | boolean | Modeling | { [index: string]: Model | undefined }
|
type _M = string | number | boolean | Modeling | { [index: string]: Model } | undefined
|
||||||
export type Model = _M | Array<_M>
|
export type Model = _M | Array<_M>
|
||||||
|
|
||||||
export type Binder<T> = (v: T) => void
|
export type Binder<T> = (v: T) => void
|
||||||
|
Reference in New Issue
Block a user