Oopenvela-robotbandx: use new assets
3a76a038创建于 4月20日历史提交
/**
 * @file stop_watch.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "page.h"

#include "../utils/lv_obj_ext_func.h"

/*********************
 *      DEFINES
 *********************/

#define BTN_WIDTH 83
#define BTN_HEIGHT 60
#define BTN_CNT 2
#define EVENT_CNT 10

/**********************
 *      TYPEDEFS
 **********************/

typedef struct {
    const char* img_src;
    lv_obj_t* btn;
    lv_obj_t* img;
} btn_sw_t;

typedef struct {
    lv_obj_t* list_btn;
    const char* text;
    const uint16_t btn_val;
} list_btn_t;

enum BtnGrpEnum {
    BTN_START_PAUSE,
    BTN_FLAG_RESET,
};

typedef struct {
    lv_fragment_t base;
    lv_obj_t* label_time;
    lv_obj_t* list_history;
    lv_timer_t* timer_sw_update;
    btn_sw_t btn_grp[BTN_CNT];

    lv_coord_t label_time_record_y;
    lv_coord_t label_time_ready_y;

    bool sw_is_pause;
    uint32_t sw_current_time;
    uint32_t sw_interval_time;
    uint32_t sw_last_time;
    uint8_t sw_history_record_cnt;
    lv_auto_event_data_t event_data[EVENT_CNT];
    lv_auto_event_t* auto_event;
} page_ctx_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**********************
 *   STATIC FUNCTIONS
 **********************/

static void line_sw_create(lv_obj_t* par)
{
    static const lv_point_precise_t line_points[] = { { 0, 0 }, { 170, 0 } };

    lv_obj_t* line = lv_line_create(par);
    lv_line_set_points(line, line_points, ARRAY_SIZE(line_points));

    lv_obj_set_style_line_color(
        line,
        lv_color_make(0x80, 0x80, 0x80),
        LV_STATE_DEFAULT);
    lv_obj_set_style_line_width(line, 2, LV_STATE_DEFAULT);
    lv_obj_align_to(line, par, LV_ALIGN_TOP_MID, 0, 286);
}

static void list_history_create(lv_obj_t* par)
{
    page_ctx_t* ctx = (page_ctx_t*)lv_obj_get_user_data(par);

    lv_obj_t* list = lv_list_create(par);
    lv_obj_set_style_text_font(list, resource_get_font(BANDX_REGULAR_FONT "_20"), LV_PART_MAIN);
    lv_obj_set_style_bg_color(list, lv_color_black(), LV_PART_MAIN);
    lv_obj_set_style_border_width(list, 0, LV_PART_MAIN);

    lv_obj_set_size(list, 170, 170);
    lv_obj_align(list, LV_ALIGN_CENTER, 0, 0);
    ctx->list_history = list;
}

static void label_time_create(lv_obj_t* par)
{
    page_ctx_t* ctx = (page_ctx_t*)lv_obj_get_user_data(par);

    lv_obj_t* label = lv_label_create(par);
    lv_obj_set_style_text_font(label, resource_get_font(BANDX_REGULAR_FONT "_38"), LV_PART_MAIN);
    lv_obj_set_style_text_color(label, lv_color_hex(0x87FFCE), LV_PART_MAIN);

    lv_label_set_text(label, "00:00.000");
    lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
    lv_obj_align(label, LV_ALIGN_TOP_MID, 0, ctx->label_time_ready_y);
    lv_obj_set_width(label, 180);
    lv_obj_set_style_border_color(label, lv_color_make(0x80, 0x80, 0x80), LV_PART_MAIN);
    lv_obj_set_style_border_width(label, 2, LV_PART_MAIN);
    lv_obj_set_style_border_side(label, LV_BORDER_SIDE_BOTTOM, LV_PART_MAIN);
    ctx->label_time = label;
}

static void sw_show_time(uint32_t ms, lv_obj_t* label_time)
{
    uint16_t min = 0, sec = 0, msec = 0;
    min = ms / 60000;
    sec = (ms - (min * 60000)) / 1000;
    msec = ms - (sec * 1000) - (min * 60000);
    lv_label_set_text_fmt(label_time, "%02d:%02d.%03d", min, sec, msec);
}

static void sw_pause(page_ctx_t* ctx)
{
    if (ctx->sw_is_pause) {
        ctx->sw_interval_time = lv_tick_get() - ctx->sw_last_time;
        lv_timer_resume(ctx->timer_sw_update);
        ctx->sw_is_pause = false;
    } else {
        ctx->sw_last_time = ctx->sw_current_time;
        lv_timer_pause(ctx->timer_sw_update);
        ctx->sw_is_pause = true;
    }
}

static void sw_reset(page_ctx_t* ctx)
{
    ctx->sw_last_time = 0;
    ctx->sw_current_time = 0;
    ctx->sw_interval_time = 0;
    ctx->sw_is_pause = true;
    ctx->sw_history_record_cnt = 0;
    lv_timer_pause(ctx->timer_sw_update);
    sw_show_time(ctx->sw_current_time, ctx->label_time);
    lv_obj_clean(ctx->list_history);

    if (lv_obj_get_y(ctx->label_time) == ctx->label_time_record_y) {
        LV_OBJ_ADD_ANIM(ctx->label_time, y, ctx->label_time_ready_y, 500);
    }
}

static void sw_update(lv_timer_t* timer)
{
    page_ctx_t* ctx = lv_timer_get_user_data(timer);

    ctx->sw_current_time = lv_tick_get() - ctx->sw_interval_time;
    sw_show_time(ctx->sw_current_time, ctx->label_time);
}

static void sw_record(page_ctx_t* ctx)
{
    lv_obj_t* list_btn;
    lv_obj_t* spangroup;
    uint16_t min = 0, sec = 0, msec = 0;

    if (ctx->sw_history_record_cnt < 10 && !ctx->sw_is_pause) {
        min = ctx->sw_current_time / 60000;
        sec = (ctx->sw_current_time - (min * 60000)) / 1000;
        msec = ctx->sw_current_time - (sec * 1000) - (min * 60000);

        char cnt_buf[8];
        char time_buf[64];

        lv_snprintf(cnt_buf, sizeof(cnt_buf), "%d. ", ctx->sw_history_record_cnt + 1);
        lv_snprintf(time_buf, sizeof(time_buf), "%02d:%02d.%03d", min, sec, msec);

        list_btn = lv_list_add_btn(ctx->list_history, NULL, NULL);
        lv_obj_clear_flag(list_btn, LV_OBJ_FLAG_CLICKABLE);
        lv_obj_set_style_border_width(list_btn, 2, LV_STATE_DEFAULT);
        lv_obj_set_style_pad_left(list_btn, 0, LV_PART_MAIN);

        spangroup = lv_spangroup_create(list_btn);

        lv_span_t* cnt_span = lv_spangroup_new_span(spangroup);
        lv_span_set_text(cnt_span, cnt_buf);
        lv_style_set_text_color(&cnt_span->style, lv_color_hex(0x87FFCE));

        lv_span_t* time_span = lv_spangroup_new_span(spangroup);
        lv_span_set_text(time_span, time_buf);
        lv_style_set_text_color(&time_span->style, lv_color_hex(0xffffff));

        lv_obj_add_state(list_btn, LV_STATE_FOCUSED);
        lv_obj_set_style_bg_color(list_btn, lv_color_black(), 0);
        lv_obj_set_style_text_decor(
            list_btn,
            LV_TEXT_DECOR_NONE,
            LV_STATE_FOCUSED);
        ctx->sw_history_record_cnt++;
    }

    if (lv_obj_get_y(ctx->label_time) == ctx->label_time_ready_y) {
        LV_OBJ_ADD_ANIM(ctx->label_time, y, ctx->label_time_record_y, 500);
    }
}

static void btn_grp_event_handler(lv_event_t* event)
{
    lv_event_code_t code = lv_event_get_code(event);
    lv_obj_t* obj = lv_event_get_current_target_obj(event);
    page_ctx_t* ctx = (page_ctx_t*)lv_event_get_user_data(event);

    if (code == LV_EVENT_CLICKED) {
        lv_obj_t* img = lv_obj_get_child(obj, 0);
        const char* img_src = lv_img_get_src(img);

        if (obj == ctx->btn_grp[BTN_START_PAUSE].btn) {
            sw_pause(ctx);
            if (ctx->sw_is_pause) {
                lv_img_set_src(ctx->btn_grp[BTN_START_PAUSE].img, resource_get_img("icon_start"));
                lv_img_set_src(ctx->btn_grp[BTN_FLAG_RESET].img, resource_get_img("icon_reset"));
            } else {
                lv_img_set_src(ctx->btn_grp[BTN_START_PAUSE].img, resource_get_img("icon_pause"));
                lv_img_set_src(ctx->btn_grp[BTN_FLAG_RESET].img, resource_get_img("icon_flag"));
                lv_obj_clear_state(ctx->btn_grp[BTN_FLAG_RESET].btn, LV_STATE_DISABLED);
            }
        } else if (obj == ctx->btn_grp[BTN_FLAG_RESET].btn) {
            if (lv_strcmp(img_src, resource_get_img("icon_flag")) == 0) {
                sw_record(ctx);
            } else {
                if (ctx->sw_is_pause) {
                    sw_reset(ctx);
                    lv_obj_add_state(ctx->btn_grp[BTN_FLAG_RESET].btn, LV_STATE_DISABLED);
                    ctx->sw_history_record_cnt = 0;
                }
            }
        }
    }
}

static void btn_grp_create(lv_obj_t* par, btn_sw_t* btn_sw, int len)
{
    page_ctx_t* ctx = (page_ctx_t*)lv_obj_get_user_data(par);

    for (int i = 0; i < len; i++) {
        lv_obj_t* btn = lv_btn_create(par);
        lv_obj_set_size(btn, BTN_WIDTH, BTN_HEIGHT);

        lv_coord_t x_ofs = BTN_WIDTH / 2 + 3;
        lv_obj_align(btn, LV_ALIGN_TOP_MID, i == 0 ? -x_ofs : x_ofs, 300);
        lv_obj_set_style_border_color(btn, lv_color_black(), LV_STATE_DEFAULT);
        lv_obj_set_style_border_width(btn, 2, LV_STATE_DEFAULT);
        lv_obj_set_style_bg_color(
            btn,
            lv_palette_main(LV_PALETTE_GREY),
            LV_STATE_PRESSED || LV_STATE_DISABLED);
        lv_obj_set_style_bg_color(
            btn,
            lv_color_make(0x87, 0xFF, 0xCE),
            LV_STATE_DEFAULT);
        lv_obj_set_style_radius(btn, 10, LV_PART_MAIN);
        lv_obj_add_event(btn, btn_grp_event_handler, LV_EVENT_ALL, ctx);

        lv_obj_t* img = lv_img_create(btn);
        lv_img_set_src(img, resource_get_img(btn_sw[i].img_src));
        lv_obj_align_to(img, btn, LV_ALIGN_CENTER, 0, 0);

        btn_sw[i].btn = btn;
        btn_sw[i].img = img;
    }
}

static void auto_event_create(page_ctx_t* ctx)
{
    btn_sw_t* btn_sw = ctx->btn_grp;
    lv_auto_event_data_t ae_grp[EVENT_CNT] = {
        { &(btn_sw[BTN_START_PAUSE].btn), LV_EVENT_CLICKED, 2000 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 453 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 888 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 1605 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 785 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 1200 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 1456 },
        { &(btn_sw[BTN_START_PAUSE].btn), LV_EVENT_CLICKED, 2000 },
        { &(btn_sw[BTN_FLAG_RESET].btn), LV_EVENT_CLICKED, 1000 },
        { &ctx->base.obj, LV_EVENT_LEAVE, 2000 },
    };
    for (int i = 0; i < EVENT_CNT; i++) {
        ctx->event_data[i] = ae_grp[i];
    }

    AUTO_EVENT_CREATE(ctx->auto_event, ctx->event_data);
}

static void on_root_event(lv_event_t* e)
{
    lv_obj_t* root = lv_event_get_current_target_obj(e);
    lv_event_code_t code = lv_event_get_code(e);
    page_ctx_t* ctx = lv_obj_get_user_data(root);

    if (code == LV_EVENT_GESTURE) {
        lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_get_act());
        if (dir == LV_DIR_RIGHT) {
            lv_obj_send_event(root, LV_EVENT_LEAVE, NULL);
        }
    } else if (code == LV_EVENT_LEAVE) {
        page_pop(&ctx->base);
    }
}

static void on_page_construct(lv_fragment_t* self, void* args)
{
    LV_LOG_INFO("self: %p args: %p", self, args);

    page_ctx_t* ctx = (page_ctx_t*)self;

    btn_sw_t btn_grp[BTN_CNT] = {
        { "icon_start" },
        { "icon_reset" },
    };

    for (int i = 0; i < BTN_CNT; i++) {
        ctx->btn_grp[i] = btn_grp[i];
    }

    ctx->label_time_record_y = 18;
    ctx->label_time_ready_y = 64;

    ctx->sw_is_pause = true;
    ctx->sw_current_time = 0;
    ctx->sw_interval_time = 0;
    ctx->sw_last_time = 0;
    ctx->sw_history_record_cnt = 0;
}

static void on_page_destruct(lv_fragment_t* self)
{
    LV_LOG_INFO("self: %p", self);
}

static void on_page_attached(lv_fragment_t* self)
{
    LV_LOG_INFO("self: %p", self);
}

static void on_page_detached(lv_fragment_t* self)
{
    LV_LOG_INFO("self: %p", self);
}

static lv_obj_t* on_page_create(lv_fragment_t* self, lv_obj_t* container)
{
    LV_LOG_INFO("self: %p container: %p", self, container);

    lv_obj_t* root = lv_obj_create(container);
    lv_obj_remove_style_all(root);
    lv_obj_add_style(root, resource_get_style("root_def"), 0);
    lv_obj_add_event(root, on_root_event, LV_EVENT_ALL, NULL);
    lv_obj_clear_flag(root, LV_OBJ_FLAG_GESTURE_BUBBLE);
    lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_user_data(root, self);
    return root;
}

static void on_page_created(lv_fragment_t* self, lv_obj_t* obj)
{
    LV_LOG_INFO("self: %p obj: %p", self, obj);

    page_ctx_t* ctx = (page_ctx_t*)self;

    ctx->timer_sw_update = lv_timer_create(sw_update, 51, ctx);

    list_history_create(obj);
    label_time_create(obj);
    btn_grp_create(obj, ctx->btn_grp, BTN_CNT);
    line_sw_create(obj);
    auto_event_create(ctx);

    ctx->sw_is_pause ? lv_timer_pause(ctx->timer_sw_update) : lv_timer_resume(ctx->timer_sw_update);
}

static void on_page_will_delete(lv_fragment_t* self, lv_obj_t* obj)
{
    LV_LOG_INFO("self: %p obj: %p", self, obj);

    page_ctx_t* ctx = (page_ctx_t*)self;

    lv_timer_del(ctx->timer_sw_update);
    AUTO_EVENT_DELETE(ctx->auto_event);
}

static void on_page_deleted(lv_fragment_t* self, lv_obj_t* obj)
{
    LV_LOG_INFO("self: %p obj: %p", self, obj);
}

static bool on_page_event(lv_fragment_t* self, int code, void* user_data)
{
    LV_LOG_INFO("self: %p code: %d user_data: %p", self, code, user_data);
    return false;
}

PAGE_CLASS_DEF(stop_watch);