awtk-widget-chart-view 是 AWTK 提供的图表自定义控件,该控件包含:曲线图、柱状图和饼图。前段时
间记录了该控件适配 AWTK-MVVM C版本的过程,具体请参考:awtk-widget-chart-view-mvvm C版本适配笔记,这次记录一下适配 JS 版本的过程。
AWTK是 ZLG 开发的开源 GUI 引擎,官网地址:https://www.zlg.cn/index/pub/awtk.html。
AWTK GitHub 仓库:http://github.com/zlgopen/awtk-mvvm
AWTK-MVVM是一套用C语言开发的,专门为嵌入式平台优化的MVVM框架。它实现了数据绑定、命令绑定和窗口导航等基本功能。
AWTK-MVVM GitHub 仓库:http://github.com/zlgopen/awtk-mvvm
chart view 图表控件的绝大部分属性都可使用 awtk-mvvm 缺省的方法进行绑定,但 series 控件 fifo 属性比较特殊,它是一个自定义的先进先出队列,用于储存图表的序列点数据。因此,数据绑定时,需要指定一个特殊的 Model。C 语言中可以调用 serties_fifo_default_create() 接口创建该 Model;JS语言中则需使用 JS 的 Array,本项目所实现的自定义 binder 会将 Array 适配为 series 所需的 fifo。
简单理解,即基于 JS 的原生 Array 数据类型实现一个 C 版本的先进先出队列 FIFO,也就是 series_fifo_js ,此处基于 JerryScript 实现 JS 与 C 之间的适配。
JerryScript 是一个轻量级的 JavaScript 引擎,它可以运行在受限制的设备上,官网地址:https://jerryscript.net/。
series_fifo_js 的接口如下:
#ifndef TK_SERIES_FIFO_JS_H
#define TK_SERIES_FIFO_JS_H
#include "base/series_fifo.h"
#include "mvvm/jerryscript/object_js_default.h"
BEGIN_C_DECLS
struct _series_fifo_js_t;
typedef struct _series_fifo_js_t series_fifo_js_t;
typedef ret_t (*series_fifo_jsobj_set_t)(object_t* obj, jsvalue_t jsindex, jsvalue_t array);
/**
* @class series_fifo_js_t
* @parent series_fifo_t
*
* 将jerry script Array适配成series_fifo。
*
*/
struct _series_fifo_js_t {
series_fifo_t base;
/**
* @property {uint32_t} capacity
* @annotation ["readable"]
* FIFO的容量大小。
*/
uint32_t capacity;
/**
* @property {uint32_t} unit_size
* @annotation ["readable"]
* FIFO中单个元素的大小。
*/
uint32_t unit_size;
/**
* @property {uint32_t} capacity
* @annotation ["readable"]
* FIFO的容量大小。
*/
series_fifo_jsobj_set_t jsobj_set;
/*private*/
object_t* native_obj;
uint8_t* temp;
uint8_t* event_data;
};
/**
* @method series_fifo_js_create
* 创建series_fifo_js对象。
*
* @annotation ["constructor"]
* @param {object_t} native_obj jerry script Array的obj对象。
* @param {uint32_t} capacity FIFO的初始容量。
* @param {const char*} type FIFO数据类型。
*
* @return {object_t*} 返回object对象。
*/
object_t* series_fifo_js_create(object_t* native_obj, uint32_t capacity, const char* type);
/* helper function */
ret_t series_fifo_jsobj_set(object_t* obj, jsvalue_t jsindex, jsvalue_t array);
ret_t series_fifo_jsobj_push(object_t* obj, jsvalue_t array);
#define SERIES_FIFO_JS(obj) (series_fifo_js_t*)(obj)
END_C_DECLS
#endif /*TK_SERIES_FIFO_JS_H*/
series_fifo_js 的实现如下:
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "tkc/value.h"
#include "series_fifo_js.h"
#include "base/series_fifo_default.h"
#define FIFO_DATA_VALUE "value"
#define FIFO_DATA_COLOR "color"
#define FIFO_DATA_MIN "min"
#define FIFO_DATA_MAX "max"
static int series_fifo_js_base_compare(object_t* obj, const void* a, const void* b) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
return memcmp(a, b, fifo->unit_size);
}
static void* series_fifo_js_default_get(object_t* obj, uint32_t index) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t elem = jsobj_get_prop_value_by_index(jsobj, index);
if (jsvalue_check(elem) == RET_OK) {
float_t data = 0;
jsvalue_t value = jsobj_get_prop_value(elem, FIFO_DATA_VALUE);
data = jsvalue_to_number(value);
memcpy(fifo->temp, &data, sizeof(float_t));
jsvalue_unref(value);
jsvalue_unref(elem);
}
return fifo->temp;
}
static void* series_fifo_js_colorful_get(object_t* obj, uint32_t index) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t elem = jsobj_get_prop_value_by_index(jsobj, index);
if (jsvalue_check(elem) == RET_OK) {
series_data_colorful_t data = {0};
jsvalue_t value = jsobj_get_prop_value(elem, FIFO_DATA_VALUE);
jsvalue_t color = jsobj_get_prop_value(elem, FIFO_DATA_COLOR);
data.v = jsvalue_to_number(value);
data.c.color = jsvalue_to_number(color);
memcpy(fifo->temp, &data, sizeof(series_data_colorful_t));
jsvalue_unref(value);
jsvalue_unref(color);
jsvalue_unref(elem);
}
return fifo->temp;
}
static void* series_fifo_js_minmax_get(object_t* obj, uint32_t index) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t elem = jsobj_get_prop_value_by_index(jsobj, index);
if (jsvalue_check(elem) == RET_OK) {
series_data_minmax_t data = {0};
jsvalue_t min_v = jsobj_get_prop_value(elem, FIFO_DATA_MIN);
jsvalue_t max_v = jsobj_get_prop_value(elem, FIFO_DATA_MAX);
data.min = jsvalue_to_number(min_v);
data.max = jsvalue_to_number(max_v);
memcpy(fifo->temp, &data, sizeof(series_data_minmax_t));
jsvalue_unref(min_v);
jsvalue_unref(max_v);
jsvalue_unref(elem);
}
return fifo->temp;
}
static uint8_t* series_fifo_js_prepare_set(object_t* obj, uint32_t* index, const void* data,
uint32_t* nr) {
int32_t i = 0;
int32_t over_nr = 0;
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t unit_size = fifo->unit_size;
uint8_t* start = (uint8_t*)(data);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t shift = jsobj_get_prop_value(jsobj, "shift");
jsvalue_t args[1];
if (*nr > fifo->capacity) {
if (start != NULL) {
start += (*nr - fifo->capacity) * unit_size;
}
*nr = fifo->capacity;
}
over_nr = *index + *nr - fifo->capacity;
if (over_nr > 0) {
args[0] = JS_UNDEFINED;
for (i = 0; i < over_nr; i++) {
jsvalue_unref(jsfunc_call(shift, jsobj, args, 1));
}
*index -= over_nr;
jsvalue_unref(args[0]);
}
jsvalue_unref(shift);
return start;
}
static ret_t series_fifo_js_default_set(object_t* obj, uint32_t index, const void* data,
uint32_t nr) {
value_t v;
int32_t i = 0;
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t unit_size = fifo->unit_size;
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t splice = jsobj_get_prop_value(jsobj, "splice");
uint8_t* start = series_fifo_js_prepare_set(obj, &index, data, &nr);
jsvalue_t args[3];
for (i = 0; i < nr; i++) {
float_t elem = 0;
if (start != NULL) {
memcpy(&elem, start + unit_size * i, unit_size);
}
args[0] = jsvalue_from_number(index + i);
args[1] = jsvalue_from_number(1);
args[2] = JS_EMPTY_OBJ;
value_set_float(&v, elem);
jsobj_set_prop(args[2], FIFO_DATA_VALUE, &v, NULL);
jsvalue_unref(jsfunc_call(splice, jsobj, args, 3));
jsvalue_unref(args[0]);
jsvalue_unref(args[1]);
jsvalue_unref(args[2]);
}
jsvalue_unref(splice);
return RET_OK;
}
static ret_t series_fifo_js_colorful_set(object_t* obj, uint32_t index, const void* data,
uint32_t nr) {
value_t v;
int32_t i = 0;
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t unit_size = fifo->unit_size;
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t splice = jsobj_get_prop_value(jsobj, "splice");
uint8_t* start = series_fifo_js_prepare_set(obj, &index, data, &nr);
jsvalue_t args[3];
for (i = 0; i < nr; i++) {
series_data_colorful_t elem = {0};
if (start != NULL) {
memcpy(&elem, start + unit_size * i, unit_size);
}
args[0] = jsvalue_from_number(index + i);
args[1] = jsvalue_from_number(1);
args[2] = JS_EMPTY_OBJ;
value_set_float(&v, elem.v);
jsobj_set_prop(args[2], FIFO_DATA_VALUE, &v, NULL);
value_set_uint32(&v, elem.c.color);
jsobj_set_prop(args[2], FIFO_DATA_COLOR, &v, NULL);
jsvalue_unref(jsfunc_call(splice, jsobj, args, 3));
jsvalue_unref(args[0]);
jsvalue_unref(args[1]);
jsvalue_unref(args[2]);
}
jsvalue_unref(splice);
return RET_OK;
}
static ret_t series_fifo_js_minmax_set(object_t* obj, uint32_t index, const void* data,
uint32_t nr) {
value_t v;
int32_t i = 0;
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t unit_size = fifo->unit_size;
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
jsvalue_t splice = jsobj_get_prop_value(jsobj, "splice");
uint8_t* start = series_fifo_js_prepare_set(obj, &index, data, &nr);
jsvalue_t args[3];
for (i = 0; i < nr; i++) {
series_data_minmax_t elem = {0};
if (start != NULL) {
memcpy(&elem, start + unit_size * i, unit_size);
}
args[0] = jsvalue_from_number(index + i);
args[1] = jsvalue_from_number(1);
args[2] = JS_EMPTY_OBJ;
value_set_float(&v, elem.min);
jsobj_set_prop(args[2], FIFO_DATA_MIN, &v, NULL);
value_set_float(&v, elem.max);
jsobj_set_prop(args[2], FIFO_DATA_MAX, &v, NULL);
jsvalue_unref(jsfunc_call(splice, jsobj, args, 3));
jsvalue_unref(args[0]);
jsvalue_unref(args[1]);
jsvalue_unref(args[2]);
}
jsvalue_unref(splice);
return RET_OK;
}
static object_t* series_fifo_js_base_part_clone(object_t* obj, uint32_t index, uint32_t nr) {
return_value_if_fail(obj != NULL, NULL);
object_t* clone = NULL;
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL, NULL);
clone = series_fifo_default_create(nr, fifo->unit_size);
series_fifo_default_t* fifo_clone = SERIES_FIFO_DEFAULT(clone);
uint32_t unit_size = fifo->unit_size;
if (fifo_clone) {
uint8_t* data = fifo_clone->buffer;
for (int32_t i = 0; i < nr; i++) {
void* iter = fifo->base.vt->get(obj, index + i);
memcpy(data + i * unit_size, iter, unit_size);
}
fifo_clone->size = nr;
fifo_clone->cursor = fifo_clone->cursor + nr - 1;
}
return clone;
}
static ret_t series_fifo_js_base_set_capacity(object_t* obj, uint32_t capacity) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL && capacity > 0, RET_BAD_PARAMS);
fifo->capacity = capacity;
return RET_OK;
}
static uint32_t series_fifo_jsobj_get_length(jsvalue_t jsobj) {
jsvalue_t value = jsobj_get_prop_value(jsobj, "length");
uint32_t length = jsvalue_to_number(value);
jsvalue_unref(value);
return length;
}
static ret_t series_fifo_jsobj_prepare_set(object_t* obj, jsvalue_t array, uint32_t* index,
uint32_t* nr) {
uint32_t i = 0;
jsvalue_t args[2];
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
uint32_t capacity = object_get_prop_uint32(obj, SERIES_FIFO_PROP_CAPACITY, 0);
uint32_t size = object_get_prop_uint32(obj, SERIES_FIFO_PROP_SIZE, 0);
jsvalue_t push = jsobj_get_prop_value(jsobj, "push");
jsvalue_t shift = jsobj_get_prop_value(jsobj, "shift");
return_value_if_fail(jsvalue_check(jsobj) == RET_OK, RET_BAD_PARAMS);
args[0] = JS_UNDEFINED;
args[1] = JS_EMPTY_OBJ;
if (*index + *nr > size) {
for (i = 0; i < *index + *nr - size; i++) {
if (series_fifo_jsobj_get_length(jsobj) >= capacity) {
jsvalue_unref(jsfunc_call(shift, array, args, 1));
}
jsvalue_unref(jsfunc_call(push, jsobj, &args[1], 1));
}
if (*nr > capacity) {
*index = 0;
for (i = 0; i < *nr - capacity; i++) {
jsvalue_unref(jsfunc_call(shift, array, args, 1));
}
*nr = capacity;
}
}
jsvalue_unref(args[0]);
jsvalue_unref(args[1]);
jsvalue_unref(push);
jsvalue_unref(shift);
return RET_OK;
}
static ret_t series_fifo_jsobj_default_set(object_t* obj, jsvalue_t jsindex, jsvalue_t array) {
uint32_t i = 0;
uint32_t unit_size = sizeof(float_t);
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t index = jsvalue_to_number(jsindex);
uint32_t nr = series_fifo_jsobj_get_length(array);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
return_value_if_fail(jsvalue_check(jsobj) == RET_OK, RET_BAD_PARAMS);
series_fifo_jsobj_prepare_set(obj, array, &index, &nr);
if (fifo->event_data != NULL) {
TKMEM_FREE(fifo->event_data);
}
series_fifo_set_event_t e;
fifo->event_data = TKMEM_ZALLOCN(uint8_t, nr * unit_size);
series_fifo_set_event_init(&e, EVT_SERIES_FIFO_WILL_SET, obj);
e.index = index;
e.nr = nr;
for (i = 0; i < nr; i++) {
float_t data = 0;
jsvalue_t elem = jsobj_get_prop_value_by_index(array, i);
jsvalue_t value = jsobj_get_prop_value(elem, FIFO_DATA_VALUE);
data = jsvalue_to_number(value);
memcpy(fifo->event_data + unit_size * i, &data, unit_size);
jsvalue_unref(value);
jsvalue_unref(elem);
}
e.data = (void*)fifo->event_data;
emitter_dispatch(EMITTER(obj), (event_t*)&e);
return RET_OK;
}
static ret_t series_fifo_jsobj_colorful_set(object_t* obj, jsvalue_t jsindex, jsvalue_t array) {
uint32_t i = 0;
uint32_t unit_size = sizeof(series_data_colorful_t);
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t index = jsvalue_to_number(jsindex);
uint32_t nr = series_fifo_jsobj_get_length(array);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
return_value_if_fail(jsvalue_check(jsobj) == RET_OK, RET_BAD_PARAMS);
series_fifo_jsobj_prepare_set(obj, array, &index, &nr);
if (fifo->event_data != NULL) {
TKMEM_FREE(fifo->event_data);
}
series_fifo_set_event_t e;
fifo->event_data = TKMEM_ZALLOCN(uint8_t, nr * unit_size);
series_fifo_set_event_init(&e, EVT_SERIES_FIFO_WILL_SET, obj);
e.index = index;
e.nr = nr;
for (i = 0; i < nr; i++) {
series_data_colorful_t data = {0};
jsvalue_t elem = jsobj_get_prop_value_by_index(array, i);
jsvalue_t value = jsobj_get_prop_value(elem, FIFO_DATA_VALUE);
jsvalue_t color = jsobj_get_prop_value(elem, FIFO_DATA_COLOR);
data.v = jsvalue_to_number(value);
data.c.color = jsvalue_to_number(color);
memcpy(fifo->event_data + unit_size * i, &data, unit_size);
jsvalue_unref(value);
jsvalue_unref(color);
jsvalue_unref(elem);
}
e.data = (void*)fifo->event_data;
emitter_dispatch(EMITTER(obj), (event_t*)&e);
return RET_OK;
}
static ret_t series_fifo_jsobj_minmax_set(object_t* obj, jsvalue_t jsindex, jsvalue_t array) {
uint32_t i = 0;
uint32_t unit_size = sizeof(series_data_minmax_t);
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
uint32_t index = jsvalue_to_number(jsindex);
uint32_t nr = series_fifo_jsobj_get_length(array);
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
return_value_if_fail(jsvalue_check(jsobj) == RET_OK, RET_BAD_PARAMS);
series_fifo_jsobj_prepare_set(obj, array, &index, &nr);
if (fifo->event_data != NULL) {
TKMEM_FREE(fifo->event_data);
}
series_fifo_set_event_t e;
fifo->event_data = TKMEM_ZALLOCN(uint8_t, nr * unit_size);
series_fifo_set_event_init(&e, EVT_SERIES_FIFO_WILL_SET, obj);
e.index = index;
e.nr = nr;
for (i = 0; i < nr; i++) {
series_data_minmax_t data = {0};
jsvalue_t elem = jsobj_get_prop_value_by_index(array, i);
jsvalue_t min_v = jsobj_get_prop_value(elem, FIFO_DATA_MIN);
jsvalue_t max_v = jsobj_get_prop_value(elem, FIFO_DATA_MAX);
data.min = jsvalue_to_number(min_v);
data.max = jsvalue_to_number(max_v);
memcpy(fifo->event_data + unit_size * i, &data, unit_size);
jsvalue_unref(min_v);
jsvalue_unref(max_v);
jsvalue_unref(elem);
}
e.data = (void*)fifo->event_data;
emitter_dispatch(EMITTER(obj), (event_t*)&e);
return RET_OK;
}
ret_t series_fifo_jsobj_set(object_t* obj, jsvalue_t jsindex, jsvalue_t array) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
return fifo->jsobj_set(obj, jsindex, array);
}
ret_t series_fifo_jsobj_push(object_t* obj, jsvalue_t array) {
jsvalue_t args[1];
jsvalue_t jsobj = object_get_prop_uint32(obj, JSOBJ_PROP_NATIVE_OBJ, 0);
return_value_if_fail(jsvalue_check(jsobj) == RET_OK, RET_BAD_PARAMS);
jsvalue_t push = jsobj_get_prop_value(jsobj, "push");
jsvalue_t shift = jsobj_get_prop_value(jsobj, "shift");
uint32_t capacity = object_get_prop_uint32(obj, SERIES_FIFO_PROP_CAPACITY, 0);
args[0] = JS_UNDEFINED;
for (uint32_t i = 0; i < series_fifo_jsobj_get_length(array); i++) {
if (series_fifo_jsobj_get_length(jsobj) >= capacity) {
jsvalue_unref(jsfunc_call(shift, jsobj, args, 1));
}
jsvalue_t elem = jsobj_get_prop_value_by_index(array, i);
jsvalue_unref(jsfunc_call(push, jsobj, &elem, 1));
jsvalue_unref(elem);
}
jsvalue_unref(args[0]);
jsvalue_unref(push);
jsvalue_unref(shift);
series_fifo_push_event_t e;
series_fifo_push_event_init(&e, EVT_SERIES_FIFO_PUSH, obj);
e.nr = series_fifo_jsobj_get_length(array);
emitter_dispatch(EMITTER(obj), (event_t*)&e);
return RET_OK;
}
static ret_t series_fifo_js_on_destroy(object_t* obj) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
OBJECT_UNREF(fifo->native_obj);
if (fifo->temp) {
TKMEM_FREE(fifo->temp);
}
if (fifo->event_data) {
TKMEM_FREE(fifo->event_data);
}
return RET_OK;
}
static ret_t series_fifo_js_get_prop(object_t* obj, const char* name, value_t* v) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
jsvalue_t jsobj = (OBJECT_JS_BASE(fifo->native_obj))->jsobj;
if (tk_str_eq(name, JSOBJ_PROP_NATIVE_OBJ)) {
value_set_uint32(v, jsobj);
return RET_OK;
} else if (tk_str_eq(name, SERIES_FIFO_PROP_CAPACITY)) {
value_set_uint32(v, fifo->capacity);
return RET_OK;
} else if (tk_str_eq(name, SERIES_FIFO_PROP_SIZE)) {
value_set_uint32(v, series_fifo_jsobj_get_length(jsobj));
return RET_OK;
} else if (tk_str_eq(name, SERIES_FIFO_PROP_CURSOR)) {
value_set_uint32(v, series_fifo_jsobj_get_length(jsobj) - 1);
return RET_OK;
} else if (tk_str_eq(name, SERIES_FIFO_PROP_UNIT_SIZE)) {
value_set_uint32(v, fifo->unit_size);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t series_fifo_js_set_prop(object_t* obj, const char* name, const value_t* v) {
series_fifo_js_t* fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(fifo != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, SERIES_FIFO_PROP_CAPACITY)) {
series_fifo_js_base_set_capacity(obj, value_uint32(v));
return RET_OK;
} else if (tk_str_eq(name, SERIES_FIFO_PROP_SIZE)) {
return RET_OK;
} else if (tk_str_eq(name, SERIES_FIFO_PROP_CURSOR)) {
return RET_OK;
}
return RET_NOT_FOUND;
}
static const object_vtable_t s_object_vtable = {.type = "series_fifo_js",
.desc = "series_fifo_js",
.size = sizeof(series_fifo_js_t),
.is_collection = FALSE,
.on_destroy = series_fifo_js_on_destroy,
.get_prop = series_fifo_js_get_prop,
.set_prop = series_fifo_js_set_prop};
static const series_fifo_vtable_t s_series_fifo_js_default_vtable = {
.part_clone = series_fifo_js_base_part_clone,
.get = series_fifo_js_default_get,
.set = series_fifo_js_default_set,
.compare = series_fifo_js_base_compare,
.set_capacity = series_fifo_js_base_set_capacity,
};
static const series_fifo_vtable_t s_series_fifo_js_colorful_vtable = {
.part_clone = series_fifo_js_base_part_clone,
.get = series_fifo_js_colorful_get,
.set = series_fifo_js_colorful_set,
.compare = series_fifo_js_base_compare,
.set_capacity = series_fifo_js_base_set_capacity,
};
static const series_fifo_vtable_t s_series_fifo_js_minmax_vtable = {
.part_clone = series_fifo_js_base_part_clone,
.get = series_fifo_js_minmax_get,
.set = series_fifo_js_minmax_set,
.compare = series_fifo_js_base_compare,
.set_capacity = series_fifo_js_base_set_capacity,
};
static object_t* series_fifo_js_create_internal(object_t* native_obj, uint32_t capacity,
uint32_t unit_size, const series_fifo_vtable_t* vt,
series_fifo_jsobj_set_t jsobj_set) {
object_t* obj = NULL;
series_fifo_t* series_fifo = NULL;
series_fifo_js_t* fifo = NULL;
obj = object_create(&s_object_vtable);
series_fifo = SERIES_FIFO(obj);
fifo = SERIES_FIFO_JS(obj);
return_value_if_fail(obj != NULL && series_fifo != NULL && fifo != NULL, NULL);
series_fifo->vt = vt;
OBJECT_REF(native_obj);
fifo->capacity = capacity;
fifo->unit_size = unit_size;
fifo->jsobj_set = jsobj_set;
fifo->native_obj = native_obj;
fifo->temp = TKMEM_ZALLOCN(uint8_t, unit_size * 1);
fifo->event_data = NULL;
return obj;
}
object_t* series_fifo_js_create(object_t* native_obj, uint32_t capacity, const char* type) {
uint32_t unit_size = sizeof(float_t);
series_fifo_jsobj_set_t jsobj_set = series_fifo_jsobj_default_set;
const series_fifo_vtable_t* vt = &s_series_fifo_js_default_vtable;
return_value_if_fail(native_obj != NULL && capacity > 0 && type != NULL, NULL);
if (strstr(type, "colorful")) {
vt = &s_series_fifo_js_colorful_vtable;
jsobj_set = series_fifo_jsobj_colorful_set;
unit_size = sizeof(series_data_colorful_t);
} else if (strstr(type, "minmax")) {
vt = &s_series_fifo_js_minmax_vtable;
jsobj_set = series_fifo_jsobj_minmax_set;
unit_size = sizeof(series_data_minmax_t);
}
return series_fifo_js_create_internal(native_obj, capacity, unit_size, vt, jsobj_set);
}
awtk-widget-chart-view-mvvm 的自定义 binder 需要将 JS ViewModel 中指定的数组数据与 series 控件的 fifo 属性绑定起来,并且当数组对象被修改时,需要重新绑定,代码如下:
#include "awtk.h"
#include "mvvm/mvvm.h"
#include "chart_view/series.h"
#include "chart_view_custom_binder.h"
#include "jerryscript/series_fifo_js.h"
#include "mvvm/jerryscript/jsobj_4_mvvm.h"
#ifdef WITH_JERRYSCRIPT
#define VIEW_MODEL_FUNC_FIFO_SET "requestFifoSet"
#define VIEW_MODEL_FUNC_FIFO_PUSH "requestFifoPush"
static JSFUNC_DECL(chart_view_series_fifo_set) {
view_model_t* vm = VIEW_MODEL(js_view_model_get_native_ptr(call_info_p->this_value));
return_value_if_fail(vm != NULL && args_count >= 3, JS_UNDEFINED);
int32_t i = 0;
series_fifo_set_event_t e;
series_fifo_set_event_init(&e, EVT_SERIES_FIFO_SET, (void*)vm);
e.ctx = (void*)args_p;
emitter_dispatch(EMITTER(vm), (event_t*)&e);
return jsvalue_from_number(RET_OK);
}
static JSFUNC_DECL(chart_view_series_fifo_push) {
view_model_t* vm = VIEW_MODEL(js_view_model_get_native_ptr(call_info_p->this_value));
return_value_if_fail(vm != NULL && args_count >= 2, JS_UNDEFINED);
series_fifo_push_event_t e;
series_fifo_push_event_init(&e, EVT_SERIES_FIFO_PUSH, (void*)vm);
e.ctx = (void*)args_p;
emitter_dispatch(EMITTER(vm), (event_t*)&e);
return jsvalue_from_number(RET_OK);
}
static bool_t chart_view_series_is_skip_event(data_binding_t* rule, jsvalue_t jsname) {
str_t temp;
bool_t ret = false;
char* target = NULL;
str_init(&temp, 0);
target = jsvalue_to_utf8(jsname, &temp);
if (!tk_str_eq(target, rule->path)) {
ret = TRUE;
}
str_reset(&temp);
return ret;
}
static ret_t chart_view_series_fifo_on_set(void* ctx, event_t* e) {
data_binding_t* rule = DATA_BINDING(ctx);
series_fifo_set_event_t* evt = (series_fifo_set_event_t*)e;
jsvalue_t* args = (jsvalue_t*)evt->ctx;
return_value_if_fail(rule != NULL && args != NULL, RET_BAD_PARAMS);
if (!chart_view_series_is_skip_event(rule, args[0])) {
value_t v;
view_model_t* vm = VIEW_MODEL(e->target);
return_value_if_fail(vm != NULL, RET_BAD_PARAMS);
if (view_model_get_prop(vm, rule->path, &v) == RET_OK && v.type == VALUE_TYPE_OBJECT) {
object_t* fifo = NULL;
object_t* native_obj = value_object(&v);
widget_t* widget = BINDING_RULE_WIDGET(rule);
series_t* series = SERIES(widget);
return_value_if_fail(series != NULL, RET_BAD_PARAMS);
fifo = series->fifo;
if ((SERIES_FIFO_JS(fifo))->native_obj != native_obj) {
series_set_fifo(widget, native_obj);
fifo = series->fifo;
}
series_fifo_jsobj_set(fifo, args[1], args[2]);
}
}
return RET_OK;
}
static ret_t chart_view_series_fifo_on_push(void* ctx, event_t* e) {
data_binding_t* rule = DATA_BINDING(ctx);
series_fifo_push_event_t* evt = (series_fifo_push_event_t*)e;
jsvalue_t* args = (jsvalue_t*)evt->ctx;
return_value_if_fail(rule != NULL && args != NULL, RET_BAD_PARAMS);
if (!chart_view_series_is_skip_event(rule, args[0])) {
value_t v;
view_model_t* vm = VIEW_MODEL(e->target);
return_value_if_fail(vm != NULL, RET_BAD_PARAMS);
if (view_model_get_prop(vm, rule->path, &v) == RET_OK && v.type == VALUE_TYPE_OBJECT) {
object_t* fifo = NULL;
object_t* native_obj = value_object(&v);
widget_t* widget = BINDING_RULE_WIDGET(rule);
series_t* series = SERIES(widget);
return_value_if_fail(series != NULL, RET_BAD_PARAMS);
fifo = series->fifo;
if ((SERIES_FIFO_JS(fifo))->native_obj != native_obj) {
series_set_fifo(widget, native_obj);
fifo = series->fifo;
}
series_fifo_jsobj_push(fifo, args[1]);
}
}
return RET_OK;
}
static ret_t chart_view_series_set_view_model_func(view_model_t* vm) {
jsvalue_t jsvm = object_get_prop_uint32(OBJECT(vm), JSOBJ_PROP_NATIVE_OBJ, 0);
return_value_if_fail(jsvalue_check(jsvm) == RET_OK, RET_BAD_PARAMS);
if (!jsobj_has_prop_func(jsvm, VIEW_MODEL_FUNC_FIFO_SET)) {
jsobj_set_prop_func(jsvm, VIEW_MODEL_FUNC_FIFO_SET, chart_view_series_fifo_set);
}
if (!jsobj_has_prop_func(jsvm, VIEW_MODEL_FUNC_FIFO_PUSH)) {
jsobj_set_prop_func(jsvm, VIEW_MODEL_FUNC_FIFO_PUSH, chart_view_series_fifo_push);
}
return RET_OK;
}
static object_t* chart_view_series_prepare_fifo_mvvm(widget_t* widget, void* ctx, object_t* obj) {
value_t v;
object_t* curr = NULL;
object_t* fifo = NULL;
uint32_t capacity = 0;
const char* curr_type = NULL;
binding_rule_t* rule = BINDING_RULE(ctx);
return_value_if_fail(rule != NULL, NULL);
if (widget_get_prop(widget, SERIES_PROP_FIFO, &v) == RET_OK) {
curr = value_object(&v);
curr_type = object_get_type(curr);
if (tk_str_eq(curr_type, "series_fifo_js") && (SERIES_FIFO_JS(curr))->native_obj == obj) {
return curr;
}
}
capacity = widget_get_prop_int(widget, SERIES_PROP_CAPACITY, 0);
fifo = series_fifo_js_create(obj, capacity, widget_get_type(widget));
return fifo;
}
static ret_t chart_view_series_bind(binding_context_t* ctx, binding_rule_t* rule) {
if (binding_rule_is_data_binding(rule)) {
widget_t* widget = BINDING_RULE_WIDGET(rule);
data_binding_t* data_rule = DATA_BINDING(rule);
return_value_if_fail(widget != NULL && data_rule != NULL && ctx != NULL, RET_BAD_PARAMS);
if (tk_str_eq(data_rule->prop, SERIES_PROP_FIFO) && widget_is_series(widget)) {
view_model_t* vm = ctx->view_model;
return_value_if_fail(vm != NULL, RET_BAD_PARAMS);
if (strstr(object_get_type(OBJECT(vm)), "jerryscript")) {
series_set_prepare_fifo(widget, chart_view_series_prepare_fifo_mvvm, (void*)rule);
chart_view_series_set_view_model_func(vm);
emitter_on(EMITTER(vm), EVT_SERIES_FIFO_SET, chart_view_series_fifo_on_set, rule);
emitter_on(EMITTER(vm), EVT_SERIES_FIFO_PUSH, chart_view_series_fifo_on_push, rule);
}
}
}
return RET_OK;
}
#else
static ret_t chart_view_series_bind(binding_context_t* ctx, binding_rule_t* rule) {
return RET_OK;
}
#endif /*WITH_JERRYSCRIPT*/
ret_t chart_view_custom_binder_register(void) {
custom_binder_register(WIDGET_TYPE_LINE_SERIES, chart_view_series_bind);
custom_binder_register(WIDGET_TYPE_LINE_SERIES_COLORFUL, chart_view_series_bind);
custom_binder_register(WIDGET_TYPE_BAR_SERIES, chart_view_series_bind);
custom_binder_register(WIDGET_TYPE_BAR_SERIES_MINMAX, chart_view_series_bind);
return RET_OK;
}
// demos\jsdemo\application.c
#include "awtk.h"
#include "mvvm/mvvm.h"
#include "chart_view_register.h"
#include "chart_view_custom_binder.h"
ret_t application_init() {
chart_view_register();
chart_view_custom_binder_register();
navigator_to("system_bar");
navigator_to("home_page");
return RET_OK;
}
在 JS 中,使用 Array 代替 FIFO 数据类型,比如创建数组 fifo1 作为 Model:
// design\default\scripts\line_normal.js
ViewModel('line_normal', {
data: {
fifo1: [],
...
}
...
});
在 XML 的界面描述文件中,通过 v-data 属性来指定 Model 即可:
<!-- design\default\ui\window_line_series.xml -->
<chart_view name="chartview" x="6%" y="13%" w="80%" h="80%">
...
<line_series style="s1" v-data:fifo="{fifo1}"/>
<line_series style="s2" v-data:fifo="{fifo2}"/>
<tooltip/>
...
</chart_view>
当 Model 的数据需要变化并通知 View 进行更新时,有以下几种方式:
MVVM 绑定过程中,自定义 binder 会在 ViewModel 上新增以下接口:
注意:以下新增接口只能在 ViewModel 中调用,且这些接口默认会触发 series 的序列点变化动画。
声明如下:
/**
* @method requestFifoSet
* 从特定位置开始设置数据。
*
* @param {string} name Model名称。
* @param {number} index 位置索引。
* @param {array} data 数据数组。
*/
requestFifoSet(name: string, index: number, data: []);
示例用法:
// design\default\scripts\line_normal.js
ViewModel('line_normal', {
methods: {
newGraph: function () {
var rand_data1 = this._randDataGen(this.capacity);
var rand_data2 = this._randDataGen(this.capacity);
this.requestFifoSet('fifo1', 0, rand_data1);
this.requestFifoSet('fifo2', 0, rand_data2);
},
}
});
声明如下:
/**
* @method requestFifoPush
* 在尾部追加数据。
*
* @param {string} name Model名称。
* @param {array} data 数据数组。
*/
requestFifoPush(name: string, data: []);
示例用法:
// design\default\scripts\line_normal.js
ViewModel('line_normal', {
methods: {
onTimer: function () {
var rand_data1 = this._randDataGen(5);
var rand_data2 = this._randDataGen(5);
this.requestFifoPush('fifo1', rand_data1);
this.requestFifoPush('fifo2', rand_data2);
return RET_REPEAT;
}
}
});
由于 Model 本身是一个数组,因此也可以直接用 JS Array 的原生接口修改 Model 数据,比如,push、shift等接口,但修改后数据并不会马上更新到界面,需要调用 ViewModel 提供的 notifyPropsChanged() 接口通知界面更新。
注意:如果使用 Array 原生接口修改 Model 数据时改变了 Model 对象,界面更新时将自动重新绑定新的 Model 对象。