summaryrefslogtreecommitdiff
path: root/lib/luavgl/simulator/widgets
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
commit8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch)
tree02b6cf23f591915747ec2994381854a79979c4a0 /lib/luavgl/simulator/widgets
parent8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff)
downloadtangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz
Convert the main menu screen to lua lol
Diffstat (limited to 'lib/luavgl/simulator/widgets')
-rw-r--r--lib/luavgl/simulator/widgets/analog_time.c113
-rw-r--r--lib/luavgl/simulator/widgets/extension.c69
-rw-r--r--lib/luavgl/simulator/widgets/lv_analog_time.c253
-rw-r--r--lib/luavgl/simulator/widgets/lv_analog_time.h64
-rw-r--r--lib/luavgl/simulator/widgets/lv_pointer.c138
-rw-r--r--lib/luavgl/simulator/widgets/lv_pointer.h67
-rw-r--r--lib/luavgl/simulator/widgets/pointer.c101
-rw-r--r--lib/luavgl/simulator/widgets/widgets.c8
-rw-r--r--lib/luavgl/simulator/widgets/widgets.h12
-rw-r--r--lib/luavgl/simulator/widgets/widgets.lua104
10 files changed, 929 insertions, 0 deletions
diff --git a/lib/luavgl/simulator/widgets/analog_time.c b/lib/luavgl/simulator/widgets/analog_time.c
new file mode 100644
index 00000000..75dc4290
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/analog_time.c
@@ -0,0 +1,113 @@
+#include <lauxlib.h>
+#include <lua.h>
+
+#include <lvgl.h>
+#include <stdlib.h>
+
+#include <luavgl.h>
+
+#include "lv_analog_time.h"
+
+static int luavgl_analog_time_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_analog_time_create);
+}
+
+static void _lv_analog_time_set_hands(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "expect date table.");
+ return;
+ }
+
+ const void *hour, *minute, *second;
+ lua_getfield(L, -1, "hour");
+
+ hour = luavgl_toimgsrc(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "minute");
+ minute = luavgl_toimgsrc(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "second");
+ second = luavgl_toimgsrc(L, -1);
+ lua_pop(L, 1);
+
+ lv_analog_time_set_hands(obj, hour, minute, second);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t analog_time_property_table[] = {
+ {"hands", SETTER_TYPE_STACK, {.setter_stack = _lv_analog_time_set_hands}},
+ {"period", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_analog_time_set_period}},
+};
+
+/* clang-format on */
+
+static int luavgl_analog_time_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, analog_time_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ printf("unkown property for analog_time: %s\n", lua_tostring(L, -2));
+ }
+
+ return -1;
+}
+
+static int luavgl_analog_time_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_analog_time_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_analog_time_pause(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_analog_time_pause(obj);
+ return 0;
+}
+
+static int luavgl_analog_time_resume(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_analog_time_resume(obj);
+ return 0;
+}
+
+static const luaL_Reg luavgl_analog_time_methods[] = {
+ {"set", luavgl_analog_time_set },
+ {"pause", luavgl_analog_time_pause },
+ {"resume", luavgl_analog_time_resume},
+
+ {NULL, NULL },
+};
+
+void luavgl_analog_time_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_analog_time_class, "lv_analog_time",
+ luavgl_analog_time_methods);
+ lua_pop(L, 1);
+
+ luaL_getmetatable(L, "widgets");
+ lua_getfield(L, -1, "__index");
+ lua_pushcfunction(L, luavgl_analog_time_create);
+ lua_setfield(L, -2, "AnalogTime");
+ lua_pop(L, 2);
+}
diff --git a/lib/luavgl/simulator/widgets/extension.c b/lib/luavgl/simulator/widgets/extension.c
new file mode 100644
index 00000000..b9cddc5e
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/extension.c
@@ -0,0 +1,69 @@
+#include <lauxlib.h>
+#include <lua.h>
+
+#include <lvgl.h>
+#include <stdlib.h>
+
+#include <luavgl.h>
+
+static int luavgl_extension_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_obj_create);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t extension_property_table[] = {
+ {"dummy", SETTER_TYPE_STACK, {.setter_stack = _lv_dummy_set}},
+};
+/* clang-format on */
+
+static int luavgl_extension_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, extension_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ printf("unkown property for extension.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_extension_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_extension_set_property_kv, obj);
+
+ return 0;
+}
+
+static const luaL_Reg luavgl_extension_methods[] = {
+ {"set", luavgl_extension_set},
+
+ {NULL, NULL},
+};
+
+void luavgl_extension_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_btn_class, "lv_extension",
+ luavgl_extension_methods);
+ lua_pop(L, 1);
+
+ luaL_getmetatable(L, "widgets");
+ lua_getfield(L, -1, "__index");
+ lua_pushcfunction(L, luavgl_extension_create);
+ lua_setfield(L, -2, "Extension");
+ lua_pop(L, 2);
+}
diff --git a/lib/luavgl/simulator/widgets/lv_analog_time.c b/lib/luavgl/simulator/widgets/lv_analog_time.c
new file mode 100644
index 00000000..646bb156
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_analog_time.c
@@ -0,0 +1,253 @@
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_analog_time.h"
+#include "lv_pointer.h"
+
+#include <time.h>
+
+#if LV_USE_ANALOG_TIME
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_analog_time_class
+
+#define UNIT_ONE_SECOND (1000)
+#define UNIT_ONE_MINUTE (60 * UNIT_ONE_SECOND)
+#define UNIT_ONE_HOUR (60 * UNIT_ONE_MINUTE)
+#define UNIT_ONE_DAY (24 * UNIT_ONE_HOUR)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_analog_time_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj);
+static void lv_analog_time_destructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj);
+static void update_time(lv_obj_t* obj);
+static void timer_cb(lv_timer_t* timer);
+static lv_obj_t* create_hand(lv_obj_t* obj, const char* img);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+const lv_obj_class_t lv_analog_time_class = {
+ .constructor_cb = lv_analog_time_constructor,
+ .destructor_cb = lv_analog_time_destructor,
+ .instance_size = sizeof(lv_analog_time_t),
+ .base_class = &lv_obj_class,
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t* lv_analog_time_create(lv_obj_t* parent)
+{
+ lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
+ lv_obj_class_init_obj(obj);
+ return obj;
+}
+
+void lv_analog_time_set_hands(lv_obj_t* obj, const void* hour,
+ const void* minute, const void* second)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ uint32_t period = UNIT_ONE_MINUTE; /* default */
+
+ /* remove hand if it's null */
+ if (analog->second) {
+ lv_obj_del(analog->second);
+ analog->second = NULL;
+ }
+
+ if (analog->minute) {
+ lv_obj_del(analog->minute);
+ analog->minute = NULL;
+ }
+
+ if (analog->hour) {
+ lv_obj_del(analog->hour);
+ analog->hour = NULL;
+ }
+
+ /* create hand if not null */
+
+ if (hour) {
+ analog->hour = create_hand(obj, hour);
+
+ /* for hour, we use minute as unit*/
+ lv_pointer_set_range(analog->hour, 0, 24 * 60, 0, 3600 * 2);
+
+ /* update period should be faster than 1minute(default) */
+ }
+
+ if (minute) {
+ analog->minute = create_hand(obj, minute);
+
+ /* for minute, update per second */
+ lv_pointer_set_range(analog->minute, 0, 60 * 60, 0, 3600);
+
+ /* update period should be faster than 1second */
+ if (period > 1 * UNIT_ONE_SECOND) {
+ period = UNIT_ONE_SECOND;
+ }
+ }
+
+ if (second) {
+ analog->second = create_hand(obj, second);
+
+ /* update per second */
+ lv_pointer_set_range(analog->second, 0, 60 * UNIT_ONE_SECOND, 0, 3600);
+
+ /* update period should be faster than 1second */
+ if (period > 1 * UNIT_ONE_SECOND) {
+ period = UNIT_ONE_SECOND;
+ }
+ }
+
+ if (period < analog->period)
+ analog->period = period;
+
+ if (analog->hour || analog->minute || analog->second) {
+ lv_timer_resume(analog->timer);
+ } else {
+ /* no more needed */
+ lv_timer_pause(analog->timer);
+ }
+
+ update_time(obj);
+}
+
+void lv_analog_time_pause(lv_obj_t* obj)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ lv_timer_pause(analog->timer);
+}
+
+void lv_analog_time_resume(lv_obj_t* obj)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ lv_timer_resume(analog->timer);
+ update_time(obj);
+}
+
+void lv_analog_time_set_period(lv_obj_t* obj, uint32_t period)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ analog->period = period;
+ lv_timer_set_period(analog->timer, period);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_analog_time_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj)
+{
+ LV_UNUSED(class_p);
+ LV_TRACE_OBJ_CREATE("begin");
+
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ analog->period = 60 * 60 * 1000; /* default to 1Hour = 60min*60sec*1000ms*/
+ lv_obj_add_flag(lv_obj_get_parent(obj), LV_OBJ_FLAG_OVERFLOW_VISIBLE);
+
+ /* hands pivot is place to 0, 0 */
+ lv_obj_remove_style_all(obj);
+ lv_obj_set_size(obj, 0, 0);
+
+ analog->timer = lv_timer_create(timer_cb, UNIT_ONE_MINUTE, obj);
+ lv_timer_pause(analog->timer);
+
+ LV_TRACE_OBJ_CREATE("finished");
+}
+
+static void lv_analog_time_destructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ lv_timer_del(analog->timer);
+}
+
+static lv_obj_t* create_hand(lv_obj_t* obj, const char* img)
+{
+ lv_obj_t* hand;
+ hand = lv_pointer_create(obj);
+ lv_img_set_src(hand, img);
+ lv_point_t pivot;
+ lv_img_get_pivot(hand, &pivot);
+
+ /* we position the hand using image's center */
+ lv_obj_set_pos(hand, -pivot.x, -pivot.y);
+ return hand;
+}
+
+static void update_time(lv_obj_t* obj)
+{
+ int value;
+ struct timespec ts;
+ struct tm* time;
+ int ret;
+
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret != 0) {
+ LV_LOG_ERROR("get real time failed");
+ return;
+ }
+
+ time = localtime(&ts.tv_sec);
+ if (time == NULL) {
+ LV_LOG_ERROR("get local time failed");
+ return;
+ }
+
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+
+ value = time->tm_sec * 1000;
+ if (analog->period < UNIT_ONE_SECOND) {
+ /* sub-second update mode */
+ value += ts.tv_nsec / 1000000; /* add ms part */
+ }
+
+ if (analog->second)
+ lv_pointer_set_value(analog->second, value);
+
+ if (analog->minute)
+ lv_pointer_set_value(analog->minute, time->tm_min * 60 + time->tm_sec);
+
+ if (analog->hour)
+ lv_pointer_set_value(analog->hour, time->tm_hour * 60 + time->tm_min);
+}
+
+static void timer_cb(lv_timer_t* t)
+{
+ /* timeup */
+ update_time(t->user_data);
+}
+
+/* examples */
+#if 0
+
+void lv_analog_time_example(void)
+{
+ lv_obj_t* scr = lv_scr_act();
+ lv_obj_t* analog = lv_analog_time_create(scr);
+ lv_obj_center(analog);
+ lv_analog_time_set_hands(analog, "/data/hand-second.png", "/data/hand-second.png", "/data/hand-second.png");
+ lv_analog_time_set_period(analog, 60);
+}
+
+#endif
+
+#endif
diff --git a/lib/luavgl/simulator/widgets/lv_analog_time.h b/lib/luavgl/simulator/widgets/lv_analog_time.h
new file mode 100644
index 00000000..f4e63fed
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_analog_time.h
@@ -0,0 +1,64 @@
+#ifndef LV_WIDGETS_ANALOG_TIME_H_
+#define LV_WIDGETS_ANALOG_TIME_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include <lvgl.h>
+
+#if LV_USE_ANALOG_TIME
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_obj_t obj;
+ lv_obj_t* hour;
+ lv_obj_t* minute;
+ lv_obj_t* second;
+ uint32_t period;
+ lv_timer_t* timer;
+} lv_analog_time_t;
+
+extern const lv_obj_class_t lv_analog_time_class;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_obj_t* lv_analog_time_create(lv_obj_t* parent);
+
+/**
+ * Set images for hands of hour, minute and second.
+ * If specified hands image is NULL, then it's deleted.
+ *
+ * @note for simplicity, image pivot is set to image center.
+ */
+void lv_analog_time_set_hands(lv_obj_t* obj, const void* hour,
+ const void* minute, const void* second);
+
+void lv_analog_time_pause(lv_obj_t* obj);
+void lv_analog_time_resume(lv_obj_t* obj);
+void lv_analog_time_set_period(lv_obj_t* obj, uint32_t period);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /* */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_WIDGETS_ANALOG_TIME_H_ */
diff --git a/lib/luavgl/simulator/widgets/lv_pointer.c b/lib/luavgl/simulator/widgets/lv_pointer.c
new file mode 100644
index 00000000..5fe8a103
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_pointer.c
@@ -0,0 +1,138 @@
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_pointer.h"
+
+#if LV_USE_POINTER
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_pointer_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_pointer_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj);
+static void angle_update(lv_obj_t* obj);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+const lv_obj_class_t lv_pointer_class = {
+ .constructor_cb = lv_pointer_constructor,
+ .instance_size = sizeof(lv_pointer_t),
+ .base_class = &lv_img_class,
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t* lv_pointer_create(lv_obj_t* parent)
+{
+ lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
+ lv_obj_class_init_obj(obj);
+
+ /* pointer can rotate out of parent's area. */
+ lv_obj_add_flag(parent, LV_OBJ_FLAG_OVERFLOW_VISIBLE);
+ return obj;
+}
+
+void lv_pointer_set_value(lv_obj_t* obj, int value)
+{
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+ if (pointer->value == value) {
+ return;
+ }
+
+ pointer->value = value;
+ angle_update(obj);
+}
+
+void lv_pointer_set_range(lv_obj_t* obj, int value_start, int value_range,
+ int angle_start, int angle_range)
+{
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+ pointer->value_start = value_start;
+ pointer->value_range = value_range;
+ pointer->angle_start = angle_start;
+ pointer->angle_range = angle_range;
+
+ /* update angle */
+ angle_update(obj);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_pointer_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj)
+{
+ LV_UNUSED(class_p);
+ LV_TRACE_OBJ_CREATE("begin");
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+
+ /* set default range parameters */
+ pointer->angle_start = 0;
+ pointer->angle_range = 360 * 10;
+ pointer->value_start = 0;
+ pointer->value_range = 100;
+ LV_TRACE_OBJ_CREATE("finished");
+}
+
+static void angle_update(lv_obj_t* obj)
+{
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+ int angle = 0;
+ int value = pointer->value;
+ int value_start = pointer->value_start;
+ int value_range = pointer->value_range;
+ int angle_start = pointer->angle_start;
+ int angle_range = pointer->angle_range;
+
+ if (value_range != 0) {
+ /* Check value overflow, overwrite angle if so. */
+ int delta = value - value_start;
+ angle = angle_start;
+ angle += (delta * angle_range) / value_range;
+
+ /* check overflow */
+ if (value_range > 0) {
+ if (delta > value_range)
+ angle = angle_start + angle_range;
+ else if (value < value_start)
+ angle = angle_start;
+ } else { /* case of value_range < 0 */
+ if (delta < value_range)
+ angle = angle_start + angle_range;
+ else if (value > value_start)
+ angle = angle_start;
+ }
+
+ while (angle >= 3600)
+ angle -= 3600;
+ while (angle < 0)
+ angle += 3600;
+ }
+
+ if (angle == lv_img_get_angle(obj)) {
+ /* not changed */
+ return;
+ }
+
+ lv_img_set_angle(obj, angle);
+}
+
+#endif
diff --git a/lib/luavgl/simulator/widgets/lv_pointer.h b/lib/luavgl/simulator/widgets/lv_pointer.h
new file mode 100644
index 00000000..76c273e4
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_pointer.h
@@ -0,0 +1,67 @@
+#ifndef LV_WIDGETS_POINTER_H_
+#define LV_WIDGETS_POINTER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include <lvgl.h>
+
+#if LV_USE_POINTER
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_img_t obj;
+ int value;
+ int value_start;
+ int value_range;
+ int angle_start;
+ int angle_range;
+} lv_pointer_t;
+
+extern const lv_obj_class_t lv_pointer_class;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_obj_t* lv_pointer_create(lv_obj_t* parent);
+
+/**
+ * Set range parameters for pointer
+ * @param value_start the value mapped to angle_start
+ * @param value_range the value range, could be negative
+ * @param angle_start the angle will be set then value equals to value_start
+ * @param angle_range the angle range, could be negative
+ */
+void lv_pointer_set_range(lv_obj_t* obj, int value_start, int value_range,
+ int angle_start, int angle_range);
+
+/**
+ * Set current value pointer points to
+ * @param value If value is out of range, then angle is also clampped to limits
+ */
+void lv_pointer_set_value(lv_obj_t* obj, int value);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /* */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_WIDGETS_POINTER_H_ */
diff --git a/lib/luavgl/simulator/widgets/pointer.c b/lib/luavgl/simulator/widgets/pointer.c
new file mode 100644
index 00000000..b1161194
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/pointer.c
@@ -0,0 +1,101 @@
+#include <lauxlib.h>
+#include <lua.h>
+
+#include <lvgl.h>
+#include <stdlib.h>
+
+#include <luavgl.h>
+
+#include "lv_pointer.h"
+
+static int luavgl_pointer_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_pointer_create);
+}
+
+static void _lv_pointer_set_range(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "expect date table.");
+ return;
+ }
+
+ uint32_t value_start, value_range, angle_start, angle_range;
+ lua_getfield(L, -1, "valueStart");
+ value_start = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "valueRange");
+ value_range = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "angleStart");
+ angle_start = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "angleRange");
+ angle_range = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_pointer_set_range(obj, value_start, value_range, angle_start,
+ angle_range);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t pointer_property_table[] = {
+ {"range", SETTER_TYPE_STACK, {.setter_stack = _lv_pointer_set_range}},
+ {"value", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_pointer_set_value}},
+};
+
+/* clang-format on */
+
+static int luavgl_pointer_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, pointer_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_img_set_property_kv(L, obj);
+ if (ret != 0) {
+ printf("unkown property for pointer: %s\n", lua_tostring(L, -2));
+ }
+
+ return -1;
+}
+
+static int luavgl_pointer_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_pointer_set_property_kv, obj);
+
+ return 0;
+}
+
+static const luaL_Reg luavgl_pointer_methods[] = {
+ {"set", luavgl_pointer_set},
+
+ {NULL, NULL },
+};
+
+void luavgl_pointer_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_pointer_class, "lv_pointer",
+ luavgl_pointer_methods);
+ lua_pop(L, 1);
+
+ luaL_getmetatable(L, "widgets");
+ lua_getfield(L, -1, "__index");
+ lua_pushcfunction(L, luavgl_pointer_create);
+ lua_setfield(L, -2, "Pointer");
+ lua_pop(L, 2);
+}
diff --git a/lib/luavgl/simulator/widgets/widgets.c b/lib/luavgl/simulator/widgets/widgets.c
new file mode 100644
index 00000000..f8bbe073
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/widgets.c
@@ -0,0 +1,8 @@
+#include "widgets.h"
+
+void luavgl_widgets_init(lua_State *L)
+{
+ luavgl_extension_init(L);
+ luavgl_pointer_init(L);
+ luavgl_analog_time_init(L);
+} \ No newline at end of file
diff --git a/lib/luavgl/simulator/widgets/widgets.h b/lib/luavgl/simulator/widgets/widgets.h
new file mode 100644
index 00000000..77ae8f79
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/widgets.h
@@ -0,0 +1,12 @@
+#ifndef WIDGETS_WIDGETS_H_
+#define WIDGETS_WIDGETS_H_
+
+#include <lua.h>
+
+void luavgl_extension_init(lua_State *L);
+void luavgl_pointer_init(lua_State *L);
+void luavgl_analog_time_init(lua_State *L);
+
+void luavgl_widgets_init(lua_State *L);
+
+#endif /* WIDGETS_WIDGETS_H_ */
diff --git a/lib/luavgl/simulator/widgets/widgets.lua b/lib/luavgl/simulator/widgets/widgets.lua
new file mode 100644
index 00000000..9a6a39c0
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/widgets.lua
@@ -0,0 +1,104 @@
+---@meta
+
+lvgl = require "lvgl"
+---
+--- Create Extension widget on parent
+--- @param parent? Object | nil
+--- @param property? ObjectStyle
+--- @return Extension
+function lvgl.Extension(parent, property)
+end
+
+--- Create Extension widget on obj
+--- @param property? ObjectStyle
+--- @return Extension
+function obj:Extension(property)
+end
+
+---
+--- Extension widget
+---@class Extension:Object
+---
+local extension = {}
+
+--- set method
+--- @param p ObjectStyle
+--- @return nil
+function extension:set(p)
+end
+
+
+---
+--- Create Pointer widget
+--- @param parent? Object | nil
+--- @param property? ObjectStyle
+--- @return Pointer
+function lvgl.Pointer(parent, property)
+end
+
+--- Create Extension widget on obj
+--- @param property? ObjectStyle
+--- @return Extension
+function obj:Pointer(property)
+end
+
+---
+--- Pointer widget
+---@class Pointer:Object
+---
+local pointer = {}
+
+--- set method
+--- @param p ObjectStyle
+--- @return nil
+function pointer:set(p)
+end
+
+
+---
+--- Create AnalogTime widget on parent
+--- @param parent? Object | nil
+--- @param property? AnalogTimeStyle
+--- @return AnalogTime
+function lvgl.AnalogTime(parent, property)
+end
+
+--- Create AnalogTime widget on obj
+--- @param property? AnalogTimeStyle
+--- @return AnalogTime
+function obj:AnalogTime(property)
+end
+
+---
+--- Extension widget
+---@class AnalogTime:Object
+---
+local analogTime = {}
+
+--- set method
+--- @param p AnalogTimeStyle
+--- @return nil
+function analogTime:set(p)
+end
+
+--- Pause the hand refresh timer
+function analogTime:pause()
+end
+
+--- Resume the hand refresh timer
+function analogTime:resume()
+end
+
+--- Analog time hands parameter
+--- @class AnalogTimeHands
+--- @field hour string Image source path for hour hand
+--- @field minute string minute hand
+--- @field second string second hand
+---
+
+
+--- Analog time widget property
+--- @class AnalogTimeStyle :StyleProp
+--- @field hands AnalogTimeHands Hands images
+--- @field period integer Timer refresh period, default to 1s/1min depending on whether there's second/min hands
+--- \ No newline at end of file