1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-21 06:53:01 +08:00
lvgl/lv_core/lv_group.c

468 lines
13 KiB
C
Raw Normal View History

2017-07-19 15:19:10 +02:00
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_group.h"
#if USE_LV_GROUP != 0
#include <stddef.h>
2017-07-19 15:19:10 +02:00
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void style_mod_def(lv_style_t * style);
static void style_mod_edit_def(lv_style_t * style);
2017-07-19 15:19:10 +02:00
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a new object group
* @return pointer to the new object group
*/
2017-07-19 15:19:10 +02:00
lv_group_t * lv_group_create(void)
{
2017-11-23 21:28:36 +01:00
lv_group_t * group = lv_mem_alloc(sizeof(lv_group_t));
lv_mem_assert(group);
if(group == NULL) return NULL;
2017-11-24 17:48:47 +01:00
lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
2017-07-19 15:19:10 +02:00
group->style_mod = style_mod_def;
group->style_mod_edit = style_mod_edit_def;
group->obj_focus = NULL;
2017-07-27 12:07:16 +02:00
group->frozen = 0;
2018-03-22 10:37:00 +01:00
group->focus_cb = NULL;
2018-07-12 23:38:27 +02:00
group->click_focus = 1;
group->edit_mode_en = 0;
group->editing = 0;
2017-07-19 15:19:10 +02:00
return group;
}
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group)
{
2018-06-19 09:49:58 +02:00
/*Defocus the the currently focused object*/
if(group->obj_focus != NULL) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
2018-06-19 09:49:58 +02:00
/*Remove the objects from the group*/
lv_obj_t ** obj;
LL_READ(group->obj_ll, obj) {
(*obj)->group_p = NULL;
}
lv_ll_clear(&(group->obj_ll));
lv_mem_free(group);
}
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
2017-07-19 15:19:10 +02:00
{
if(group == NULL) return;
2017-07-19 15:19:10 +02:00
obj->group_p = group;
2017-11-24 17:48:47 +01:00
lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
lv_mem_assert(next);
if(next == NULL) return;
2017-07-19 15:19:10 +02:00
*next = obj;
/* If the head and the tail is equal then there is only one object in the linked list.
* In this case automatically activate it*/
2017-11-24 17:48:47 +01:00
if(lv_ll_get_head(&group->obj_ll) == next) {
lv_group_focus_next(group);
}
2017-07-19 15:19:10 +02:00
}
/**
* Remove an object from its group
2017-11-19 20:45:40 +01:00
* @param obj pointer to an object to remove
*/
2017-11-19 20:45:40 +01:00
void lv_group_remove_obj(lv_obj_t * obj)
2017-07-19 15:19:10 +02:00
{
lv_group_t * g = obj->group_p;
if(g == NULL) return;
2018-06-19 09:49:58 +02:00
if(g->obj_focus == NULL) return; /*Just to be sure (Not possible if there is at least one object in the group)*/
2018-03-22 10:37:00 +01:00
if(*g->obj_focus == obj) {
2018-06-19 09:49:58 +02:00
lv_group_focus_next(g);
}
/* If the focuses object is still the same then it was the only object in the group but it will be deleted.
* Set the `obj_focus` to NULL to get back to the initial state of the group with zero objects*/
if(*g->obj_focus == obj) {
2018-06-19 09:49:58 +02:00
g->obj_focus = NULL;
}
2018-03-22 10:37:00 +01:00
/*Search the object and remove it from its group */
lv_obj_t ** i;
LL_READ(g->obj_ll, i) {
if(*i == obj) {
2017-11-24 17:48:47 +01:00
lv_ll_rem(&g->obj_ll, i);
lv_mem_free(i);
obj->group_p = NULL;
}
}
2017-07-19 15:19:10 +02:00
}
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj)
2017-07-19 15:19:10 +02:00
{
lv_group_t * g = obj->group_p;
if(g == NULL) return;
2017-07-27 12:07:16 +02:00
if(g->frozen != 0) return;
lv_obj_t ** i;
LL_READ(g->obj_ll, i) {
if(*i == obj) {
2018-07-12 23:38:27 +02:00
if(g->obj_focus == i) return; /*Don't focus the already focused object again*/
if(g->obj_focus != NULL) {
(*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
2018-06-19 09:49:58 +02:00
if(g->obj_focus != NULL) {
(*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_FOCUS, NULL);
2018-07-12 23:38:27 +02:00
if(g->focus_cb) g->focus_cb(g);
lv_obj_invalidate(*g->obj_focus);
}
break;
}
}
2017-07-19 15:19:10 +02:00
}
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group)
2017-07-19 15:19:10 +02:00
{
if(group->frozen) return;
2017-07-27 12:07:16 +02:00
if(group->obj_focus) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
2017-07-19 15:19:10 +02:00
lv_obj_t ** obj_next;
2017-11-24 17:48:47 +01:00
if(group->obj_focus == NULL) obj_next = lv_ll_get_head(&group->obj_ll);
else obj_next = lv_ll_get_next(&group->obj_ll, group->obj_focus);
2017-07-19 15:19:10 +02:00
2017-11-24 17:48:47 +01:00
if(obj_next == NULL) obj_next = lv_ll_get_head(&group->obj_ll);
group->obj_focus = obj_next;
2017-07-19 15:19:10 +02:00
2018-06-19 09:49:58 +02:00
if(group->obj_focus) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
}
2017-07-19 15:19:10 +02:00
}
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group)
2017-07-19 15:19:10 +02:00
{
if(group->frozen) return;
2017-07-27 12:07:16 +02:00
if(group->obj_focus) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
2017-07-19 15:19:10 +02:00
lv_obj_t ** obj_next;
2017-11-24 17:48:47 +01:00
if(group->obj_focus == NULL) obj_next = lv_ll_get_tail(&group->obj_ll);
else obj_next = lv_ll_get_prev(&group->obj_ll, group->obj_focus);
2017-07-19 15:19:10 +02:00
2017-11-24 17:48:47 +01:00
if(obj_next == NULL) obj_next = lv_ll_get_tail(&group->obj_ll);
group->obj_focus = obj_next;
2017-07-19 15:19:10 +02:00
2018-06-19 09:49:58 +02:00
if(group->obj_focus != NULL) {
(*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
}
2017-07-19 15:19:10 +02:00
}
2017-07-27 12:07:16 +02:00
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
2017-11-19 19:28:45 +01:00
* @param c a character (use LV_GROUP_KEY_.. to navigate)
*/
2017-11-19 20:45:40 +01:00
void lv_group_send_data(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return;
act->signal_func(act, LV_SIGNAL_CONTROLL, &c);
}
/**
* Set a function for a group which will modify the object's style if it is in focus
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
2018-06-19 09:49:58 +02:00
void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
{
group->style_mod = style_mod_func;
if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
}
/**
* Set a function for a group which will modify the object's style if it is in focus in edit mode
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
{
group->style_mod_edit = style_mod_func;
if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
}
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
group->focus_cb = focus_cb;
}
/**
* Enable the switching between edit and navigate mode on long press of LV_GROUP_KEY_ENTER.
* User can get the current mode and decide the whether to send
* LV_GROUP_KEY_PREV/NEXT or LV_GROUP_KEY_LEFT/RIGHT on left/right button.
* useful if there is only one encoder to navigate,
* (push: ENTER; long push: mode switch; left/right: focus or edit)
* @param group pointer to group
* @param en true or false to enable or disable this feature.
*/
void lv_group_set_edit_enable(lv_group_t * group, bool en)
{
group->edit_mode_en = en ? 1 : 0;
}
/**
* Manually set the current mode (edit or navigate).
* Edit mode needs to be enabled with `lv_group_set_edit_enabel`.
* @param group pointer to group
* @param edit: true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit)
{
group->editing = edit ? 1 : 0;
}
2018-07-12 23:38:27 +02:00
/**
* Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_click_focus(lv_group_t * group, bool en)
{
group->click_focus = en ? 1 : 0;
}
/**
* Modify a style with the set 'style_mod' function. The input style remains unchanged.
* @param group pointer to group
* @param style pointer to a style to modify
* @return a copy of the input style but modified with the 'style_mod' function
*/
lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style)
{
lv_style_copy(&group->style_tmp, style);
if(group->editing) {
if(group->style_mod_edit != NULL) group->style_mod_edit(&group->style_tmp);
else style_mod_edit_def(&group->style_tmp);
} else {
if(group->style_mod != NULL) group->style_mod(&group->style_tmp);
else style_mod_def(&group->style_tmp);
}
return &group->style_tmp;
}
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
lv_obj_t * lv_group_get_focused(lv_group_t * group)
{
2018-07-12 23:38:27 +02:00
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
/**
* Get a the style modifier function of a group
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_cb(lv_group_t * group)
{
2018-07-12 23:38:27 +02:00
if(!group) return false;
return group->style_mod ;
}
/**
* Get a the style modifier function of a group in edit mode
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(lv_group_t * group)
{
2018-07-12 23:38:27 +02:00
if(!group) return false;
return group->style_mod_edit;
}
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(lv_group_t * group)
{
2018-07-12 23:38:27 +02:00
if(!group) return false;
return group->focus_cb;
}
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(lv_group_t * group)
{
2018-07-12 23:38:27 +02:00
if(!group) return false;
return group->editing ? true : false;
}
2018-07-12 23:38:27 +02:00
/**
* Get the `click_focus` attribute.
* @param group pointer to group
* @return true: `click_focus` is enabled; false: disabled
*/
bool lv_group_get_click_focus(lv_group_t * group)
{
if(!group) return false;
return group->click_focus ? true : false;
}
2017-07-19 15:19:10 +02:00
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Default style modifier function
* @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
*/
static void style_mod_def(lv_style_t * style)
2017-07-19 15:19:10 +02:00
{
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
2017-11-23 21:28:36 +01:00
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_ORANGE;
/*If not empty or has border then emphasis the border*/
if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
2017-11-23 21:28:36 +01:00
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_ORANGE, LV_OPA_60);
2017-11-23 21:28:36 +01:00
style->text.color = lv_color_mix(style->text.color, LV_COLOR_ORANGE, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 2;
#endif
}
/**
* Default style modifier function
* @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
*/
static void style_mod_edit_def(lv_style_t * style)
{
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_GREEN;
/*If not empty or has border then emphasis the border*/
if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_GREEN, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_GREEN, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_GREEN, LV_OPA_60);
style->text.color = lv_color_mix(style->text.color, LV_COLOR_GREEN, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 3;
#endif
2017-07-19 15:19:10 +02:00
}
#endif /*USE_LV_GROUP != 0*/