convert lvgl from submodule to a plain old directory

This commit is contained in:
jacqueline
2023-06-01 15:41:47 +10:00
parent 6fd588e970
commit dd27c35304
1173 changed files with 359339 additions and 4 deletions
@@ -0,0 +1,138 @@
/**
* @file lv_animimg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_animimg.h"
#if LV_USE_ANIMIMG != 0
/*Testing of dependencies*/
#if LV_USE_IMG == 0
#error "lv_animimg: lv_img is required. Enable it in lv_conf.h (LV_USE_IMG 1) "
#endif
#include "../../../misc/lv_assert.h"
#include "../../../draw/lv_img_decoder.h"
#include "../../../misc/lv_fs.h"
#include "../../../misc/lv_txt.h"
#include "../../../misc/lv_math.h"
#include "../../../misc/lv_log.h"
#include "../../../misc/lv_anim.h"
/*********************
* DEFINES
*********************/
#define LV_OBJX_NAME "lv_animimg"
#define MY_CLASS &lv_animimg_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void index_change(lv_obj_t * obj, int32_t index);
static void lv_animimg_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_animimg_class = {
.constructor_cb = lv_animimg_constructor,
.instance_size = sizeof(lv_animimg_t),
.base_class = &lv_img_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_animimg_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_animimg_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_animimg_set_src(lv_obj_t * obj, lv_img_dsc_t * dsc[], uint8_t num)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
animimg->dsc = dsc;
animimg->pic_count = num;
lv_anim_set_values(&animimg->anim, 0, num);
}
void lv_animimg_start(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_start(&animimg->anim);
}
/*=====================
* Setter functions
*====================*/
void lv_animimg_set_duration(lv_obj_t * obj, uint32_t duration)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_set_time(&animimg->anim, duration);
lv_anim_set_playback_delay(&animimg->anim, duration);
}
void lv_animimg_set_repeat_count(lv_obj_t * obj, uint16_t count)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_set_repeat_count(&animimg->anim, count);
}
/*=====================
* Getter functions
*====================*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_animimg_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
animimg->dsc = NULL;
animimg->pic_count = -1;
//initial animation
lv_anim_init(&animimg->anim);
lv_anim_set_var(&animimg->anim, obj);
lv_anim_set_time(&animimg->anim, 30);
lv_anim_set_exec_cb(&animimg->anim, (lv_anim_exec_xcb_t)index_change);
lv_anim_set_values(&animimg->anim, 0, 1);
lv_anim_set_repeat_count(&animimg->anim, LV_ANIM_REPEAT_INFINITE);
}
static void index_change(lv_obj_t * obj, int32_t index)
{
lv_coord_t idx;
lv_animimg_t * animimg = (lv_animimg_t *)obj;
idx = index % animimg->pic_count;
lv_img_set_src(obj, animimg->dsc[idx]);
}
#endif
@@ -0,0 +1,103 @@
/**
* @file lv_animimg.h
*
*/
#ifndef LV_ANIM_IMG_H
#define LV_ANIM_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_ANIMIMG != 0
/*Testing of dependencies*/
#if LV_USE_IMG == 0
#error "lv_animimg: lv_img is required. Enable it in lv_conf.h (LV_USE_IMG 1)"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_animimg_class;
/*Data of image*/
typedef struct {
lv_img_t img;
lv_anim_t anim;
/*picture sequence */
lv_img_dsc_t ** dsc;
int8_t pic_count;
} lv_animimg_t;
/*Image parts*/
enum {
LV_ANIM_IMG_PART_MAIN,
};
typedef uint8_t lv_animimg_part_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an animation image objects
* @param parent pointer to an object, it will be the parent of the new button
* @return pointer to the created animation image object
*/
lv_obj_t * lv_animimg_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the image animation images source.
* @param img pointer to an animation image object
* @param dsc pointer to a series images
* @param num images' number
*/
void lv_animimg_set_src(lv_obj_t * img, lv_img_dsc_t * dsc[], uint8_t num);
/**
* Startup the image animation.
* @param obj pointer to an animation image object
*/
void lv_animimg_start(lv_obj_t * obj);
/**
* Set the image animation duration time. unit:ms
* @param img pointer to an animation image object
*/
void lv_animimg_set_duration(lv_obj_t * img, uint32_t duration);
/**
* Set the image animation reapeatly play times.
* @param img pointer to an animation image object
* @param count the number of times to repeat the animation
*/
void lv_animimg_set_repeat_count(lv_obj_t * img, uint16_t count);
/*=====================
* Getter functions
*====================*/
#endif /*LV_USE_ANIMIMG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ANIM_IMG_H*/
@@ -0,0 +1,402 @@
/**
* @file lv_calendar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar.h"
#include "../../../lvgl.h"
#if LV_USE_CALENDAR
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define LV_CALENDAR_CTRL_TODAY LV_BTNMATRIX_CTRL_CUSTOM_1
#define LV_CALENDAR_CTRL_HIGHLIGHT LV_BTNMATRIX_CTRL_CUSTOM_2
#define MY_CLASS &lv_calendar_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_part_begin_event_cb(lv_event_t * e);
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
static uint8_t get_month_length(int32_t year, int32_t month);
static uint8_t is_leap_year(uint32_t year);
static void highlight_update(lv_obj_t * calendar);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_class = {
.constructor_cb = lv_calendar_constructor,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = (LV_DPI_DEF * 3) / 2,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_calendar_t),
.base_class = &lv_obj_class
};
static const char * day_names_def[7] = LV_CALENDAR_DEFAULT_DAY_NAMES;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
void lv_calendar_set_day_names(lv_obj_t * obj, const char * day_names[])
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint32_t i;
for(i = 0; i < 7; i++) {
calendar->map[i] = day_names[i];
}
lv_obj_invalidate(obj);
}
void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->today.year = year;
calendar->today.month = month;
calendar->today.day = day;
highlight_update(obj);
}
void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num)
{
LV_ASSERT_NULL(highlighted);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->highlighted_dates = highlighted;
calendar->highlighted_dates_num = date_num;
highlight_update(obj);
}
void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->showed_date.year = year;
calendar->showed_date.month = month;
calendar->showed_date.day = 1;
lv_calendar_date_t d;
d.year = calendar->showed_date.year;
d.month = calendar->showed_date.month;
d.day = calendar->showed_date.day;
uint32_t i;
/*Remove the disabled state but revert it for day names*/
lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_DISABLED);
for(i = 0; i < 7; i++) {
lv_btnmatrix_set_btn_ctrl(calendar->btnm, i, LV_BTNMATRIX_CTRL_DISABLED);
}
uint8_t act_mo_len = get_month_length(d.year, d.month);
uint8_t day_first = get_day_of_week(d.year, d.month, 1);
uint8_t c;
for(i = day_first, c = 1; i < act_mo_len + day_first; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
}
uint8_t prev_mo_len = get_month_length(d.year, d.month - 1);
for(i = 0, c = prev_mo_len - day_first + 1; i < day_first; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
lv_btnmatrix_set_btn_ctrl(calendar->btnm, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
}
for(i = day_first + act_mo_len, c = 1; i < 6 * 7; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
lv_btnmatrix_set_btn_ctrl(calendar->btnm, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
}
highlight_update(obj);
/*Reset the focused button if the days changes*/
if(lv_btnmatrix_get_selected_btn(calendar->btnm) != LV_BTNMATRIX_BTN_NONE) {
lv_btnmatrix_set_selected_btn(calendar->btnm, day_first + 7);
}
lv_obj_invalidate(obj);
/* The children of the calendar are probably headers.
* Notify them to let the headers updated to the new date*/
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(child == calendar->btnm) continue;
lv_event_send(child, LV_EVENT_VALUE_CHANGED, obj);
}
}
/*=====================
* Getter functions
*====================*/
lv_obj_t * lv_calendar_get_btnmatrix(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->btnm;
}
const lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return &calendar->today;
}
const lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return &calendar->showed_date;
}
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->highlighted_dates;
}
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->highlighted_dates_num;
}
lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * obj, lv_calendar_date_t * date)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint16_t d = lv_btnmatrix_get_selected_btn(calendar->btnm);
if(d == LV_BTNMATRIX_BTN_NONE) {
date->year = 0;
date->month = 0;
date->day = 0;
return LV_RES_INV;
}
const char * txt = lv_btnmatrix_get_btn_text(calendar->btnm, lv_btnmatrix_get_selected_btn(calendar->btnm));
if(txt[1] == 0) date->day = txt[0] - '0';
else date->day = (txt[0] - '0') * 10 + (txt[1] - '0');
date->year = calendar->showed_date.year;
date->month = calendar->showed_date.month;
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
/*Initialize the allocated 'ext'*/
calendar->today.year = 2020;
calendar->today.month = 1;
calendar->today.day = 1;
calendar->showed_date.year = 2020;
calendar->showed_date.month = 1;
calendar->showed_date.day = 1;
calendar->highlighted_dates = NULL;
calendar->highlighted_dates_num = 0;
lv_memset_00(calendar->nums, sizeof(calendar->nums));
uint8_t i;
uint8_t j = 0;
for(i = 0; i < 8 * 7; i++) {
/*Every 8th string is "\n"*/
if(i != 0 && (i + 1) % 8 == 0) {
calendar->map[i] = "\n";
}
else if(i < 7) {
calendar->map[i] = day_names_def[i];
}
else {
calendar->nums[j][0] = 'x';
calendar->map[i] = calendar->nums[j];
j++;
}
}
calendar->map[8 * 7 - 1] = "";
calendar->btnm = lv_btnmatrix_create(obj);
lv_btnmatrix_set_map(calendar->btnm, calendar->map);
lv_btnmatrix_set_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
lv_obj_add_event_cb(calendar->btnm, draw_part_begin_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
lv_obj_set_width(calendar->btnm, lv_pct(100));
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_grow(calendar->btnm, 1);
lv_calendar_set_showed_date(obj, calendar->showed_date.year, calendar->showed_date.month);
lv_calendar_set_today_date(obj, calendar->today.year, calendar->today.month, calendar->today.day);
lv_obj_add_flag(calendar->btnm, LV_OBJ_FLAG_EVENT_BUBBLE);
}
static void draw_part_begin_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
if(dsc->part == LV_PART_ITEMS) {
/*Day name styles*/
if(dsc->id < 7) {
dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
}
else if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED)) {
dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
dsc->label_dsc->color = lv_palette_main(LV_PALETTE_GREY);
}
if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_HIGHLIGHT)) {
dsc->rect_dsc->bg_opa = LV_OPA_40;
dsc->rect_dsc->bg_color = lv_theme_get_color_primary(obj);
if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) {
dsc->rect_dsc->bg_opa = LV_OPA_70;
}
}
if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_TODAY)) {
dsc->rect_dsc->border_opa = LV_OPA_COVER;
dsc->rect_dsc->border_color = lv_theme_get_color_primary(obj);
dsc->rect_dsc->border_width += 1;
}
}
}
/**
* Get the number of days in a month
* @param year a year
* @param month a month. The range is basically [1..12] but [-11..0] or [13..24] is also
* supported to handle next/prev. year
* @return [28..31]
*/
static uint8_t get_month_length(int32_t year, int32_t month)
{
month--;
if(month < 0) {
year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/
month = 12 + month; /*`month` is negative, the result will be < 12*/
}
if(month >= 12) {
year++;
month -= 12;
}
/*month == 1 is february*/
return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
}
/**
* Tells whether a year is leap year or not
* @param year a year
* @return 0: not leap year; 1: leap year
*/
static uint8_t is_leap_year(uint32_t year)
{
return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
}
/**
* Get the day of the week
* @param year a year
* @param month a month [1..12]
* @param day a day [1..32]
* @return [0..6] which means [Sun..Sat] or [Mon..Sun] depending on LV_CALENDAR_WEEK_STARTS_MONDAY
*/
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
{
uint32_t a = month < 3 ? 1 : 0;
uint32_t b = year - a;
#if LV_CALENDAR_WEEK_STARTS_MONDAY
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400) - 1) % 7;
#else
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400)) % 7;
#endif
return day_of_week ;
}
static void highlight_update(lv_obj_t * obj)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint16_t i;
/*Clear all kind of selection*/
lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_CALENDAR_CTRL_TODAY | LV_CALENDAR_CTRL_HIGHLIGHT);
uint8_t day_first = get_day_of_week(calendar->showed_date.year, calendar->showed_date.month, 1);
if(calendar->highlighted_dates) {
for(i = 0; i < calendar->highlighted_dates_num; i++) {
if(calendar->highlighted_dates[i].year == calendar->showed_date.year &&
calendar->highlighted_dates[i].month == calendar->showed_date.month) {
lv_btnmatrix_set_btn_ctrl(calendar->btnm, calendar->highlighted_dates[i].day - 1 + day_first + 7,
LV_CALENDAR_CTRL_HIGHLIGHT);
}
}
}
if(calendar->showed_date.year == calendar->today.year && calendar->showed_date.month == calendar->today.month) {
lv_btnmatrix_set_btn_ctrl(calendar->btnm, calendar->today.day - 1 + day_first + 7, LV_CALENDAR_CTRL_TODAY);
}
}
#endif /*LV_USE_CALENDAR*/
@@ -0,0 +1,164 @@
/**
* @file lv_calendar.h
*
*/
#ifndef LV_CALENDAR_H
#define LV_CALENDAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../widgets/lv_btnmatrix.h"
#if LV_USE_CALENDAR
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Represents a date on the calendar object (platform-agnostic).
*/
typedef struct {
uint16_t year;
int8_t month; /** 1..12*/
int8_t day; /** 1..31*/
} lv_calendar_date_t;
/*Data of calendar*/
typedef struct {
lv_obj_t obj;
lv_obj_t * btnm;
/*New data for this type*/
lv_calendar_date_t today; /*Date of today*/
lv_calendar_date_t showed_date; /*Currently visible month (day is ignored)*/
lv_calendar_date_t *
highlighted_dates; /*Apply different style on these days (pointer to an array defined by the user)*/
uint16_t highlighted_dates_num; /*Number of elements in `highlighted_days`*/
const char * map[8 * 7];
char nums [7 * 6][4];
} lv_calendar_t;
extern const lv_obj_class_t lv_calendar_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_calendar_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the today's date
* @param obj pointer to a calendar object
* @param year today's year
* @param month today's month [1..12]
* @param day today's day [1..31]
*/
void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day);
/**
* Set the currently showed
* @param obj pointer to a calendar object
* @param year today's year
* @param month today's month [1..12]
*/
void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month);
/**
* Set the highlighted dates
* @param obj pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates.
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num);
/**
* Set the name of the days
* @param obj pointer to a calendar object
* @param day_names pointer to an array with the names.
* E.g. `const char * days[7] = {"Sun", "Mon", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_day_names(lv_obj_t * obj, const char ** day_names);
/*=====================
* Getter functions
*====================*/
/**
* Get the button matrix object of the calendar.
* It shows the dates and day names.
* @param obj pointer to a calendar object
* @return pointer to a the button matrix
*/
lv_obj_t * lv_calendar_get_btnmatrix(const lv_obj_t * obj);
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
const lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar);
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
const lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar);
/**
* Get the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar);
/**
* Get the number of the highlighted dates
* @param calendar pointer to a calendar object
* @return number of highlighted days
*/
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar);
/**
* Get the currently pressed day
* @param calendar pointer to a calendar object
* @param date store the pressed date here
* @return LV_RES_OK: there is a valid pressed date; LV_RES_INV: there is no pressed data
*/
lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * calendar, lv_calendar_date_t * date);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/
@@ -0,0 +1,149 @@
/**
* @file lv_calendar_header_arrow.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar_header_arrow.h"
#if LV_USE_CALENDAR_HEADER_ARROW
#include "lv_calendar.h"
#include "../../../widgets/lv_btn.h"
#include "../../../widgets/lv_label.h"
#include "../../layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void month_event_cb(lv_event_t * e);
static void value_changed_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_header_arrow_class = {
.base_class = &lv_obj_class,
.constructor_cb = my_constructor,
.width_def = LV_PCT(100),
.height_def = LV_DPI_DEF / 3
};
static const char * month_names_def[12] = LV_CALENDAR_DEFAULT_MONTH_NAMES;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_header_arrow_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_obj_move_to_index(obj, 0);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_t * mo_prev = lv_btn_create(obj);
lv_obj_set_style_bg_img_src(mo_prev, LV_SYMBOL_LEFT, 0);
lv_obj_set_height(mo_prev, lv_pct(100));
lv_obj_update_layout(mo_prev);
lv_coord_t btn_size = lv_obj_get_height(mo_prev);
lv_obj_set_width(mo_prev, btn_size);
lv_obj_add_event_cb(mo_prev, month_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_clear_flag(mo_prev, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_flex_grow(label, 1);
lv_obj_t * mo_next = lv_btn_create(obj);
lv_obj_set_style_bg_img_src(mo_next, LV_SYMBOL_RIGHT, 0);
lv_obj_set_size(mo_next, btn_size, btn_size);
lv_obj_add_event_cb(mo_next, month_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_clear_flag(mo_next, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_event_cb(obj, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*Refresh the drop downs*/
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}
static void month_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * header = lv_obj_get_parent(btn);
lv_obj_t * calendar = lv_obj_get_parent(header);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
/*The last child is the right button*/
if(lv_obj_get_child(header, 0) == btn) {
if(newd.month == 1) {
newd.month = 12;
newd.year --;
}
else {
newd.month --;
}
}
else {
if(newd.month == 12) {
newd.month = 1;
newd.year ++;
}
else {
newd.month ++;
}
}
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
lv_obj_t * label = lv_obj_get_child(header, 1);
lv_label_set_text_fmt(label, "%d %s", newd.year, month_names_def[newd.month - 1]);
}
static void value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * header = lv_event_get_target(e);
lv_obj_t * calendar = lv_obj_get_parent(header);
const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
lv_obj_t * label = lv_obj_get_child(header, 1);
lv_label_set_text_fmt(label, "%d %s", cur_date->year, month_names_def[cur_date->month - 1]);
}
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
@@ -0,0 +1,49 @@
/**
* @file lv_calendar_header_arrow.h
*
*/
#ifndef LV_CALENDAR_HEADER_ARROW_H
#define LV_CALENDAR_HEADER_ARROW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_CALENDAR_HEADER_ARROW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_calendar_header_arrow_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar header with drop-drowns to select the year and month
* @param parent pointer to a calendar object.
* @return the created header
*/
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_HEADER_ARROW_H*/
@@ -0,0 +1,142 @@
/**
* @file lv_calendar_obj_dropdown.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar_header_dropdown.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN
#include "lv_calendar.h"
#include "../../../widgets/lv_dropdown.h"
#include "../../layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void year_event_cb(lv_event_t * e);
static void month_event_cb(lv_event_t * e);
static void value_changed_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_header_dropdown_class = {
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT,
.constructor_cb = my_constructor
};
static const char * month_list = "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12";
static const char * year_list = {
"2023\n2022\n2021\n"
"2020\n2019\n2018\n2017\n2016\n2015\n2014\n2013\n2012\n2011\n2010\n2009\n2008\n2007\n2006\n2005\n2004\n2003\n2002\n2001\n"
"2000\n1999\n1998\n1997\n1996\n1995\n1994\n1993\n1992\n1991\n1990\n1989\n1988\n1987\n1986\n1985\n1984\n1983\n1982\n1981\n"
"1980\n1979\n1978\n1977\n1976\n1975\n1974\n1973\n1972\n1971\n1970\n1969\n1968\n1967\n1966\n1965\n1964\n1963\n1962\n1961\n"
"1960\n1959\n1958\n1957\n1956\n1955\n1954\n1953\n1952\n1951\n1950\n1949\n1948\n1947\n1946\n1945\n1944\n1943\n1942\n1941\n"
"1940\n1939\n1938\n1937\n1936\n1935\n1934\n1933\n1932\n1931\n1930\n1929\n1928\n1927\n1926\n1925\n1924\n1923\n1922\n1921\n"
"1920\n1919\n1918\n1917\n1916\n1915\n1914\n1913\n1912\n1911\n1910\n1909\n1908\n1907\n1906\n1905\n1904\n1903\n1902\n1901"
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_header_dropdown_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_header_dropdown_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_obj_t * calendar = lv_obj_get_parent(obj);
lv_obj_move_to_index(obj, 0);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_t * year_dd = lv_dropdown_create(obj);
lv_dropdown_set_options(year_dd, year_list);
lv_obj_add_event_cb(year_dd, year_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
lv_obj_set_flex_grow(year_dd, 1);
lv_obj_t * month_dd = lv_dropdown_create(obj);
lv_dropdown_set_options(month_dd, month_list);
lv_obj_add_event_cb(month_dd, month_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
lv_obj_set_flex_grow(month_dd, 1);
lv_obj_add_event_cb(obj, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*Refresh the drop downs*/
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}
static void month_event_cb(lv_event_t * e)
{
lv_obj_t * dropdown = lv_event_get_target(e);
lv_obj_t * calendar = lv_event_get_user_data(e);
uint16_t sel = lv_dropdown_get_selected(dropdown);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
newd.month = sel + 1;
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}
static void year_event_cb(lv_event_t * e)
{
lv_obj_t * dropdown = lv_event_get_target(e);
lv_obj_t * calendar = lv_event_get_user_data(e);
uint16_t sel = lv_dropdown_get_selected(dropdown);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
newd.year = 2023 - sel;
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}
static void value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * header = lv_event_get_target(e);
lv_obj_t * calendar = lv_obj_get_parent(header);
const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
lv_obj_t * year_dd = lv_obj_get_child(header, 0);
lv_dropdown_set_selected(year_dd, 2023 - cur_date->year);
lv_obj_t * month_dd = lv_obj_get_child(header, 1);
lv_dropdown_set_selected(month_dd, cur_date->month - 1);
}
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
@@ -0,0 +1,49 @@
/**
* @file lv_calendar_header_dropdown.h
*
*/
#ifndef LV_CALENDAR_HEADER_DROPDOWN_H
#define LV_CALENDAR_HEADER_DROPDOWN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_calendar_header_dropdown_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar header with drop-drowns to select the year and month
* @param parent pointer to a calendar object.
* @return the created header
*/
lv_obj_t * lv_calendar_header_dropdown_create(lv_obj_t * parent);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_HEADER_DROPDOWN_H*/
File diff suppressed because it is too large Load Diff
+460
View File
@@ -0,0 +1,460 @@
/**
* @file lv_chart.h
*
*/
#ifndef LV_CHART_H
#define LV_CHART_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_CHART != 0
/*********************
* DEFINES
*********************/
/**Default value of points. Can be used to not draw a point*/
#if LV_USE_LARGE_COORD
#define LV_CHART_POINT_NONE (INT32_MAX)
#else
#define LV_CHART_POINT_NONE (INT16_MAX)
#endif
LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE);
/**********************
* TYPEDEFS
**********************/
/**
* Chart types
*/
enum {
LV_CHART_TYPE_NONE, /**< Don't draw the series*/
LV_CHART_TYPE_LINE, /**< Connect the points with lines*/
LV_CHART_TYPE_BAR, /**< Draw columns*/
LV_CHART_TYPE_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
};
typedef uint8_t lv_chart_type_t;
/**
* Chart update mode for `lv_chart_set_next`
*/
enum {
LV_CHART_UPDATE_MODE_SHIFT, /**< Shift old data to the left and add the new one the right*/
LV_CHART_UPDATE_MODE_CIRCULAR, /**< Add the new data in a circular way*/
};
typedef uint8_t lv_chart_update_mode_t;
/**
* Enumeration of the axis'
*/
enum {
LV_CHART_AXIS_PRIMARY_Y = 0x00,
LV_CHART_AXIS_SECONDARY_Y = 0x01,
LV_CHART_AXIS_PRIMARY_X = 0x02,
LV_CHART_AXIS_SECONDARY_X = 0x04,
_LV_CHART_AXIS_LAST
};
typedef uint8_t lv_chart_axis_t;
/**
* Descriptor a chart series
*/
typedef struct {
lv_coord_t * x_points;
lv_coord_t * y_points;
lv_color_t color;
uint16_t start_point;
uint8_t hidden : 1;
uint8_t x_ext_buf_assigned : 1;
uint8_t y_ext_buf_assigned : 1;
uint8_t x_axis_sec : 1;
uint8_t y_axis_sec : 1;
} lv_chart_series_t;
typedef struct {
lv_point_t pos;
lv_coord_t point_id;
lv_color_t color;
lv_chart_series_t * ser;
lv_dir_t dir;
uint8_t pos_set: 1; /*1: pos is set; 0: point_id is set*/
} lv_chart_cursor_t;
typedef struct {
lv_coord_t major_len;
lv_coord_t minor_len;
lv_coord_t draw_size;
uint32_t minor_cnt : 15;
uint32_t major_cnt : 15;
uint32_t label_en : 1;
} lv_chart_tick_dsc_t;
typedef struct {
lv_obj_t obj;
lv_ll_t series_ll; /**< Linked list for the series (stores lv_chart_series_t)*/
lv_ll_t cursor_ll; /**< Linked list for the cursors (stores lv_chart_cursor_t)*/
lv_chart_tick_dsc_t tick[4];
lv_coord_t ymin[2];
lv_coord_t ymax[2];
lv_coord_t xmin[2];
lv_coord_t xmax[2];
lv_coord_t pressed_point_id;
uint16_t hdiv_cnt; /**< Number of horizontal division lines*/
uint16_t vdiv_cnt; /**< Number of vertical division lines*/
uint16_t point_cnt; /**< Point number in a data line*/
uint16_t zoom_x;
uint16_t zoom_y;
lv_chart_type_t type : 3; /**< Line or column chart*/
lv_chart_update_mode_t update_mode : 1;
} lv_chart_t;
extern const lv_obj_class_t lv_chart_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_chart_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_CHART_DRAW_PART_DIV_LINE_INIT, /**< Used before/after drawn the div lines*/
LV_CHART_DRAW_PART_DIV_LINE_HOR, /**< Used for each horizontal division lines*/
LV_CHART_DRAW_PART_DIV_LINE_VER, /**< Used for each vertical division lines*/
LV_CHART_DRAW_PART_LINE_AND_POINT, /**< Used on line and scatter charts for lines and points*/
LV_CHART_DRAW_PART_BAR, /**< Used on bar charts for the rectangles*/
LV_CHART_DRAW_PART_CURSOR, /**< Used on cursor lines and points*/
LV_CHART_DRAW_PART_TICK_LABEL, /**< Used on tick lines and labels*/
} lv_chart_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a chart object
* @param parent pointer to an object, it will be the parent of the new chart
* @return pointer to the created chart
*/
lv_obj_t * lv_chart_create(lv_obj_t * parent);
/**
* Set a new type for a chart
* @param obj pointer to a chart object
* @param type new type of the chart (from 'lv_chart_type_t' enum)
*/
void lv_chart_set_type(lv_obj_t * obj, lv_chart_type_t type);
/**
* Set the number of points on a data line on a chart
* @param obj pointer to a chart object
* @param cnt new number of points on the data lines
*/
void lv_chart_set_point_count(lv_obj_t * obj, uint16_t cnt);
/**
* Set the minimal and maximal y values on an axis
* @param obj pointer to a chart object
* @param axis `LV_CHART_AXIS_PRIMARY_Y` or `LV_CHART_AXIS_SECONDARY_Y`
* @param min minimum value of the y axis
* @param max maximum value of the y axis
*/
void lv_chart_set_range(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t min, lv_coord_t max);
/**
* Set update mode of the chart object. Affects
* @param obj pointer to a chart object
* @param mode the update mode
*/
void lv_chart_set_update_mode(lv_obj_t * obj, lv_chart_update_mode_t update_mode);
/**
* Set the number of horizontal and vertical division lines
* @param obj pointer to a chart object
* @param hdiv number of horizontal division lines
* @param vdiv number of vertical division lines
*/
void lv_chart_set_div_line_count(lv_obj_t * obj, uint8_t hdiv, uint8_t vdiv);
/**
* Zoom into the chart in X direction
* @param obj pointer to a chart object
* @param zoom_x zoom in x direction. LV_ZOOM_NONE or 256 for no zoom, 512 double zoom
*/
void lv_chart_set_zoom_x(lv_obj_t * obj, uint16_t zoom_x);
/**
* Zoom into the chart in Y direction
* @param obj pointer to a chart object
* @param zoom_y zoom in y direction. LV_ZOOM_NONE or 256 for no zoom, 512 double zoom
*/
void lv_chart_set_zoom_y(lv_obj_t * obj, uint16_t zoom_y);
/**
* Get X zoom of a chart
* @param obj pointer to a chart object
* @return the X zoom value
*/
uint16_t lv_chart_get_zoom_x(const lv_obj_t * obj);
/**
* Get Y zoom of a chart
* @param obj pointer to a chart object
* @return the Y zoom value
*/
uint16_t lv_chart_get_zoom_y(const lv_obj_t * obj);
/**
* Set the number of tick lines on an axis
* @param obj pointer to a chart object
* @param axis an axis which ticks count should be set
* @param major_len length of major ticks
* @param minor_len length of minor ticks
* @param major_cnt number of major ticks on the axis
* @param minor_cnt number of minor ticks between two major ticks
* @param label_en true: enable label drawing on major ticks
* @param draw_size extra size required to draw the tick and labels
* (start with 20 px and increase if the ticks/labels are clipped)
*/
void lv_chart_set_axis_tick(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t major_len, lv_coord_t minor_len,
lv_coord_t major_cnt, lv_coord_t minor_cnt, bool label_en, lv_coord_t draw_size);
/**
* Get the type of a chart
* @param obj pointer to chart object
* @return type of the chart (from 'lv_chart_t' enum)
*/
lv_chart_type_t lv_chart_get_type(const lv_obj_t * obj);
/**
* Get the data point number per data line on chart
* @param chart pointer to chart object
* @return point number on each data line
*/
uint16_t lv_chart_get_point_count(const lv_obj_t * obj);
/**
* Get the current index of the x-axis start point in the data array
* @param chart pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the index of the current x start point in the data array
*/
uint16_t lv_chart_get_x_start_point(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the position of a point to the chart.
* @param chart pointer to a chart object
* @param ser pointer to series
* @param id the index.
* @param p_out store the result position here
*/
void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_point_t * p_out);
/**
* Refresh a chart if its data line has changed
* @param chart pointer to chart object
*/
void lv_chart_refresh(lv_obj_t * obj);
/*======================
* Series
*=====================*/
/**
* Allocate and add a data series to the chart
* @param obj pointer to a chart object
* @param color color of the data series
* @param axis the y axis to which the series should be attached (::LV_CHART_AXIS_PRIMARY_Y or ::LV_CHART_AXIS_SECONDARY_Y)
* @return pointer to the allocated data series
*/
lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_chart_axis_t axis);
/**
* Deallocate and remove a data series from a chart
* @param chart pointer to a chart object
* @param series pointer to a data series on 'chart'
*/
void lv_chart_remove_series(lv_obj_t * obj, lv_chart_series_t * series);
/**
* Hide/Unhide a single series of a chart.
* @param obj pointer to a chart object.
* @param series pointer to a series object
* @param hide true: hide the series
*/
void lv_chart_hide_series(lv_obj_t * chart, lv_chart_series_t * series, bool hide);
/**
* Change the color of a series
* @param obj pointer to a chart object.
* @param series pointer to a series object
* @param color the new color of the series
*/
void lv_chart_set_series_color(lv_obj_t * chart, lv_chart_series_t * series, lv_color_t color);
/**
* Set the index of the x-axis start point in the data array.
* This point will be considers the first (left) point and the other points will be drawn after it.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the data array
*/
void lv_chart_set_x_start_point(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id);
/**
* Get the next series.
* @param chart pointer to a chart
* @param ser the previous series or NULL to get the first
* @return the next series or NULL if there is no more.
*/
lv_chart_series_t * lv_chart_get_series_next(const lv_obj_t * chart, const lv_chart_series_t * ser);
/*=====================
* Cursor
*====================*/
/**
* Add a cursor with a given color
* @param obj pointer to chart object
* @param color color of the cursor
* @param dir direction of the cursor. `LV_DIR_RIGHT/LEFT/TOP/DOWN/HOR/VER/ALL`. OR-ed values are possible
* @return pointer to the created cursor
*/
lv_chart_cursor_t * lv_chart_add_cursor(lv_obj_t * obj, lv_color_t color, lv_dir_t dir);
/**
* Set the coordinate of the cursor with respect to the paddings
* @param obj pointer to a chart object
* @param cursor pointer to the cursor
* @param pos the new coordinate of cursor relative to the chart
*/
void lv_chart_set_cursor_pos(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_point_t * pos);
/**
* Stick the cursor to a point
* @param obj pointer to a chart object
* @param cursor pointer to the cursor
* @param ser pointer to a series
* @param point_id the point's index or `LV_CHART_POINT_NONE` to not assign to any points.
*/
void lv_chart_set_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_chart_series_t * ser,
uint16_t point_id);
/**
* Get the coordinate of the cursor with respect to the paddings
* @param obj pointer to a chart object
* @param cursor pointer to cursor
* @return coordinate of the cursor as lv_point_t
*/
lv_point_t lv_chart_get_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor);
/*=====================
* Set/Get value(s)
*====================*/
/**
* Initialize all data points of a series with a value
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param value the new value for all points. `LV_CHART_POINT_NONE` can be used to hide the points.
*/
void lv_chart_set_all_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value);
/**
* Set the next point's Y value according to the update mode policy.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param value the new value of the next data
*/
void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value);
/**
* Set the next point's X and Y value according to the update mode policy.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param x_value the new X value of the next data
* @param y_value the new Y value of the next data
*/
void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t x_value, lv_coord_t y_value);
/**
* Set an individual point's y value of a chart's series directly based on its index
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @param value value to assign to array point
*/
void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t value);
/**
* Set an individual point's x and y value of a chart's series directly based on its index
* Can be used only with `LV_CHART_TYPE_SCATTER`.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @param x_value the new X value of the next data
* @param y_value the new Y value of the next data
*/
void lv_chart_set_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t x_value,
lv_coord_t y_value);
/**
* Set an external array for the y data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_y_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Set an external array for the x data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_x_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Get the array of y values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_y_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the array of x values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_x_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the index of the currently pressed point. It's the same for every series.
* @param obj pointer to a chart object
* @return the index of the point [0 .. point count] or LV_CHART_POINT_ID_NONE if no point is being pressed
*/
uint32_t lv_chart_get_pressed_point(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CHART*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CHART_H*/
@@ -0,0 +1,713 @@
/**
* @file lv_colorwheel.c
*
* Based on the work of @AloyseTech and @paulpv.
*/
/*********************
* INCLUDES
*********************/
#include "lv_colorwheel.h"
#if LV_USE_COLORWHEEL
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_colorwheel_class
#define LV_CPICKER_DEF_QF 3
/**
* The OUTER_MASK_WIDTH define is required to assist with the placing of a mask over the outer ring of the widget as when the
* multicoloured radial lines are calculated for the outer ring of the widget their lengths are jittering because of the
* integer based arithmetic. From tests the maximum delta was found to be 2 so the current value is set to 3 to achieve
* appropriate masking.
*/
#define OUTER_MASK_WIDTH 3
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_colorwheel_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_colorwheel_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_disc_grad(lv_event_t * e);
static void draw_knob(lv_event_t * e);
static void invalidate_knob(lv_obj_t * obj);
static lv_area_t get_knob_area(lv_obj_t * obj);
static void next_color_mode(lv_obj_t * obj);
static lv_res_t double_click_reset(lv_obj_t * obj);
static void refr_knob_pos(lv_obj_t * obj);
static lv_color_t angle_to_mode_color_fast(lv_obj_t * obj, uint16_t angle);
static uint16_t get_angle(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_colorwheel_class = {.instance_size = sizeof(lv_colorwheel_t), .base_class = &lv_obj_class,
.constructor_cb = lv_colorwheel_constructor,
.event_cb = lv_colorwheel_event,
.width_def = LV_DPI_DEF * 2,
.height_def = LV_DPI_DEF * 2,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
};
static bool create_knob_recolor;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a color_picker object
* @param parent pointer to an object, it will be the parent of the new color_picker
* @return pointer to the created color_picker
*/
lv_obj_t * lv_colorwheel_create(lv_obj_t * parent, bool knob_recolor)
{
LV_LOG_INFO("begin");
create_knob_recolor = knob_recolor;
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the current hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected hsv
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv)
{
if(hsv.h > 360) hsv.h %= 360;
if(hsv.s > 100) hsv.s = 100;
if(hsv.v > 100) hsv.v = 100;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(colorwheel->hsv.h == hsv.h && colorwheel->hsv.s == hsv.s && colorwheel->hsv.v == hsv.v) return false;
colorwheel->hsv = hsv;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
return true;
}
/**
* Set the current color of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected color
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color)
{
lv_color32_t c32;
c32.full = lv_color_to32(color);
return lv_colorwheel_set_hsv(obj, lv_color_rgb_to_hsv(c32.ch.red, c32.ch.green, c32.ch.blue));
}
/**
* Set the current color mode.
* @param colorwheel pointer to color wheel object
* @param mode color mode (hue/sat/val)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = mode;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
}
/**
* Set if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @param fixed color mode cannot be changed on long press
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode_fixed = fixed;
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current selected hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @return current selected hsv
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->hsv;
}
/**
* Get the current selected color of a color wheel.
* @param colorwheel pointer to color wheel object
* @return color current selected color
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return lv_color_hsv_to_rgb(colorwheel->hsv.h, colorwheel->hsv.s, colorwheel->hsv.v);
}
/**
* Get the current color mode.
* @param colorwheel pointer to color wheel object
* @return color mode (hue/sat/val)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode;
}
/**
* Get if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @return mode cannot be changed on long press
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode_fixed;
}
/*=====================
* Other functions
*====================*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_colorwheel_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->hsv.h = 0;
colorwheel->hsv.s = 100;
colorwheel->hsv.v = 100;
colorwheel->mode = LV_COLORWHEEL_MODE_HUE;
colorwheel->mode_fixed = 0;
colorwheel->last_click_time = 0;
colorwheel->last_change_time = 0;
colorwheel->knob.recolor = create_knob_recolor;
lv_obj_add_flag(obj, LV_OBJ_FLAG_ADV_HITTEST);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
refr_knob_pos(obj);
}
static void draw_disc_grad(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t cx = obj->coords.x1 + w / 2;
lv_coord_t cy = obj->coords.y1 + h / 2;
lv_coord_t r = w / 2;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
line_dsc.width = (r * 628 / (256 / LV_CPICKER_DEF_QF)) / 100;
line_dsc.width += 2;
uint16_t i;
uint32_t a = 0;
lv_coord_t cir_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
#if LV_DRAW_COMPLEX
/*Mask outer and inner ring of widget to tidy up ragged edges of lines while drawing outer ring*/
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &obj->coords, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, 0);
lv_area_t mask_area;
lv_area_copy(&mask_area, &obj->coords);
mask_area.x1 += cir_w;
mask_area.x2 -= cir_w;
mask_area.y1 += cir_w;
mask_area.y2 -= cir_w;
lv_draw_mask_radius_param_t mask_in_param;
lv_draw_mask_radius_init(&mask_in_param, &mask_area, LV_RADIUS_CIRCLE, true);
int16_t mask_in_id = lv_draw_mask_add(&mask_in_param, 0);
/*The inner and outer line ends will be masked out.
*So make lines a little bit longer because the masking makes a more even result*/
lv_coord_t cir_w_extra = line_dsc.width;
#else
lv_coord_t cir_w_extra = 0;
#endif
for(i = 0; i <= 256; i += LV_CPICKER_DEF_QF, a += 360 * LV_CPICKER_DEF_QF) {
line_dsc.color = angle_to_mode_color_fast(obj, i);
uint16_t angle_trigo = (uint16_t)(a >> 8); /*i * 360 / 256 is the scale to apply, but we can skip multiplication here*/
lv_point_t p[2];
p[0].x = cx + ((r + cir_w_extra) * lv_trigo_sin(angle_trigo) >> LV_TRIGO_SHIFT);
p[0].y = cy + ((r + cir_w_extra) * lv_trigo_cos(angle_trigo) >> LV_TRIGO_SHIFT);
p[1].x = cx + ((r - cir_w - cir_w_extra) * lv_trigo_sin(angle_trigo) >> LV_TRIGO_SHIFT);
p[1].y = cy + ((r - cir_w - cir_w_extra) * lv_trigo_cos(angle_trigo) >> LV_TRIGO_SHIFT);
lv_draw_line(draw_ctx, &line_dsc, &p[0], &p[1]);
}
#if LV_DRAW_COMPLEX
lv_draw_mask_free_param(&mask_out_param);
lv_draw_mask_free_param(&mask_in_param);
lv_draw_mask_remove_id(mask_out_id);
lv_draw_mask_remove_id(mask_in_id);
#endif
}
static void draw_knob(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &cir_dsc);
cir_dsc.radius = LV_RADIUS_CIRCLE;
if(colorwheel->knob.recolor) {
cir_dsc.bg_color = lv_colorwheel_get_rgb(obj);
}
lv_area_t knob_area = get_knob_area(obj);
lv_draw_rect(draw_ctx, &cir_dsc, &knob_area);
}
static void invalidate_knob(lv_obj_t * obj)
{
lv_area_t knob_area = get_knob_area(obj);
lv_obj_invalidate_area(obj, &knob_area);
}
static lv_area_t get_knob_area(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
/*Get knob's radius*/
uint16_t r = 0;
r = lv_obj_get_style_arc_width(obj, LV_PART_MAIN) / 2;
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_area_t knob_area;
knob_area.x1 = obj->coords.x1 + colorwheel->knob.pos.x - r - left;
knob_area.y1 = obj->coords.y1 + colorwheel->knob.pos.y - r - right;
knob_area.x2 = obj->coords.x1 + colorwheel->knob.pos.x + r + top;
knob_area.y2 = obj->coords.y1 + colorwheel->knob.pos.y + r + bottom;
return knob_area;
}
static void lv_colorwheel_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_coord_t knob_pad = LV_MAX4(left, right, top, bottom) + 2;
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, knob_pad);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
void * param = lv_event_get_param(e);
/*Refresh extended draw area to make knob visible*/
if(lv_obj_get_width(obj) != lv_area_get_width(param) ||
lv_obj_get_height(obj) != lv_area_get_height(param)) {
refr_knob_pos(obj);
}
}
else if(code == LV_EVENT_STYLE_CHANGED) {
/*Refresh extended draw area to make knob visible*/
refr_knob_pos(obj);
}
else if(code == LV_EVENT_KEY) {
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = (colorwheel->hsv.h + 1) % 360;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = (colorwheel->hsv.s + 1) % 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = (colorwheel->hsv.v + 1) % 100;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = colorwheel->hsv.h > 0 ? (colorwheel->hsv.h - 1) : 360;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = colorwheel->hsv.s > 0 ? (colorwheel->hsv.s - 1) : 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = colorwheel->hsv.v > 0 ? (colorwheel->hsv.v - 1) : 100;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
}
else if(code == LV_EVENT_PRESSED) {
colorwheel->last_change_time = lv_tick_get();
lv_indev_get_point(lv_indev_get_act(), &colorwheel->last_press_point);
res = double_click_reset(obj);
if(res != LV_RES_OK) return;
}
else if(code == LV_EVENT_PRESSING) {
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) return;
lv_indev_type_t indev_type = lv_indev_get_type(indev);
lv_point_t p;
if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
p.x = obj->coords.x1 + lv_obj_get_width(obj) / 2;
p.y = obj->coords.y1 + lv_obj_get_height(obj) / 2;
}
else {
lv_indev_get_point(indev, &p);
}
lv_coord_t drag_limit = indev->driver->scroll_limit;
if((LV_ABS(p.x - colorwheel->last_press_point.x) > drag_limit) ||
(LV_ABS(p.y - colorwheel->last_press_point.y) > drag_limit)) {
colorwheel->last_change_time = lv_tick_get();
colorwheel->last_press_point.x = p.x;
colorwheel->last_press_point.y = p.y;
}
p.x -= obj->coords.x1;
p.y -= obj->coords.y1;
/*Ignore pressing in the inner area*/
uint16_t w = lv_obj_get_width(obj);
int16_t angle = 0;
lv_coord_t cir_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
lv_coord_t r_in = w / 2;
p.x -= r_in;
p.y -= r_in;
bool on_ring = true;
r_in -= cir_w;
if(r_in > LV_DPI_DEF / 2) {
lv_coord_t inner = cir_w / 2;
r_in -= inner;
if(r_in < LV_DPI_DEF / 2) r_in = LV_DPI_DEF / 2;
}
if(p.x * p.x + p.y * p.y < r_in * r_in) {
on_ring = false;
}
/*If the inner area is being pressed, go to the next color mode on long press*/
uint32_t diff = lv_tick_elaps(colorwheel->last_change_time);
if(!on_ring && diff > indev->driver->long_press_time && !colorwheel->mode_fixed) {
next_color_mode(obj);
lv_indev_wait_release(lv_indev_get_act());
return;
}
/*Set the angle only if pressed on the ring*/
if(!on_ring) return;
angle = lv_atan2(p.x, p.y) % 360;
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = angle;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = (angle * 100) / 360;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = (angle * 100) / 360;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_HIT_TEST) {
lv_hit_test_info_t * info = lv_event_get_param(e);;
/*Valid clicks can be only in the circle*/
info->res = _lv_area_is_point_on(&obj->coords, info->point, LV_RADIUS_CIRCLE);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_disc_grad(e);
draw_knob(e);
}
else if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
}
}
static void next_color_mode(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = (colorwheel->mode + 1) % 3;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
}
static void refr_knob_pos(lv_obj_t * obj)
{
invalidate_knob(obj);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t scale_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
lv_coord_t r = (w - scale_w) / 2;
uint16_t angle = get_angle(obj);
colorwheel->knob.pos.x = (((int32_t)r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT);
colorwheel->knob.pos.y = (((int32_t)r * lv_trigo_cos(angle)) >> LV_TRIGO_SHIFT);
colorwheel->knob.pos.x = colorwheel->knob.pos.x + w / 2;
colorwheel->knob.pos.y = colorwheel->knob.pos.y + w / 2;
invalidate_knob(obj);
}
static lv_res_t double_click_reset(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_indev_t * indev = lv_indev_get_act();
/*Double clicked? Use long press time as double click time out*/
if(lv_tick_elaps(colorwheel->last_click_time) < indev->driver->long_press_time) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = 0;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = 100;
break;
}
lv_indev_wait_release(indev);
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
}
colorwheel->last_click_time = lv_tick_get();
return LV_RES_OK;
}
#define SWAPPTR(A, B) do { uint8_t * t = A; A = B; B = t; } while(0)
#define HSV_PTR_SWAP(sextant,r,g,b) if((sextant) & 2) { SWAPPTR((r), (b)); } if((sextant) & 4) { SWAPPTR((g), (b)); } if(!((sextant) & 6)) { \
if(!((sextant) & 1)) { SWAPPTR((r), (g)); } } else { if((sextant) & 1) { SWAPPTR((r), (g)); } }
/**
* Based on the idea from https://www.vagrearg.org/content/hsvrgb
* Here we want to compute an approximate RGB value from a HSV input color space. We don't want to be accurate
* (for that, there's lv_color_hsv_to_rgb), but we want to be fast.
*
* Few tricks are used here: Hue is in range [0; 6 * 256] (so that the sextant is in the high byte and the fractional part is in the low byte)
* both s and v are in [0; 255] range (very convenient to avoid divisions).
*
* We fold all symmetry by swapping the R, G, B pointers so that the code is the same for all sextants.
* We replace division by 255 by a division by 256, a.k.a a shift right by 8 bits.
* This is wrong, but since this is only used to compute the pixels on the screen and not the final color, it's ok.
*/
static void fast_hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t * r, uint8_t * g, uint8_t * b)
{
if(!s) {
*r = *g = *b = v;
return;
}
uint8_t sextant = h >> 8;
HSV_PTR_SWAP(sextant, r, g, b); /*Swap pointers so the conversion code is the same*/
*g = v;
uint8_t bb = ~s;
uint16_t ww = v * bb; /*Don't try to be precise, but instead, be fast*/
*b = ww >> 8;
uint8_t h_frac = h & 0xff;
if(!(sextant & 1)) {
/*Up slope*/
ww = !h_frac ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_frac)); /*Skip multiply if not required*/
}
else {
/*Down slope*/
ww = s * h_frac;
}
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
*r = ww >> 8;
}
static lv_color_t angle_to_mode_color_fast(lv_obj_t * obj, uint16_t angle)
{
lv_colorwheel_t * ext = (lv_colorwheel_t *)obj;
uint8_t r = 0, g = 0, b = 0;
static uint16_t h = 0;
static uint8_t s = 0, v = 0, m = 255;
static uint16_t angle_saved = 0xffff;
/*If the angle is different recalculate scaling*/
if(angle_saved != angle) m = 255;
angle_saved = angle;
switch(ext->mode) {
default:
case LV_COLORWHEEL_MODE_HUE:
/*Don't recompute costly scaling if it does not change*/
if(m != ext->mode) {
s = (uint8_t)(((uint16_t)ext->hsv.s * 51) / 20);
v = (uint8_t)(((uint16_t)ext->hsv.v * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(angle * 6, s, v, &r, &g,
&b); /*A smart compiler will replace x * 6 by (x << 2) + (x << 1) if it's more efficient*/
break;
case LV_COLORWHEEL_MODE_SATURATION:
/*Don't recompute costly scaling if it does not change*/
if(m != ext->mode) {
h = (uint16_t)(((uint32_t)ext->hsv.h * 6 * 256) / 360);
v = (uint8_t)(((uint16_t)ext->hsv.v * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(h, angle, v, &r, &g, &b);
break;
case LV_COLORWHEEL_MODE_VALUE:
/*Don't recompute costly scaling if it does not change*/
if(m != ext->mode) {
h = (uint16_t)(((uint32_t)ext->hsv.h * 6 * 256) / 360);
s = (uint8_t)(((uint16_t)ext->hsv.s * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(h, s, angle, &r, &g, &b);
break;
}
return lv_color_make(r, g, b);
}
static uint16_t get_angle(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
uint16_t angle;
switch(colorwheel->mode) {
default:
case LV_COLORWHEEL_MODE_HUE:
angle = colorwheel->hsv.h;
break;
case LV_COLORWHEEL_MODE_SATURATION:
angle = (colorwheel->hsv.s * 360) / 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
angle = (colorwheel->hsv.v * 360) / 100 ;
break;
}
return angle;
}
#endif /*LV_USE_COLORWHEEL*/
@@ -0,0 +1,142 @@
/**
* @file lv_colorwheel.h
*
*/
#ifndef LV_COLORWHEEL_H
#define LV_COLORWHEEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_COLORWHEEL
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_COLORWHEEL_MODE_HUE,
LV_COLORWHEEL_MODE_SATURATION,
LV_COLORWHEEL_MODE_VALUE
};
typedef uint8_t lv_colorwheel_mode_t;
/*Data of color picker*/
typedef struct {
lv_obj_t obj;
lv_color_hsv_t hsv;
struct {
lv_point_t pos;
uint8_t recolor : 1;
} knob;
uint32_t last_click_time;
uint32_t last_change_time;
lv_point_t last_press_point;
lv_colorwheel_mode_t mode : 2;
uint8_t mode_fixed : 1;
} lv_colorwheel_t;
extern const lv_obj_class_t lv_colorwheel_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a color picker object with disc shape
* @param parent pointer to an object, it will be the parent of the new color picker
* @param knob_recolor true: set the knob's color to the current color
* @return pointer to the created color picker
*/
lv_obj_t * lv_colorwheel_create(lv_obj_t * parent, bool knob_recolor);
/*=====================
* Setter functions
*====================*/
/**
* Set the current hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected hsv
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv);
/**
* Set the current color of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected color
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color);
/**
* Set the current color mode.
* @param colorwheel pointer to color wheel object
* @param mode color mode (hue/sat/val)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode);
/**
* Set if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @param fixed color mode cannot be changed on long press
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed);
/*=====================
* Getter functions
*====================*/
/**
* Get the current selected hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @return current selected hsv
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj);
/**
* Get the current selected color of a color wheel.
* @param colorwheel pointer to color wheel object
* @return color current selected color
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj);
/**
* Get the current color mode.
* @param colorwheel pointer to color wheel object
* @return color mode (hue/sat/val)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj);
/**
* Get if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @return mode cannot be changed on long press
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_COLORWHEEL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_COLORWHEEL_H*/
@@ -0,0 +1,377 @@
/**
* @file lv_imgbtn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_imgbtn.h"
#if LV_USE_IMGBTN != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_imgbtn_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_main(lv_event_t * e);
static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void refr_img(lv_obj_t * imgbtn);
static lv_imgbtn_state_t suggest_state(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_imgbtn_class = {
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_imgbtn_t),
.constructor_cb = lv_imgbtn_constructor,
.event_cb = lv_imgbtn_event,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create an image button object
* @param parent pointer to an object, it will be the parent of the new image button
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set images for a state of the image button
* @param obj pointer to an image button object
* @param state for which state set the new image
* @param src_left pointer to an image source for the left side of the button (a C array or path to
* a file)
* @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C
* array or path to a file)
* @param src_right pointer to an image source for the right side of the button (a C array or path
* to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * obj, lv_imgbtn_state_t state, const void * src_left, const void * src_mid,
const void * src_right)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
imgbtn->img_src_left[state] = src_left;
imgbtn->img_src_mid[state] = src_mid;
imgbtn->img_src_right[state] = src_right;
refr_img(obj);
}
void lv_imgbtn_set_state(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t obj_state = LV_STATE_DEFAULT;
if(state == LV_IMGBTN_STATE_PRESSED || state == LV_IMGBTN_STATE_CHECKED_PRESSED) obj_state |= LV_STATE_PRESSED;
if(state == LV_IMGBTN_STATE_DISABLED || state == LV_IMGBTN_STATE_CHECKED_DISABLED) obj_state |= LV_STATE_DISABLED;
if(state == LV_IMGBTN_STATE_CHECKED_DISABLED || state == LV_IMGBTN_STATE_CHECKED_PRESSED ||
state == LV_IMGBTN_STATE_CHECKED_RELEASED) {
obj_state |= LV_STATE_CHECKED;
}
lv_obj_clear_state(obj, LV_STATE_CHECKED | LV_STATE_PRESSED | LV_STATE_DISABLED);
lv_obj_add_state(obj, obj_state);
refr_img(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the left image in a given state
* @param obj pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_left(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_left[state];
}
/**
* Get the middle image in a given state
* @param obj pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the middle image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_middle(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_mid[state];
}
/**
* Get the right image in a given state
* @param obj pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_right(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_right[state];
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
/*Initialize the allocated 'ext'*/
lv_memset_00((void *)imgbtn->img_src_mid, sizeof(imgbtn->img_src_mid));
lv_memset_00(imgbtn->img_src_left, sizeof(imgbtn->img_src_left));
lv_memset_00(imgbtn->img_src_right, sizeof(imgbtn->img_src_right));
imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
}
static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(&lv_imgbtn_class, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
refr_img(obj);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_main(e);
}
else if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
}
else if(code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_self_size_info(e);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
if(imgbtn->img_src_left[state] == NULL &&
imgbtn->img_src_mid[state] != NULL &&
imgbtn->img_src_right[state] == NULL) {
lv_img_header_t header;
lv_img_decoder_get_info(imgbtn->img_src_mid[state], &header);
p->x = LV_MAX(p->x, header.w);
}
}
}
static void draw_main(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
/*Just draw_main an image*/
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
/*Simply draw the middle src if no tiled*/
const void * src = imgbtn->img_src_left[state];
lv_coord_t tw = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t th = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= tw;
coords.x2 += tw;
coords.y1 -= th;
coords.y2 += th;
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc);
lv_img_header_t header;
lv_area_t coords_part;
lv_coord_t left_w = 0;
lv_coord_t right_w = 0;
if(src) {
lv_img_decoder_get_info(src, &header);
left_w = header.w;
coords_part.x1 = coords.x1;
coords_part.y1 = coords.y1;
coords_part.x2 = coords.x1 + header.w - 1;
coords_part.y2 = coords.y1 + header.h - 1;
lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
}
src = imgbtn->img_src_right[state];
if(src) {
lv_img_decoder_get_info(src, &header);
right_w = header.w;
coords_part.x1 = coords.x2 - header.w + 1;
coords_part.y1 = coords.y1;
coords_part.x2 = coords.x2;
coords_part.y2 = coords.y1 + header.h - 1;
lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
}
src = imgbtn->img_src_mid[state];
if(src) {
lv_area_t clip_area_center;
clip_area_center.x1 = coords.x1 + left_w;
clip_area_center.x2 = coords.x2 - right_w;
clip_area_center.y1 = coords.y1;
clip_area_center.y2 = coords.y2;
bool comm_res;
comm_res = _lv_area_intersect(&clip_area_center, &clip_area_center, draw_ctx->clip_area);
if(comm_res) {
lv_coord_t i;
lv_img_decoder_get_info(src, &header);
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
draw_ctx->clip_area = &clip_area_center;
coords_part.x1 = coords.x1 + left_w;
coords_part.y1 = coords.y1;
coords_part.x2 = coords_part.x1 + header.w - 1;
coords_part.y2 = coords_part.y1 + header.h - 1;
for(i = coords_part.x1; i < (lv_coord_t)(clip_area_center.x2 + header.w - 1); i += header.w) {
lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
coords_part.x1 = coords_part.x2 + 1;
coords_part.x2 += header.w;
}
draw_ctx->clip_area = clip_area_ori;
}
}
}
static void refr_img(lv_obj_t * obj)
{
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
lv_img_header_t header;
const void * src = imgbtn->img_src_mid[state];
if(src == NULL) return;
lv_res_t info_res = LV_RES_OK;
info_res = lv_img_decoder_get_info(src, &header);
if(info_res == LV_RES_OK) {
imgbtn->act_cf = header.cf;
lv_obj_refresh_self_size(obj);
lv_obj_set_height(obj, header.h); /*Keep the user defined width*/
}
else {
imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
}
lv_obj_invalidate(obj);
}
/**
* If `src` is not defined for the current state try to get a state which is related to the current but has `src`.
* E.g. if the PRESSED src is not set but the RELEASED does, use the RELEASED.
* @param imgbtn pointer to an image button
* @param state the state to convert
* @return the suggested state
*/
static lv_imgbtn_state_t suggest_state(lv_obj_t * obj, lv_imgbtn_state_t state)
{
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
if(imgbtn->img_src_mid[state] == NULL) {
switch(state) {
case LV_IMGBTN_STATE_PRESSED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_RELEASED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_PRESSED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_PRESSED]) return LV_IMGBTN_STATE_PRESSED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_DISABLED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_DISABLED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
default:
break;
}
}
return state;
}
lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn)
{
LV_ASSERT_OBJ(imgbtn, MY_CLASS);
lv_state_t obj_state = lv_obj_get_state(imgbtn);
if(obj_state & LV_STATE_DISABLED) {
if(obj_state & LV_STATE_CHECKED) return LV_IMGBTN_STATE_CHECKED_DISABLED;
else return LV_IMGBTN_STATE_DISABLED;
}
if(obj_state & LV_STATE_CHECKED) {
if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_CHECKED_PRESSED;
else return LV_IMGBTN_STATE_CHECKED_RELEASED;
}
else {
if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_PRESSED;
else return LV_IMGBTN_STATE_RELEASED;
}
}
#endif
@@ -0,0 +1,131 @@
/**
* @file lv_imgbtn.h
*
*/
#ifndef LV_IMGBTN_H
#define LV_IMGBTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_IMGBTN != 0
/*********************
* DEFINES
*********************/
typedef enum {
LV_IMGBTN_STATE_RELEASED,
LV_IMGBTN_STATE_PRESSED,
LV_IMGBTN_STATE_DISABLED,
LV_IMGBTN_STATE_CHECKED_RELEASED,
LV_IMGBTN_STATE_CHECKED_PRESSED,
LV_IMGBTN_STATE_CHECKED_DISABLED,
_LV_IMGBTN_STATE_NUM,
} lv_imgbtn_state_t;
/**********************
* TYPEDEFS
**********************/
/*Data of image button*/
typedef struct {
lv_obj_t obj;
const void * img_src_mid[_LV_IMGBTN_STATE_NUM]; /*Store center images to each state*/
const void * img_src_left[_LV_IMGBTN_STATE_NUM]; /*Store left side images to each state*/
const void * img_src_right[_LV_IMGBTN_STATE_NUM]; /*Store right side images to each state*/
lv_img_cf_t act_cf; /*Color format of the currently active image*/
} lv_imgbtn_t;
extern const lv_obj_class_t lv_imgbtn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an image button object
* @param parent pointer to an object, it will be the parent of the new image button
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image
* @param src_left pointer to an image source for the left side of the button (a C array or path to
* a file)
* @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C
* array or path to a file)
* @param src_right pointer to an image source for the right side of the button (a C array or path
* to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_imgbtn_state_t state, const void * src_left, const void * src_mid,
const void * src_right);
/**
* Use this function instead of `lv_obj_add/clear_state` to set a state manually
* @param imgbtn pointer to an image button object
* @param state the new state
*/
void lv_imgbtn_set_state(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/*=====================
* Getter functions
*====================*/
/**
* Get the left image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/**
* Get the middle image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the middle image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/**
* Get the right image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_IMGBTN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMGBTN_H*/
@@ -0,0 +1,430 @@
/**
* @file lv_keyboard.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_keyboard.h"
#if LV_USE_KEYBOARD
#include "../../../widgets/lv_textarea.h"
#include "../../../misc/lv_assert.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_keyboard_class
#define LV_KB_BTN(width) LV_BTNMATRIX_CTRL_POPOVER | width
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_keyboard_update_map(lv_obj_t * obj);
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_keyboard_class = {
.constructor_cb = lv_keyboard_constructor,
.width_def = LV_PCT(100),
.height_def = LV_PCT(50),
.instance_size = sizeof(lv_keyboard_t),
.editable = 1,
.base_class = &lv_btnmatrix_class
};
static const char * const default_kb_map_lc[] = {"1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n",
"ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_lc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_uc[] = {"1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n",
"abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_uc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_spec[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n",
"abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n",
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_spec_map[] = {
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | 2,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_num[] = {"1", "2", "3", LV_SYMBOL_KEYBOARD, "\n",
"4", "5", "6", LV_SYMBOL_OK, "\n",
"7", "8", "9", LV_SYMBOL_BACKSPACE, "\n",
"+/-", "0", ".", LV_SYMBOL_LEFT, LV_SYMBOL_RIGHT, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_num_map[] = {
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, 2,
1, 1, 1, 1, 1
};
static const char * * kb_map[9] = {
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_uc,
(const char * *)default_kb_map_spec,
(const char * *)default_kb_map_num,
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_lc,
(const char * *)NULL,
};
static const lv_btnmatrix_ctrl_t * kb_ctrl[9] = {
default_kb_ctrl_lc_map,
default_kb_ctrl_uc_map,
default_kb_ctrl_spec_map,
default_kb_ctrl_num_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
NULL,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a Keyboard object
* @param parent pointer to an object, it will be the parent of the new keyboard
* @return pointer to the created keyboard
*/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_keyboard_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_keyboard_set_textarea(lv_obj_t * obj, lv_obj_t * ta)
{
if(ta) {
LV_ASSERT_OBJ(ta, &lv_textarea_class);
}
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
/*Hide the cursor of the old Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_clear_state(obj, LV_STATE_FOCUSED);
}
keyboard->ta = ta;
/*Show the cursor of the new Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_add_flag(obj, LV_STATE_FOCUSED);
}
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t * obj, lv_keyboard_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->mode == mode) return;
keyboard->mode = mode;
lv_keyboard_update_map(obj);
}
/**
* Show the button title in a popover when pressed.
* @param kb pointer to a Keyboard object
* @param en whether "popovers" mode is enabled
*/
void lv_keyboard_set_popovers(lv_obj_t * obj, bool en)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->popovers == en) {
return;
}
keyboard->popovers = en;
lv_keyboard_update_ctrl_map(obj);
}
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t * obj, lv_keyboard_mode_t mode, const char * map[],
const lv_btnmatrix_ctrl_t ctrl_map[])
{
kb_map[mode] = map;
kb_ctrl[mode] = ctrl_map;
lv_keyboard_update_map(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->ta;
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->mode;
}
/**
* Tell whether "popovers" mode is enabled or not.
* @param kb pointer to a Keyboard object
* @return true: "popovers" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_popovers(const lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->popovers;
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function can be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
uint16_t btn_id = lv_btnmatrix_get_selected_btn(obj);
if(btn_id == LV_BTNMATRIX_BTN_NONE) return;
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));
if(txt == NULL) return;
if(strcmp(txt, "abc") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]);
lv_keyboard_update_ctrl_map(obj);
return;
}
else if(strcmp(txt, "ABC") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_UPPER;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]);
lv_keyboard_update_ctrl_map(obj);
return;
}
else if(strcmp(txt, "1#") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_SPECIAL;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_SPECIAL]);
lv_keyboard_update_ctrl_map(obj);
return;
}
else if(strcmp(txt, LV_SYMBOL_CLOSE) == 0 || strcmp(txt, LV_SYMBOL_KEYBOARD) == 0) {
lv_res_t res = lv_event_send(obj, LV_EVENT_CANCEL, NULL);
if(res != LV_RES_OK) return;
if(keyboard->ta) {
res = lv_event_send(keyboard->ta, LV_EVENT_CANCEL, NULL);
if(res != LV_RES_OK) return;
}
return;
}
else if(strcmp(txt, LV_SYMBOL_OK) == 0) {
lv_res_t res = lv_event_send(obj, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
if(keyboard->ta) {
res = lv_event_send(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
}
return;
}
/*Add the characters to the text area if set*/
if(keyboard->ta == NULL) return;
if(strcmp(txt, "Enter") == 0 || strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) {
lv_textarea_add_char(keyboard->ta, '\n');
if(lv_textarea_get_one_line(keyboard->ta)) {
lv_res_t res = lv_event_send(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
}
}
else if(strcmp(txt, LV_SYMBOL_LEFT) == 0) {
lv_textarea_cursor_left(keyboard->ta);
}
else if(strcmp(txt, LV_SYMBOL_RIGHT) == 0) {
lv_textarea_cursor_right(keyboard->ta);
}
else if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
lv_textarea_del_char(keyboard->ta);
}
else if(strcmp(txt, "+/-") == 0) {
uint16_t cur = lv_textarea_get_cursor_pos(keyboard->ta);
const char * ta_txt = lv_textarea_get_text(keyboard->ta);
if(ta_txt[0] == '-') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_del_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '+');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else if(ta_txt[0] == '+') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_del_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else {
lv_textarea_set_cursor_pos(keyboard->ta, 0);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur + 1);
}
}
else {
lv_textarea_add_text(keyboard->ta, txt);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
keyboard->ta = NULL;
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
keyboard->popovers = 0;
lv_obj_align(obj, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_event_cb(obj, lv_keyboard_def_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_LTR, 0);
lv_keyboard_update_map(obj);
}
/**
* Update the key and control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
lv_btnmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_keyboard_update_ctrl_map(obj);
}
/**
* Update the control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->popovers) {
/*Apply the current control map (already includes LV_BTNMATRIX_CTRL_POPOVER flags)*/
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
}
else {
/*Make a copy of the current control map*/
lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;
lv_btnmatrix_ctrl_t * ctrl_map = lv_mem_alloc(btnm->btn_cnt * sizeof(lv_btnmatrix_ctrl_t));
lv_memcpy(ctrl_map, kb_ctrl[keyboard->mode], sizeof(lv_btnmatrix_ctrl_t) * btnm->btn_cnt);
/*Remove all LV_BTNMATRIX_CTRL_POPOVER flags*/
for(uint16_t i = 0; i < btnm->btn_cnt; i++) {
ctrl_map[i] &= (~LV_BTNMATRIX_CTRL_POPOVER);
}
/*Apply new control map and clean up*/
lv_btnmatrix_set_ctrl_map(obj, ctrl_map);
lv_mem_free(ctrl_map);
}
}
#endif /*LV_USE_KEYBOARD*/
@@ -0,0 +1,187 @@
/**
* @file lv_keyboard.h
*
*/
#ifndef LV_KEYBOARD_H
#define LV_KEYBOARD_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../widgets/lv_btnmatrix.h"
#if LV_USE_KEYBOARD
/*Testing of dependencies*/
#if LV_USE_BTNMATRIX == 0
#error "lv_kb: lv_btnm is required. Enable it in lv_conf.h (LV_USE_BTNMATRIX 1) "
#endif
#if LV_USE_TEXTAREA == 0
#error "lv_kb: lv_ta is required. Enable it in lv_conf.h (LV_USE_TEXTAREA 1) "
#endif
/*********************
* DEFINES
*********************/
#define LV_KEYBOARD_CTRL_BTN_FLAGS (LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_CHECKED)
/**********************
* TYPEDEFS
**********************/
/** Current keyboard mode.*/
enum {
LV_KEYBOARD_MODE_TEXT_LOWER,
LV_KEYBOARD_MODE_TEXT_UPPER,
LV_KEYBOARD_MODE_SPECIAL,
LV_KEYBOARD_MODE_NUMBER,
LV_KEYBOARD_MODE_USER_1,
LV_KEYBOARD_MODE_USER_2,
LV_KEYBOARD_MODE_USER_3,
LV_KEYBOARD_MODE_USER_4,
};
typedef uint8_t lv_keyboard_mode_t;
/*Data of keyboard*/
typedef struct {
lv_btnmatrix_t btnm;
lv_obj_t * ta; /*Pointer to the assigned text area*/
lv_keyboard_mode_t mode; /*Key map type*/
uint8_t popovers : 1; /*Show button titles in popovers on press*/
} lv_keyboard_t;
extern const lv_obj_class_t lv_keyboard_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a Keyboard object
* @param parent pointer to an object, it will be the parent of the new keyboard
* @return pointer to the created keyboard
*/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_keyboard_set_textarea(lv_obj_t * kb, lv_obj_t * ta);
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t * kb, lv_keyboard_mode_t mode);
/**
* Show the button title in a popover when pressed.
* @param kb pointer to a Keyboard object
* @param en whether "popovers" mode is enabled
*/
void lv_keyboard_set_popovers(lv_obj_t * kb, bool en);
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const char * map[],
const lv_btnmatrix_ctrl_t ctrl_map[]);
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * kb);
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * kb);
/**
* Tell whether "popovers" mode is enabled or not.
* @param kb pointer to a Keyboard object
* @return true: "popovers" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_popovers(const lv_obj_t * obj);
/**
* Get the current map of a keyboard
* @param kb pointer to a keyboard object
* @return the current map
*/
static inline const char ** lv_keyboard_get_map_array(const lv_obj_t * kb)
{
return lv_btnmatrix_get_map(kb);
}
/**
* Get the index of the lastly "activated" button by the user (pressed, released, focused etc)
* Useful in the `event_cb` to get the text of the button, check if hidden etc.
* @param obj pointer to button matrix object
* @return index of the last released button (LV_BTNMATRIX_BTN_NONE: if unset)
*/
static inline uint16_t lv_keyboard_get_selected_btn(const lv_obj_t * obj)
{
return lv_btnmatrix_get_selected_btn(obj);
}
/**
* Get the button's text
* @param obj pointer to button matrix object
* @param btn_id the index a button not counting new line characters.
* @return text of btn_index` button
*/
static inline const char * lv_keyboard_get_btn_text(const lv_obj_t * obj, uint16_t btn_id)
{
return lv_btnmatrix_get_btn_text(obj, btn_id);
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function can be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_event_t * e);
/**********************
* MACROS
**********************/
#endif /*LV_USE_KEYBOARD*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_KEYBOARD_H*/
+221
View File
@@ -0,0 +1,221 @@
/**
* @file lv_led.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_led.h"
#if LV_USE_LED
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_led_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_led_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_led_class = {
.base_class = &lv_obj_class,
.constructor_cb = lv_led_constructor,
.width_def = LV_DPI_DEF / 5,
.height_def = LV_DPI_DEF / 5,
.event_cb = lv_led_event,
.instance_size = sizeof(lv_led_t),
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a led object
* @param parent pointer to an object, it will be the parent of the new led
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the color of the LED
* @param led pointer to a LED object
* @param color the color of the LED
*/
void lv_led_set_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_led_t * led = (lv_led_t *)obj;
led->color = color;
lv_obj_invalidate(obj);
}
/**
* Set the brightness of a LED object
* @param led pointer to a LED object
* @param bright LV_LED_BRIGHT_MIN (max. dark) ... LV_LED_BRIGHT_MAX (max. light)
*/
void lv_led_set_brightness(lv_obj_t * obj, uint8_t bright)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_led_t * led = (lv_led_t *)obj;
if(led->bright == bright) return;
led->bright = LV_CLAMP(LV_LED_BRIGHT_MIN, bright, LV_LED_BRIGHT_MAX);
/*Invalidate the object there fore it will be redrawn*/
lv_obj_invalidate(obj);
}
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led)
{
lv_led_set_brightness(led, LV_LED_BRIGHT_MAX);
}
/**
* Light off a LED
* @param led pointer to a LED object
*/
void lv_led_off(lv_obj_t * led)
{
lv_led_set_brightness(led, LV_LED_BRIGHT_MIN);
}
/**
* Toggle the state of a LED
* @param led pointer to a LED object
*/
void lv_led_toggle(lv_obj_t * obj)
{
uint8_t bright = lv_led_get_brightness(obj);
if(bright > (LV_LED_BRIGHT_MIN + LV_LED_BRIGHT_MAX) >> 1)
lv_led_off(obj);
else
lv_led_on(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the brightness of a LEd object
* @param led pointer to LED object
* @return bright 0 (max. dark) ... 255 (max. light)
*/
uint8_t lv_led_get_brightness(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_led_t * led = (lv_led_t *)obj;
return led->bright;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_led_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_led_t * led = (lv_led_t *)obj;
led->color = lv_theme_get_color_primary(obj);
led->bright = LV_LED_BRIGHT_MAX;
}
static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/* Call the ancestor's event handler */
lv_event_code_t code = lv_event_get_code(e);
if(code != LV_EVENT_DRAW_MAIN && code != LV_EVENT_DRAW_MAIN_END) {
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
}
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
/*Make darker colors in a temporary style according to the brightness*/
lv_led_t * led = (lv_led_t *)obj;
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &rect_dsc);
/*Use the original colors brightness to modify color->led*/
rect_dsc.bg_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_color));
rect_dsc.bg_grad.stops[0].color = lv_color_mix(led->color, lv_color_black(),
lv_color_brightness(rect_dsc.bg_grad.stops[0].color));
rect_dsc.bg_grad.stops[1].color = lv_color_mix(led->color, lv_color_black(),
lv_color_brightness(rect_dsc.bg_grad.stops[1].color));
rect_dsc.shadow_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.shadow_color));
rect_dsc.border_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.border_color));
rect_dsc.outline_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.outline_color));
/*Mix. the color with black proportionally with brightness*/
rect_dsc.bg_color = lv_color_mix(rect_dsc.bg_color, lv_color_black(), led->bright);
rect_dsc.bg_grad.stops[0].color = lv_color_mix(rect_dsc.bg_grad.stops[0].color, lv_color_black(), led->bright);
rect_dsc.bg_grad.stops[1].color = lv_color_mix(rect_dsc.bg_grad.stops[1].color, lv_color_black(), led->bright);
rect_dsc.border_color = lv_color_mix(rect_dsc.border_color, lv_color_black(), led->bright);
rect_dsc.shadow_color = lv_color_mix(rect_dsc.shadow_color, lv_color_black(), led->bright);
rect_dsc.outline_color = lv_color_mix(rect_dsc.outline_color, lv_color_black(), led->bright);
/*Set the current shadow width according to brightness proportionally between LV_LED_BRIGHT_OFF
* and LV_LED_BRIGHT_ON*/
rect_dsc.shadow_width = ((led->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_width) /
(LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN);
rect_dsc.shadow_spread = ((led->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_spread) /
(LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.draw_area = &obj->coords;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_LED_DRAW_PART_RECTANGLE;
part_draw_dsc.rect_dsc = &rect_dsc;
part_draw_dsc.part = LV_PART_MAIN;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(draw_ctx, &rect_dsc, &obj->coords);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
#endif
+116
View File
@@ -0,0 +1,116 @@
/**
* @file lv_led.h
*
*/
#ifndef LV_LED_H
#define LV_LED_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_LED
/*********************
* DEFINES
*********************/
/** Brightness when the LED if OFF */
#ifndef LV_LED_BRIGHT_MIN
# define LV_LED_BRIGHT_MIN 80
#endif
/** Brightness when the LED if ON */
#ifndef LV_LED_BRIGHT_MAX
# define LV_LED_BRIGHT_MAX 255
#endif
/**********************
* TYPEDEFS
**********************/
/*Data of led*/
typedef struct {
lv_obj_t obj;
lv_color_t color;
uint8_t bright; /**< Current brightness of the LED (0..255)*/
} lv_led_t;
extern const lv_obj_class_t lv_led_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_led_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_LED_DRAW_PART_RECTANGLE, /**< The main rectangle*/
} lv_led_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a led object
* @param parent pointer to an object, it will be the parent of the new led
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * parent);
/**
* Set the color of the LED
* @param led pointer to a LED object
* @param color the color of the LED
*/
void lv_led_set_color(lv_obj_t * led, lv_color_t color);
/**
* Set the brightness of a LED object
* @param led pointer to a LED object
* @param bright LV_LED_BRIGHT_MIN (max. dark) ... LV_LED_BRIGHT_MAX (max. light)
*/
void lv_led_set_brightness(lv_obj_t * led, uint8_t bright);
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led);
/**
* Light off a LED
* @param led pointer to a LED object
*/
void lv_led_off(lv_obj_t * led);
/**
* Toggle the state of a LED
* @param led pointer to a LED object
*/
void lv_led_toggle(lv_obj_t * led);
/**
* Get the brightness of a LEd object
* @param led pointer to LED object
* @return bright 0 (max. dark) ... 255 (max. light)
*/
uint8_t lv_led_get_brightness(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LED*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LED_H*/
+120
View File
@@ -0,0 +1,120 @@
/**
* @file lv_list.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_list.h"
#include "../../../core/lv_disp.h"
#include "../../../widgets/lv_label.h"
#include "../../../widgets/lv_img.h"
#include "../../../widgets/lv_btn.h"
#if LV_USE_LIST
/*********************
* DEFINES
*********************/
#define MV_CLASS &lv_list
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
const lv_obj_class_t lv_list_class = {
.base_class = &lv_obj_class,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = LV_DPI_DEF * 2
};
const lv_obj_class_t lv_list_btn_class = {
.base_class = &lv_btn_class,
};
const lv_obj_class_t lv_list_text_class = {
.base_class = &lv_label_class,
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_list_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_class, parent);
lv_obj_class_init_obj(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
return obj;
}
lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_text_class, list);
lv_obj_class_init_obj(obj);
lv_label_set_text(obj, txt);
lv_label_set_long_mode(obj, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(obj, LV_PCT(100));
return obj;
}
lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * icon, const char * txt)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_btn_class, list);
lv_obj_class_init_obj(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
#if LV_USE_IMG == 1
if(icon) {
lv_obj_t * img = lv_img_create(obj);
lv_img_set_src(img, icon);
}
#endif
if(txt) {
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label, txt);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_flex_grow(label, 1);
}
return obj;
}
const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn)
{
LV_UNUSED(list);
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(btn); i++) {
lv_obj_t * child = lv_obj_get_child(btn, i);
if(lv_obj_check_type(child, &lv_label_class)) {
return lv_label_get_text(child);
}
}
return "";
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_LIST*/
+54
View File
@@ -0,0 +1,54 @@
/**
* @file lv_win.h
*
*/
#ifndef LV_LIST_H
#define LV_LIST_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#include "../../layouts/flex/lv_flex.h"
#if LV_USE_LIST
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_list_class;
extern const lv_obj_class_t lv_list_text_class;
extern const lv_obj_class_t lv_list_btn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_list_create(lv_obj_t * parent);
lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt);
lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * icon, const char * txt);
const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LIST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LIST_H*/
+56
View File
@@ -0,0 +1,56 @@
/**
* @file lv_widgets.h
*
*/
#ifndef LV_WIDGETS_H
#define LV_WIDGETS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "animimg/lv_animimg.h"
#include "calendar/lv_calendar.h"
#include "calendar/lv_calendar_header_arrow.h"
#include "calendar/lv_calendar_header_dropdown.h"
#include "chart/lv_chart.h"
#include "keyboard/lv_keyboard.h"
#include "list/lv_list.h"
#include "menu/lv_menu.h"
#include "msgbox/lv_msgbox.h"
#include "meter/lv_meter.h"
#include "spinbox/lv_spinbox.h"
#include "spinner/lv_spinner.h"
#include "tabview/lv_tabview.h"
#include "tileview/lv_tileview.h"
#include "win/lv_win.h"
#include "colorwheel/lv_colorwheel.h"
#include "led/lv_led.h"
#include "imgbtn/lv_imgbtn.h"
#include "span/lv_span.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WIDGETS_H*/
+767
View File
@@ -0,0 +1,767 @@
/**
* @file lv_menu.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_menu.h"
#if LV_USE_MENU
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_menu_class
#include "../../../core/lv_obj.h"
#include "../../layouts/flex/lv_flex.h"
#include "../../../widgets/lv_label.h"
#include "../../../widgets/lv_btn.h"
#include "../../../widgets/lv_img.h"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_menu_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_page_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_page_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_cont_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_section_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
const lv_obj_class_t lv_menu_class = {
.constructor_cb = lv_menu_constructor,
.destructor_cb = lv_menu_destructor,
.base_class = &lv_obj_class,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = LV_DPI_DEF * 2,
.instance_size = sizeof(lv_menu_t)
};
const lv_obj_class_t lv_menu_page_class = {
.constructor_cb = lv_menu_page_constructor,
.destructor_cb = lv_menu_page_destructor,
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_menu_page_t)
};
const lv_obj_class_t lv_menu_cont_class = {
.constructor_cb = lv_menu_cont_constructor,
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT
};
const lv_obj_class_t lv_menu_section_class = {
.constructor_cb = lv_menu_section_constructor,
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT
};
const lv_obj_class_t lv_menu_separator_class = {
.base_class = &lv_obj_class,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT
};
const lv_obj_class_t lv_menu_sidebar_cont_class = {
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_menu_main_cont_class = {
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_menu_main_header_cont_class = {
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_menu_sidebar_header_cont_class = {
.base_class = &lv_obj_class
};
static void lv_menu_refr(lv_obj_t * obj);
static void lv_menu_refr_sidebar_header_mode(lv_obj_t * obj);
static void lv_menu_refr_main_header_mode(lv_obj_t * obj);
static void lv_menu_load_page_event_cb(lv_event_t * e);
static void lv_menu_obj_del_event_cb(lv_event_t * e);
static void lv_menu_back_event_cb(lv_event_t * e);
static void lv_menu_value_changed_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_menu_item_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj);
void lv_menu_clear_history(lv_obj_t * obj);
lv_obj_t * lv_menu_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_menu_page_create(lv_obj_t * parent, char * title)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_page_class, parent);
lv_obj_class_init_obj(obj);
lv_menu_page_t * page = (lv_menu_page_t *)obj;
if(title) {
page->title = lv_mem_alloc(strlen(title) + 1);
LV_ASSERT_MALLOC(page->title);
if(page->title == NULL) return NULL;
strcpy(page->title, title);
}
else {
page->title = NULL;
}
return obj;
}
lv_obj_t * lv_menu_cont_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_cont_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_menu_section_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_section_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_menu_separator_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_separator_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_menu_refr(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
lv_ll_t * history_ll = &(menu->history_ll);
/* The current menu */
lv_menu_history_t * act_hist = _lv_ll_get_head(history_ll);
lv_obj_t * page = NULL;
if(act_hist != NULL) {
page = act_hist->page;
/* Delete the current item from the history */
_lv_ll_remove(history_ll, act_hist);
lv_mem_free(act_hist);
menu->cur_depth--;
}
/* Set it */
lv_menu_set_page(obj, page);
}
/*=====================
* Setter functions
*====================*/
void lv_menu_set_page(lv_obj_t * obj, lv_obj_t * page)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
/* Hide previous page */
if(menu->main_page != NULL) {
lv_obj_set_parent(menu->main_page, menu->storage);
}
if(page != NULL) {
/* Add a new node */
lv_ll_t * history_ll = &(menu->history_ll);
lv_menu_history_t * new_node = _lv_ll_ins_head(history_ll);
LV_ASSERT_MALLOC(new_node);
new_node->page = page;
menu->cur_depth++;
/* Place page in main */
lv_obj_set_parent(page, menu->main);
}
else {
/* Empty page, clear history */
lv_menu_clear_history(obj);
}
menu->main_page = page;
/* If there is a selected tab, update checked state */
if(menu->selected_tab != NULL) {
if(menu->sidebar_page != NULL) {
lv_obj_add_state(menu->selected_tab, LV_STATE_CHECKED);
}
else {
lv_obj_clear_state(menu->selected_tab, LV_STATE_CHECKED);
}
}
/* Back btn management */
if(menu->sidebar_page != NULL) {
/* With sidebar enabled */
if(menu->sidebar_generated) {
if(menu->mode_root_back_btn == LV_MENU_ROOT_BACK_BTN_ENABLED) {
/* Root back btn is always shown if enabled*/
lv_obj_clear_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
else {
lv_obj_add_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
}
if(menu->cur_depth >= 2) {
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
else {
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
}
else {
/* With sidebar disabled */
if(menu->cur_depth >= 2 || menu->mode_root_back_btn == LV_MENU_ROOT_BACK_BTN_ENABLED) {
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
else {
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
}
lv_event_send((lv_obj_t *)menu, LV_EVENT_VALUE_CHANGED, NULL);
lv_menu_refr_main_header_mode(obj);
}
void lv_menu_set_sidebar_page(lv_obj_t * obj, lv_obj_t * page)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
/* Sidebar management*/
if(page != NULL) {
/* Sidebar should be enabled */
if(!menu->sidebar_generated) {
/* Create sidebar */
lv_obj_t * sidebar_cont = lv_obj_class_create_obj(&lv_menu_sidebar_cont_class, obj);
lv_obj_class_init_obj(sidebar_cont);
lv_obj_move_to_index(sidebar_cont, 1);
lv_obj_set_size(sidebar_cont, LV_PCT(30), LV_PCT(100));
lv_obj_set_flex_flow(sidebar_cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_flag(sidebar_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_clear_flag(sidebar_cont, LV_OBJ_FLAG_CLICKABLE);
menu->sidebar = sidebar_cont;
lv_obj_t * sidebar_header = lv_obj_class_create_obj(&lv_menu_sidebar_header_cont_class, sidebar_cont);
lv_obj_class_init_obj(sidebar_header);
lv_obj_set_size(sidebar_header, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(sidebar_header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(sidebar_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(sidebar_header, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(sidebar_header, LV_OBJ_FLAG_EVENT_BUBBLE);
menu->sidebar_header = sidebar_header;
lv_obj_t * sidebar_header_back_btn = lv_btn_create(menu->sidebar_header);
lv_obj_add_event_cb(sidebar_header_back_btn, lv_menu_back_event_cb, LV_EVENT_CLICKED, menu);
lv_obj_add_flag(sidebar_header_back_btn, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_set_flex_flow(sidebar_header_back_btn, LV_FLEX_FLOW_ROW);
menu->sidebar_header_back_btn = sidebar_header_back_btn;
lv_obj_t * sidebar_header_back_icon = lv_img_create(menu->sidebar_header_back_btn);
lv_img_set_src(sidebar_header_back_icon, LV_SYMBOL_LEFT);
lv_obj_t * sidebar_header_title = lv_label_create(menu->sidebar_header);
lv_obj_add_flag(sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
menu->sidebar_header_title = sidebar_header_title;
menu->sidebar_generated = true;
}
lv_obj_set_parent(page, menu->sidebar);
lv_menu_refr_sidebar_header_mode(obj);
}
else {
/* Sidebar should be disabled */
if(menu->sidebar_generated) {
lv_obj_set_parent(menu->sidebar_page, menu->storage);
lv_obj_del(menu->sidebar);
menu->sidebar_generated = false;
}
}
menu->sidebar_page = page;
lv_menu_refr(obj);
}
void lv_menu_set_mode_header(lv_obj_t * obj, lv_menu_mode_header_t mode_header)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->mode_header != mode_header) {
menu->mode_header = mode_header;
lv_menu_refr_main_header_mode(obj);
if(menu->sidebar_generated) lv_menu_refr_sidebar_header_mode(obj);
}
}
void lv_menu_set_mode_root_back_btn(lv_obj_t * obj, lv_menu_mode_root_back_btn_t mode_root_back_btn)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->mode_root_back_btn != mode_root_back_btn) {
menu->mode_root_back_btn = mode_root_back_btn;
lv_menu_refr(obj);
}
}
void lv_menu_set_load_page_event(lv_obj_t * menu, lv_obj_t * obj, lv_obj_t * page)
{
LV_ASSERT_OBJ(menu, MY_CLASS);
lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
/* Remove old event */
if(lv_obj_remove_event_cb(obj, lv_menu_load_page_event_cb)) {
lv_event_send(obj, LV_EVENT_DELETE, NULL);
lv_obj_remove_event_cb(obj, lv_menu_obj_del_event_cb);
}
lv_menu_load_page_event_data_t * event_data = lv_mem_alloc(sizeof(lv_menu_load_page_event_data_t));
event_data->menu = menu;
event_data->page = page;
lv_obj_add_event_cb(obj, lv_menu_load_page_event_cb, LV_EVENT_CLICKED, event_data);
lv_obj_add_event_cb(obj, lv_menu_obj_del_event_cb, LV_EVENT_DELETE, event_data);
}
/*=====================
* Getter functions
*====================*/
lv_obj_t * lv_menu_get_cur_main_page(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->main_page;
}
lv_obj_t * lv_menu_get_cur_sidebar_page(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->sidebar_page;
}
lv_obj_t * lv_menu_get_main_header(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->main_header;
}
lv_obj_t * lv_menu_get_main_header_back_btn(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->main_header_back_btn;
}
lv_obj_t * lv_menu_get_sidebar_header(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->sidebar_header;
}
lv_obj_t * lv_menu_get_sidebar_header_back_btn(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->sidebar_header_back_btn;
}
bool lv_menu_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj)
{
LV_ASSERT_OBJ(menu, MY_CLASS);
if(obj == ((lv_menu_t *)menu)->sidebar_header_back_btn) {
return true;
}
if(obj == ((lv_menu_t *)menu)->main_header_back_btn && ((lv_menu_t *)menu)->prev_depth <= 1) {
return true;
}
return false;
}
void lv_menu_clear_history(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
lv_ll_t * history_ll = &(menu->history_ll);
_lv_ll_clear(history_ll);
menu->cur_depth = 0;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_menu_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_obj_set_layout(obj, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_menu_t * menu = (lv_menu_t *)obj;
menu->mode_header = LV_MENU_HEADER_TOP_FIXED;
menu->mode_root_back_btn = LV_MENU_ROOT_BACK_BTN_DISABLED;
menu->cur_depth = 0;
menu->prev_depth = 0;
menu->sidebar_generated = false;
_lv_ll_init(&(menu->history_ll), sizeof(lv_menu_history_t));
menu->storage = lv_obj_create(obj);
lv_obj_add_flag(menu->storage, LV_OBJ_FLAG_HIDDEN);
menu->sidebar = NULL;
menu->sidebar_header = NULL;
menu->sidebar_header_back_btn = NULL;
menu->sidebar_header_title = NULL;
menu->sidebar_page = NULL;
lv_obj_t * main_cont = lv_obj_class_create_obj(&lv_menu_main_cont_class, obj);
lv_obj_class_init_obj(main_cont);
lv_obj_set_height(main_cont, LV_PCT(100));
lv_obj_set_flex_grow(main_cont, 1);
lv_obj_set_flex_flow(main_cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_flag(main_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_clear_flag(main_cont, LV_OBJ_FLAG_CLICKABLE);
menu->main = main_cont;
lv_obj_t * main_header = lv_obj_class_create_obj(&lv_menu_main_header_cont_class, main_cont);
lv_obj_class_init_obj(main_header);
lv_obj_set_size(main_header, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(main_header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(main_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(main_header, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(main_header, LV_OBJ_FLAG_EVENT_BUBBLE);
menu->main_header = main_header;
/* Create the default simple back btn and title */
lv_obj_t * main_header_back_btn = lv_btn_create(menu->main_header);
lv_obj_add_event_cb(main_header_back_btn, lv_menu_back_event_cb, LV_EVENT_CLICKED, menu);
lv_obj_add_flag(main_header_back_btn, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_set_flex_flow(main_header_back_btn, LV_FLEX_FLOW_ROW);
menu->main_header_back_btn = main_header_back_btn;
lv_obj_t * main_header_back_icon = lv_img_create(menu->main_header_back_btn);
lv_img_set_src(main_header_back_icon, LV_SYMBOL_LEFT);
lv_obj_t * main_header_title = lv_label_create(menu->main_header);
lv_obj_add_flag(main_header_title, LV_OBJ_FLAG_HIDDEN);
menu->main_header_title = main_header_title;
menu->main_page = NULL;
menu->selected_tab = NULL;
lv_obj_add_event_cb(obj, lv_menu_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, menu);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_menu_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_menu_t * menu = (lv_menu_t *)obj;
lv_ll_t * history_ll = &(menu->history_ll);
_lv_ll_clear(history_ll);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_menu_page_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_menu_t * menu = (lv_menu_t *)lv_obj_get_parent(obj);
lv_obj_set_parent(obj, ((lv_menu_t *)menu)->storage);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE);
}
static void lv_menu_page_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_menu_page_t * page = (lv_menu_page_t *)obj;
if(page->title != NULL) {
lv_mem_free(page->title);
page->title = NULL;
}
}
static void lv_menu_cont_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
}
static void lv_menu_section_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
}
static void lv_menu_refr_sidebar_header_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->sidebar_header == NULL || menu->sidebar_page == NULL) return;
switch(menu->mode_header) {
case LV_MENU_HEADER_TOP_FIXED:
/* Content should fill the remaining space */
lv_obj_move_to_index(menu->sidebar_header, 0);
lv_obj_set_flex_grow(menu->sidebar_page, 1);
break;
case LV_MENU_HEADER_TOP_UNFIXED:
lv_obj_move_to_index(menu->sidebar_header, 0);
lv_obj_set_flex_grow(menu->sidebar_page, 0);
break;
case LV_MENU_HEADER_BOTTOM_FIXED:
lv_obj_move_to_index(menu->sidebar_header, 1);
lv_obj_set_flex_grow(menu->sidebar_page, 1);
break;
}
lv_obj_refr_size(menu->sidebar_header);
lv_obj_refr_size(menu->sidebar_page);
if(lv_obj_get_content_height(menu->sidebar_header) == 0) {
lv_obj_add_flag(menu->sidebar_header, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_clear_flag(menu->sidebar_header, LV_OBJ_FLAG_HIDDEN);
}
}
static void lv_menu_refr_main_header_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->main_header == NULL || menu->main_page == NULL) return;
switch(menu->mode_header) {
case LV_MENU_HEADER_TOP_FIXED:
/* Content should fill the remaining space */
lv_obj_move_to_index(menu->main_header, 0);
lv_obj_set_flex_grow(menu->main_page, 1);
break;
case LV_MENU_HEADER_TOP_UNFIXED:
lv_obj_move_to_index(menu->main_header, 0);
lv_obj_set_flex_grow(menu->main_page, 0);
break;
case LV_MENU_HEADER_BOTTOM_FIXED:
lv_obj_move_to_index(menu->main_header, 1);
lv_obj_set_flex_grow(menu->main_page, 1);
break;
}
lv_obj_refr_size(menu->main_header);
lv_obj_refr_size(menu->main_page);
lv_obj_update_layout(menu->main_header);
if(lv_obj_get_content_height(menu->main_header) == 0) {
lv_obj_add_flag(menu->main_header, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_clear_flag(menu->main_header, LV_OBJ_FLAG_HIDDEN);
}
}
static void lv_menu_load_page_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_menu_load_page_event_data_t * event_data = lv_event_get_user_data(e);
lv_menu_t * menu = (lv_menu_t *)(event_data->menu);
lv_obj_t * page = event_data->page;
if(menu->sidebar_page != NULL) {
/* Check if clicked obj is in the sidebar */
bool sidebar = false;
lv_obj_t * parent = obj;
while(parent) {
if(parent == (lv_obj_t *)menu) break;
if(parent == menu->sidebar) {
sidebar = true;
break;
}
parent = lv_obj_get_parent(parent);
}
if(sidebar) {
/* Clear checked state of previous obj */
if(menu->selected_tab != obj && menu->selected_tab != NULL) {
lv_obj_clear_state(menu->selected_tab, LV_STATE_CHECKED);
}
lv_menu_clear_history((lv_obj_t *)menu);
menu->selected_tab = obj;
}
}
lv_menu_set_page((lv_obj_t *)menu, page);
if(lv_group_get_default() != NULL && menu->sidebar_page == NULL) {
/* Sidebar is not supported for now*/
lv_group_focus_next(lv_group_get_default());
}
}
static void lv_menu_obj_del_event_cb(lv_event_t * e)
{
lv_menu_load_page_event_data_t * event_data = lv_event_get_user_data(e);
lv_mem_free(event_data);
}
static void lv_menu_back_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
/* LV_EVENT_CLICKED */
if(code == LV_EVENT_CLICKED) {
lv_obj_t * obj = lv_event_get_target(e);
lv_menu_t * menu = (lv_menu_t *)lv_event_get_user_data(e);
if(!(obj == menu->main_header_back_btn || obj == menu->sidebar_header_back_btn)) return;
menu->prev_depth = menu->cur_depth; /* Save the previous value for user event handler */
if(lv_menu_back_btn_is_root((lv_obj_t *)menu, obj)) return;
lv_ll_t * history_ll = &(menu->history_ll);
/* The current menu */
lv_menu_history_t * act_hist = _lv_ll_get_head(history_ll);
/* The previous menu */
lv_menu_history_t * prev_hist = _lv_ll_get_next(history_ll, act_hist);
if(prev_hist != NULL) {
/* Previous menu exists */
/* Delete the current item from the history */
_lv_ll_remove(history_ll, act_hist);
lv_mem_free(act_hist);
menu->cur_depth--;
/* Create the previous menu.
* Remove it from the history because `lv_menu_set_page` will add it again */
_lv_ll_remove(history_ll, prev_hist);
menu->cur_depth--;
lv_menu_set_page(&(menu->obj), prev_hist->page);
lv_mem_free(prev_hist);
}
}
}
static void lv_menu_value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_user_data(e);
lv_menu_t * menu = (lv_menu_t *)obj;
lv_menu_page_t * main_page = (lv_menu_page_t *)lv_menu_get_cur_main_page(obj);
if(main_page != NULL && menu->main_header_title != NULL) {
if(main_page->title != NULL) {
lv_label_set_text(menu->main_header_title, main_page->title);
lv_obj_clear_flag(menu->main_header_title, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_add_flag(menu->main_header_title, LV_OBJ_FLAG_HIDDEN);
}
}
lv_menu_page_t * sidebar_page = (lv_menu_page_t *)lv_menu_get_cur_sidebar_page(obj);
if(sidebar_page != NULL && menu->sidebar_header_title != NULL) {
if(sidebar_page->title != NULL) {
lv_label_set_text(menu->sidebar_header_title, sidebar_page->title);
lv_obj_clear_flag(menu->sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_add_flag(menu->sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
}
}
}
#endif /*LV_USE_MENU*/
+233
View File
@@ -0,0 +1,233 @@
/**
* @file lv_menu.h
*
*/
#ifndef LV_MENU_H
#define LV_MENU_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_MENU
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_MENU_HEADER_TOP_FIXED, /* Header is positioned at the top */
LV_MENU_HEADER_TOP_UNFIXED, /* Header is positioned at the top and can be scrolled out of view*/
LV_MENU_HEADER_BOTTOM_FIXED /* Header is positioned at the bottom */
};
typedef uint8_t lv_menu_mode_header_t;
enum {
LV_MENU_ROOT_BACK_BTN_DISABLED,
LV_MENU_ROOT_BACK_BTN_ENABLED
};
typedef uint8_t lv_menu_mode_root_back_btn_t;
typedef struct lv_menu_load_page_event_data_t {
lv_obj_t * menu;
lv_obj_t * page;
} lv_menu_load_page_event_data_t;
typedef struct {
lv_obj_t * page;
} lv_menu_history_t;
typedef struct {
lv_obj_t obj;
lv_obj_t * storage; /* a pointer to obj that is the parent of all pages not displayed */
lv_obj_t * main;
lv_obj_t * main_page;
lv_obj_t * main_header;
lv_obj_t *
main_header_back_btn; /* a pointer to obj that on click triggers back btn event handler, can be same as 'main_header' */
lv_obj_t * main_header_title;
lv_obj_t * sidebar;
lv_obj_t * sidebar_page;
lv_obj_t * sidebar_header;
lv_obj_t *
sidebar_header_back_btn; /* a pointer to obj that on click triggers back btn event handler, can be same as 'sidebar_header' */
lv_obj_t * sidebar_header_title;
lv_obj_t * selected_tab;
lv_ll_t history_ll;
uint8_t cur_depth;
uint8_t prev_depth;
uint8_t sidebar_generated : 1;
lv_menu_mode_header_t mode_header : 2;
lv_menu_mode_root_back_btn_t mode_root_back_btn : 1;
} lv_menu_t;
typedef struct {
lv_obj_t obj;
char * title;
} lv_menu_page_t;
extern const lv_obj_class_t lv_menu_class;
extern const lv_obj_class_t lv_menu_page_class;
extern const lv_obj_class_t lv_menu_cont_class;
extern const lv_obj_class_t lv_menu_section_class;
extern const lv_obj_class_t lv_menu_separator_class;
extern const lv_obj_class_t lv_menu_sidebar_cont_class;
extern const lv_obj_class_t lv_menu_main_cont_class;
extern const lv_obj_class_t lv_menu_sidebar_header_cont_class;
extern const lv_obj_class_t lv_menu_main_header_cont_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a menu object
* @param parent pointer to an object, it will be the parent of the new menu
* @return pointer to the created menu
*/
lv_obj_t * lv_menu_create(lv_obj_t * parent);
/**
* Create a menu page object
* @param parent pointer to menu object
* @param title pointer to text for title in header (NULL to not display title)
* @return pointer to the created menu page
*/
lv_obj_t * lv_menu_page_create(lv_obj_t * parent, char * title);
/**
* Create a menu cont object
* @param parent pointer to an object, it will be the parent of the new menu cont object
* @return pointer to the created menu cont
*/
lv_obj_t * lv_menu_cont_create(lv_obj_t * parent);
/**
* Create a menu section object
* @param parent pointer to an object, it will be the parent of the new menu section object
* @return pointer to the created menu section
*/
lv_obj_t * lv_menu_section_create(lv_obj_t * parent);
/**
* Create a menu separator object
* @param parent pointer to an object, it will be the parent of the new menu separator object
* @return pointer to the created menu separator
*/
lv_obj_t * lv_menu_separator_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set menu page to display in main
* @param obj pointer to the menu
* @param page pointer to the menu page to set (NULL to clear main and clear menu history)
*/
void lv_menu_set_page(lv_obj_t * obj, lv_obj_t * page);
/**
* Set menu page to display in sidebar
* @param obj pointer to the menu
* @param page pointer to the menu page to set (NULL to clear sidebar)
*/
void lv_menu_set_sidebar_page(lv_obj_t * obj, lv_obj_t * page);
/**
* Set the how the header should behave and its position
* @param obj pointer to a menu
* @param mode_header
*/
void lv_menu_set_mode_header(lv_obj_t * obj, lv_menu_mode_header_t mode_header);
/**
* Set whether back button should appear at root
* @param obj pointer to a menu
* @param mode_root_back_btn
*/
void lv_menu_set_mode_root_back_btn(lv_obj_t * obj, lv_menu_mode_root_back_btn_t mode_root_back_btn);
/**
* Add menu to the menu item
* @param menu pointer to the menu
* @param obj pointer to the obj
* @param page pointer to the page to load when obj is clicked
*/
void lv_menu_set_load_page_event(lv_obj_t * menu, lv_obj_t * obj, lv_obj_t * page);
/*=====================
* Getter functions
*====================*/
/**
* Get a pointer to menu page that is currently displayed in main
* @param obj pointer to the menu
* @return pointer to current page
*/
lv_obj_t * lv_menu_get_cur_main_page(lv_obj_t * obj);
/**
* Get a pointer to menu page that is currently displayed in sidebar
* @param obj pointer to the menu
* @return pointer to current page
*/
lv_obj_t * lv_menu_get_cur_sidebar_page(lv_obj_t * obj);
/**
* Get a pointer to main header obj
* @param obj pointer to the menu
* @return pointer to main header obj
*/
lv_obj_t * lv_menu_get_main_header(lv_obj_t * obj);
/**
* Get a pointer to main header back btn obj
* @param obj pointer to the menu
* @return pointer to main header back btn obj
*/
lv_obj_t * lv_menu_get_main_header_back_btn(lv_obj_t * obj);
/**
* Get a pointer to sidebar header obj
* @param obj pointer to the menu
* @return pointer to sidebar header obj
*/
lv_obj_t * lv_menu_get_sidebar_header(lv_obj_t * obj);
/**
* Get a pointer to sidebar header obj
* @param obj pointer to the menu
* @return pointer to sidebar header back btn obj
*/
lv_obj_t * lv_menu_get_sidebar_header_back_btn(lv_obj_t * obj);
/**
* Check if an obj is a root back btn
* @param menu pointer to the menu
* @return true if it is a root back btn
*/
bool lv_menu_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj);
/**
* Clear menu history
* @param obj pointer to the menu
*/
void lv_menu_clear_history(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MENU*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MENU_H*/
+697
View File
@@ -0,0 +1,697 @@
/**
* @file lv_meter.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_meter.h"
#if LV_USE_METER != 0
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_meter_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_arcs(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
static void draw_needles(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value);
static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_meter_class = {
.constructor_cb = lv_meter_constructor,
.destructor_cb = lv_meter_destructor,
.event_cb = lv_meter_event,
.instance_size = sizeof(lv_meter_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_meter_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Add scale
*====================*/
lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_scale_t * scale = _lv_ll_ins_head(&meter->scale_ll);
LV_ASSERT_MALLOC(scale);
lv_memset_00(scale, sizeof(lv_meter_scale_t));
scale->angle_range = 270;
scale->rotation = 90 + (360 - scale->angle_range) / 2;
scale->min = 0;
scale->max = 100;
scale->tick_cnt = 6;
scale->tick_length = 8;
scale->tick_width = 2;
scale->label_gap = 2;
return scale;
}
void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len,
lv_color_t color)
{
scale->tick_cnt = cnt;
scale->tick_width = width;
scale->tick_length = len;
scale->tick_color = color;
lv_obj_invalidate(obj);
}
void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width,
uint16_t len, lv_color_t color, int16_t label_gap)
{
scale->tick_major_nth = nth;
scale->tick_major_width = width;
scale->tick_major_length = len;
scale->tick_major_color = color;
scale->label_gap = label_gap;
lv_obj_invalidate(obj);
}
void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range,
uint32_t rotation)
{
scale->min = min;
scale->max = max;
scale->angle_range = angle_range;
scale->rotation = rotation;
lv_obj_invalidate(obj);
}
/*=====================
* Add indicator
*====================*/
lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width,
lv_color_t color, int16_t r_mod)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_LINE;
indic->type_data.needle_line.width = width;
indic->type_data.needle_line.color = color;
indic->type_data.needle_line.r_mod = r_mod;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src,
lv_coord_t pivot_x, lv_coord_t pivot_y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_IMG;
indic->type_data.needle_img.src = src;
indic->type_data.needle_img.pivot.x = pivot_x;
indic->type_data.needle_img.pivot.y = pivot_y;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color,
int16_t r_mod)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_ARC;
indic->type_data.arc.width = width;
indic->type_data.arc.color = color;
indic->type_data.arc.r_mod = r_mod;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start,
lv_color_t color_end, bool local, int16_t width_mod)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_SCALE_LINES;
indic->type_data.scale_lines.color_start = color_start;
indic->type_data.scale_lines.color_end = color_end;
indic->type_data.scale_lines.local_grad = local;
indic->type_data.scale_lines.width_mod = width_mod;
lv_obj_invalidate(obj);
return indic;
}
/*=====================
* Set indicator value
*====================*/
void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_start = indic->start_value;
int32_t old_end = indic->end_value;
indic->start_value = value;
indic->end_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_start, value);
inv_arc(obj, indic, old_end, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_start);
inv_line(obj, indic, old_end);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_value = indic->start_value;
indic->start_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_value, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_value);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_value = indic->end_value;
indic->end_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_value, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_value);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_meter_t * meter = (lv_meter_t *)obj;
_lv_ll_init(&meter->scale_ll, sizeof(lv_meter_scale_t));
_lv_ll_init(&meter->indicator_ll, sizeof(lv_meter_indicator_t));
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
_lv_ll_clear(&meter->indicator_ll);
_lv_ll_clear(&meter->scale_ll);
}
static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
draw_arcs(obj, draw_ctx, &scale_area);
draw_ticks_and_labels(obj, draw_ctx, &scale_area);
draw_needles(obj, draw_ctx, &scale_area);
lv_coord_t r_edge = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_edge;
scale_center.y = scale_area.y1 + r_edge;
lv_draw_rect_dsc_t mid_dsc;
lv_draw_rect_dsc_init(&mid_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &mid_dsc);
lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
lv_area_t nm_cord;
nm_cord.x1 = scale_center.x - w;
nm_cord.y1 = scale_center.y - h;
nm_cord.x2 = scale_center.x + w;
nm_cord.y2 = scale_center.y + h;
lv_draw_rect(draw_ctx, &mid_dsc, &nm_cord);
}
}
static void draw_arcs(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_draw_arc_dsc_t arc_dsc;
lv_draw_arc_dsc_init(&arc_dsc);
arc_dsc.rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
lv_coord_t r_out = lv_area_get_width(scale_area) / 2 ;
lv_point_t scale_center;
scale_center.x = scale_area->x1 + r_out;
scale_center.y = scale_area->y1 + r_out;
lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
lv_meter_indicator_t * indic;
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.arc_dsc = &arc_dsc;
part_draw_dsc.part = LV_PART_INDICATOR;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_METER_DRAW_PART_ARC;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
if(indic->type != LV_METER_INDICATOR_TYPE_ARC) continue;
arc_dsc.color = indic->type_data.arc.color;
arc_dsc.width = indic->type_data.arc.width;
arc_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
lv_meter_scale_t * scale = indic->scale;
int32_t start_angle = lv_map(indic->start_value, scale->min, scale->max, scale->rotation,
scale->rotation + scale->angle_range);
int32_t end_angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation,
scale->rotation + scale->angle_range);
part_draw_dsc.radius = r_out + indic->type_data.arc.r_mod;
part_draw_dsc.sub_part_ptr = indic;
part_draw_dsc.p1 = &scale_center;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_arc(draw_ctx, &arc_dsc, &scale_center, part_draw_dsc.radius, start_angle, end_angle);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_point_t p_center;
lv_coord_t r_edge = LV_MIN(lv_area_get_width(scale_area) / 2, lv_area_get_height(scale_area) / 2);
p_center.x = scale_area->x1 + r_edge;
p_center.y = scale_area->y1 + r_edge;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_TICKS, &line_dsc);
line_dsc.raw_end = 1;
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_obj_init_draw_label_dsc(obj, LV_PART_TICKS, &label_dsc);
lv_meter_scale_t * scale;
lv_draw_mask_radius_param_t inner_minor_mask;
lv_draw_mask_radius_param_t inner_major_mask;
lv_draw_mask_radius_param_t outer_mask;
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.part = LV_PART_TICKS;
part_draw_dsc.type = LV_METER_DRAW_PART_TICK;
part_draw_dsc.line_dsc = &line_dsc;
_LV_LL_READ_BACK(&meter->scale_ll, scale) {
part_draw_dsc.sub_part_ptr = scale;
lv_coord_t r_out = r_edge;
lv_coord_t r_in_minor = r_out - scale->tick_length;
lv_coord_t r_in_major = r_out - scale->tick_major_length;
lv_area_t area_inner_minor;
area_inner_minor.x1 = p_center.x - r_in_minor;
area_inner_minor.y1 = p_center.y - r_in_minor;
area_inner_minor.x2 = p_center.x + r_in_minor;
area_inner_minor.y2 = p_center.y + r_in_minor;
lv_draw_mask_radius_init(&inner_minor_mask, &area_inner_minor, LV_RADIUS_CIRCLE, true);
lv_area_t area_inner_major;
area_inner_major.x1 = p_center.x - r_in_major;
area_inner_major.y1 = p_center.y - r_in_major;
area_inner_major.x2 = p_center.x + r_in_major - 1;
area_inner_major.y2 = p_center.y + r_in_major - 1;
lv_draw_mask_radius_init(&inner_major_mask, &area_inner_major, LV_RADIUS_CIRCLE, true);
lv_area_t area_outer;
area_outer.x1 = p_center.x - r_out;
area_outer.y1 = p_center.y - r_out;
area_outer.x2 = p_center.x + r_out - 1;
area_outer.y2 = p_center.y + r_out - 1;
lv_draw_mask_radius_init(&outer_mask, &area_outer, LV_RADIUS_CIRCLE, false);
int16_t outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
int16_t inner_act_mask_id = LV_MASK_ID_INV; /*Will be added later*/
uint32_t minor_cnt = scale->tick_major_nth ? scale->tick_major_nth - 1 : 0xFFFF;
uint16_t i;
for(i = 0; i < scale->tick_cnt; i++) {
minor_cnt++;
bool major = false;
if(minor_cnt == scale->tick_major_nth) {
minor_cnt = 0;
major = true;
}
int32_t value_of_line = lv_map(i, 0, scale->tick_cnt - 1, scale->min, scale->max);
part_draw_dsc.value = value_of_line;
lv_color_t line_color = major ? scale->tick_major_color : scale->tick_color;
lv_color_t line_color_ori = line_color;
lv_coord_t line_width_ori = major ? scale->tick_major_width : scale->tick_width;
lv_coord_t line_width = line_width_ori;
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
if(indic->type != LV_METER_INDICATOR_TYPE_SCALE_LINES) continue;
if(value_of_line >= indic->start_value && value_of_line <= indic->end_value) {
line_width += indic->type_data.scale_lines.width_mod;
if(indic->type_data.scale_lines.color_start.full == indic->type_data.scale_lines.color_end.full) {
line_color = indic->type_data.scale_lines.color_start;
}
else {
lv_opa_t ratio;
if(indic->type_data.scale_lines.local_grad) {
ratio = lv_map(value_of_line, indic->start_value, indic->end_value, LV_OPA_TRANSP, LV_OPA_COVER);
}
else {
ratio = lv_map(value_of_line, scale->min, scale->max, LV_OPA_TRANSP, LV_OPA_COVER);
}
line_color = lv_color_mix(indic->type_data.scale_lines.color_end, indic->type_data.scale_lines.color_start, ratio);
}
}
}
int32_t angle_upscale = ((i * scale->angle_range) * 10) / (scale->tick_cnt - 1) + + scale->rotation * 10;
line_dsc.color = line_color;
line_dsc.width = line_width;
/*Draw a little bit longer lines to be sure the mask will clip them correctly
*and to get a better precision*/
lv_point_t p_outer;
p_outer.x = p_center.x + r_out + LV_MAX(LV_DPI_DEF, r_out);
p_outer.y = p_center.y;
lv_point_transform(&p_outer, angle_upscale, 256, &p_center);
part_draw_dsc.p1 = &p_center;
part_draw_dsc.p2 = &p_outer;
part_draw_dsc.id = i;
part_draw_dsc.label_dsc = &label_dsc;
/*Draw the text*/
if(major) {
lv_draw_mask_remove_id(outer_mask_id);
uint32_t r_text = r_in_major - scale->label_gap;
lv_point_t p;
p.x = p_center.x + r_text;
p.y = p_center.y;
lv_point_transform(&p, angle_upscale, 256, &p_center);
lv_draw_label_dsc_t label_dsc_tmp;
lv_memcpy(&label_dsc_tmp, &label_dsc, sizeof(label_dsc_tmp));
part_draw_dsc.label_dsc = &label_dsc_tmp;
char buf[16];
lv_snprintf(buf, sizeof(buf), "%" LV_PRId32, value_of_line);
part_draw_dsc.text = buf;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_point_t label_size;
lv_txt_get_size(&label_size, part_draw_dsc.text, label_dsc.font, label_dsc.letter_space, label_dsc.line_space,
LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_area_t label_cord;
label_cord.x1 = p.x - label_size.x / 2;
label_cord.y1 = p.y - label_size.y / 2;
label_cord.x2 = label_cord.x1 + label_size.x;
label_cord.y2 = label_cord.y1 + label_size.y;
lv_draw_label(draw_ctx, part_draw_dsc.label_dsc, &label_cord, part_draw_dsc.text, NULL);
outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
}
else {
part_draw_dsc.label_dsc = NULL;
part_draw_dsc.text = NULL;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
}
inner_act_mask_id = lv_draw_mask_add(major ? &inner_major_mask : &inner_minor_mask, NULL);
lv_draw_line(draw_ctx, &line_dsc, &p_outer, &p_center);
lv_draw_mask_remove_id(inner_act_mask_id);
lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, &part_draw_dsc);
line_dsc.color = line_color_ori;
line_dsc.width = line_width_ori;
}
lv_draw_mask_free_param(&inner_minor_mask);
lv_draw_mask_free_param(&inner_major_mask);
lv_draw_mask_free_param(&outer_mask);
lv_draw_mask_remove_id(outer_mask_id);
}
}
static void draw_needles(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_coord_t r_edge = lv_area_get_width(scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area->x1 + r_edge;
scale_center.y = scale_area->y1 + r_edge;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &line_dsc);
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_ITEMS, &img_dsc);
lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.p1 = &scale_center;
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
lv_meter_scale_t * scale = indic->scale;
part_draw_dsc.sub_part_ptr = indic;
if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_coord_t r_out = r_edge + scale->r_mod + indic->type_data.needle_line.r_mod;
lv_point_t p_end;
p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
line_dsc.color = indic->type_data.needle_line.color;
line_dsc.width = indic->type_data.needle_line.width;
line_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
part_draw_dsc.id = LV_METER_DRAW_PART_NEEDLE_LINE;
part_draw_dsc.line_dsc = &line_dsc;
part_draw_dsc.p2 = &p_end;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_line(draw_ctx, &line_dsc, &scale_center, &p_end);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
if(indic->type_data.needle_img.src == NULL) continue;
int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_img_header_t info;
lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
lv_area_t a;
a.x1 = scale_center.x - indic->type_data.needle_img.pivot.x;
a.y1 = scale_center.y - indic->type_data.needle_img.pivot.y;
a.x2 = a.x1 + info.w - 1;
a.y2 = a.y1 + info.h - 1;
img_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
img_dsc.pivot.x = indic->type_data.needle_img.pivot.x;
img_dsc.pivot.y = indic->type_data.needle_img.pivot.y;
angle = angle * 10;
if(angle > 3600) angle -= 3600;
img_dsc.angle = angle;
part_draw_dsc.img_dsc = &img_dsc;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_img(draw_ctx, &img_dsc, &a, indic->type_data.needle_img.src);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
}
static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value)
{
bool rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_out;
scale_center.y = scale_area.y1 + r_out;
r_out += indic->type_data.arc.r_mod;
lv_meter_scale_t * scale = indic->scale;
int32_t start_angle = lv_map(old_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
int32_t end_angle = lv_map(new_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
lv_area_t a;
lv_draw_arc_get_area(scale_center.x, scale_center.y, r_out, LV_MIN(start_angle, end_angle), LV_MAX(start_angle,
end_angle), indic->type_data.arc.width, rounded, &a);
lv_obj_invalidate_area(obj, &a);
}
static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_out;
scale_center.y = scale_area.y1 + r_out;
lv_meter_scale_t * scale = indic->scale;
if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
r_out += scale->r_mod + indic->type_data.needle_line.r_mod;
lv_point_t p_end;
p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
lv_area_t a;
a.x1 = LV_MIN(scale_center.x, p_end.x) - indic->type_data.needle_line.width - 2;
a.y1 = LV_MIN(scale_center.y, p_end.y) - indic->type_data.needle_line.width - 2;
a.x2 = LV_MAX(scale_center.x, p_end.x) + indic->type_data.needle_line.width + 2;
a.y2 = LV_MAX(scale_center.y, p_end.y) + indic->type_data.needle_line.width + 2;
lv_obj_invalidate_area(obj, &a);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_img_header_t info;
lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
angle = angle * 10;
if(angle > 3600) angle -= 3600;
scale_center.x -= indic->type_data.needle_img.pivot.x;
scale_center.y -= indic->type_data.needle_img.pivot.y;
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, info.w, info.h, angle, LV_IMG_ZOOM_NONE, &indic->type_data.needle_img.pivot);
a.x1 += scale_center.x - 2;
a.y1 += scale_center.y - 2;
a.x2 += scale_center.x + 2;
a.y2 += scale_center.y + 2;
lv_obj_invalidate_area(obj, &a);
}
}
#endif
+267
View File
@@ -0,0 +1,267 @@
/**
* @file lv_meter.h
*
*/
#ifndef LV_METER_H
#define LV_METER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_METER != 0
/*Testing of dependencies*/
#if LV_DRAW_COMPLEX == 0
#error "lv_meter: Complex drawing is required. Enable it in lv_conf.h (LV_DRAW_COMPLEX 1)"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t tick_color;
uint16_t tick_cnt;
uint16_t tick_length;
uint16_t tick_width;
lv_color_t tick_major_color;
uint16_t tick_major_nth;
uint16_t tick_major_length;
uint16_t tick_major_width;
int16_t label_gap;
int16_t label_color;
int32_t min;
int32_t max;
int16_t r_mod;
uint16_t angle_range;
int16_t rotation;
} lv_meter_scale_t;
enum {
LV_METER_INDICATOR_TYPE_NEEDLE_IMG,
LV_METER_INDICATOR_TYPE_NEEDLE_LINE,
LV_METER_INDICATOR_TYPE_SCALE_LINES,
LV_METER_INDICATOR_TYPE_ARC,
};
typedef uint8_t lv_meter_indicator_type_t;
typedef struct {
lv_meter_scale_t * scale;
lv_meter_indicator_type_t type;
lv_opa_t opa;
int32_t start_value;
int32_t end_value;
union {
struct {
const void * src;
lv_point_t pivot;
} needle_img;
struct {
uint16_t width;
int16_t r_mod;
lv_color_t color;
} needle_line;
struct {
uint16_t width;
const void * src;
lv_color_t color;
int16_t r_mod;
} arc;
struct {
int16_t width_mod;
lv_color_t color_start;
lv_color_t color_end;
uint8_t local_grad : 1;
} scale_lines;
} type_data;
} lv_meter_indicator_t;
/*Data of line meter*/
typedef struct {
lv_obj_t obj;
lv_ll_t scale_ll;
lv_ll_t indicator_ll;
} lv_meter_t;
extern const lv_obj_class_t lv_meter_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_meter_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_METER_DRAW_PART_ARC, /**< The arc indicator*/
LV_METER_DRAW_PART_NEEDLE_LINE, /**< The needle lines*/
LV_METER_DRAW_PART_NEEDLE_IMG, /**< The needle images*/
LV_METER_DRAW_PART_TICK, /**< The tick lines and labels*/
} lv_meter_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a Meter object
* @param parent pointer to an object, it will be the parent of the new bar.
* @return pointer to the created meter
*/
lv_obj_t * lv_meter_create(lv_obj_t * parent);
/*=====================
* Add scale
*====================*/
/**
* Add a new scale to the meter.
* @param obj pointer to a meter object
* @return the new scale
* @note Indicators can be attached to scales.
*/
lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj);
/**
* Set the properties of the ticks of a scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param cnt number of tick lines
* @param width width of tick lines
* @param len length of tick lines
* @param color color of tick lines
*/
void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len,
lv_color_t color);
/**
* Make some "normal" ticks major ticks and set their attributes.
* Texts with the current value are also added to the major ticks.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param nth make every Nth normal tick major tick. (start from the first on the left)
* @param width width of the major ticks
* @param len length of the major ticks
* @param color color of the major ticks
* @param label_gap gap between the major ticks and the labels
*/
void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width,
uint16_t len, lv_color_t color, int16_t label_gap);
/**
* Set the value and angular range of a scale.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param min the minimum value
* @param max the maximal value
* @param angle_range the angular range of the scale
* @param rotation the angular offset from the 3 o'clock position (clock-wise)
*/
void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range,
uint32_t rotation);
/*=====================
* Add indicator
*====================*/
/**
* Add a needle line indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param width width of the line
* @param color color of the line
* @param r_mod the radius modifier (added to the scale's radius) to get the lines length
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width,
lv_color_t color, int16_t r_mod);
/**
* Add a needle image indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param src the image source of the indicator. path or pointer to ::lv_img_dsc_t
* @param pivot_x the X pivot point of the needle
* @param pivot_y the Y pivot point of the needle
* @return the new indicator
* @note the needle image should point to the right, like -O----->
*/
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src,
lv_coord_t pivot_x, lv_coord_t pivot_y);
/**
* Add an arc indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param width width of the arc
* @param color color of the arc
* @param r_mod the radius modifier (added to the scale's radius) to get the outer radius of the arc
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color,
int16_t r_mod);
/**
* Add a scale line indicator the scale. It will modify the ticks.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param color_start the start color
* @param color_end the end color
* @param local tell how to map start and end color. true: the indicator's start and end_value; false: the scale's min max value
* @param width_mod add this the affected tick's width
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start,
lv_color_t color_end, bool local, int16_t width_mod);
/*=====================
* Set indicator value
*====================*/
/**
* Set the value of the indicator. It will set start and and value to the same value
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**
* Set the start value of the indicator.
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**
* Set the start value of the indicator.
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**********************
* MACROS
**********************/
#endif /*LV_USE_METER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_METER_H*/
@@ -0,0 +1,209 @@
/**
* @file lv_msgbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_msgbox.h"
#if LV_USE_MSGBOX
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define LV_MSGBOX_FLAG_AUTO_PARENT LV_OBJ_FLAG_WIDGET_1 /*Mark that the parent was automatically created*/
#define MY_CLASS &lv_msgbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void msgbox_close_click_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_msgbox_class = {
.base_class = &lv_obj_class,
.width_def = LV_DPI_DEF * 2,
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_msgbox_t)
};
const lv_obj_class_t lv_msgbox_content_class = {
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_obj_t)
};
const lv_obj_class_t lv_msgbox_backdrop_class = {
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_PCT(100),
.instance_size = sizeof(lv_obj_t)
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * txt, const char * btn_txts[],
bool add_close_btn)
{
LV_LOG_INFO("begin");
bool auto_parent = false;
if(parent == NULL) {
auto_parent = true;
parent = lv_obj_class_create_obj(&lv_msgbox_backdrop_class, lv_layer_top());
LV_ASSERT_MALLOC(parent);
lv_obj_class_init_obj(parent);
lv_obj_clear_flag(parent, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_size(parent, LV_PCT(100), LV_PCT(100));
}
lv_obj_t * obj = lv_obj_class_create_obj(&lv_msgbox_class, parent);
LV_ASSERT_MALLOC(obj);
if(obj == NULL) return NULL;
lv_obj_class_init_obj(obj);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
if(auto_parent) lv_obj_add_flag(obj, LV_MSGBOX_FLAG_AUTO_PARENT);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_WRAP);
bool has_title = title && strlen(title) > 0;
/*When a close button is required, we need the empty label as spacer to push the button to the right*/
if(add_close_btn || has_title) {
mbox->title = lv_label_create(obj);
lv_label_set_text(mbox->title, has_title ? title : "");
lv_label_set_long_mode(mbox->title, LV_LABEL_LONG_SCROLL_CIRCULAR);
if(add_close_btn) lv_obj_set_flex_grow(mbox->title, 1);
else lv_obj_set_width(mbox->title, LV_PCT(100));
}
if(add_close_btn) {
mbox->close_btn = lv_btn_create(obj);
lv_obj_set_ext_click_area(mbox->close_btn, LV_DPX(10));
lv_obj_add_event_cb(mbox->close_btn, msgbox_close_click_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(mbox->close_btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
const lv_font_t * font = lv_obj_get_style_text_font(mbox->close_btn, LV_PART_MAIN);
lv_coord_t close_btn_size = lv_font_get_line_height(font) + LV_DPX(10);
lv_obj_set_size(mbox->close_btn, close_btn_size, close_btn_size);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
mbox->content = lv_obj_class_create_obj(&lv_msgbox_content_class, obj);
bool has_txt = txt && strlen(txt) > 0;
if(has_txt) {
mbox->text = lv_label_create(mbox->content);
lv_label_set_text(mbox->text, txt);
lv_label_set_long_mode(mbox->text, LV_LABEL_LONG_WRAP);
lv_obj_set_width(mbox->text, lv_pct(100));
}
if(btn_txts) {
mbox->btns = lv_btnmatrix_create(obj);
lv_btnmatrix_set_map(mbox->btns, btn_txts);
lv_btnmatrix_set_btn_ctrl_all(mbox->btns, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
uint32_t btn_cnt = 0;
while(btn_txts[btn_cnt] && btn_txts[btn_cnt][0] != '\0') {
btn_cnt++;
}
const lv_font_t * font = lv_obj_get_style_text_font(mbox->btns, LV_PART_ITEMS);
lv_coord_t btn_h = lv_font_get_line_height(font) + LV_DPI_DEF / 10;
lv_obj_set_size(mbox->btns, btn_cnt * (2 * LV_DPI_DEF / 3), btn_h);
lv_obj_set_style_max_width(mbox->btns, lv_pct(100), 0);
lv_obj_add_flag(mbox->btns, LV_OBJ_FLAG_EVENT_BUBBLE); /*To see the event directly on the message box*/
}
return obj;
}
lv_obj_t * lv_msgbox_get_title(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->title;
}
lv_obj_t * lv_msgbox_get_close_btn(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->close_btn;
}
lv_obj_t * lv_msgbox_get_text(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->text;
}
lv_obj_t * lv_msgbox_get_content(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->content;
}
lv_obj_t * lv_msgbox_get_btns(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->btns;
}
uint16_t lv_msgbox_get_active_btn(lv_obj_t * mbox)
{
lv_obj_t * btnm = lv_msgbox_get_btns(mbox);
return lv_btnmatrix_get_selected_btn(btnm);
}
const char * lv_msgbox_get_active_btn_text(lv_obj_t * mbox)
{
lv_obj_t * btnm = lv_msgbox_get_btns(mbox);
return lv_btnmatrix_get_btn_text(btnm, lv_btnmatrix_get_selected_btn(btnm));
}
void lv_msgbox_close(lv_obj_t * mbox)
{
if(lv_obj_has_flag(mbox, LV_MSGBOX_FLAG_AUTO_PARENT)) lv_obj_del(lv_obj_get_parent(mbox));
else lv_obj_del(mbox);
}
void lv_msgbox_close_async(lv_obj_t * dialog)
{
if(lv_obj_has_flag(dialog, LV_MSGBOX_FLAG_AUTO_PARENT)) lv_obj_del_async(lv_obj_get_parent(dialog));
else lv_obj_del_async(dialog);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void msgbox_close_click_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * mbox = lv_obj_get_parent(btn);
lv_msgbox_close(mbox);
}
#endif /*LV_USE_MSGBOX*/
@@ -0,0 +1,99 @@
/**
* @file lv_mbox.h
*
*/
#ifndef LV_MSGBOX_H
#define LV_MSGBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_MSGBOX
/*Testing of dependencies*/
#if LV_USE_BTNMATRIX == 0
#error "lv_mbox: lv_btnm is required. Enable it in lv_conf.h (LV_USE_BTNMATRIX 1) "
#endif
#if LV_USE_LABEL == 0
#error "lv_mbox: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1) "
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
lv_obj_t * title;
lv_obj_t * close_btn;
lv_obj_t * content;
lv_obj_t * text;
lv_obj_t * btns;
} lv_msgbox_t;
extern const lv_obj_class_t lv_msgbox_class;
extern const lv_obj_class_t lv_msgbox_content_class;
extern const lv_obj_class_t lv_msgbox_backdrop_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a message box object
* @param parent pointer to parent or NULL to create a full screen modal message box
* @param title the title of the message box
* @param txt the text of the message box
* @param btn_txts the buttons as an array of texts terminated by an "" element. E.g. {"btn1", "btn2", ""}
* @param add_close_btn true: add a close button
* @return pointer to the message box object
*/
lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * txt, const char * btn_txts[],
bool add_close_btn);
lv_obj_t * lv_msgbox_get_title(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_close_btn(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_text(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_content(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_btns(lv_obj_t * obj);
/**
* Get the index of the selected button
* @param mbox message box object
* @return index of the button (LV_BTNMATRIX_BTN_NONE: if unset)
*/
uint16_t lv_msgbox_get_active_btn(lv_obj_t * mbox);
const char * lv_msgbox_get_active_btn_text(lv_obj_t * mbox);
void lv_msgbox_close(lv_obj_t * mbox);
void lv_msgbox_close_async(lv_obj_t * mbox);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MSGBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MSGBOX_H*/
File diff suppressed because it is too large Load Diff
+245
View File
@@ -0,0 +1,245 @@
/**
* @file lv_span.h
*
*/
#ifndef LV_SPAN_H
#define LV_SPAN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPAN != 0
/*********************
* DEFINES
*********************/
#ifndef LV_SPAN_SNIPPET_STACK_SIZE
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_SPAN_OVERFLOW_CLIP,
LV_SPAN_OVERFLOW_ELLIPSIS,
};
typedef uint8_t lv_span_overflow_t;
enum {
LV_SPAN_MODE_FIXED, /**< fixed the obj size*/
LV_SPAN_MODE_EXPAND, /**< Expand the object size to the text size*/
LV_SPAN_MODE_BREAK, /**< Keep width, break the too long lines and expand height*/
};
typedef uint8_t lv_span_mode_t;
typedef struct {
char * txt; /* a pointer to display text */
lv_obj_t * spangroup; /* a pointer to spangroup */
lv_style_t style; /* display text style */
uint8_t static_flag : 1;/* the text is static flag */
} lv_span_t;
/** Data of label*/
typedef struct {
lv_obj_t obj;
int32_t lines;
lv_coord_t indent; /* first line indent */
lv_coord_t cache_w; /* the cache automatically calculates the width */
lv_coord_t cache_h; /* similar cache_w */
lv_ll_t child_ll;
uint8_t mode : 2; /* details see lv_span_mode_t */
uint8_t overflow : 1; /* details see lv_span_overflow_t */
uint8_t refresh : 1; /* the spangroup need refresh cache_w and cache_h */
} lv_spangroup_t;
extern const lv_obj_class_t lv_spangroup_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a spangroup object
* @param par pointer to an object, it will be the parent of the new spangroup
* @return pointer to the created spangroup
*/
lv_obj_t * lv_spangroup_create(lv_obj_t * par);
/**
* Create a span string descriptor and add to spangroup.
* @param obj pointer to a spangroup object.
* @return pointer to the created span.
*/
lv_span_t * lv_spangroup_new_span(lv_obj_t * obj);
/**
* Remove the span from the spangroup and free memory.
* @param obj pointer to a spangroup object.
* @param span pointer to a span.
*/
void lv_spangroup_del_span(lv_obj_t * obj, lv_span_t * span);
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a span. Memory will be allocated to store the text by the span.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text(lv_span_t * span, const char * text);
/**
* Set a static text. It will not be saved by the span so the 'text' variable
* has to be 'alive' while the span exist.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text_static(lv_span_t * span, const char * text);
/**
* Set the align of the spangroup.
* @param obj pointer to a spangroup object.
* @param align see lv_text_align_t for details.
*/
void lv_spangroup_set_align(lv_obj_t * obj, lv_text_align_t align);
/**
* Set the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @param overflow see lv_span_overflow_t for details.
*/
void lv_spangroup_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow);
/**
* Set the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @param indent The first line indentation
*/
void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent);
/**
* Set the mode of the spangroup.
* @param obj pointer to a spangroup object.
* @param mode see lv_span_mode_t for details.
*/
void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode);
/**
* Set lines of the spangroup.
* @param obj pointer to a spangroup object.
* @param lines max lines that can be displayed in LV_SPAN_MODE_BREAK mode. < 0 means no limit.
*/
void lv_spangroup_set_lines(lv_obj_t * obj, int32_t lines);
/*=====================
* Getter functions
*====================*/
/**
* Get a spangroup child by its index.
*
* @param obj The spangroup object
* @param id the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @return The child span at index `id`, or NULL if the ID does not exist
*/
lv_span_t * lv_spangroup_get_child(const lv_obj_t * obj, int32_t id);
/**
*
* @param obj The spangroup object to get the child count of.
* @return The span count of the spangroup.
*/
uint32_t lv_spangroup_get_child_cnt(const lv_obj_t * obj);
/**
* get the align of the spangroup.
* @param obj pointer to a spangroup object.
* @return the align value.
*/
lv_text_align_t lv_spangroup_get_align(lv_obj_t * obj);
/**
* get the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @return the overflow value.
*/
lv_span_overflow_t lv_spangroup_get_overflow(lv_obj_t * obj);
/**
* get the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @return the indent value.
*/
lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj);
/**
* get the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj);
/**
* get lines of the spangroup.
* @param obj pointer to a spangroup object.
* @return the lines value.
*/
int32_t lv_spangroup_get_lines(lv_obj_t * obj);
/**
* get max line height of all span in the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_max_line_h(lv_obj_t * obj);
/**
* get the text content width when all span of spangroup on a line.
* @param obj pointer to a spangroup object.
* @param max_width if text content width >= max_width, return max_width
* to reduce computation, if max_width == 0, returns the text content width.
* @return text content width or max_width.
*/
uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width);
/**
* get the text content height with width fixed.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width);
/*=====================
* Other functions
*====================*/
/**
* update the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
void lv_spangroup_refr_mode(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPAN*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_SPAN_H*/
@@ -0,0 +1,516 @@
/**
* @file lv_spinbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinbox.h"
#if LV_USE_SPINBOX
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_spinbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_spinbox_updatevalue(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_spinbox_class = {
.constructor_cb = lv_spinbox_constructor,
.event_cb = lv_spinbox_event,
.width_def = LV_DPI_DEF,
.instance_size = sizeof(lv_spinbox_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.base_class = &lv_textarea_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param obj pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(i > spinbox->range_max) i = spinbox->range_max;
if(i < spinbox->range_min) i = spinbox->range_min;
spinbox->value = i;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox rollover function
* @param spinbox pointer to spinbox
* @param b true or false to enable or disable (default)
*/
void lv_spinbox_set_rollover(lv_obj_t * obj, bool b)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->rollover = b;
}
/**
* Set spinbox digit format (digit count and decimal format)
* @param spinbox pointer to spinbox
* @param digit_count number of digit excluding the decimal separator and the sign
* @param separator_position number of digit before the decimal point. If 0, decimal point is not
* shown
*/
void lv_spinbox_set_digit_format(lv_obj_t * obj, uint8_t digit_count, uint8_t separator_position)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) digit_count = LV_SPINBOX_MAX_DIGIT_COUNT;
if(separator_position >= digit_count) separator_position = 0;
if(digit_count < LV_SPINBOX_MAX_DIGIT_COUNT) {
int64_t max_val = lv_pow(10, digit_count);
if(spinbox->range_max > max_val - 1) spinbox->range_max = max_val - 1;
if(spinbox->range_min < - max_val + 1) spinbox->range_min = - max_val + 1;
}
spinbox->digit_count = digit_count;
spinbox->dec_point_pos = separator_position;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement
*/
void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->step = step;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox value range
* @param spinbox pointer to spinbox
* @param range_min maximum value, inclusive
* @param range_max minimum value, inclusive
*/
void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->range_max = range_max;
spinbox->range_min = range_min;
if(spinbox->value > spinbox->range_max) spinbox->value = spinbox->range_max;
if(spinbox->value < spinbox->range_min) spinbox->value = spinbox->range_min;
lv_spinbox_updatevalue(obj);
}
/**
* Set cursor position to a specific digit for edition
* @param spinbox pointer to spinbox
* @param pos selected position in spinbox
*/
void lv_spinbox_set_cursor_pos(lv_obj_t * obj, uint8_t pos)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * lv_pow(10, pos);
if(pos <= 0) spinbox->step = 1;
else if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Set direction of digit step when clicking an encoder button while in editing mode
* @param spinbox pointer to spinbox
* @param direction the direction (LV_DIR_RIGHT or LV_DIR_LEFT)
*/
void lv_spinbox_set_digit_step_direction(lv_obj_t * obj, lv_dir_t direction)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->digit_step_dir = direction;
lv_spinbox_updatevalue(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the spinbox numeral value (user has to convert to float according to its digit format)
* @param obj pointer to spinbox
* @return value integer value of the spinbox
*/
int32_t lv_spinbox_get_value(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->value;
}
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param obj pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->step;
}
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition
* @param obj pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t new_step = spinbox->step / 10;
if((new_step) > 0)
spinbox->step = new_step;
else
spinbox->step = 1;
lv_spinbox_updatevalue(obj);
}
/**
* Select next higher digit for edition
* @param obj pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * 10;
if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Get spinbox rollover function status
* @param obj pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->rollover;
}
/**
* Increment spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value + spinbox->step <= spinbox->range_max) {
/*Special mode when zero crossing*/
if((spinbox->value + spinbox->step) > 0 && spinbox->value < 0) spinbox->value = -spinbox->value;
spinbox->value += spinbox->step;
}
else {
// Rollover?
if((spinbox->rollover) && (spinbox->value == spinbox->range_max))
spinbox->value = spinbox->range_min;
else
spinbox->value = spinbox->range_max;
}
lv_spinbox_updatevalue(obj);
}
/**
* Decrement spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value - spinbox->step >= spinbox->range_min) {
/*Special mode when zero crossing*/
if((spinbox->value - spinbox->step) < 0 && spinbox->value > 0) spinbox->value = -spinbox->value;
spinbox->value -= spinbox->step;
}
else {
/*Rollover?*/
if((spinbox->rollover) && (spinbox->value == spinbox->range_min))
spinbox->value = spinbox->range_max;
else
spinbox->value = spinbox->range_min;
}
lv_spinbox_updatevalue(obj);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_LOG_TRACE("begin");
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
/*Initialize the allocated 'ext'*/
spinbox->value = 0;
spinbox->dec_point_pos = 0;
spinbox->digit_count = 5;
spinbox->step = 1;
spinbox->range_max = 99999;
spinbox->range_min = -99999;
spinbox->rollover = false;
spinbox->digit_step_dir = LV_DIR_RIGHT;
lv_textarea_set_one_line(obj, true);
lv_textarea_set_cursor_click_pos(obj, true);
lv_spinbox_updatevalue(obj);
LV_LOG_TRACE("Spinbox constructor finished");
}
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = LV_RES_OK;
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(code == LV_EVENT_RELEASED) {
/*If released with an ENCODER then move to the next digit*/
lv_indev_t * indev = lv_indev_get_act();
if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) {
if(lv_group_get_editing(lv_obj_get_group(obj))) {
if(spinbox->digit_count > 1) {
if(spinbox->digit_step_dir == LV_DIR_RIGHT) {
if(spinbox->step > 1) {
lv_spinbox_step_next(obj);
}
else {
/*Restart from the MSB*/
spinbox->step = lv_pow(10, spinbox->digit_count - 2);
lv_spinbox_step_prev(obj);
}
}
else {
if(spinbox->step < lv_pow(10, spinbox->digit_count - 1)) {
lv_spinbox_step_prev(obj);
}
else {
/*Restart from the LSB*/
spinbox->step = 10;
lv_spinbox_step_next(obj);
}
}
}
}
}
/*The cursor has been positioned to a digit.
* Set `step` accordingly*/
else {
const char * txt = lv_textarea_get_text(obj);
size_t txt_len = strlen(txt);
if(txt[spinbox->ta.cursor.pos] == '.') {
lv_textarea_cursor_left(obj);
}
else if(spinbox->ta.cursor.pos == (uint32_t)txt_len) {
lv_textarea_set_cursor_pos(obj, txt_len - 1);
}
else if(spinbox->ta.cursor.pos == 0 && spinbox->range_min < 0) {
lv_textarea_set_cursor_pos(obj, 1);
}
size_t len = spinbox->digit_count - 1;
uint16_t cp = spinbox->ta.cursor.pos;
if(spinbox->ta.cursor.pos > spinbox->dec_point_pos && spinbox->dec_point_pos != 0) cp--;
uint32_t pos = len - cp;
if(spinbox->range_min < 0) pos++;
spinbox->step = 1;
uint16_t i;
for(i = 0; i < pos; i++) spinbox->step *= 10;
}
}
else if(code == LV_EVENT_KEY) {
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_increment(obj);
else
lv_spinbox_step_next(obj);
}
else if(c == LV_KEY_LEFT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_decrement(obj);
else
lv_spinbox_step_prev(obj);
}
else if(c == LV_KEY_UP) {
lv_spinbox_increment(obj);
}
else if(c == LV_KEY_DOWN) {
lv_spinbox_decrement(obj);
}
else {
lv_textarea_add_char(obj, c);
}
}
}
static void lv_spinbox_updatevalue(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8];
lv_memset_00(buf, sizeof(buf));
char * buf_p = buf;
uint8_t cur_shift_left = 0;
if(spinbox->range_min < 0) { // hide sign if there are only positive values
/*Add the sign*/
(*buf_p) = spinbox->value >= 0 ? '+' : '-';
buf_p++;
}
else {
/*Cursor need shift to left*/
cur_shift_left++;
}
int32_t i;
char digits[LV_SPINBOX_MAX_DIGIT_COUNT + 4];
/*Convert the numbers to string (the sign is already handled so always covert positive number)*/
lv_snprintf(digits, sizeof(digits), "%" LV_PRId32, LV_ABS(spinbox->value));
/*Add leading zeros*/
int lz_cnt = spinbox->digit_count - (int)strlen(digits);
if(lz_cnt > 0) {
for(i = (uint16_t)strlen(digits); i >= 0; i--) {
digits[i + lz_cnt] = digits[i];
}
for(i = 0; i < lz_cnt; i++) {
digits[i] = '0';
}
}
int32_t intDigits;
intDigits = (spinbox->dec_point_pos == 0) ? spinbox->digit_count : spinbox->dec_point_pos;
/*Add the decimal part*/
for(i = 0; i < intDigits && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
if(spinbox->dec_point_pos != 0) {
/*Insert the decimal point*/
(*buf_p) = '.';
buf_p++;
for(/*Leave i*/; i < spinbox->digit_count && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
}
/*Refresh the text*/
lv_textarea_set_text(obj, (char *)buf);
/*Set the cursor position*/
int32_t step = spinbox->step;
uint8_t cur_pos = (uint8_t)spinbox->digit_count;
while(step >= 10) {
step /= 10;
cur_pos--;
}
if(cur_pos > intDigits) cur_pos++; /*Skip the decimal point*/
cur_pos -= cur_shift_left;
lv_textarea_set_cursor_pos(obj, cur_pos);
}
#endif /*LV_USE_SPINBOX*/
@@ -0,0 +1,182 @@
/**
* @file lv_spinbox.h
*
*/
#ifndef LV_SPINBOX_H
#define LV_SPINBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPINBOX
/*Testing of dependencies*/
#if LV_USE_TEXTAREA == 0
#error "lv_spinbox: lv_ta is required. Enable it in lv_conf.h (LV_USE_TEXTAREA 1) "
#endif
/*********************
* DEFINES
*********************/
#define LV_SPINBOX_MAX_DIGIT_COUNT 10
/**********************
* TYPEDEFS
**********************/
/*Data of spinbox*/
typedef struct {
lv_textarea_t ta; /*Ext. of ancestor*/
/*New data for this type*/
int32_t value;
int32_t range_max;
int32_t range_min;
int32_t step;
uint16_t digit_count : 4;
uint16_t dec_point_pos : 4; /*if 0, there is no separator and the number is an integer*/
uint16_t rollover : 1; // Set to true for rollover functionality
uint16_t digit_step_dir : 2; // the direction the digit will step on encoder button press when editing
} lv_spinbox_t;
extern const lv_obj_class_t lv_spinbox_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a Spinbox object
* @param parent pointer to an object, it will be the parent of the new spinbox
* @return pointer to the created spinbox
*/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param obj pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i);
/**
* Set spinbox rollover function
* @param obj pointer to spinbox
* @param b true or false to enable or disable (default)
*/
void lv_spinbox_set_rollover(lv_obj_t * obj, bool b);
/**
* Set spinbox digit format (digit count and decimal format)
* @param obj pointer to spinbox
* @param digit_count number of digit excluding the decimal separator and the sign
* @param separator_position number of digit before the decimal point. If 0, decimal point is not
* shown
*/
void lv_spinbox_set_digit_format(lv_obj_t * obj, uint8_t digit_count, uint8_t separator_position);
/**
* Set spinbox step
* @param obj pointer to spinbox
* @param step steps on increment/decrement. Can be 1, 10, 100, 1000, etc the digit that will change.
*/
void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step);
/**
* Set spinbox value range
* @param obj pointer to spinbox
* @param range_min maximum value, inclusive
* @param range_max minimum value, inclusive
*/
void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max);
/**
* Set cursor position to a specific digit for edition
* @param obj pointer to spinbox
* @param pos selected position in spinbox
*/
void lv_spinbox_set_cursor_pos(lv_obj_t * obj, uint8_t pos);
/**
* Set direction of digit step when clicking an encoder button while in editing mode
* @param obj pointer to spinbox
* @param direction the direction (LV_DIR_RIGHT or LV_DIR_LEFT)
*/
void lv_spinbox_set_digit_step_direction(lv_obj_t * obj, lv_dir_t direction);
/*=====================
* Getter functions
*====================*/
/**
* Get spinbox rollover function status
* @param obj pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t * obj);
/**
* Get the spinbox numeral value (user has to convert to float according to its digit format)
* @param obj pointer to spinbox
* @return value integer value of the spinbox
*/
int32_t lv_spinbox_get_value(lv_obj_t * obj);
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param obj pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition by dividing the step by 10
* @param obj pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * obj);
/**
* Select next higher digit for edition by multiplying the step by 10
* @param obj pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj);
/**
* Increment spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj);
/**
* Decrement spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj);
/**********************
* MACROS
**********************/
/* It was ambiguous in MicroPython. See https://github.com/lvgl/lvgl/issues/3301
* TODO remove in v9*/
#define lv_spinbox_set_pos lv_spinbox_set_cursor_pos
#endif /*LV_USE_SPINBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SPINBOX_H*/
@@ -0,0 +1,104 @@
/**
* @file lv_spinner.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinner.h"
#if LV_USE_SPINNER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spinner_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void arc_anim_start_angle(void * obj, int32_t v);
static void arc_anim_end_angle(void * obj, int32_t v);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_spinner_class = {
.base_class = &lv_arc_class,
.constructor_cb = lv_spinner_constructor
};
static uint32_t time_param;
static uint32_t arc_length_param;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a spinner object
* @param parent pointer to an object, it will be the parent of the new spinner
* @return pointer to the created spinner
*/
lv_obj_t * lv_spinner_create(lv_obj_t * parent, uint32_t time, uint32_t arc_length)
{
time_param = time;
arc_length_param = arc_length;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_spinner_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spinner_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a, arc_anim_end_angle);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_time(&a, time_param);
lv_anim_set_values(&a, arc_length_param, 360 + arc_length_param);
lv_anim_start(&a);
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
lv_anim_set_values(&a, 0, 360);
lv_anim_set_exec_cb(&a, arc_anim_start_angle);
lv_anim_start(&a);
lv_arc_set_bg_angles(obj, 0, 360);
lv_arc_set_rotation(obj, 270);
}
static void arc_anim_start_angle(void * obj, int32_t v)
{
lv_arc_set_start_angle(obj, (uint16_t) v);
}
static void arc_anim_end_angle(void * obj, int32_t v)
{
lv_arc_set_end_angle(obj, (uint16_t) v);
}
#endif /*LV_USE_SPINNER*/
@@ -0,0 +1,50 @@
/**
* @file lv_spinner.h
*
*/
#ifndef LV_SPINNER_H
#define LV_SPINNER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPINNER
/*Testing of dependencies*/
#if LV_USE_ARC == 0
#error "lv_spinner: lv_arc is required. Enable it in lv_conf.h (LV_USE_ARC 1) "
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_spinner_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_spinner_create(lv_obj_t * parent, uint32_t time, uint32_t arc_length);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPINNER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SPINNER_H*/
+352
View File
@@ -0,0 +1,352 @@
/**
* @file lv_tabview.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_tabview.h"
#if LV_USE_TABVIEW
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_tabview_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_tabview_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_tabview_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void btns_value_changed_event_cb(lv_event_t * e);
static void cont_scroll_end_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_tabview_class = {
.constructor_cb = lv_tabview_constructor,
.destructor_cb = lv_tabview_destructor,
.event_cb = lv_tabview_event,
.width_def = LV_PCT(100),
.height_def = LV_PCT(100),
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_tabview_t)
};
static lv_dir_t tabpos_create;
static lv_coord_t tabsize_create;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab_size)
{
LV_LOG_INFO("begin");
tabpos_create = tab_pos;
tabsize_create = tab_size;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tabview_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_tabview_add_tab(lv_obj_t * obj, const char * name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
lv_obj_t * cont = lv_tabview_get_content(obj);
lv_obj_t * page = lv_obj_create(cont);
lv_obj_set_size(page, LV_PCT(100), LV_PCT(100));
lv_obj_clear_flag(page, LV_OBJ_FLAG_CLICK_FOCUSABLE);
uint32_t tab_id = lv_obj_get_child_cnt(cont);
lv_obj_t * btns = lv_tabview_get_tab_btns(obj);
char ** old_map = tabview->map;
char ** new_map;
/*top or bottom dir*/
if(tabview->tab_pos & LV_DIR_VER) {
new_map = lv_mem_alloc((tab_id + 1) * sizeof(const char *));
lv_memcpy_small(new_map, old_map, sizeof(const char *) * (tab_id - 1));
new_map[tab_id - 1] = lv_mem_alloc(strlen(name) + 1);
strcpy((char *)new_map[tab_id - 1], name);
new_map[tab_id] = "";
}
/*left or right dir*/
else {
new_map = lv_mem_alloc((tab_id * 2) * sizeof(const char *));
lv_memcpy_small(new_map, old_map, sizeof(const char *) * (tab_id - 1) * 2);
if(tabview->tab_cnt == 0) {
new_map[0] = lv_mem_alloc(strlen(name) + 1);
strcpy((char *)new_map[0], name);
new_map[1] = "";
}
else {
new_map[tab_id * 2 - 3] = "\n";
new_map[tab_id * 2 - 2] = lv_mem_alloc(strlen(name) + 1);
new_map[tab_id * 2 - 1] = "";
strcpy((char *)new_map[(tab_id * 2) - 2], name);
}
}
tabview->map = new_map;
lv_btnmatrix_set_map(btns, (const char **)new_map);
lv_mem_free(old_map);
lv_btnmatrix_set_btn_ctrl_all(btns, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_CLICK_TRIG |
LV_BTNMATRIX_CTRL_NO_REPEAT);
tabview->tab_cnt++;
if(tabview->tab_cnt == 1) {
lv_tabview_set_act(obj, 0, LV_ANIM_OFF);
}
lv_btnmatrix_set_btn_ctrl(btns, tabview->tab_cur, LV_BTNMATRIX_CTRL_CHECKED);
return page;
}
void lv_tabview_rename_tab(lv_obj_t * obj, uint32_t id, const char * new_name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
if(id >= tabview->tab_cnt) return;
if(tabview->tab_pos & LV_DIR_HOR) id *= 2;
lv_mem_free(tabview->map[id]);
tabview->map[id] = lv_mem_alloc(strlen(new_name) + 1);
strcpy(tabview->map[id], new_name);
lv_obj_invalidate(obj);
}
void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
if(id >= tabview->tab_cnt) {
id = tabview->tab_cnt - 1;
}
/*To be sure lv_obj_get_content_width will return valid value*/
lv_obj_update_layout(obj);
lv_obj_t * cont = lv_tabview_get_content(obj);
if(cont == NULL) return;
if((tabview->tab_pos & LV_DIR_VER) != 0) {
lv_coord_t gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_content_width(cont);
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
lv_obj_scroll_to_x(cont, id * (gap + w), anim_en);
}
else {
int32_t id_rtl = -(int32_t)id;
lv_obj_scroll_to_x(cont, (gap + w) * id_rtl, anim_en);
}
}
else {
lv_coord_t gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_content_height(cont);
lv_obj_scroll_to_y(cont, id * (gap + h), anim_en);
}
lv_obj_t * btns = lv_tabview_get_tab_btns(obj);
lv_btnmatrix_set_btn_ctrl(btns, id, LV_BTNMATRIX_CTRL_CHECKED);
tabview->tab_cur = id;
}
uint16_t lv_tabview_get_tab_act(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
return tabview->tab_cur;
}
lv_obj_t * lv_tabview_get_content(lv_obj_t * tv)
{
return lv_obj_get_child(tv, 1);
}
lv_obj_t * lv_tabview_get_tab_btns(lv_obj_t * tv)
{
return lv_obj_get_child(tv, 0);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
tabview->tab_pos = tabpos_create;
switch(tabview->tab_pos) {
case LV_DIR_TOP:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
break;
case LV_DIR_BOTTOM:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN_REVERSE);
break;
case LV_DIR_LEFT:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
break;
case LV_DIR_RIGHT:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_REVERSE);
break;
}
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_t * btnm;
lv_obj_t * cont;
btnm = lv_btnmatrix_create(obj);
cont = lv_obj_create(obj);
lv_btnmatrix_set_one_checked(btnm, true);
tabview->map = lv_mem_alloc(sizeof(const char *));
tabview->map[0] = "";
lv_btnmatrix_set_map(btnm, (const char **)tabview->map);
lv_obj_add_event_cb(btnm, btns_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_flag(btnm, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_add_event_cb(cont, cont_scroll_end_event_cb, LV_EVENT_ALL, NULL);
lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);
switch(tabview->tab_pos) {
case LV_DIR_TOP:
case LV_DIR_BOTTOM:
lv_obj_set_size(btnm, LV_PCT(100), tabsize_create);
lv_obj_set_width(cont, LV_PCT(100));
lv_obj_set_flex_grow(cont, 1);
break;
case LV_DIR_LEFT:
case LV_DIR_RIGHT:
lv_obj_set_size(btnm, tabsize_create, LV_PCT(100));
lv_obj_set_height(cont, LV_PCT(100));
lv_obj_set_flex_grow(cont, 1);
break;
}
lv_group_t * g = lv_group_get_default();
if(g) lv_group_add_obj(g, btnm);
if((tabview->tab_pos & LV_DIR_VER) != 0) {
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_CENTER);
}
else {
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
}
lv_obj_add_flag(cont, LV_OBJ_FLAG_SCROLL_ONE);
lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
}
static void lv_tabview_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
uint32_t i;
if(tabview->tab_pos & LV_DIR_VER) {
for(i = 0; i < tabview->tab_cnt; i++) {
lv_mem_free(tabview->map[i]);
tabview->map[i] = NULL;
}
}
if(tabview->tab_pos & LV_DIR_HOR) {
for(i = 0; i < tabview->tab_cnt; i++) {
lv_mem_free(tabview->map[i * 2]);
tabview->map[i * 2] = NULL;
}
}
lv_mem_free(tabview->map);
tabview->map = NULL;
}
static void lv_tabview_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(&lv_tabview_class, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * target = lv_event_get_target(e);
if(code == LV_EVENT_SIZE_CHANGED) {
lv_tabview_set_act(target, lv_tabview_get_tab_act(target), LV_ANIM_OFF);
}
}
static void btns_value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * btns = lv_event_get_target(e);
lv_obj_t * tv = lv_obj_get_parent(btns);
uint32_t id = lv_btnmatrix_get_selected_btn(btns);
lv_tabview_set_act(tv, id, LV_ANIM_ON);
}
static void cont_scroll_end_event_cb(lv_event_t * e)
{
lv_obj_t * cont = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * tv = lv_obj_get_parent(cont);
lv_tabview_t * tv_obj = (lv_tabview_t *)tv;
if(code == LV_EVENT_LAYOUT_CHANGED) {
lv_tabview_set_act(tv, lv_tabview_get_tab_act(tv), LV_ANIM_OFF);
}
else if(code == LV_EVENT_SCROLL_END) {
lv_indev_t * indev = lv_indev_get_act();
if(indev && indev->proc.state == LV_INDEV_STATE_PRESSED) {
return;
}
lv_point_t p;
lv_obj_get_scroll_end(cont, &p);
lv_coord_t t;
if((tv_obj->tab_pos & LV_DIR_VER) != 0) {
lv_coord_t w = lv_obj_get_content_width(cont);
if(lv_obj_get_style_base_dir(tv, LV_PART_MAIN) == LV_BASE_DIR_RTL) t = -(p.x - w / 2) / w;
else t = (p.x + w / 2) / w;
}
else {
lv_coord_t h = lv_obj_get_content_height(cont);
t = (p.y + h / 2) / h;
}
if(t < 0) t = 0;
bool new_tab = false;
if(t != lv_tabview_get_tab_act(tv)) new_tab = true;
lv_tabview_set_act(tv, t, LV_ANIM_ON);
if(new_tab) lv_event_send(tv, LV_EVENT_VALUE_CHANGED, NULL);
}
}
#endif /*LV_USE_TABVIEW*/
@@ -0,0 +1,65 @@
/**
* @file lv_templ.h
*
*/
#ifndef LV_TABVIEW_H
#define LV_TABVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_TABVIEW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
char ** map;
uint16_t tab_cnt;
uint16_t tab_cur;
lv_dir_t tab_pos;
} lv_tabview_t;
extern const lv_obj_class_t lv_tabview_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab_size);
lv_obj_t * lv_tabview_add_tab(lv_obj_t * tv, const char * name);
void lv_tabview_rename_tab(lv_obj_t * obj, uint32_t tab_id, const char * new_name);
lv_obj_t * lv_tabview_get_content(lv_obj_t * tv);
lv_obj_t * lv_tabview_get_tab_btns(lv_obj_t * tv);
void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en);
uint16_t lv_tabview_get_tab_act(lv_obj_t * tv);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TABVIEW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TABVIEW_H*/
@@ -0,0 +1,194 @@
/**
* @file lv_tileview.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_tileview.h"
#include "../../../core/lv_indev.h"
#if LV_USE_TILEVIEW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_tileview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_tileview_tile_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void tileview_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_tileview_class = {.constructor_cb = lv_tileview_constructor,
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_tileview_t)
};
const lv_obj_class_t lv_tileview_tile_class = {.constructor_cb = lv_tileview_tile_constructor,
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_tileview_tile_t)
};
static lv_dir_t create_dir;
static uint32_t create_col_id;
static uint32_t create_row_id;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_tileview_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tileview_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*======================
* Add/remove functions
*=====================*/
lv_obj_t * lv_tileview_add_tile(lv_obj_t * tv, uint8_t col_id, uint8_t row_id, lv_dir_t dir)
{
LV_LOG_INFO("begin");
create_dir = dir;
create_col_id = col_id;
create_row_id = row_id;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tileview_tile_class, tv);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_obj_set_tile(lv_obj_t * obj, lv_obj_t * tile_obj, lv_anim_enable_t anim_en)
{
lv_coord_t tx = lv_obj_get_x(tile_obj);
lv_coord_t ty = lv_obj_get_y(tile_obj);
lv_tileview_tile_t * tile = (lv_tileview_tile_t *)tile_obj;
lv_tileview_t * tv = (lv_tileview_t *) obj;
tv->tile_act = (lv_obj_t *)tile;
lv_obj_set_scroll_dir(obj, tile->dir);
lv_obj_scroll_to(obj, tx, ty, anim_en);
}
void lv_obj_set_tile_id(lv_obj_t * tv, uint32_t col_id, uint32_t row_id, lv_anim_enable_t anim_en)
{
lv_obj_update_layout(tv);
lv_coord_t w = lv_obj_get_content_width(tv);
lv_coord_t h = lv_obj_get_content_height(tv);
lv_coord_t tx = col_id * w;
lv_coord_t ty = row_id * h;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(tv); i++) {
lv_obj_t * tile_obj = lv_obj_get_child(tv, i);
lv_coord_t x = lv_obj_get_x(tile_obj);
lv_coord_t y = lv_obj_get_y(tile_obj);
if(x == tx && y == ty) {
lv_obj_set_tile(tv, tile_obj, anim_en);
return;
}
}
LV_LOG_WARN("No tile found with at (%d,%d) index", (int)col_id, (int)row_id);
}
lv_obj_t * lv_tileview_get_tile_act(lv_obj_t * obj)
{
lv_tileview_t * tv = (lv_tileview_t *) obj;
return tv->tile_act;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_tileview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_add_event_cb(obj, tileview_event_cb, LV_EVENT_ALL, NULL);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ONE);
lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scroll_snap_y(obj, LV_SCROLL_SNAP_CENTER);
}
static void lv_tileview_tile_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_update_layout(obj); /*Be sure the size is correct*/
lv_obj_set_pos(obj, create_col_id * lv_obj_get_content_width(parent),
create_row_id * lv_obj_get_content_height(parent));
lv_tileview_tile_t * tile = (lv_tileview_tile_t *)obj;
tile->dir = create_dir;
if(create_col_id == 0 && create_row_id == 0) {
lv_obj_set_scroll_dir(parent, create_dir);
}
}
static void tileview_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_tileview_t * tv = (lv_tileview_t *) obj;
if(code == LV_EVENT_SCROLL_END) {
lv_indev_t * indev = lv_indev_get_act();
if(indev && indev->proc.state == LV_INDEV_STATE_PRESSED) {
return;
}
lv_coord_t w = lv_obj_get_content_width(obj);
lv_coord_t h = lv_obj_get_content_height(obj);
lv_point_t scroll_end;
lv_obj_get_scroll_end(obj, &scroll_end);
lv_coord_t left = scroll_end.x;
lv_coord_t top = scroll_end.y;
lv_coord_t tx = ((left + (w / 2)) / w) * w;
lv_coord_t ty = ((top + (h / 2)) / h) * h;
lv_dir_t dir = LV_DIR_ALL;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * tile_obj = lv_obj_get_child(obj, i);
lv_coord_t x = lv_obj_get_x(tile_obj);
lv_coord_t y = lv_obj_get_y(tile_obj);
if(x == tx && y == ty) {
lv_tileview_tile_t * tile = (lv_tileview_tile_t *)tile_obj;
tv->tile_act = (lv_obj_t *)tile;
dir = tile->dir;
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
break;
}
}
lv_obj_set_scroll_dir(obj, dir);
}
}
#endif /*LV_USE_TILEVIEW*/
@@ -0,0 +1,72 @@
/**
* @file lv_tileview.h
*
*/
#ifndef LV_TILEVIEW_H
#define LV_TILEVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_TILEVIEW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
lv_obj_t * tile_act;
} lv_tileview_t;
typedef struct {
lv_obj_t obj;
lv_dir_t dir;
} lv_tileview_tile_t;
extern const lv_obj_class_t lv_tileview_class;
extern const lv_obj_class_t lv_tileview_tile_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a Tileview object
* @param parent pointer to an object, it will be the parent of the new tileview
* @return pointer to the created tileview
*/
lv_obj_t * lv_tileview_create(lv_obj_t * parent);
lv_obj_t * lv_tileview_add_tile(lv_obj_t * tv, uint8_t col_id, uint8_t row_id, lv_dir_t dir);
void lv_obj_set_tile(lv_obj_t * tv, lv_obj_t * tile_obj, lv_anim_enable_t anim_en);
void lv_obj_set_tile_id(lv_obj_t * tv, uint32_t col_id, uint32_t row_id, lv_anim_enable_t anim_en);
lv_obj_t * lv_tileview_get_tile_act(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_TILEVIEW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TILEVIEW_H*/
+110
View File
@@ -0,0 +1,110 @@
/**
* @file lv_win.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_win.h"
#if LV_USE_WIN
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_win_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_win_class = {
.constructor_cb = lv_win_constructor,
.width_def = LV_PCT(100),
.height_def = LV_PCT(100),
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_win_t)
};
static lv_coord_t create_header_height;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_win_create(lv_obj_t * parent, lv_coord_t header_height)
{
LV_LOG_INFO("begin");
create_header_height = header_height;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_win_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_win_add_title(lv_obj_t * win, const char * txt)
{
lv_obj_t * header = lv_win_get_header(win);
lv_obj_t * title = lv_label_create(header);
lv_label_set_long_mode(title, LV_LABEL_LONG_DOT);
lv_label_set_text(title, txt);
lv_obj_set_flex_grow(title, 1);
return title;
}
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * icon, lv_coord_t btn_w)
{
lv_obj_t * header = lv_win_get_header(win);
lv_obj_t * btn = lv_btn_create(header);
lv_obj_set_size(btn, btn_w, LV_PCT(100));
lv_obj_t * img = lv_img_create(btn);
lv_img_set_src(img, icon);
lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);
return btn;
}
lv_obj_t * lv_win_get_header(lv_obj_t * win)
{
return lv_obj_get_child(win, 0);
}
lv_obj_t * lv_win_get_content(lv_obj_t * win)
{
return lv_obj_get_child(win, 1);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_win_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_obj_set_size(obj, lv_obj_get_width(parent), lv_obj_get_height(parent));
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_t * header = lv_obj_create(obj);
lv_obj_set_size(header, LV_PCT(100), create_header_height);
lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_t * cont = lv_obj_create(obj);
lv_obj_set_flex_grow(cont, 1);
lv_obj_set_width(cont, LV_PCT(100));
}
#endif
+51
View File
@@ -0,0 +1,51 @@
/**
* @file lv_win.h
*
*/
#ifndef LV_WIN_H
#define LV_WIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
} lv_win_t;
extern const lv_obj_class_t lv_win_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_win_create(lv_obj_t * parent, lv_coord_t header_height);
lv_obj_t * lv_win_add_title(lv_obj_t * win, const char * txt);
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * icon, lv_coord_t btn_w);
lv_obj_t * lv_win_get_header(lv_obj_t * win);
lv_obj_t * lv_win_get_content(lv_obj_t * win);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WIN_H*/