1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

Merge branch 'dev'

This commit is contained in:
Gabor Kiss-Vamosi 2020-06-16 13:56:42 +02:00
commit 975ab3f4cc
21 changed files with 713 additions and 183 deletions

View File

@ -1,5 +1,17 @@
# Changelog
## v7.1.0 (under development)
*Available in the `dev` branch*
### New features
- Add `focus_parent` attribute to `lv_obj`
- Allow using buttons in encoder input device
- Add lv_btnmatrix_set/get_align capability
- DMA2D: Remove dependency on ST CubeMX HAL
- Added `max_used` propriety to `lv_mem_monitor_t` struct
### Bugfixes
- None
## v7.0.2 (16.06.2020)

View File

@ -178,6 +178,9 @@ typedef void * lv_group_user_data_t;
/* 1: Enable GPU interface*/
#define LV_USE_GPU 1 /*Only enables `gpu_fill_cb` and `gpu_blend_cb` in the disp. drv- */
#define LV_USE_GPU_STM32_DMA2D 0
/*If enabling LV_USE_GPU_STM32_DMA2D, LV_GPU_DMA2D_CMSIS_INCLUDE must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h" */
#define LV_GPU_DMA2D_CMSIS_INCLUDE
/* 1: Enable file system (might be required for images */
#define LV_USE_FILESYSTEM 1
@ -249,6 +252,10 @@ typedef void * lv_img_decoder_user_data_t;
*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning
/* Prefix variables that are used in GPU accelerated operations, often these need to be
* placed in RAM sections that are DMA accessible */
#define LV_ATTRIBUTE_DMA
/*===================
* HAL settings
*==================*/

View File

@ -176,6 +176,15 @@ static inline lv_obj_t * lv_page_get_scrl(lv_obj_t * page)
}
#endif
#if LV_USE_WIN
static inline lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src)
{
return lv_win_add_btn_right(win, img_src);
}
#endif
#endif /*LV_USE_API_EXTENSION_V6*/
/**********************

View File

@ -363,6 +363,12 @@
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning
#endif
/* Prefix variables that are used in GPU accelerated operations, often these need to be
* placed in RAM sections that are DMA accessible */
#ifndef LV_ATTRIBUTE_DMA
#define LV_ATTRIBUTE_DMA
#endif
/*===================
* HAL settings
*==================*/

View File

@ -607,8 +607,158 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
indev_obj_act = lv_group_get_focused(g);
if(indev_obj_act == NULL) return;
/*Process the steps first. They are valid only with released button*/
if(data->state == LV_INDEV_STATE_REL) {
/*Process the steps they are valid only with released button*/
if(data->state != LV_INDEV_STATE_REL) {
data->enc_diff = 0;
}
/*Refresh the focused object. It might change due to lv_group_focus_prev/next*/
indev_obj_act = lv_group_get_focused(g);
if(indev_obj_act == NULL) return;
/*Button press happened*/
if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_REL) {
i->proc.pr_timestamp = lv_tick_get();
if (data->key == LV_KEY_ENTER) {
bool editable = false;
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
if(lv_group_get_editing(g) == true || editable == false) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
if(indev_reset_check(&i->proc)) return;
}
} else if(data->key == LV_KEY_LEFT) {
/*emulate encoder left*/
data->enc_diff--;
} else if(data->key == LV_KEY_RIGHT) {
/*emulate encoder right*/
data->enc_diff++;
} else if(data->key == LV_KEY_ESC) {
/*Send the ESC as a normal KEY*/
lv_group_send_data(g, LV_KEY_ESC);
lv_event_send(indev_obj_act, LV_EVENT_CANCEL, NULL);
if(indev_reset_check(&i->proc)) return;
}
/*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
else {
lv_group_send_data(g, data->key);
}
}
/*Pressing*/
else if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_PR) {
/* Long press*/
if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver.long_press_time) {
i->proc.long_pr_sent = 1;
i->proc.longpr_rep_timestamp = lv_tick_get();
if (data->key == LV_KEY_ENTER) {
bool editable = false;
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
/*On enter long press toggle edit mode.*/
if(editable) {
/*Don't leave edit mode if there is only one object (nowhere to navigate)*/
if(_lv_ll_is_empty(&g->obj_ll) == false) {
lv_group_set_editing(g, lv_group_get_editing(g) ? false : true); /*Toggle edit mode on long press*/
}
}
/*If not editable then just send a long press signal*/
else {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
if(indev_reset_check(&i->proc)) return;
}
}
i->proc.long_pr_sent = 1;
}
/*Long press repeated time has elapsed?*/
else if(i->proc.long_pr_sent != 0 && lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver.long_press_rep_time) {
i->proc.longpr_rep_timestamp = lv_tick_get();
if(data->key == LV_KEY_ENTER) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS_REP, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, NULL);
if(indev_reset_check(&i->proc)) return;
}
else if(data->key == LV_KEY_LEFT) {
/*emulate encoder left*/
data->enc_diff--;
} else if(data->key == LV_KEY_RIGHT) {
/*emulate encoder right*/
data->enc_diff++;
} else {
lv_group_send_data(g, data->key);
if(indev_reset_check(&i->proc)) return;
}
}
}
/*Release happened*/
else if(data->state == LV_INDEV_STATE_REL && last_state == LV_INDEV_STATE_PR) {
if (data->key == LV_KEY_ENTER) {
bool editable = false;
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
/*The button was released on a non-editable object. Just send enter*/
if(editable == false) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
if(i->proc.long_pr_sent == 0) lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
}
/*An object is being edited and the button is released. */
else if(g->editing) {
/*Ignore long pressed enter release because it comes from mode switch*/
if(!i->proc.long_pr_sent || _lv_ll_is_empty(&g->obj_ll)) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_group_send_data(g, LV_KEY_ENTER);
}
}
/*If the focused object is editable and now in navigate mode then on enter switch edit
mode*/
else if(editable && !g->editing && !i->proc.long_pr_sent) {
lv_group_set_editing(g, true); /*Set edit mode*/
}
}
i->proc.pr_timestamp = 0;
i->proc.long_pr_sent = 0;
}
indev_obj_act = NULL;
/*if encoder steps or simulated steps via left/right keys*/
if (data->enc_diff != 0) {
/*In edit mode send LEFT/RIGHT keys*/
if(lv_group_get_editing(g)) {
int32_t s;
@ -631,96 +781,6 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
}
}
/*Refresh the focused object. It might change due to lv_group_focus_prev/next*/
indev_obj_act = lv_group_get_focused(g);
if(indev_obj_act == NULL) return;
/*Button press happened*/
if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_REL) {
bool editable = false;
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
i->proc.pr_timestamp = lv_tick_get();
if(lv_group_get_editing(g) == true || editable == false) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_PRESSED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_PRESSED, NULL);
if(indev_reset_check(&i->proc)) return;
}
}
/*Pressing*/
else if(data->state == LV_INDEV_STATE_PR && last_state == LV_INDEV_STATE_PR) {
if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver.long_press_time) {
bool editable = false;
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
/*On enter long press toggle edit mode.*/
if(editable) {
/*Don't leave edit mode if there is only one object (nowhere to navigate)*/
if(_lv_ll_is_empty(&g->obj_ll) == false) {
lv_group_set_editing(g, lv_group_get_editing(g) ? false : true); /*Toggle edit mode on long press*/
}
}
/*If not editable then just send a long press signal*/
else {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_LONG_PRESS, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, NULL);
if(indev_reset_check(&i->proc)) return;
}
i->proc.long_pr_sent = 1;
}
}
/*Release happened*/
else if(data->state == LV_INDEV_STATE_REL && last_state == LV_INDEV_STATE_PR) {
bool editable = false;
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_GET_EDITABLE, &editable);
/*The button was released on a non-editable object. Just send enter*/
if(editable == false) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
if(i->proc.long_pr_sent == 0) lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
}
/*An object is being edited and the button is released. */
else if(g->editing) {
/*Ignore long pressed enter release because it comes from mode switch*/
if(!i->proc.long_pr_sent || _lv_ll_is_empty(&g->obj_ll)) {
indev_obj_act->signal_cb(indev_obj_act, LV_SIGNAL_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_CLICKED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_event_send(indev_obj_act, LV_EVENT_RELEASED, NULL);
if(indev_reset_check(&i->proc)) return;
lv_group_send_data(g, LV_KEY_ENTER);
}
}
/*If the focused object is editable and now in navigate mode then on enter switch edit
mode*/
else if(editable && !g->editing && !i->proc.long_pr_sent) {
lv_group_set_editing(g, true); /*Set edit mode*/
}
i->proc.pr_timestamp = 0;
i->proc.long_pr_sent = 0;
}
indev_obj_act = NULL;
#else
(void)data; /*Unused*/
(void)i; /*Unused*/
@ -1092,8 +1152,9 @@ lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point)
static void indev_click_focus(lv_indev_proc_t * proc)
{
/*Handle click focus*/
lv_obj_t * obj_to_focus = lv_obj_get_focused_obj(indev_obj_act);
if(lv_obj_is_protected(indev_obj_act, LV_PROTECT_CLICK_FOCUS) == false &&
proc->types.pointer.last_pressed != indev_obj_act) {
proc->types.pointer.last_pressed != obj_to_focus) {
#if LV_USE_GROUP
lv_group_t * g_act = lv_obj_get_group(indev_obj_act);
lv_group_t * g_prev = proc->types.pointer.last_pressed ? lv_obj_get_group(proc->types.pointer.last_pressed) : NULL;
@ -1172,7 +1233,7 @@ static void indev_click_focus(lv_indev_proc_t * proc)
lv_event_send(indev_obj_act, LV_EVENT_FOCUSED, NULL);
if(indev_reset_check(proc)) return;
#endif
proc->types.pointer.last_pressed = indev_obj_act;
proc->types.pointer.last_pressed = obj_to_focus;
}
}

View File

@ -38,6 +38,10 @@
#include LV_THEME_DEFAULT_INCLUDE
#if LV_USE_GPU_STM32_DMA2D
#include "../lv_gpu/lv_gpu_stm32_dma2d.h"
#endif
/*********************
* DEFINES
*********************/
@ -140,6 +144,11 @@ void lv_init(void)
_lv_group_init();
#endif
#if LV_USE_GPU_STM32_DMA2D
/*Initialize DMA2D GPU*/
lv_gpu_stm32_dma2d_init();
#endif
_lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(lv_style_trans_t));
_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
@ -298,6 +307,7 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
#if LV_USE_GROUP
new_obj->group_p = NULL;
#endif
/*Set attributes*/
@ -312,6 +322,7 @@ lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
new_obj->protect = LV_PROTECT_NONE;
new_obj->parent_event = 0;
new_obj->gesture_parent = parent ? 1 : 0;
new_obj->focus_parent = 0;
new_obj->state = LV_STATE_DEFAULT;
new_obj->ext_attr = NULL;
@ -1543,6 +1554,31 @@ void lv_obj_set_gesture_parent(lv_obj_t * obj, bool en)
obj->gesture_parent = (en == true ? 1 : 0);
}
/**
* Enable to use parent for focus state.
* When object is focused the parent will get the state instead (visual only)
* @param obj pointer to an object
* @param en true: enable the 'focus parent' for the object
*/
void lv_obj_set_focus_parent(lv_obj_t * obj, bool en)
{
if (lv_obj_is_focused(obj)) {
if (en) {
obj->focus_parent = 1;
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED);
lv_obj_set_state(lv_obj_get_focused_obj(obj), LV_STATE_FOCUSED);
}
else {
lv_obj_clear_state(lv_obj_get_focused_obj(obj), LV_STATE_FOCUSED | LV_STATE_EDITED);
lv_obj_set_state(obj, LV_STATE_FOCUSED);
obj->focus_parent = 0;
}
}
else {
obj->focus_parent = (en == true ? 1 : 0);
}
}
/**
* Propagate the events to the parent too
* @param obj pointer to an object
@ -2744,6 +2780,16 @@ bool lv_obj_get_gesture_parent(const lv_obj_t * obj)
return obj->gesture_parent == 0 ? false : true;
}
/**
* Get the focus parent attribute of an object
* @param obj pointer to an object
* @return true: focus parent is enabled
*/
bool lv_obj_get_focus_parent(const lv_obj_t * obj)
{
return obj->focus_parent == 0 ? false : true;
}
/**
* Get the drag parent attribute of an object
* @param obj pointer to an object
@ -3638,6 +3684,23 @@ static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area
return LV_DESIGN_RES_OK;
}
/**
* Get the really focused object by taking `focus_parent` into account.
* @param obj the start object
* @return the object to really focus
*/
lv_obj_t * lv_obj_get_focused_obj(const lv_obj_t * obj)
{
if(obj == NULL) return NULL;
const lv_obj_t * focus_obj = obj;
while(lv_obj_get_focus_parent(focus_obj) != false && focus_obj != NULL) {
focus_obj = lv_obj_get_parent(focus_obj);
}
return (lv_obj_t*)focus_obj;
}
/**
* Signal function of the basic object
* @param obj pointer to an object
@ -3686,14 +3749,26 @@ static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
if(lv_group_get_editing(lv_obj_get_group(obj))) {
uint8_t state = LV_STATE_FOCUSED;
state |= LV_STATE_EDITED;
/*if using focus mode, change target to parent*/
obj = lv_obj_get_focused_obj(obj);
lv_obj_add_state(obj, state);
}
else {
/*if using focus mode, change target to parent*/
obj = lv_obj_get_focused_obj(obj);
lv_obj_add_state(obj, LV_STATE_FOCUSED);
lv_obj_clear_state(obj, LV_STATE_EDITED);
}
}
else if(sign == LV_SIGNAL_DEFOCUS) {
/*if using focus mode, change target to parent*/
obj = lv_obj_get_focused_obj(obj);
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED);
}
#endif
@ -4063,4 +4138,3 @@ static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_fin
return false;
}

View File

@ -221,7 +221,8 @@ typedef struct _lv_obj_t {
uint8_t top : 1; /**< 1: If the object or its children is clicked it goes to the foreground*/
uint8_t parent_event : 1; /**< 1: Send the object's events to the parent too. */
uint8_t adv_hittest : 1; /**< 1: Use advanced hit-testing (slower) */
uint8_t gesture_parent : 1; /**< 1: Parent will be gesture instead*/
uint8_t gesture_parent : 1; /**< 1: Parent will be gesture instead*/
uint8_t focus_parent : 1; /**< 1: Parent will be focused instead*/
lv_drag_dir_t drag_dir : 3; /**< Which directions the object can be dragged in */
lv_bidi_dir_t base_dir : 2; /**< Base direction of texts related to this object */
@ -685,6 +686,14 @@ void lv_obj_set_drag_throw(lv_obj_t * obj, bool en);
*/
void lv_obj_set_drag_parent(lv_obj_t * obj, bool en);
/**
* Enable to use parent for focus state.
* When object is focused the parent will get the state instead (visual only)
* @param obj pointer to an object
* @param en true: enable the 'focus parent' for the object
*/
void lv_obj_set_focus_parent(lv_obj_t * obj, bool en);
/**
* Enable to use parent for gesture related operations.
* If trying to gesture the object the parent will be moved instead
@ -1187,6 +1196,15 @@ bool lv_obj_get_drag_throw(const lv_obj_t * obj);
*/
bool lv_obj_get_drag_parent(const lv_obj_t * obj);
/**
* Get the focus parent attribute of an object
* @param obj pointer to an object
* @return true: focus parent is enabled
*/
bool lv_obj_get_focus_parent(const lv_obj_t * obj);
/**
* Get the drag parent attribute of an object
* @param obj pointer to an object
@ -1318,6 +1336,13 @@ void * lv_obj_get_group(const lv_obj_t * obj);
*/
bool lv_obj_is_focused(const lv_obj_t * obj);
/**
* Get the really focused object by taking `focus_parent` into account.
* @param obj the start object
* @return the object to really focus
*/
lv_obj_t * lv_obj_get_focused_obj(const lv_obj_t * obj);
/*-------------------
* OTHER FUNCTIONS
*------------------*/

View File

@ -199,6 +199,99 @@ _LV_OBJ_STYLE_SET_GET_DECLARE(SCALE_END_COLOR, scale_end_color, lv_color_t, _col
#undef _LV_OBJ_STYLE_SET_GET_DECLARE
static inline void lv_obj_set_style_local_pad_all(lv_obj_t * obj, uint8_t part, lv_state_t state, lv_style_int_t value)
{
lv_obj_set_style_local_pad_top(obj, part, state, value);
lv_obj_set_style_local_pad_bottom(obj, part, state, value);
lv_obj_set_style_local_pad_left(obj, part, state, value);
lv_obj_set_style_local_pad_right(obj, part, state, value);
}
static inline void lv_style_set_pad_all(lv_style_t * style, lv_state_t state, lv_style_int_t value)
{
lv_style_set_pad_top(style, state, value);
lv_style_set_pad_bottom(style, state, value);
lv_style_set_pad_left(style, state, value);
lv_style_set_pad_right(style, state, value);
}
static inline void lv_obj_set_style_local_pad_hor(lv_obj_t * obj, uint8_t part, lv_state_t state, lv_style_int_t value)
{
lv_obj_set_style_local_pad_left(obj, part, state, value);
lv_obj_set_style_local_pad_right(obj, part, state, value);
}
static inline void lv_style_set_pad_hor(lv_style_t * style, lv_state_t state, lv_style_int_t value)
{
lv_style_set_pad_left(style, state, value);
lv_style_set_pad_right(style, state, value);
}
static inline void lv_obj_set_style_local_pad_ver(lv_obj_t * obj, uint8_t part, lv_state_t state, lv_style_int_t value)
{
lv_obj_set_style_local_pad_top(obj, part, state, value);
lv_obj_set_style_local_pad_bottom(obj, part, state, value);
}
static inline void lv_style_set_pad_ver(lv_style_t * style, lv_state_t state, lv_style_int_t value)
{
lv_style_set_pad_top(style, state, value);
lv_style_set_pad_bottom(style, state, value);
}
static inline void lv_obj_set_style_local_margin_all(lv_obj_t * obj, uint8_t part, lv_state_t state, lv_style_int_t value)
{
lv_obj_set_style_local_margin_top(obj, part, state, value);
lv_obj_set_style_local_margin_bottom(obj, part, state, value);
lv_obj_set_style_local_margin_left(obj, part, state, value);
lv_obj_set_style_local_margin_right(obj, part, state, value);
}
static inline void lv_style_set_margin_all(lv_style_t * style, lv_state_t state, lv_style_int_t value)
{
lv_style_set_margin_top(style, state, value);
lv_style_set_margin_bottom(style, state, value);
lv_style_set_margin_left(style, state, value);
lv_style_set_margin_right(style, state, value);
}
static inline void lv_obj_set_style_local_margin_hor(lv_obj_t * obj, uint8_t part, lv_state_t state, lv_style_int_t value)
{
lv_obj_set_style_local_margin_left(obj, part, state, value);
lv_obj_set_style_local_margin_right(obj, part, state, value);
}
static inline void lv_style_set_margin_hor(lv_style_t * style, lv_state_t state, lv_style_int_t value)
{
lv_style_set_margin_left(style, state, value);
lv_style_set_margin_right(style, state, value);
}
static inline void lv_obj_set_style_local_margin_ver(lv_obj_t * obj, uint8_t part, lv_state_t state, lv_style_int_t value)
{
lv_obj_set_style_local_margin_top(obj, part, state, value);
lv_obj_set_style_local_margin_bottom(obj, part, state, value);
}
static inline void lv_style_set_margin_ver(lv_style_t * style, lv_state_t state, lv_style_int_t value)
{
lv_style_set_margin_top(style, state, value);
lv_style_set_margin_bottom(style, state, value);
}
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -60,6 +60,10 @@ static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_co
* STATIC VARIABLES
**********************/
#if LV_USE_GPU || LV_USE_GPU_STM32_DMA2D
LV_ATTRIBUTE_DMA static lv_color_t blend_buf[LV_HOR_RES_MAX];
#endif
/**********************
* MACROS
**********************/
@ -338,7 +342,6 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_co
else {
#if LV_USE_GPU
if(disp->driver.gpu_blend_cb && lv_area_get_size(draw_area) > GPU_SIZE_LIMIT) {
static lv_color_t blend_buf[LV_HOR_RES_MAX];
for(x = 0; x < draw_area_w ; x++) blend_buf[x].full = color.full;
for(y = draw_area->y1; y <= draw_area->y2; y++) {
@ -352,7 +355,6 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_co
#if LV_USE_GPU_STM32_DMA2D
if(lv_area_get_size(draw_area) >= 240) {
static lv_color_t blend_buf[LV_HOR_RES_MAX] = {0};
if(blend_buf[0].full != color.full) lv_color_fill(blend_buf, color, LV_HOR_RES_MAX);
lv_coord_t line_h = LV_HOR_RES_MAX / draw_area_w;

View File

@ -528,8 +528,8 @@ void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t
LV_UNUSED(pivot);
res->x1 = 0;
res->y1 = 0;
res->x2 = w;
res->y2 = h;
res->x2 = w - 1;
res->y2 = h - 1;
#endif
}

View File

@ -11,19 +11,14 @@
#if LV_USE_GPU_STM32_DMA2D
#if defined(STM32F4)
#include "stm32f4xx_hal.h"
#elif defined(STM32F7)
#include "stm32f7xx_hal.h"
#else
#error "Not supported STM32 family to use DMA2D"
#endif
#include LV_GPU_DMA2D_CMSIS_INCLUDE
/*********************
* DEFINES
*********************/
#if LV_COLOR_16_SWAP
// TODO: F7 has red blue swap bit in control register for all layers and output
#error "Can't use DMA2D with LV_COLOR_16_SWAP 1"
#endif
@ -32,11 +27,9 @@
#endif
#if LV_COLOR_DEPTH == 16
#define DMA2D_OUTPUT_FORMAT DMA2D_OUTPUT_RGB565
#define DMA2D_INPUT_FORMAT DMA2D_INPUT_RGB565
#define DMA2D_COLOR_FORMAT DMA2D_RGB565
#elif LV_COLOR_DEPTH == 32
#define DMA2D_OUTPUT_FORMAT DMA2D_OUTPUT_ARGB8888
#define DMA2D_INPUT_FORMAT DMA2D_INPUT_ARGB8888
#define DMA2D_COLOR_FORMAT DMA2D_ARGB8888
#else
/*Can't use GPU with other formats*/
#endif
@ -54,7 +47,6 @@ static void dma2d_wait(void);
/**********************
* STATIC VARIABLES
**********************/
static DMA2D_HandleTypeDef hdma2d;
/**********************
* MACROS
@ -64,6 +56,21 @@ static DMA2D_HandleTypeDef hdma2d;
* GLOBAL FUNCTIONS
**********************/
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_gpu_stm32_dma2d_init(void)
{
/* Enable DMA2D clock */
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
/* Delay after setting peripheral clock */
volatile uint32_t temp = RCC->AHB1ENR;
/* set output colour mode */
DMA2D->OPFCCR = DMA2D_COLOR_FORMAT;
}
/**
* Fill an area in the buffer with a color
* @param buf a buffer which should be filled
@ -77,18 +84,16 @@ void lv_gpu_stm32_dma2d_fill(lv_color_t * buf, lv_coord_t buf_w, lv_color_t colo
{
invalidate_cache();
hdma2d.Instance = DMA2D;
hdma2d.Init.Mode = DMA2D_R2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_FORMAT;
hdma2d.Init.OutputOffset = buf_w - fill_w;
hdma2d.LayerCfg[1].InputAlpha = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_FORMAT;
hdma2d.LayerCfg[1].InputOffset = 0;
DMA2D->CR = 0x30000;
DMA2D->OMAR = (uint32_t)buf;
/* as input color mode is same as output we don't need to convert here do we? */
DMA2D->OCOLR = color.full;
DMA2D->OOR = buf_w - fill_w;
DMA2D->NLR = (fill_w << DMA2D_NLR_PL_Pos) | (fill_h << DMA2D_NLR_NL_Pos);
/* start transfer */
DMA2D->CR |= DMA2D_CR_START_Msk;
/* DMA2D Initialization */
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_Start(&hdma2d, (uint32_t)lv_color_to32(color), (uint32_t)buf, fill_w, fill_h);
dma2d_wait();
}
@ -106,6 +111,7 @@ void lv_gpu_stm32_dma2d_fill(lv_color_t * buf, lv_coord_t buf_w, lv_color_t colo
void lv_gpu_stm32_dma2d_fill_mask(lv_color_t * buf, lv_coord_t buf_w, lv_color_t color, const lv_opa_t * mask,
lv_opa_t opa, lv_coord_t fill_w, lv_coord_t fill_h)
{
#if 0
invalidate_cache();
/* Configure the DMA2D Mode, Color Mode and line output offset */
@ -134,6 +140,7 @@ void lv_gpu_stm32_dma2d_fill_mask(lv_color_t * buf, lv_coord_t buf_w, lv_color_t
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t) mask, (uint32_t) buf, (uint32_t)buf, fill_w, fill_h);
dma2d_wait();
#endif
}
/**
@ -151,22 +158,17 @@ void lv_gpu_stm32_dma2d_copy(lv_color_t * buf, lv_coord_t buf_w, const lv_color_
{
invalidate_cache();
hdma2d.Instance = DMA2D;
hdma2d.Init.Mode = DMA2D_M2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_FORMAT;
hdma2d.Init.OutputOffset = buf_w - copy_w;
DMA2D->CR = 0;
/* copy output colour mode, this register controls both input and output colour format */
DMA2D->FGPFCCR = DMA2D_COLOR_FORMAT;
DMA2D->FGMAR = (uint32_t)map;
DMA2D->FGOR = map_w - copy_w;
DMA2D->OMAR = (uint32_t)buf;
DMA2D->OOR = buf_w - copy_w;
DMA2D->NLR = (copy_w << DMA2D_NLR_PL_Pos) | (copy_h << DMA2D_NLR_NL_Pos);
/* Foreground layer */
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0xFF;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_FORMAT;
hdma2d.LayerCfg[1].InputOffset = map_w - copy_w;
/* DMA2D Initialization */
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_Start(&hdma2d, (uint32_t)map, (uint32_t)buf, copy_w, copy_h);
/* start transfer */
DMA2D->CR |= DMA2D_CR_START_Msk;
dma2d_wait();
}
@ -185,28 +187,26 @@ void lv_gpu_stm32_dma2d_blend(lv_color_t * buf, lv_coord_t buf_w, const lv_color
lv_coord_t map_w, lv_coord_t copy_w, lv_coord_t copy_h)
{
invalidate_cache();
DMA2D->CR = 0x20000;
hdma2d.Instance = DMA2D;
hdma2d.Init.Mode = DMA2D_M2M_BLEND;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_FORMAT;
hdma2d.Init.OutputOffset = buf_w - copy_w;
DMA2D->BGPFCCR = DMA2D_COLOR_FORMAT;
DMA2D->BGMAR = (uint32_t)buf;
DMA2D->BGOR = buf_w - copy_w;
/* Background layer */
hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_FORMAT;
hdma2d.LayerCfg[0].InputOffset = buf_w - copy_w;
DMA2D->FGPFCCR = (uint32_t)DMA2D_COLOR_FORMAT
/* alpha mode 2, replace with foreground * alpha value */
| (2 << DMA2D_FGPFCCR_AM_Pos)
/* alpha value */
| (opa << DMA2D_FGPFCCR_ALPHA_Pos);
DMA2D->FGMAR = (uint32_t)map;
DMA2D->FGOR = map_w - copy_w;
/* Foreground layer */
hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = opa;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_FORMAT;
hdma2d.LayerCfg[1].InputOffset = map_w - copy_w;
DMA2D->OMAR = (uint32_t)buf;
DMA2D->OOR = buf_w - copy_w;
DMA2D->NLR = (copy_w << DMA2D_NLR_PL_Pos) | (copy_h << DMA2D_NLR_NL_Pos);
/* DMA2D Initialization */
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t)map, (uint32_t)buf, (uint32_t)buf, copy_w, copy_h);
/* start transfer */
DMA2D->CR |= DMA2D_CR_START_Msk;
dma2d_wait();
}
@ -226,7 +226,7 @@ static void invalidate_cache(void)
static void dma2d_wait(void)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
while(HAL_DMA2D_PollForTransfer(&hdma2d, 0) == HAL_TIMEOUT) {
while(DMA2D->CR & DMA2D_CR_START_Msk) {
if(disp->driver.wait_cb) disp->driver.wait_cb(&disp->driver);
}
}

View File

@ -20,6 +20,12 @@ extern "C" {
* DEFINES
*********************/
#define DMA2D_ARGB8888 0
#define DMA2D_RGB888 1
#define DMA2D_RGB565 2
#define DMA2D_ARGB1555 3
#define DMA2D_ARGB4444 4
/**********************
* TYPEDEFS
**********************/
@ -28,6 +34,11 @@ extern "C" {
* GLOBAL PROTOTYPES
**********************/
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_gpu_stm32_dma2d_init(void);
/**
* Fill an area in the buffer with a color
* @param buf a buffer which should be filled

View File

@ -143,6 +143,11 @@ bool _lv_indev_read(lv_indev_t * indev, lv_indev_data_t * data)
else if(indev->driver.type == LV_INDEV_TYPE_KEYPAD) {
data->key = indev->proc.types.keypad.last_key;
}
/*For compatibility assume that used button was enter (encoder push) */
else if(indev->driver.type == LV_INDEV_TYPE_ENCODER) {
data->key = LV_KEY_ENTER;
data->enc_diff = 0;
}
if(indev->driver.read_cb) {
LV_LOG_TRACE("idnev read started");

View File

@ -85,7 +85,7 @@ typedef struct {
#endif
static uint32_t zero_mem; /*Give the address of this variable if 0 byte should be allocated*/
static uint32_t mem_max_size; /*Tracks the maximum total size of memory ever used from the internal heap*/
static uint8_t mem_buf1_32[MEM_BUF_SMALL_SIZE];
static uint8_t mem_buf2_32[MEM_BUF_SMALL_SIZE];
@ -118,10 +118,11 @@ void _lv_mem_init(void)
/*Allocate a large array to store the dynamically allocated data*/
static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
work_mem = (uint8_t *)work_mem_int;
mem_max_size = 0;
#else
work_mem = (uint8_t *)LV_MEM_ADR;
#endif
lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem;
full->header.s.used = 0;
/*The total mem size id reduced by the first header and the close patterns */
@ -200,7 +201,18 @@ void * lv_mem_alloc(size_t size)
if(alloc != NULL) _lv_memset(alloc, 0xaa, size);
#endif
if(alloc == NULL) LV_LOG_WARN("Couldn't allocate memory");
if(alloc == NULL) {
LV_LOG_WARN("Couldn't allocate memory");
}else{
#if LV_MEM_CUSTOM == 0
/* just a safety check, should always be true */
if ((uintptr_t) alloc > (uintptr_t) work_mem) {
if ((((uintptr_t) alloc - (uintptr_t) work_mem) + size) > mem_max_size) {
mem_max_size = ((uintptr_t) alloc - (uintptr_t) work_mem) + size;
}
}
#endif
}
return alloc;
}
@ -424,6 +436,7 @@ void lv_mem_monitor(lv_mem_monitor_t * mon_p)
e = ent_get_next(e);
}
mon_p->total_size = LV_MEM_SIZE;
mon_p->max_used = mem_max_size;
mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
if(mon_p->free_size > 0) {
mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size;

View File

@ -41,6 +41,7 @@ typedef struct {
uint32_t free_size; /**< Size of available memory */
uint32_t free_biggest_size;
uint32_t used_cnt;
uint32_t max_used; /**< Max size of Heap memory used */
uint8_t used_pct; /**< Percentage used */
uint8_t frag_pct; /**< Amount of fragmentation */
} lv_mem_monitor_t;

View File

@ -422,6 +422,23 @@ void lv_btnmatrix_set_one_check(lv_obj_t * btnm, bool one_chk)
make_one_button_toggled(btnm, 0);
}
/**
* Set the align of the map text (left, right or center)
* @param btnm pointer to a btnmatrix object
* @param align LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER
*/
void lv_btnmatrix_set_align(lv_obj_t* btnm, lv_label_align_t align)
{
LV_ASSERT_OBJ(btnm, LV_OBJX_NAME);
lv_btnmatrix_ext_t* ext = lv_obj_get_ext_attr(btnm);
if (ext->align == align) return;
ext->align = align;
lv_obj_invalidate(btnm);
}
/*=====================
* Getter functions
*====================*/
@ -563,6 +580,32 @@ bool lv_btnmatrix_get_one_check(const lv_obj_t * btnm)
return ext->one_check;
}
/**
* Get the align attribute
* @param btnm pointer to a btnmatrix object
* @return LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER
*/
lv_label_align_t lv_btnmatrix_get_align(const lv_obj_t* btnm)
{
LV_ASSERT_OBJ(btnm, LV_OBJX_NAME);
lv_btnmatrix_ext_t* ext = lv_obj_get_ext_attr(btnm);
lv_label_align_t align = ext->align;
if (align == LV_LABEL_ALIGN_AUTO) {
#if LV_USE_BIDI
lv_bidi_dir_t base_dir = lv_obj_get_base_dir(btnm);
if (base_dir == LV_BIDI_DIR_RTL) align = LV_LABEL_ALIGN_RIGHT;
else align = LV_LABEL_ALIGN_LEFT;
#else
align = LV_LABEL_ALIGN_LEFT;
#endif
}
return align;
}
/**********************
* STATIC FUNCTIONS
**********************/
@ -598,7 +641,10 @@ static lv_design_res_t lv_btnmatrix_design(lv_obj_t * btnm, const lv_area_t * cl
uint16_t btn_i = 0;
uint16_t txt_i = 0;
lv_txt_flag_t txt_flag = LV_TXT_FLAG_NONE;
if(ext->recolor) txt_flag = LV_TXT_FLAG_RECOLOR;
if (ext->recolor) txt_flag |= LV_TXT_FLAG_RECOLOR;
lv_label_align_t align = lv_btnmatrix_get_align(btnm);
if (align == LV_LABEL_ALIGN_CENTER) txt_flag |= LV_TXT_FLAG_CENTER;
if (align == LV_LABEL_ALIGN_RIGHT) txt_flag |= LV_TXT_FLAG_RIGHT;
lv_draw_rect_dsc_t draw_rect_rel_dsc;
lv_draw_label_dsc_t draw_label_rel_dsc;

View File

@ -59,6 +59,7 @@ typedef struct {
uint16_t btn_id_act; /*Index of the active button (being pressed/released etc) or LV_BTNMATRIX_BTN_NONE */
uint8_t recolor : 1; /*Enable button recoloring*/
uint8_t one_check : 1; /*Single button toggled at once*/
uint8_t align : 2; /*Align type from 'lv_label_align_t'*/
} lv_btnmatrix_ext_t;
enum {
@ -169,6 +170,13 @@ void lv_btnmatrix_set_btn_width(lv_obj_t * btnm, uint16_t btn_id, uint8_t width)
* @param one_chk Whether "one check" mode is enabled
*/
void lv_btnmatrix_set_one_check(lv_obj_t * btnm, bool one_chk);
/**
* Set the align of the map text (left, right or center)
* @param btnm pointer to a btnmatrix object
* @param align LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER
*/
void lv_btnmatrix_set_align(lv_obj_t* btnm, lv_label_align_t align);
/*=====================
* Getter functions
@ -236,6 +244,14 @@ bool lv_btnmatrix_get_btn_ctrl(lv_obj_t * btnm, uint16_t btn_id, lv_btnmatrix_ct
* @return whether "one toggle" mode is enabled
*/
bool lv_btnmatrix_get_one_check(const lv_obj_t * btnm);
/**
* Get the align attribute
* @param btnm pointer to a btnmatrix object
* @return LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER
*/
lv_label_align_t lv_btnmatrix_get_align(const lv_obj_t* btnm);
/**********************
* MACROS
**********************/

View File

@ -712,6 +712,11 @@ static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param)
if(ext->last_sel_btn) lv_list_focus_btn(list, ext->last_sel_btn);
else lv_list_focus_btn(list, lv_list_get_next_btn(list, NULL));
}
if(indev_type == LV_INDEV_TYPE_ENCODER && lv_group_get_editing(g) == false) {
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
if(ext->act_sel_btn) lv_obj_clear_state(ext->act_sel_btn, LV_STATE_PRESSED);
if(ext->last_sel_btn) lv_obj_clear_state(ext->last_sel_btn, LV_STATE_PRESSED);
}
#endif
}
else if(sign == LV_SIGNAL_DEFOCUS) {
@ -811,8 +816,10 @@ static lv_res_t lv_list_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * para
else if(sign == LV_SIGNAL_CLEANUP) {
#if LV_USE_GROUP
lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn));
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
lv_obj_t * sel = lv_list_get_btn_selected(list);
if(sel == btn) lv_list_focus_btn(list, lv_list_get_next_btn(list, btn));
if(ext->last_sel_btn == btn) ext->last_sel_btn = NULL;
#endif
}

View File

@ -485,6 +485,9 @@ void lv_page_focus(lv_obj_t * page, const lv_obj_t * obj, lv_anim_enable_t anim_
lv_anim_del(ext->scrl, (lv_anim_exec_xcb_t)lv_obj_set_y);
#endif
/*if using focus mode, change target to parent*/
obj = lv_obj_get_focused_obj(obj);
/*If obj is higher then the page focus where the "error" is smaller*/
lv_coord_t obj_y = obj->coords.y1 - ext->scrl->coords.y1;

View File

@ -23,6 +23,22 @@
* TYPEDEFS
**********************/
/** Extended data of win_btn*/
typedef struct {
/** Ext. of ancestor*/
lv_btn_ext_t btn;
/** Which side of the header should the button be aligned to.
* 0: Align to right (default), 1: Align to left */
uint8_t alignment_in_header : 1;
} lv_win_btn_ext_t;
enum {
LV_WIN_BTN_ALIGN_RIGHT = 0, /**< Align button to right of the header */
LV_WIN_BTN_ALIGN_LEFT /**< Align button to left of the header */
};
typedef uint8_t lv_win_btn_align_t;
/**********************
* STATIC PROTOTYPES
**********************/
@ -30,6 +46,9 @@ static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param);
static lv_design_res_t lv_win_header_design(lv_obj_t * header, const lv_area_t * clip_area, lv_design_mode_t mode);
static lv_style_list_t * lv_win_get_style(lv_obj_t * win, uint8_t part);
static void lv_win_realign(lv_obj_t * win);
static lv_obj_t * lv_win_btn_create(lv_obj_t * par, const void * img_src);
static void lv_win_btn_set_alignment(lv_obj_t * par, const lv_win_btn_align_t alignment);
static lv_win_btn_align_t lv_win_btn_get_alignment(const lv_obj_t * par);
/**********************
* STATIC VARIABLES
@ -166,23 +185,39 @@ void lv_win_clean(lv_obj_t * win)
* Add control button to the header of the window
* @param win pointer to a window object
* @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
* @param alignment button alignment on the header
* @return pointer to the created button object
*/
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src)
lv_obj_t * lv_win_add_btn_right(lv_obj_t * win, const void * img_src)
{
LV_ASSERT_OBJ(win, LV_OBJX_NAME);
LV_ASSERT_NULL(img_src);
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_obj_t * btn = lv_btn_create(ext->header, NULL);
lv_theme_apply(btn, LV_THEME_WIN_BTN);
lv_coord_t btn_size = lv_obj_get_height_fit(ext->header);
lv_obj_set_size(btn, btn_size, btn_size);
lv_obj_t * btn = lv_win_btn_create(ext->header, img_src);
lv_win_btn_set_alignment(btn, LV_WIN_BTN_ALIGN_RIGHT);
lv_obj_t * img = lv_img_create(btn, NULL);
lv_obj_set_click(img, false);
lv_img_set_src(img, img_src);
lv_win_realign(win);
return btn;
}
/**
* Add control button on the left side of the window header
* @param win pointer to a window object
* @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
* @return pointer to the created button object
*/
lv_obj_t * lv_win_add_btn_left(lv_obj_t * win, const void * img_src)
{
LV_ASSERT_OBJ(win, LV_OBJX_NAME);
LV_ASSERT_NULL(img_src);
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_obj_t * btn = lv_win_btn_create(ext->header, img_src);
lv_win_btn_set_alignment(btn, LV_WIN_BTN_ALIGN_LEFT);
lv_win_realign(win);
@ -501,7 +536,8 @@ static lv_design_res_t lv_win_header_design(lv_obj_t * header, const lv_area_t *
lv_obj_t * win = lv_obj_get_parent(header);
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_style_int_t left = lv_obj_get_style_pad_left(header, LV_OBJ_PART_MAIN);
lv_style_int_t header_left = lv_obj_get_style_pad_left(win, LV_WIN_PART_HEADER);
lv_style_int_t header_inner = lv_obj_get_style_pad_inner(win, LV_WIN_PART_HEADER);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
@ -510,13 +546,29 @@ static lv_design_res_t lv_win_header_design(lv_obj_t * header, const lv_area_t *
lv_area_t txt_area;
lv_point_t txt_size;
_lv_txt_get_size(&txt_size, ext->title_txt, label_dsc.font, label_dsc.letter_space, label_dsc.line_space, LV_COORD_MAX,
label_dsc.flag);
txt_area.x1 = header->coords.x1 + left;
lv_obj_t * btn = NULL;
lv_coord_t btn_h = lv_obj_get_height_fit(header);
lv_coord_t btn_w = ext->btn_w != 0 ? ext->btn_w : btn_h;
/*Get x position of the title (should be on the right of the buttons on the left)*/
lv_coord_t left_btn_offset = 0;
btn = lv_obj_get_child_back(ext->header, NULL);
while(btn != NULL) {
if (LV_WIN_BTN_ALIGN_LEFT == lv_win_btn_get_alignment(btn)) {
left_btn_offset += btn_w + header_inner;
}
btn = lv_obj_get_child_back(header, btn);
}
txt_area.x1 = header->coords.x1 + header_left + left_btn_offset;
txt_area.y1 = header->coords.y1 + (lv_obj_get_height(header) - txt_size.y) / 2;
txt_area.x2 = txt_area.x1 + txt_size.x;
txt_area.x2 = txt_area.x1 + txt_size.x + left_btn_offset;
txt_area.y2 = txt_area.y1 + txt_size.y;
lv_draw_label(&txt_area, clip_area, &label_dsc, ext->title_txt, NULL);
@ -644,23 +696,52 @@ static void lv_win_realign(lv_obj_t * win)
lv_obj_set_width(ext->header, lv_obj_get_width(win));
lv_obj_t * btn;
lv_obj_t * btn_prev = NULL;
lv_obj_t * btn_prev_at_left = NULL;
lv_obj_t * btn_prev_at_right = NULL;
bool is_header_right_side_empty = true;
bool is_header_left_side_empty = true;
lv_coord_t btn_h = lv_obj_get_height_fit(ext->header);
lv_coord_t btn_w = ext->btn_w != 0 ? ext->btn_w : btn_h;
lv_style_int_t header_inner = lv_obj_get_style_pad_inner(win, LV_WIN_PART_HEADER);
lv_style_int_t header_right = lv_obj_get_style_pad_right(win, LV_WIN_PART_HEADER);
lv_style_int_t header_left = lv_obj_get_style_pad_left(win, LV_WIN_PART_HEADER);
/*Refresh the size of all control buttons*/
btn = lv_obj_get_child_back(ext->header, NULL);
while(btn != NULL) {
lv_obj_set_size(btn, btn_h, btn_w);
if(btn_prev == NULL) {
lv_obj_align(btn, ext->header, LV_ALIGN_IN_RIGHT_MID, -header_right, 0);
lv_obj_set_size(btn, btn_w, btn_h);
uint8_t btn_alignment = lv_win_btn_get_alignment(btn);
if (LV_WIN_BTN_ALIGN_RIGHT == btn_alignment) {
if (is_header_right_side_empty) {
/* Align the button to the right of the header */
lv_obj_align(btn, ext->header, LV_ALIGN_IN_RIGHT_MID, -header_right, 0);
is_header_right_side_empty = false;
} else {
/* Align the button to the left of the previous button */
lv_obj_align(btn, btn_prev_at_right, LV_ALIGN_OUT_LEFT_MID, -header_inner, 0);
}
btn_prev_at_right = btn;
}
else {
lv_obj_align(btn, btn_prev, LV_ALIGN_OUT_LEFT_MID, - header_inner, 0);
else if (LV_WIN_BTN_ALIGN_LEFT == btn_alignment) {
if (is_header_left_side_empty) {
/* Align the button to the right of the header */
lv_obj_align(btn, ext->header, LV_ALIGN_IN_LEFT_MID, header_left, 0);
is_header_left_side_empty = false;
} else {
/* Align the button to the right of the previous button */
lv_obj_align(btn, btn_prev_at_left, LV_ALIGN_OUT_RIGHT_MID, header_inner, 0);
}
btn_prev_at_left = btn;
}
btn_prev = btn;
btn = lv_obj_get_child_back(ext->header, btn);
btn = lv_obj_get_child_back(ext->header, btn);
}
lv_obj_set_pos(ext->header, 0, 0);
@ -669,4 +750,54 @@ static void lv_win_realign(lv_obj_t * win)
lv_obj_align(ext->page, ext->header, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
}
static lv_obj_t * lv_win_btn_create(lv_obj_t * par, const void * img_src)
{
LV_LOG_TRACE("win btn create started");
lv_obj_t * win_btn;
win_btn = lv_btn_create(par, NULL);
LV_ASSERT_MEM(win_btn);
if(win_btn == NULL) return NULL;
/*Allocate the extended data*/
lv_win_btn_ext_t * ext = lv_obj_allocate_ext_attr(win_btn, sizeof(lv_win_btn_ext_t));
LV_ASSERT_MEM(ext);
if(ext == NULL) {
lv_obj_del(win_btn);
return NULL;
}
ext->alignment_in_header = LV_WIN_BTN_ALIGN_RIGHT;
lv_obj_set_click(win_btn, true);
lv_win_btn_set_alignment(win_btn, LV_WIN_BTN_ALIGN_RIGHT);
lv_theme_apply(win_btn, LV_THEME_WIN_BTN);
lv_coord_t btn_size = lv_obj_get_height_fit(par);
lv_obj_set_size(win_btn, btn_size, btn_size);
lv_obj_t * img = lv_img_create(win_btn, NULL);
lv_obj_set_click(img, false);
lv_img_set_src(img, img_src);
LV_LOG_INFO("win btn created");
return win_btn;
}
static void lv_win_btn_set_alignment(lv_obj_t * win_btn, const uint8_t alignment)
{
lv_win_btn_ext_t * ext = lv_obj_get_ext_attr(win_btn);
ext->alignment_in_header = alignment;
}
static uint8_t lv_win_btn_get_alignment(const lv_obj_t * win_btn)
{
lv_win_btn_ext_t * ext = lv_obj_get_ext_attr(win_btn);
return ext->alignment_in_header;
}
#endif

View File

@ -92,12 +92,20 @@ void lv_win_clean(lv_obj_t * win);
*=====================*/
/**
* Add control button to the header of the window
* Add control button on the right side of the window header
* @param win pointer to a window object
* @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
* @return pointer to the created button object
*/
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src);
lv_obj_t * lv_win_add_btn_right(lv_obj_t * win, const void * img_src);
/**
* Add control button on the left side of the window header
* @param win pointer to a window object
* @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
* @return pointer to the created button object
*/
lv_obj_t * lv_win_add_btn_left(lv_obj_t * win, const void * img_src);
/*=====================
* Setter functions