From c801a2cfa2449792f380d72f2136cdbe35216fbb Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Fri, 28 Aug 2020 18:48:52 +0200 Subject: [PATCH] basic grid working --- src/lv_core/lv_align.c | 50 +++++++++ src/lv_core/lv_align.h | 90 ++++++++++++++-- src/lv_core/lv_obj.c | 240 +++++++++++++++++++++++++++++------------ src/lv_core/lv_obj.h | 3 + src/lv_misc/lv_area.h | 11 +- 5 files changed, 316 insertions(+), 78 deletions(-) diff --git a/src/lv_core/lv_align.c b/src/lv_core/lv_align.c index 580c08a5c..a9ed4cf83 100644 --- a/src/lv_core/lv_align.c +++ b/src/lv_core/lv_align.c @@ -6,6 +6,7 @@ /********************* * INCLUDES *********************/ +#include "lv_align.h" /********************* * DEFINES @@ -31,6 +32,55 @@ * GLOBAL FUNCTIONS **********************/ +void grid_calc(lv_grid_t * grid, _lv_grid_calc_t * calc) +{ + calc->col_dsc_len = grid->col_dsc_len + 1; + calc->col_dsc = _lv_mem_buf_get(sizeof(lv_coord_t) * calc->col_dsc_len); + calc->row_dsc_len = grid->row_dsc_len + 1; + calc->row_dsc = _lv_mem_buf_get(sizeof(lv_coord_t) * calc->row_dsc_len); + + calc->col_dsc[0] = 0; + calc->row_dsc[0] = 0; + + uint8_t i; + for(i = 0; i < grid->col_dsc_len; i++) { + calc->col_dsc[i + 1] = calc->col_dsc[i] + grid->col_dsc[i]; + } + for(i = 0; i < grid->row_dsc_len; i++) { + calc->row_dsc[i + 1] = calc->row_dsc[i] + grid->row_dsc[i]; + } +} + +void grid_calc_free(_lv_grid_calc_t * calc) +{ + _lv_mem_buf_release(calc->col_dsc); + _lv_mem_buf_release(calc->row_dsc); +} + +uint8_t _lv_grid_get_col_type(lv_grid_t * grid, uint32_t col) +{ + lv_coord_t x; + /*If explicitly defined get the column size else use the first column*/ + if(col < grid->col_dsc_len) x = grid->col_dsc[col]; + else x = grid->col_dsc[0]; + + if(_GRID_IS_FR(x)) return _GRID_CELL_SIZE_FR; + else if(_GRID_IS_AUTO(x)) return _GRID_CELL_SIZE_AUTO; + else return _GRID_CELL_SIZE_PX; +} + +uint8_t _lv_grid_get_row_type(lv_grid_t * grid, uint32_t row) +{ + lv_coord_t x; + /*If explicitly defined get the row size else use the first row*/ + if(row < grid->row_dsc_len) x = grid->row_dsc[row]; + else x = grid->row_dsc[0]; + + if(_GRID_IS_FR(x)) return _GRID_CELL_SIZE_FR; + else if(_GRID_IS_AUTO(x)) return _GRID_CELL_SIZE_AUTO; + else return _GRID_CELL_SIZE_PX; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/lv_core/lv_align.h b/src/lv_core/lv_align.h index 74f0af775..c32e422a6 100644 --- a/src/lv_core/lv_align.h +++ b/src/lv_core/lv_align.h @@ -1,5 +1,5 @@ /** - * @file lv_templ.h + * @file lv_grid.h * */ @@ -13,24 +13,94 @@ extern "C" { /********************* * INCLUDES *********************/ +#include "../lv_misc/lv_area.h" /********************* * DEFINES *********************/ +#define LV_GRID_CENTER 0 +#define LV_GRID_START 1 +#define LV_GRID_END 2 +#define LV_GRID_STRETCH 3 +#define LV_GRID_SPACE_EVENLY 4 +#define LV_GRID_SPACE_AROUND 5 +#define LV_GRID_SPACE_BETWEEN 6 + +/** + * Some helper defines + * */ +#define _GRID_IS_CELL(c) ((c) > LV_COORD_MAX ? true : false) +#define _GRID_CELL_SHIFT 5 +#define _GRID_CELL_MAX ((1 << _GRID_CELL_SHIFT) - 1) +#define _GRID_CELL_POS_MASK ((1 << _GRID_CELL_SHIFT) - 1) +#define _GRID_CELL_SPAN_MASK (_GRID_CELL_POS_MASK << _GRID_CELL_SHIFT) +#define _GRID_CELL_FLAG_MASK (_GRID_CELL_POS_MASK << (2 * _GRID_CELL_SHIFT)) +#define _GRID_CELL_FLAG(b) ((b) << (_GRID_CELL_SHIFT * 2)) +#define _GRID_CELL_AUTO _GRID_CELL_MAX +#define _GRID_GET_CELL_POS(c) ((c) & _GRID_CELL_POS_MASK) +#define _GRID_GET_CELL_SPAN(c) (((c) & _GRID_CELL_SPAN_MASK) >> _GRID_CELL_SHIFT) +#define _GRID_GET_CELL_FLAG(c) ((c) >> (_GRID_CELL_SHIFT * 2) & 0x3) + +#define _GRID_CELL_SIZE_PX 0 /* The cell size is set in pixels*/ +#define _GRID_CELL_SIZE_FR 1 /* The cell size is set in free units*/ +#define _GRID_CELL_SIZE_AUTO 2 /* The cell size is automatically set to content*/ + +#define LV_GRID_FR(x) (LV_COORD_MAX + (x)) +#define LV_GRID_AUTO LV_COORD_AUTO + +#define _GRID_IS_PX(x) ((_GRID_IS_FR(x) == false) && (_GRID_IS_AUTO(x) == false) ? true : false) +#define _GRID_IS_FR(x) (x > LV_COORD_MAX ? true : false) +#define _GRID_IS_AUTO(x) (x == LV_GRID_AUTO ? true : false) + + +/** + * Define a grid cell with position and span. + * Can be used like `lv_obj_set_pos(btn, LV_GRID_CELL(3,2), LV_GRID_CELL(2,1))` + */ +#define LV_GRID_CELL_START(pos, span) ((LV_COORD_MAX + (pos) + (span << _GRID_CELL_SHIFT)) | _GRID_CELL_FLAG(LV_GRID_START)) +#define LV_GRID_CELL_END(pos, span) ((LV_COORD_MAX + (pos) + (span << _GRID_CELL_SHIFT)) | _GRID_CELL_FLAG(LV_GRID_END)) +#define LV_GRID_CELL_CENTER(pos, span) ((LV_COORD_MAX + (pos) + (span << _GRID_CELL_SHIFT)) | _GRID_CELL_FLAG(LV_GRID_CENTER)) +#define LV_GRID_CELL_STRETCH(pos, span) ((LV_COORD_MAX + (pos) + (span << _GRID_CELL_SHIFT)) | _GRID_CELL_FLAG(LV_GRID_STRETCH)) + +/** + * Special LV_GRID_CELL position to flow the object automatically. + * Both X (column) and Y (row) value needs to be AUTO or explicitly specified*/ +#define LV_GRID_AUTO_START LV_GRID_CELL_START(_GRID_CELL_AUTO, 0) +#define LV_GRID_AUTO_END LV_GRID_CELL_END(_GRID_CELL_AUTO, 0) +#define LV_GRID_AUTO_CENTER LV_GRID_CELL_CENTER(_GRID_CELL_AUTO, 0) +#define LV_GRID_AUTO_STRETCH LV_GRID_CELL_STRETCH(_GRID_CELL_AUTO, 0) + /********************** * TYPEDEFS **********************/ + +/** + * Describe how to flow LV_GRID_POS_AUTO elements + */ +typedef enum { + LV_GRID_FLOW_COLUMN, /**Fill each column and add now columns if required*/ + LV_GRID_FLOW_ROW, /**Fill each row and add now rows if required*/ +}lv_grid_flow_t; + typedef struct { const lv_coord_t * col_dsc; const lv_coord_t * row_dsc; - lv_coord_t gap_col; - lv_coord_t gap_row; - uint8_t flow; + uint8_t col_dsc_len; + uint8_t row_dsc_len; + lv_coord_t col_gap; + lv_coord_t row_gap; + lv_grid_flow_t flow; uint8_t place_content; - uint8_t place_items; }lv_grid_t; +typedef struct { + lv_coord_t * col_dsc; + lv_coord_t * row_dsc; + uint8_t col_dsc_len; + uint8_t row_dsc_len; +}_lv_grid_calc_t; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -39,9 +109,15 @@ void lv_grid_init(lv_grid_t * grid); void lv_grid_set_template(lv_grid_t * grid, const lv_coord_t * col_dsc, const lv_coord_t * row_dsc); -void lv_grid_set_place_content(lv_grid_t * grid, uint8_t col_mode, uint8_t row_mode); +void lv_grid_set_place_content(lv_grid_t * grid, uint8_t col_place, uint8_t row_place); -void lv_grid_set_place_items(lv_grid_t * grid, uint8_t col_mode, uint8_t row_mode); +void grid_calc(lv_grid_t * grid, _lv_grid_calc_t * calc); + +void grid_calc_free(_lv_grid_calc_t * calc); + +uint8_t _lv_grid_get_col_type(lv_grid_t * grid, uint32_t row); + +uint8_t _lv_grid_get_row_type(lv_grid_t * grid, uint32_t col); /********************** * MACROS diff --git a/src/lv_core/lv_obj.c b/src/lv_core/lv_obj.c index b04278d01..7f19e3865 100644 --- a/src/lv_core/lv_obj.c +++ b/src/lv_core/lv_obj.c @@ -699,19 +699,25 @@ void lv_obj_move_background(lv_obj_t * obj) * Coordinate set * ------------------*/ -static void refr_pos(lv_obj_t * obj) +static void refr_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) { /*Convert x and y to absolute coordinates*/ lv_obj_t * par = obj->parent; - lv_coord_t x = obj->x_set; - lv_coord_t y = obj->y_set; if(par) { lv_coord_t pad_left = lv_obj_get_style_pad_left(par, LV_OBJ_PART_MAIN); lv_coord_t pad_top = lv_obj_get_style_pad_top(par, LV_OBJ_PART_MAIN); x += pad_left + par->coords.x1 - lv_obj_get_scroll_left(par); y += pad_top + par->coords.y1 - lv_obj_get_scroll_top(par); + } else { + /*If no parent then it's screen but screen can't be on a grid*/ + if(_GRID_IS_CELL(obj->x_set) || _GRID_IS_CELL(obj->x_set)) { + obj->x_set = 0; + obj->y_set = 0; + x = 0; + y = 0; + } } /*Calculate and set the movement*/ @@ -748,76 +754,18 @@ static void refr_pos(lv_obj_t * obj) lv_obj_invalidate(obj); } -/** - * Set relative the position of an object (relative to the parent) - * @param obj pointer to an object - * @param x new distance from the left side of the parent - * @param y new distance from the top of the parent - */ -void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) + +static bool refr_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h) { - LV_ASSERT_OBJ(obj, LV_OBJX_NAME); - - obj->x_set = x; - obj->y_set = y; - - refr_pos(obj); - -} - -/** - * Set the x coordinate of a object - * @param obj pointer to an object - * @param x new distance from the left side from the parent - */ -void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x) -{ - LV_ASSERT_OBJ(obj, LV_OBJX_NAME); - - lv_obj_set_pos(obj, x, lv_obj_get_y(obj)); -} - -/** - * Set the y coordinate of a object - * @param obj pointer to an object - * @param y new distance from the top of the parent - */ -void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y) -{ - LV_ASSERT_OBJ(obj, LV_OBJX_NAME); - - lv_obj_set_pos(obj, lv_obj_get_x(obj), y); -} - -static void refr_size(lv_obj_t * obj) -{ - lv_coord_t w = 0; - lv_coord_t h = 0; - - - if(LV_COORD_IS_AUTO(obj->w_set)) { - lv_obj_scroll_to_x(obj, 0, LV_ANIM_OFF); - lv_coord_t scroll_right = lv_obj_get_scroll_right(obj); - w = lv_obj_get_width(obj) + scroll_right; - } else { - w = obj->w_set; - } - - if(LV_COORD_IS_AUTO(obj->h_set)) { - lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF); - lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(obj); - h = lv_obj_get_height(obj) + scroll_bottom; - } else { - h = obj->h_set; - } /* Do nothing if the size is not changed */ /* It is very important else recursive resizing can * occur without size change*/ if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) { - return; + return false; } + /*Invalidate the original area*/ lv_obj_invalidate(obj); @@ -849,6 +797,121 @@ static void refr_size(lv_obj_t * obj) /*Invalidate the new area*/ lv_obj_invalidate(obj); + + return true; +} + + + +static void lv_grid_refresh_item_pos(lv_obj_t * obj) +{ + /*Calculate the grid*/ + lv_obj_t * parent = lv_obj_get_parent(obj); + lv_coord_t * col_dsc; + lv_coord_t * row_dsc; + uint8_t col_num; + uint8_t row_num; + _lv_grid_calc_t calc; + grid_calc(parent->grid, &calc); + + uint8_t col_pos = _GRID_GET_CELL_POS(obj->x_set); + uint8_t col_span = _GRID_GET_CELL_SPAN(obj->x_set); + uint8_t row_pos = _GRID_GET_CELL_POS(obj->y_set); + uint8_t row_span = _GRID_GET_CELL_SPAN(obj->y_set); + + lv_coord_t col_w = calc.col_dsc[col_pos + col_span] - calc.col_dsc[col_pos]; + lv_coord_t row_h = calc.row_dsc[row_pos + row_span] - calc.row_dsc[row_pos]; + + uint8_t x_flag = _GRID_GET_CELL_FLAG(obj->x_set); + uint8_t y_flag = _GRID_GET_CELL_FLAG(obj->y_set); + + lv_coord_t x; + lv_coord_t y; + lv_coord_t w = lv_obj_get_width(obj); + lv_coord_t h = lv_obj_get_height(obj); + + switch(x_flag) { + case LV_GRID_START: + x = calc.col_dsc[col_pos]; + break; + case LV_GRID_STRETCH: + x = calc.col_dsc[col_pos]; + w = col_w; + break; + case LV_GRID_CENTER: + x = calc.col_dsc[col_pos] + (col_w - w) / 2; + break; + case LV_GRID_END: + x = calc.col_dsc[col_pos + 1] - lv_obj_get_width(obj); + break; + } + + switch(y_flag) { + case LV_GRID_START: + y = calc.row_dsc[row_pos]; + break; + case LV_GRID_STRETCH: + y = calc.row_dsc[row_pos]; + h = row_h; + break; + case LV_GRID_CENTER: + y = calc.row_dsc[row_pos] + (row_h - h) / 2; + break; + case LV_GRID_END: + y = calc.row_dsc[row_pos + 1] - lv_obj_get_height(obj); + break; + } + + refr_pos(obj, x, y); + refr_size(obj, w, h); + + grid_calc_free(&calc); +} + + +/** + * Set relative the position of an object (relative to the parent) + * @param obj pointer to an object + * @param x new distance from the left side of the parent + * @param y new distance from the top of the parent + */ +void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) +{ + LV_ASSERT_OBJ(obj, LV_OBJX_NAME); + + obj->x_set = x; + obj->y_set = y; + + /*If the object is on a grid item let grid to position it. */ + if(_GRID_IS_CELL(obj->x_set) && _GRID_IS_CELL(obj->x_set)) { + lv_grid_refresh_item_pos(obj); + } else { + refr_pos(obj, x, y); + } +} + +/** + * Set the x coordinate of a object + * @param obj pointer to an object + * @param x new distance from the left side from the parent + */ +void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x) +{ + LV_ASSERT_OBJ(obj, LV_OBJX_NAME); + + lv_obj_set_pos(obj, x, lv_obj_get_y(obj)); +} + +/** + * Set the y coordinate of a object + * @param obj pointer to an object + * @param y new distance from the top of the parent + */ +void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y) +{ + LV_ASSERT_OBJ(obj, LV_OBJX_NAME); + + lv_obj_set_pos(obj, lv_obj_get_x(obj), y); } /** @@ -864,7 +927,52 @@ void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h) obj->w_set = w; obj->h_set = h; - refr_size(obj); + /*If the object is a grid element and stretched, prevent settings its size */ + bool x_stretch = false; + bool y_stretch = false; + x_stretch = _GRID_GET_CELL_FLAG(obj->x_set) == LV_GRID_STRETCH ? true : false; + y_stretch = _GRID_GET_CELL_FLAG(obj->y_set) == LV_GRID_STRETCH ? true : false; + + if(x_stretch && y_stretch) return; + + lv_coord_t grid_w = 0; + lv_coord_t grid_h = 0; + if(((LV_COORD_IS_AUTO(obj->w_set) && !x_stretch) || + (LV_COORD_IS_AUTO(obj->h_set) && !y_stretch))) { + _lv_grid_calc_t calc; + grid_calc(obj->grid, &calc); + grid_w = calc.col_dsc[calc.col_dsc_len - 1]; + grid_h = calc.row_dsc[calc.row_dsc_len - 1]; + grid_calc_free(&calc); + } + + + if(x_stretch) w = lv_obj_get_width(obj); + else { + if(LV_COORD_IS_AUTO(obj->w_set)) { + lv_obj_scroll_to_x(obj, 0, LV_ANIM_OFF); + lv_coord_t scroll_right = lv_obj_get_scroll_right(obj); + w = lv_obj_get_width(obj) + scroll_right; + w = LV_MATH_MAX(w, grid_w); + } + } + + if(y_stretch) h = lv_obj_get_height(obj); + else { + if(LV_COORD_IS_AUTO(obj->h_set) && !x_stretch) { + lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF); + lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(obj); + h = lv_obj_get_height(obj) + scroll_bottom; + h = LV_MATH_MAX(h, grid_h); + } + } + + bool chg = refr_size(obj, w, h); + + /*Refresh the position in the grid item if required*/ + if(chg && (_GRID_IS_CELL(obj->x_set) || _GRID_IS_CELL(obj->y_set))) { + lv_grid_refresh_item_pos(obj); + } } /** diff --git a/src/lv_core/lv_obj.h b/src/lv_core/lv_obj.h index e0188a0b5..9c0ee22fc 100644 --- a/src/lv_core/lv_obj.h +++ b/src/lv_core/lv_obj.h @@ -18,6 +18,7 @@ extern "C" { #include #include #include "lv_style.h" +#include "lv_align.h" #include "../lv_misc/lv_types.h" #include "../lv_misc/lv_area.h" #include "../lv_misc/lv_color.h" @@ -243,6 +244,8 @@ typedef struct _lv_obj_t { lv_obj_user_data_t user_data; /**< Custom user data for object. */ #endif + lv_grid_t * grid; + } lv_obj_t; enum { diff --git a/src/lv_misc/lv_area.h b/src/lv_misc/lv_area.h index 6bbb3afb2..86ee4a186 100644 --- a/src/lv_misc/lv_area.h +++ b/src/lv_misc/lv_area.h @@ -22,8 +22,9 @@ extern "C" { /********************* * DEFINES *********************/ -/*To avoid overflow don't let the max ranges (reduce with 1000) */ -#define LV_COORD_MAX ((lv_coord_t)((uint32_t)((uint32_t)1 << (8 * sizeof(lv_coord_t) - 1)) - 1000)) +#define _LV_COORD_MAX_REDUCE 8192 +/*To allow some special values in the end reduce the max value */ +#define LV_COORD_MAX ((lv_coord_t)((uint32_t)((uint32_t)1 << (8 * sizeof(lv_coord_t) - 1)) - _LV_COORD_MAX_REDUCE)) #define LV_COORD_MIN (-LV_COORD_MAX) LV_EXPORT_CONST_INT(LV_COORD_MAX); @@ -209,10 +210,10 @@ void _lv_area_align(const lv_area_t * base, const lv_area_t * to_align, lv_align **********************/ #define LV_COORD_PX(x) (x) -#define LV_COORD_AUTO (LV_COORD_MAX - 100) +#define LV_COORD_AUTO (LV_COORD_MAX - 1) -#define LV_COORD_IS_PX(x) (((x) < LV_COORD_MAX - 1000) ? true : false) -#define LV_COORD_IS_AUTO(x) (((x) == LV_COORD_MAX - 100) ? true : false) +#define LV_COORD_IS_PX(x) (((x) < LV_COORD_MAX) ? true : false) +#define LV_COORD_IS_AUTO(x) (((x) == LV_COORD_AUTO) ? true : false) #ifdef __cplusplus