6377 lines
200 KiB
C
6377 lines
200 KiB
C
/*
|
|
* Created on: 02-Apr-2020
|
|
*/
|
|
/**
|
|
* @file lame_ui.c
|
|
* @author Avra Mitra
|
|
* @brief Source FIle of LameUI GUI library. Must include lame_ui.h. No other file is mandatory.
|
|
* @version 2.0
|
|
* @date 2023-10-26
|
|
*
|
|
* @copyright Copyright (c) 2020-2023
|
|
*
|
|
*/
|
|
|
|
#include "lame_ui.h"
|
|
|
|
|
|
/*-------------------------------------
|
|
* Global variables
|
|
*-------------------------------------
|
|
*/
|
|
static uint8_t g_lui_needs_render = 0;
|
|
static _lui_mem_block_t g_lui_mem_block;
|
|
static _lui_main_t* g_lui_main;
|
|
/*-------------------------------------
|
|
* End
|
|
*-------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* Main functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
int8_t lui_init(uint8_t mem_block[], uint32_t size)
|
|
{
|
|
_lui_mem_init(mem_block, size);
|
|
g_lui_main = (_lui_main_t* )_lui_mem_alloc(sizeof(_lui_main_t));
|
|
if (g_lui_main == NULL)
|
|
return -1;
|
|
|
|
// g_lui_main->scenes = {NULL};
|
|
g_lui_main->default_font = &LUI_DEFAULT_FONT;
|
|
g_lui_main->disp_drv = NULL;
|
|
g_lui_main->touch_input_dev = NULL;
|
|
g_lui_main->last_touch_data.x = -1;
|
|
g_lui_main->last_touch_data.y = -1;
|
|
g_lui_main->last_touch_data.is_pressed = 0;
|
|
g_lui_main->input_event_clicked = 0;
|
|
g_lui_main->input_state_pressed = 0;
|
|
g_lui_main->total_scenes = 0;
|
|
g_lui_main->active_scene = NULL;
|
|
g_lui_main->active_obj = NULL;
|
|
g_lui_main->total_created_objects = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t lui_set_default_font(const lui_font_t* font)
|
|
{
|
|
if (font == NULL)
|
|
return -1;
|
|
|
|
g_lui_main->default_font = font;
|
|
return 0;
|
|
}
|
|
|
|
void lui_update()
|
|
{
|
|
if ( g_lui_main->active_scene == NULL)
|
|
return;
|
|
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
|
|
lui_obj_t* obj_caused_cb = NULL;
|
|
// Reading input
|
|
obj_caused_cb = _lui_process_input_of_act_scene();
|
|
if (g_lui_needs_render)
|
|
{
|
|
_lui_object_render_parent_with_children( g_lui_main->active_scene);
|
|
}
|
|
|
|
//All rendering done, now we'll handle the callback
|
|
// if the object that caused callback is not NULL and if the object has a callback func,
|
|
if (obj_caused_cb != NULL && obj_caused_cb->obj_event_cb != NULL)
|
|
{
|
|
// Call the user-supplied callback
|
|
obj_caused_cb->obj_event_cb(obj_caused_cb);
|
|
//event callback handled, so reset it
|
|
obj_caused_cb->event = LUI_EVENT_NONE;
|
|
}
|
|
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_LABEL related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
void lui_label_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LABEL) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_label_t* lbl = (lui_label_t* )(obj->obj_main_data);
|
|
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
lui_area_t lbl_area = {
|
|
.x = obj->x,
|
|
.y = obj->y,
|
|
.w = obj->common_style.width,
|
|
.h = obj->common_style.height
|
|
};
|
|
|
|
uint16_t bg_color = obj->common_style.bg_color;
|
|
const lui_bitmap_t* parent_bmp = NULL;
|
|
lui_bitmap_mono_pal_t* mono_palette = NULL;
|
|
lui_area_t bmp_crop;
|
|
if (obj->parent && lbl->style.is_transparent_bg)
|
|
{
|
|
bg_color = obj->parent->common_style.bg_color;
|
|
/* NOTE: panel and scene both have same first items in the struct. So even if
|
|
* the parent is scene, we can use lui_panel_t for casting.
|
|
*/
|
|
lui_panel_t* panel = (lui_panel_t* )(obj->parent->obj_main_data);
|
|
if (panel->bg_image)
|
|
{
|
|
parent_bmp = panel->bg_image;
|
|
mono_palette = &panel->img_pal;
|
|
bmp_crop.x = obj->x - obj->parent->x;
|
|
bmp_crop.y = obj->y - obj->parent->y;
|
|
bmp_crop.w = lbl_area.w;
|
|
bmp_crop.h = lbl_area.h;
|
|
|
|
// if (panel->image_crop)
|
|
// {
|
|
// bmp_crop.x += panel->image_crop->x;
|
|
// bmp_crop.y += panel->image_crop->y;
|
|
// }
|
|
}
|
|
}
|
|
lui_gfx_draw_string_advanced(
|
|
lbl->text,
|
|
&lbl_area,
|
|
lbl->style.text_color,
|
|
bg_color,
|
|
parent_bmp,
|
|
mono_palette,
|
|
&bmp_crop,
|
|
1,
|
|
lbl->font);
|
|
|
|
// Draw the label border if needed
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, obj->common_style.width,
|
|
obj->common_style.height, obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
|
|
}
|
|
|
|
lui_obj_t* lui_label_create(void)
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_label_t* initial_label = (lui_label_t* )_lui_mem_alloc(sizeof(*initial_label));
|
|
if (initial_label == NULL)
|
|
return NULL;
|
|
|
|
initial_label->text = "";
|
|
initial_label->font = g_lui_main->default_font;
|
|
initial_label->style.text_color = LUI_STYLE_LABEL_TEXT_COLOR;
|
|
initial_label->style.is_transparent_bg = 1;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// objeect type
|
|
obj->obj_type = LUI_OBJ_LABEL;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_LABEL_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_LABEL_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_LABEL_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_LABEL_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_LABEL_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_label;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_label_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(label, obj_parent)
|
|
}
|
|
|
|
void lui_label_set_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LABEL) < 0)
|
|
return;
|
|
if (font == NULL)
|
|
return;
|
|
|
|
lui_label_t* lbl = (lui_label_t* )(obj->obj_main_data);
|
|
lbl->font = font;
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
|
|
void lui_label_set_text(lui_obj_t* obj, const char* text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LABEL) < 0)
|
|
return;
|
|
|
|
lui_label_t* lbl = (lui_label_t* )(obj->obj_main_data);
|
|
lbl->text = (char*)text;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_label_set_text_color(lui_obj_t* obj, uint16_t text_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LABEL) < 0)
|
|
return;
|
|
|
|
lui_label_t* lbl = (lui_label_t* )(obj->obj_main_data);
|
|
if (lbl->style.text_color == text_color)
|
|
return;
|
|
lbl->style.text_color = text_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_label_set_bg_transparent(lui_obj_t* obj, uint8_t is_transparent)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LABEL) < 0)
|
|
return;
|
|
|
|
lui_label_t* lbl = (lui_label_t* )(obj->obj_main_data);
|
|
if (lbl->style.is_transparent_bg == is_transparent)
|
|
return;
|
|
lbl->style.is_transparent_bg = is_transparent ? 1 : 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_LINE_CHART related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_LINECHART)
|
|
|
|
/*
|
|
* Draw a line chart
|
|
*/
|
|
void lui_linechart_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
|
|
uint16_t temp_x = obj->x;
|
|
uint16_t temp_y = obj->y;
|
|
uint16_t width = obj->common_style.width;
|
|
uint16_t height = obj->common_style.height;
|
|
uint16_t line_color = chart->style.line_color;
|
|
uint16_t data_points = chart->data.points;
|
|
|
|
double mapped_data[g_lui_main->disp_drv->display_hor_res * 2];
|
|
|
|
double x_data_min_new = obj->x;
|
|
double x_data_max_new = obj->x + width - 1;
|
|
//[0][0] element of 2D array is x_min
|
|
// address of [0][0] = base address
|
|
double x_data_min_old = *(chart->data.source);
|
|
//[n][0] element of 2D array is x_max
|
|
//address of [n][0] = base address + (n*2) - 2
|
|
double x_data_max_old = *(chart->data.source + (data_points*2) - 2);
|
|
|
|
|
|
double y_data_min_new = obj->y + height - 1;
|
|
double y_data_max_new = obj->y;
|
|
double y_data_min_old;
|
|
double y_data_max_old;
|
|
|
|
// If data auto-scale is enabled, find out the Y max and min value
|
|
if (chart->data.auto_scale == 1)
|
|
{
|
|
// Initially, Max and Min both are set to the first Y value of the source array
|
|
double y_max = *(chart->data.source + 1);
|
|
double y_min = y_max;
|
|
|
|
// Now compare max and min with y values from source array to find the maximum and minimum
|
|
for (uint16_t i = 1; i < data_points; i++)
|
|
{
|
|
double y_val = *(chart->data.source + (i*2) + 1);
|
|
if (y_max < y_val)
|
|
y_max = y_val;
|
|
if (y_min > y_val)
|
|
y_min = y_val;
|
|
}
|
|
if (y_max == y_min)
|
|
{
|
|
y_max = _LUI_MAX(y_max, y_max + 1); // preventing overflow
|
|
}
|
|
y_data_min_old = y_min;
|
|
y_data_max_old = y_max;
|
|
}
|
|
// If not enabled, use user-supplied max and min value
|
|
else
|
|
{
|
|
y_data_min_old = chart->data.y_min_value;
|
|
y_data_max_old = chart->data.y_max_value;
|
|
}
|
|
|
|
|
|
// Draw the chart background
|
|
lui_gfx_draw_rect_fill(temp_x, temp_y, width, height, obj->common_style.bg_color);
|
|
|
|
// Draw the scale numbers
|
|
|
|
// Draw the chart grid if needed
|
|
if (chart->style.grid_visible)
|
|
{
|
|
uint16_t hor_grid_spacing = height / (chart->grid.hor_count + 1);
|
|
uint16_t vert_grid_spacing = width / (chart->grid.vert_count + 1);
|
|
|
|
// Draw the vertical grids from left to right
|
|
for (int i = 1; i <= chart->grid.vert_count; i++)
|
|
{
|
|
uint16_t temp_x_new = temp_x + (i * vert_grid_spacing);
|
|
lui_gfx_draw_line(temp_x_new, temp_y, temp_x_new, temp_y + height - 1, 1, chart->style.grid_color);
|
|
}
|
|
|
|
// Draw the horizontal grids from bottom to top
|
|
uint16_t y_bottom = temp_y + height;
|
|
for (int i = 1; i <= chart->grid.hor_count; i++)
|
|
{
|
|
uint16_t temp_y_new = y_bottom - (i * hor_grid_spacing);
|
|
lui_gfx_draw_line(temp_x, temp_y_new, temp_x + width - 1, temp_y_new, 1, chart->style.grid_color);
|
|
}
|
|
}
|
|
|
|
// Map all the point values to pixel co-ordinate values
|
|
for (uint16_t i = 0; i < data_points; i++)
|
|
{
|
|
double x_data_old = *(chart->data.source + (i*2));
|
|
double y_data_old = *(chart->data.source + (i*2) + 1);
|
|
// Mapping range of x values
|
|
mapped_data[i*2] = _lui_map_range(x_data_old, x_data_max_old, x_data_min_old, x_data_max_new, x_data_min_new);
|
|
|
|
// Mapping range of y values
|
|
mapped_data[i*2 + 1] = _lui_map_range(y_data_old, y_data_max_old, y_data_min_old, y_data_max_new, y_data_min_new);
|
|
}
|
|
|
|
|
|
lui_area_t clip_win = {temp_x, temp_y, width, height};
|
|
// Now draw the lines using the mapped points to make the graph
|
|
for (uint16_t i = 0; i < data_points - 1; i++)
|
|
{
|
|
uint32_t i_cur_x = (i * 2), i_cur_y = (i * 2 + 1);
|
|
uint32_t i_nxt_x = (i * 2 + 2), i_nxt_y = (i * 2 + 3);
|
|
if (chart->style.draw_mode & LUI_LINECHART_DRAW_MODE_LINE)
|
|
{
|
|
if (chart->data.auto_scale)
|
|
{
|
|
lui_gfx_draw_line_clipped(
|
|
mapped_data[i_cur_x], mapped_data[i_cur_y],
|
|
mapped_data [i_nxt_x], mapped_data [i_nxt_y],
|
|
&clip_win, chart->style.line_width, line_color);
|
|
}
|
|
else
|
|
{
|
|
double current[2] = {mapped_data[i_cur_x], mapped_data[i_cur_y]};
|
|
double next[2] = {mapped_data [i_nxt_x], mapped_data [i_nxt_y]};
|
|
// modifies `current` and `next`
|
|
uint8_t flag_accept = _lui_clip_line(current, next, &clip_win);
|
|
if (flag_accept)
|
|
{
|
|
lui_gfx_draw_line_clipped(
|
|
current[0], current[1], next[0], next[1],
|
|
&clip_win, chart->style.line_width, line_color);
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
}
|
|
// Draw point only if point draw mode enabled and points are within the clip area/window
|
|
if (chart->style.draw_mode & LUI_LINECHART_DRAW_MODE_POINT)
|
|
{
|
|
if (mapped_data[i_cur_x] >= clip_win.x && mapped_data[i_cur_x] <= clip_win.x + clip_win.w - 1 &&
|
|
mapped_data[i_cur_y] >= clip_win.y && mapped_data[i_cur_y] <= clip_win.y + clip_win.h - 1)
|
|
{
|
|
double px = mapped_data[i_cur_x] - chart->style.point_width / 2;
|
|
double py = mapped_data[i_cur_y] - chart->style.point_width / 2;
|
|
lui_gfx_draw_rect_fill_clipped(px, py, chart->style.point_width, chart->style.point_width, &clip_win, chart->style.point_color);
|
|
}
|
|
|
|
// draw the last remaining point
|
|
if (i == chart->data.points - 2 &&
|
|
mapped_data[i_nxt_x] >= clip_win.x && mapped_data[i_nxt_x] <= clip_win.x + clip_win.w - 1 &&
|
|
mapped_data[i_nxt_y] >= clip_win.y && mapped_data[i_nxt_y] <= clip_win.y + clip_win.h - 1)
|
|
{
|
|
double px = mapped_data[i_nxt_x] - chart->style.point_width / 2.;
|
|
double py = mapped_data[i_nxt_y] - chart->style.point_width / 2.;
|
|
lui_gfx_draw_rect_fill_clipped(px, py, chart->style.point_width, chart->style.point_width, &clip_win, chart->style.point_color);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Draw the chart border if needed
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(temp_x, temp_y, width, height, obj->common_style.border_width, obj->common_style.border_color);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize a line chart with default values
|
|
*/
|
|
lui_obj_t* lui_linechart_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_chart_t* initial_line_chart = (lui_chart_t* )_lui_mem_alloc(sizeof(*initial_line_chart));
|
|
if (initial_line_chart == NULL)
|
|
return NULL;
|
|
|
|
double tmp_data[] = {0};
|
|
initial_line_chart->data.source = tmp_data;
|
|
initial_line_chart->data.y_max_value = 0;
|
|
initial_line_chart->data.y_min_value = g_lui_main->disp_drv->display_vert_res;
|
|
initial_line_chart->data.points = 0;
|
|
initial_line_chart->data.auto_scale = 1;
|
|
|
|
initial_line_chart->style.line_color = LUI_STYLE_LINECHART_LINE_COLOR;
|
|
initial_line_chart->style.grid_color = LUI_STYLE_LINECHART_GRID_COLOR;
|
|
initial_line_chart->style.grid_visible = LUI_STYLE_LINECHART_GRID_VISIBLE;
|
|
initial_line_chart->style.line_width = 2;
|
|
initial_line_chart->style.point_width = 7;
|
|
initial_line_chart->style.point_color = LUI_STYLE_LINECHART_POINT_COLOR;
|
|
initial_line_chart->style.draw_mode = LUI_LINECHART_DRAW_MODE_LINE | LUI_LINECHART_DRAW_MODE_POINT;
|
|
initial_line_chart->grid.hor_count = 5;
|
|
initial_line_chart->grid.vert_count = 5;
|
|
initial_line_chart->font = g_lui_main->default_font;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_LINECHART;
|
|
// object common style
|
|
obj->common_style.border_color = LUI_STYLE_LINECHART_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_LINECHART_BORDER_THICKNESS;
|
|
obj->common_style.bg_color = LUI_STYLE_LINECHART_BG_COLOR;
|
|
obj->common_style.height = LUI_STYLE_LINECHART_HEIGHT;
|
|
obj->common_style.width = LUI_STYLE_LINECHART_WIDTH;
|
|
|
|
obj->obj_main_data = (void* )initial_line_chart;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_linechart_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(linechart, obj_parent)
|
|
}
|
|
|
|
void lui_linechart_set_grid_count(lui_obj_t* obj, uint16_t hor_lines, uint16_t vert_lines)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->grid.hor_count == hor_lines && chart->grid.vert_count == vert_lines)
|
|
return;
|
|
_lui_object_set_need_refresh(obj);
|
|
chart->grid.hor_count = hor_lines;
|
|
chart->grid.vert_count = vert_lines;
|
|
}
|
|
|
|
void lui_linechart_set_grid_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.grid_color == color)
|
|
return;
|
|
_lui_object_set_need_refresh(obj);
|
|
chart->style.grid_color = color;
|
|
}
|
|
|
|
void lui_linechart_set_grid_visible(lui_obj_t* obj, uint8_t state)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.grid_visible == state)
|
|
return;
|
|
_lui_object_set_need_refresh(obj);
|
|
chart->style.grid_visible = state;
|
|
}
|
|
|
|
void lui_linechart_set_line_color(lui_obj_t* obj, uint16_t line_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.line_color == line_color)
|
|
return;
|
|
chart->style.line_color = line_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_line_width(lui_obj_t* obj, uint8_t line_width)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.line_width == line_width)
|
|
return;
|
|
chart->style.line_width = _LUI_MAX(line_width, 1);
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_point_color(lui_obj_t* obj, uint16_t point_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.point_color == point_color)
|
|
return;
|
|
chart->style.point_color = point_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_point_width(lui_obj_t* obj, uint8_t point_width)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.point_width == point_width)
|
|
return;
|
|
chart->style.point_width = _LUI_MAX(point_width, 1);
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_draw_mode(lui_obj_t* obj, uint8_t mode_flag)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
if (mode_flag == 0 || mode_flag > 3)
|
|
return;
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->style.draw_mode == mode_flag)
|
|
return;
|
|
chart->style.draw_mode = mode_flag;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_data_auto_scale(lui_obj_t* obj, uint8_t auto_scale)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (auto_scale == chart->data.auto_scale)
|
|
return;
|
|
chart->data.auto_scale = auto_scale ? 1 : 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_data_range(lui_obj_t* obj, double y_min, double y_max)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
if (chart->data.y_max_value == y_max && chart->data.y_min_value == y_min && chart->data.auto_scale == 0)
|
|
return;
|
|
|
|
chart->data.y_max_value = y_max;
|
|
chart->data.y_min_value = y_min;
|
|
chart->data.auto_scale = 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_linechart_set_data_source(lui_obj_t* obj, double *source, uint16_t points)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LINECHART) < 0)
|
|
return;
|
|
|
|
lui_chart_t* chart = (lui_chart_t* )(obj->obj_main_data);
|
|
// if (chart->data.points == points)
|
|
// return;
|
|
_lui_object_set_need_refresh(obj);
|
|
chart->data.source = source;
|
|
chart->data.points = points;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_BUTTON related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* Draw a button
|
|
*/
|
|
void lui_button_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
|
|
uint8_t padding = 2;
|
|
uint16_t temp_x = obj->x;
|
|
uint16_t temp_y = obj->y;
|
|
uint16_t btn_height = obj->common_style.height;
|
|
uint16_t btn_width = obj->common_style.width;
|
|
|
|
uint16_t str_dim[2];
|
|
|
|
/* If button's background is transparent, draw parent's bg color or parent's bitmap as bg */
|
|
if (obj->parent && btn->style.is_transparent_bg && obj->state != LUI_STATE_SELECTED)
|
|
{
|
|
/* NOTE: panel and scene both have same first items in the struct. So even if
|
|
* the parent is scene, we can use lui_panel_t for casting.
|
|
*/
|
|
lui_panel_t* panel = (lui_panel_t* )(obj->parent->obj_main_data);
|
|
if (panel->bg_image)
|
|
{
|
|
lui_area_t bmp_crop;
|
|
const lui_bitmap_t* bg_bmp = panel->bg_image;
|
|
bmp_crop.x = temp_x - obj->parent->x;
|
|
bmp_crop.y = temp_y - obj->parent->y;
|
|
bmp_crop.w = btn_width;
|
|
bmp_crop.h = btn_height;
|
|
|
|
lui_gfx_draw_bitmap(bg_bmp, &panel->img_pal, temp_x, temp_y, &bmp_crop);
|
|
}
|
|
else
|
|
{
|
|
lui_gfx_draw_rect_fill(temp_x, temp_y, btn_width, btn_height, obj->parent->common_style.bg_color);
|
|
}
|
|
}
|
|
/* Else draw the button's bg color depending on its current state */
|
|
else
|
|
{
|
|
uint16_t btn_color = obj->common_style.bg_color;
|
|
if (obj->state == LUI_STATE_SELECTED)
|
|
btn_color = btn->style.selection_color;
|
|
else if (obj->state == LUI_STATE_PRESSED || (btn->is_checkable && obj->value))
|
|
btn_color = btn->style.pressed_color;
|
|
// else if (btn->state == LUI_STATE_IDLE)
|
|
// btn_color = btn->color;
|
|
|
|
lui_gfx_draw_rect_fill(temp_x, temp_y, btn_width, btn_height, btn_color);
|
|
}
|
|
|
|
/* Draw background bitmap if not NULL */
|
|
if (btn->img_idle || btn->img_pressed)
|
|
{
|
|
lui_area_t crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = obj->common_style.width,
|
|
.h = obj->common_style.height
|
|
};
|
|
if ((obj->state == LUI_STATE_PRESSED || (btn->is_checkable && obj->value)) &&
|
|
btn->img_pressed)
|
|
{
|
|
lui_gfx_draw_bitmap(
|
|
btn->img_pressed,
|
|
&btn->img_press_pal,
|
|
temp_x, temp_y,
|
|
&crop);
|
|
}
|
|
else if (obj->state == LUI_STATE_IDLE && btn->img_idle)
|
|
{
|
|
lui_gfx_draw_bitmap(
|
|
btn->img_idle,
|
|
&btn->img_idle_pal,
|
|
temp_x, temp_y,
|
|
&crop);
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw the button label (text) if not NULL */
|
|
if (btn->label.text && btn->label.text[0] != '\0')
|
|
{
|
|
uint16_t lbl_color = btn->style.label_color;
|
|
const char* lbl_txt = btn->label.text;
|
|
if (obj->state == LUI_STATE_PRESSED || (btn->is_checkable && obj->value))
|
|
{
|
|
lbl_color = btn->style.label_pressed_color;
|
|
lbl_txt = btn->label.text_pressed;
|
|
if (lbl_txt == NULL || lbl_txt[0] == '\0')
|
|
return;
|
|
}
|
|
|
|
lui_gfx_get_string_dimension(lbl_txt, btn->label.font, btn_width, str_dim);
|
|
|
|
str_dim[0] = str_dim[0] > btn_width ? btn_width : str_dim[0];
|
|
str_dim[1] = str_dim[1] > btn_height ? btn_height : str_dim[1];
|
|
|
|
temp_y = temp_y + (btn_height - str_dim[1]) / 2;
|
|
if (btn->label.text_align == LUI_ALIGN_CENTER)
|
|
{
|
|
temp_x = temp_x + (btn_width - str_dim[0]) / 2;
|
|
}
|
|
else if (btn->label.text_align == LUI_ALIGN_RIGHT)
|
|
{
|
|
temp_x = temp_x + (btn_width - str_dim[0]) - padding;
|
|
}
|
|
else
|
|
{
|
|
temp_x = temp_x + padding;
|
|
}
|
|
|
|
lui_area_t btn_lbl_area = {
|
|
.x = temp_x,
|
|
.y = temp_y,
|
|
.w = str_dim[0],
|
|
.h = str_dim[1]
|
|
};
|
|
lui_gfx_draw_string_advanced(lbl_txt, &btn_lbl_area, lbl_color, 0, NULL, NULL, NULL, 0, btn->label.font);
|
|
}
|
|
|
|
/* Finally Draw the border if needed */
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, btn_width, btn_height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create a button with default variables
|
|
*/
|
|
lui_obj_t* lui_button_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_button_t* initial_button = (lui_button_t* )_lui_mem_alloc(sizeof(*initial_button));
|
|
if (initial_button == NULL)
|
|
return NULL;
|
|
|
|
initial_button->style.pressed_color = LUI_STYLE_BUTTON_PRESSED_COLOR;
|
|
initial_button->style.selection_color = LUI_STYLE_BUTTON_SELECTION_COLOR;
|
|
initial_button->style.label_color = LUI_STYLE_BUTTON_LABEL_COLOR;
|
|
initial_button->style.label_pressed_color = LUI_STYLE_BUTTON_LABEL_COLOR;
|
|
initial_button->style.is_transparent_bg = 0;
|
|
|
|
initial_button->img_idle = NULL;
|
|
initial_button->img_pressed = NULL;
|
|
initial_button->img_idle_pal.fore_color = LUI_STYLE_BUTTON_LABEL_COLOR;
|
|
initial_button->img_idle_pal.back_color = LUI_STYLE_BUTTON_BG_COLOR;
|
|
initial_button->img_idle_pal.is_backgrnd = 1;
|
|
initial_button->img_press_pal.fore_color = LUI_STYLE_BUTTON_LABEL_COLOR;
|
|
initial_button->img_press_pal.back_color = LUI_STYLE_BUTTON_BG_COLOR;
|
|
initial_button->img_press_pal.is_backgrnd = 1;
|
|
|
|
initial_button->is_checkable = 0;
|
|
|
|
initial_button->label.text = NULL;
|
|
initial_button->label.text_pressed = NULL;
|
|
initial_button->label.font = g_lui_main->default_font;
|
|
initial_button->label.text_align = LUI_ALIGN_CENTER;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_BUTTON;
|
|
obj->enabled = 1;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_BUTTON_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_BUTTON_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_BUTTON_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_BUTTON_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_BUTTON_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_button;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_button_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(button, obj_parent)
|
|
}
|
|
|
|
void lui_button_set_label_texts(lui_obj_t* obj, const char* idle_text, const char* pressed_text)
|
|
{
|
|
lui_button_set_label_text(obj, idle_text);
|
|
lui_button_set_label_text_pressed(obj, pressed_text);
|
|
}
|
|
|
|
void lui_button_set_label_text(lui_obj_t* obj, const char* text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
btn->label.text = text;
|
|
btn->label.text_pressed = text;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_label_text_pressed(lui_obj_t* obj, const char* pressed_text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
btn->label.text_pressed = pressed_text;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_label_align(lui_obj_t *obj, uint8_t alignment)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (btn->label.text_align != alignment)
|
|
{
|
|
btn->label.text_align = alignment;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
}
|
|
|
|
void lui_button_set_label_colors(lui_obj_t* obj, uint16_t idle_color, uint16_t pressed_color)
|
|
{
|
|
lui_button_set_label_color(obj, idle_color);
|
|
lui_button_set_label_color_pressed(obj, pressed_color);
|
|
}
|
|
|
|
void lui_button_set_label_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (btn->style.label_color == color)
|
|
return;
|
|
btn->style.label_color = color;
|
|
btn->style.label_pressed_color = color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_label_color_pressed(lui_obj_t* obj, uint16_t pressed_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (btn->style.label_pressed_color == pressed_color)
|
|
return;
|
|
btn->style.label_pressed_color = pressed_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_label_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
btn->label.font = (lui_font_t* )font;
|
|
// parent needs refresh (along with all its children)
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
|
|
void lui_button_set_bitmap_images(lui_obj_t* obj, const lui_bitmap_t* idle_bitmap, const lui_bitmap_t* pressed_bitmap)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (idle_bitmap && pressed_bitmap)
|
|
btn->style.is_transparent_bg = 1;
|
|
else
|
|
btn->style.is_transparent_bg = 0;
|
|
btn->img_idle = idle_bitmap;
|
|
btn->img_pressed = pressed_bitmap;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_bitmap_images_mono_palette(lui_obj_t* obj, lui_bitmap_mono_pal_t* idle_palette, lui_bitmap_mono_pal_t* press_palette)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
|
|
if (idle_palette)
|
|
btn->img_idle_pal = *idle_palette;
|
|
if (press_palette)
|
|
btn->img_press_pal = *press_palette;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_value(lui_obj_t* obj, uint8_t value)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (obj->value == value || !btn->is_checkable)
|
|
return;
|
|
obj->value = value ? 1 : 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_checked(lui_obj_t* obj)
|
|
{
|
|
lui_button_set_value(obj, 1);
|
|
}
|
|
|
|
void lui_button_set_unchecked(lui_obj_t* obj)
|
|
{
|
|
lui_button_set_value(obj, 0);
|
|
}
|
|
|
|
void lui_button_set_checkable(lui_obj_t* obj, uint8_t is_checkable)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
btn->is_checkable = is_checkable ? 1 : 0;
|
|
}
|
|
|
|
uint8_t lui_button_get_checkable(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return 0;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
return btn->is_checkable;
|
|
}
|
|
|
|
uint8_t lui_button_get_check_value(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return 0;
|
|
return obj->value;
|
|
}
|
|
|
|
void lui_button_set_extra_colors(lui_obj_t* obj, uint16_t pressed_color, uint16_t selection_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (btn->style.pressed_color == pressed_color && btn->style.selection_color == selection_color)
|
|
return;
|
|
btn->style.pressed_color = pressed_color;
|
|
btn->style.selection_color = selection_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_button_set_bg_transparent(lui_obj_t* obj, uint8_t is_transparent)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BUTTON) < 0)
|
|
return;
|
|
|
|
lui_button_t* btn = (lui_button_t* )(obj->obj_main_data);
|
|
if (btn->style.is_transparent_bg == is_transparent)
|
|
return;
|
|
btn->style.is_transparent_bg = is_transparent;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_LIST related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_LIST)
|
|
|
|
void lui_list_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
|
|
if (list->is_dropdown && !list->is_expanded)
|
|
return;
|
|
|
|
/* Draw bg to clear old elements */
|
|
lui_gfx_draw_rect_fill(
|
|
obj->x,
|
|
obj->y,
|
|
obj->common_style.width,
|
|
obj->common_style.height,
|
|
obj->common_style.bg_color);
|
|
|
|
uint8_t lim = list->page_first_item_index + list->items_per_page;
|
|
for (uint8_t i = list->page_first_item_index; i < lim; i++)
|
|
{
|
|
if (i == list->items_cnt)
|
|
break;
|
|
// uint16_t item_bg_color = obj->common_style.bg_color;
|
|
// if (obj->state == LUI_STATE_SELECTED)
|
|
// btn_color = btn->style.selection_color;
|
|
// else if (obj->state == LUI_STATE_PRESSED)
|
|
// btn_color = btn->style.pressed_color;
|
|
// else if (btn->state == LUI_STATE_IDLE)
|
|
// btn_color = btn->color;
|
|
|
|
/* Convert local coordinates of item to global coordinates */
|
|
uint16_t x = obj->x + list->items[i]->area.x1;
|
|
uint16_t y = obj->y + list->items[i]->area.y1;
|
|
uint16_t w = list->items[i]->area.x2 - list->items[i]->area.x1 + 1;
|
|
uint16_t h = list->items[i]->area.y2 - list->items[i]->area.y1 + 1;
|
|
|
|
// TODO: Make pressed color changeable
|
|
if (i == list->selected_item_index)
|
|
lui_gfx_draw_rect_fill(x, y, w, h, LUI_STYLE_LIST_ITEM_PRESSED_COLOR);
|
|
|
|
if (list->style.item_has_border == 1)
|
|
lui_gfx_draw_rect(x, y, w, h+1, 1, list->style.item_border_color);
|
|
|
|
// Draw the text
|
|
uint16_t dim[2];
|
|
lui_gfx_get_string_dimension(list->items[i]->text, list->font, w, dim);
|
|
|
|
dim[0] = dim[0] > w ? w : dim[0];
|
|
dim[1] = dim[1] > h ? h : dim[1];
|
|
|
|
y = y + ((h - dim[1]) / 2);
|
|
uint8_t padding = 2;
|
|
if (list->text_align == LUI_ALIGN_CENTER)
|
|
x = x + (w - dim[0]) / 2;
|
|
else if (list->text_align == LUI_ALIGN_RIGHT)
|
|
x = x + (w - dim[0]) - padding;
|
|
else
|
|
x = x + padding;
|
|
|
|
lui_area_t lst_item_area = {
|
|
.x = x,
|
|
.y = y,
|
|
.w = dim[0],
|
|
.h = dim[1]
|
|
};
|
|
|
|
lui_gfx_draw_string_advanced(
|
|
list->items[i]->text,
|
|
&lst_item_area,
|
|
list->style.item_label_color,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
list->font);
|
|
}
|
|
if (obj->common_style.border_width)
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, obj->common_style.width,
|
|
obj->common_style.height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
|
|
}
|
|
|
|
lui_obj_t* lui_list_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_list_t* initial_list = (lui_list_t* )_lui_mem_alloc(sizeof(*initial_list));
|
|
if (initial_list == NULL)
|
|
return NULL;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_LIST;
|
|
obj->obj_main_data = (void* )initial_list;
|
|
obj->enabled = 1;
|
|
initial_list->page_count = 0;
|
|
initial_list->current_page_index = 0;
|
|
initial_list->selected_item_index = 0; // unused
|
|
initial_list->items_per_page = 0;
|
|
initial_list->item_min_height = 0;
|
|
initial_list->font = g_lui_main->default_font;
|
|
initial_list->text_align = LUI_ALIGN_LEFT;
|
|
initial_list->is_dropdown = 0;
|
|
initial_list->is_expanded = 1;
|
|
initial_list->max_items = 0;
|
|
initial_list->items_cnt = 0;
|
|
initial_list->items = NULL;
|
|
|
|
initial_list->style.item_has_border = LUI_STYLE_LIST_ITEM_BORDER_THICKNESS;
|
|
initial_list->style.item_label_color = LUI_STYLE_LIST_ITEM_LABEL_COLOR;
|
|
initial_list->style.item_border_color = LUI_STYLE_LIST_ITEM_BORDER_COLOR;
|
|
|
|
// set common styles for list object
|
|
obj->common_style.bg_color = LUI_STYLE_LIST_ITEM_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_LIST_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_LIST_BORDER_THICKNESS;
|
|
obj->common_style.height = LUI_STYLE_LIST_HEIGHT;
|
|
obj->common_style.width = LUI_STYLE_LIST_WIDTH;
|
|
|
|
// create navigation buttons
|
|
_lui_list_add_nav_buttons(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_list_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(list, obj_parent)
|
|
}
|
|
|
|
void lui_list_prepare(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
// list has no item (button), so return
|
|
// if (obj->children_count == 0)
|
|
// return;
|
|
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
|
|
uint16_t item_h_padding = 10;
|
|
/* usable height depends on navigation buttons' existence
|
|
if no nav button (for single page list), total height is usable */
|
|
if (list->item_min_height < list->font->bitmap->size_y)
|
|
list->item_min_height = list->font->bitmap->size_y;
|
|
uint16_t list_usable_height = obj->common_style.height;
|
|
uint8_t item_height = list->item_min_height + item_h_padding;
|
|
if (list->is_dropdown)
|
|
list_usable_height = list_usable_height - item_height; // 1 row is used by `expand` button. So, subtract it
|
|
|
|
/* buttons_per_page is calculated excluding the nav buttons. Only items are considered */
|
|
list->items_per_page = list_usable_height / item_height;
|
|
/* when item per page is higher than total item count, we need more than 1 page
|
|
whenever more than 1 page is needed, nav buttons are added.
|
|
so, we calculate the usable_height and recalculate items per page accordingly.
|
|
*/
|
|
if (list->items_per_page < list->items_cnt)
|
|
{
|
|
list_usable_height = list_usable_height - item_height; // leaving some space for < and > nav buttons (they're in same row)
|
|
list->items_per_page -= 1;
|
|
}
|
|
// if items per page is less than or equal to total items, we need only 1 page
|
|
// so, set item heights in such way that they fill the entire height
|
|
else
|
|
{
|
|
list->items_per_page = list->items_cnt;
|
|
}
|
|
// item_height = list_usable_height / list->items_per_page;
|
|
|
|
// fast way to round up a/b: (a+b-1)/b
|
|
list->page_count = (list->items_cnt + list->items_per_page - 1) / list->items_per_page;
|
|
list->current_page_index = 0;
|
|
list->page_first_item_index = (list->current_page_index * list->items_per_page);
|
|
|
|
// set x, y coordinates and height, width of all the list items.
|
|
// NOTE: Item (x, y) coordinates are local coordinates with respect to the List
|
|
uint8_t item_pos = 0;
|
|
uint16_t item_start_y = 0;
|
|
if (list->is_dropdown)
|
|
item_start_y += item_height;
|
|
for (uint8_t i = 0; i < list->items_cnt; i++)
|
|
{
|
|
struct _lui_list_item* item = list->items[i];
|
|
item->area.x1 = 0;
|
|
// starting offset is set by item_pos and btn_per_page. Taking remainder of item_pos/btn_per_page so that
|
|
// every time item_pos becomes big enough to come to next page, the offset becomes 0
|
|
item->area.y1 = item_start_y + ((item_pos % list->items_per_page) * item_height);
|
|
item->area.x2 = item->area.x1 + obj->common_style.width - 1;
|
|
item->area.y2 = item->area.y1 + item_height - 1;
|
|
item_pos++;
|
|
}
|
|
|
|
|
|
// navigation button x,y, w, h set.
|
|
lui_obj_t* nav_prev = obj->first_child;
|
|
lui_obj_t* nav_nxt = nav_prev->next_sibling;
|
|
lui_obj_t* nav_expand = nav_nxt->next_sibling;
|
|
lui_obj_t* nav_text = nav_expand->next_sibling;
|
|
|
|
nav_nxt->x = obj->x + (obj->common_style.width / 2) + 0; // index 1 = nav button nxt
|
|
nav_prev->x = obj->x + 0; // index 0 = nav button prev
|
|
nav_nxt->y = nav_prev->y = obj->y + item_start_y + list_usable_height;
|
|
nav_nxt->common_style.width = nav_prev->common_style.width = (obj->common_style.width / 2) - 0; // 4 is just margin
|
|
nav_nxt->common_style.height = nav_prev->common_style.height = item_height;
|
|
((lui_button_t* )nav_nxt->obj_main_data)->label.font = list->font;
|
|
((lui_button_t* )nav_prev->obj_main_data)->label.font = list->font;
|
|
|
|
nav_text->x = obj->x;
|
|
nav_text->y = obj->y;
|
|
nav_text->common_style.width = obj->common_style.width - (list->font->bitmap->size_y + 4);
|
|
nav_text->common_style.height = item_height;
|
|
nav_expand->x = obj->x + nav_text->common_style.width;
|
|
nav_expand->y = obj->y;
|
|
nav_expand->common_style.width = list->font->bitmap->size_y + 4; //obj->common_style.width - nav_text->common_style.width;
|
|
nav_expand->common_style.height = item_height;
|
|
((lui_button_t* )nav_expand->obj_main_data)->label.font = list->font;
|
|
((lui_button_t* )nav_text->obj_main_data)->label.font = list->font;
|
|
lui_button_set_label_text(nav_text, list->items[list->selected_item_index]->text);
|
|
|
|
// both nav buttons won't be rendered unless the list is multi page
|
|
// if list page count is more than 0, draw the nav button
|
|
nav_nxt->visible = 0;
|
|
nav_prev->visible = 0;
|
|
if (list->page_count > 0 && (list->is_expanded || !list->is_dropdown))
|
|
{
|
|
// draw "next" or "prev" button only if there's a next or previous page
|
|
if (list->current_page_index + 1 < list->page_count)
|
|
nav_nxt->visible = 1;
|
|
if (list->current_page_index - 1 >= 0)
|
|
nav_prev->visible = 1;
|
|
}
|
|
|
|
if (list->is_dropdown)
|
|
nav_expand->visible = nav_text->visible = 1;
|
|
else
|
|
nav_expand->visible = nav_text->visible = 0;
|
|
|
|
// whenever prepare function is called, list (and all children of it) will be redrawn
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
int8_t lui_list_set_max_items_count(lui_obj_t* obj, uint8_t max_items_cnt)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (list->items != NULL)
|
|
return -1;
|
|
|
|
list->items = (struct _lui_list_item**)_lui_mem_alloc(sizeof(*(list->items)) * max_items_cnt);
|
|
if (list->items == NULL)
|
|
return -1;
|
|
list->max_items = max_items_cnt;
|
|
return 0;
|
|
|
|
}
|
|
|
|
int8_t lui_list_add_item(lui_obj_t* obj, const char* text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (list->items_cnt == list->max_items)
|
|
return -1;
|
|
|
|
struct _lui_list_item* item = (struct _lui_list_item* )_lui_mem_alloc(sizeof(*item));
|
|
if (item == NULL)
|
|
return -1;
|
|
|
|
item->text = (char*)text;
|
|
list->items[list->items_cnt] = item;
|
|
list->items_cnt++;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t lui_list_remove_item(lui_obj_t* obj, uint8_t item_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
|
|
if (item_index >= list->items_cnt)
|
|
return -1;
|
|
|
|
for (uint16_t i = item_index; i < list->items_cnt - 1; i++)
|
|
list->items[item_index] = list->items[item_index + 1];
|
|
|
|
list->items_cnt--;
|
|
_lui_object_set_need_refresh(obj);
|
|
return 0;
|
|
}
|
|
|
|
int8_t lui_list_remove_all(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
list->items_cnt = 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
return 0;
|
|
}
|
|
|
|
int16_t lui_list_get_selected_item_index(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
return ((lui_list_t* )(obj->obj_main_data))->selected_item_index;
|
|
}
|
|
|
|
int16_t lui_list_set_selected_item_index(lui_obj_t* obj, uint8_t item_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (item_index >= list->items_cnt)
|
|
return -1;
|
|
|
|
list->selected_item_index = item_index;
|
|
return 0;
|
|
/* Must call list_prepare() after calling this */
|
|
}
|
|
|
|
const char* lui_list_get_item_text(lui_obj_t* obj, uint8_t item_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return NULL;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (item_index >= list->items_cnt)
|
|
return NULL;
|
|
return list->items[item_index]->text;
|
|
}
|
|
|
|
int8_t lui_list_set_item_text(lui_obj_t* obj, const char* text, uint8_t item_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (item_index >= list->items_cnt)
|
|
return -1;
|
|
|
|
list->items[item_index]->text = text;
|
|
return 0;
|
|
|
|
/* Must call list_prepare() after calling this */
|
|
}
|
|
|
|
int8_t lui_list_set_dropdown_mode(lui_obj_t* obj, uint8_t is_dropdown)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (list->is_dropdown == is_dropdown)
|
|
return -1;
|
|
list->is_dropdown = is_dropdown;
|
|
if (is_dropdown)
|
|
{
|
|
list->is_expanded = 0;
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
else
|
|
{
|
|
list->is_expanded = 1;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
return 0;
|
|
|
|
/* Must call list_prepare() after calling this */
|
|
}
|
|
|
|
int8_t lui_list_set_dropdown_expand(lui_obj_t* obj, uint8_t is_expanded)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return -1;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
if (!list->is_dropdown)
|
|
return -1;
|
|
if (list->is_expanded == is_expanded)
|
|
return -1;
|
|
list->is_expanded = is_expanded;
|
|
if (is_expanded)
|
|
{
|
|
obj->value = obj->layer; // Hack: Storing layer data temporarily in value property
|
|
lui_object_set_layer(obj, LUI_LAYER_SYSTEM);
|
|
}
|
|
else
|
|
{
|
|
lui_object_set_layer(obj, obj->value);
|
|
}
|
|
return 0;
|
|
|
|
/* Must call list_prepare() after calling this */
|
|
}
|
|
|
|
void lui_list_set_item_min_height(lui_obj_t* obj, uint8_t height)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
|
|
if (height >= list->font->bitmap->size_y)
|
|
{
|
|
list->item_min_height = height;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
// this min_height is applied to all items in `prepare` func
|
|
}
|
|
|
|
void lui_list_set_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
if (font == NULL)
|
|
return;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
list->font = font;
|
|
|
|
// parent needs refresh (along with all its children)
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
|
|
// this font is set to all items in `prepare` func
|
|
}
|
|
|
|
void lui_list_set_text_align(lui_obj_t *obj, uint8_t alignment)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
if (list->text_align != alignment)
|
|
{
|
|
list->text_align = alignment;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
// this alignment is set to all items in `prepare` func
|
|
}
|
|
|
|
void lui_list_set_text_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
if (list->style.item_label_color != color)
|
|
{
|
|
list->style.item_label_color = color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
}
|
|
|
|
void lui_list_set_item_border(lui_obj_t* obj, uint8_t has_border, uint16_t border_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
if (list->style.item_has_border != has_border ||
|
|
list->style.item_border_color != border_color)
|
|
{
|
|
list->style.item_has_border = has_border;
|
|
list->style.item_border_color = border_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
}
|
|
|
|
void lui_list_set_nav_btn_label_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_button_set_label_color(obj->first_child, color);
|
|
lui_button_set_label_color(obj->first_child->next_sibling, color);
|
|
}
|
|
|
|
void lui_list_set_nav_btn_bg_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_object_set_bg_color(obj->first_child, color);
|
|
lui_object_set_bg_color(obj->first_child->next_sibling, color);
|
|
}
|
|
|
|
void lui_list_set_nav_btn_extra_colors(lui_obj_t* obj, uint16_t pressed_color, uint16_t selection_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_button_set_extra_colors(obj->first_child, pressed_color, selection_color);
|
|
lui_button_set_extra_colors(obj->first_child->next_sibling, pressed_color, selection_color);
|
|
}
|
|
|
|
void lui_list_set_nav_btn_label_text(lui_obj_t* obj, const char* btn_prev_text, const char* btn_nxt_text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_button_set_label_text(obj->first_child, btn_prev_text);
|
|
lui_button_set_label_text(obj->first_child->next_sibling, btn_nxt_text);
|
|
}
|
|
|
|
void lui_list_set_nav_btn_border_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_object_set_border_color(obj->first_child, color);
|
|
lui_object_set_border_color(obj->first_child->next_sibling, color);
|
|
}
|
|
|
|
void lui_list_set_page_index(lui_obj_t* obj, uint8_t index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_LIST) < 0)
|
|
return;
|
|
|
|
lui_list_t* list = (lui_list_t* )obj->obj_main_data;
|
|
|
|
if (index >= list->page_count || index == list->current_page_index)
|
|
return;
|
|
list->current_page_index = index;
|
|
_lui_object_set_need_refresh(obj);
|
|
|
|
list->page_first_item_index = (list->current_page_index * list->items_per_page);
|
|
|
|
// navigation button x,y, w, h set.
|
|
lui_obj_t* nav_prev = obj->first_child;
|
|
lui_obj_t* nav_nxt = nav_prev->next_sibling;
|
|
// if list page count is more than 0, draw the nav button
|
|
lui_object_set_visibility(nav_nxt , 0);
|
|
lui_object_set_visibility(nav_prev , 0);
|
|
if (list->page_count > 0 && (list->is_expanded || !list->is_dropdown))
|
|
{
|
|
// draw "next" or "prev" button only if there's a next or previous page
|
|
if (list->current_page_index + 1 < list->page_count)
|
|
lui_object_set_visibility(nav_nxt , 1);
|
|
if (list->current_page_index - 1 >= 0)
|
|
lui_object_set_visibility(nav_prev , 1);
|
|
|
|
}
|
|
}
|
|
|
|
void _lui_list_add_button_obj(lui_obj_t* obj_list, lui_obj_t* obj_btn)
|
|
{
|
|
if (obj_list->first_child == NULL)
|
|
{
|
|
obj_list->first_child = obj_btn;
|
|
}
|
|
else
|
|
{
|
|
lui_obj_t* next_child = obj_list->first_child;
|
|
|
|
while (next_child->next_sibling != NULL)
|
|
{
|
|
next_child = next_child->next_sibling;
|
|
}
|
|
|
|
next_child->next_sibling = obj_btn;
|
|
}
|
|
|
|
// Common things to do
|
|
obj_btn->parent = obj_list;
|
|
obj_list->children_count++;
|
|
_lui_object_set_need_refresh(obj_btn->parent);
|
|
}
|
|
|
|
void _lui_list_add_nav_buttons(lui_obj_t* obj)
|
|
{
|
|
// create navigation buttons
|
|
lui_obj_t* obj_nav_btn_prev = lui_button_create();
|
|
lui_obj_t* obj_nav_btn_nxt = lui_button_create();
|
|
lui_button_set_label_text(obj_nav_btn_prev, LUI_ICON_CARET_BACK);
|
|
lui_button_set_label_text( obj_nav_btn_nxt, LUI_ICON_CARET_FORWARD);
|
|
// set nav button styles (nav button height and width are calculated by `prepare` function)
|
|
lui_button_set_label_color(obj_nav_btn_prev, LUI_STYLE_LIST_NAV_LABEL_COLOR);
|
|
lui_button_set_label_color(obj_nav_btn_nxt, LUI_STYLE_LIST_NAV_LABEL_COLOR);
|
|
lui_button_set_extra_colors(obj_nav_btn_prev, LUI_STYLE_LIST_NAV_PRESSED_COLOR, LUI_STYLE_LIST_NAV_SELECTION_COLOR);
|
|
lui_button_set_extra_colors(obj_nav_btn_nxt, LUI_STYLE_LIST_NAV_PRESSED_COLOR, LUI_STYLE_LIST_NAV_SELECTION_COLOR);
|
|
obj_nav_btn_nxt->common_style.border_width = obj_nav_btn_prev->common_style.border_width = 0;
|
|
obj_nav_btn_nxt->common_style.border_color = obj_nav_btn_prev->common_style.border_color = LUI_STYLE_LIST_ITEM_BORDER_COLOR;
|
|
obj_nav_btn_nxt->common_style.bg_color = obj_nav_btn_prev->common_style.bg_color = LUI_STYLE_LIST_NAV_BG_COLOR;
|
|
obj_nav_btn_prev->obj_event_cb = obj_nav_btn_nxt->obj_event_cb = _lui_list_nav_btn_cb;
|
|
|
|
_lui_list_add_button_obj(obj, obj_nav_btn_prev);
|
|
_lui_list_add_button_obj(obj, obj_nav_btn_nxt);
|
|
|
|
/* For handling `dropdown` functionalities */
|
|
/* `nav_btn_expand` button is the dropdown arrow,
|
|
`nav_btn_text` button is the selected item text button
|
|
Clicking on either of them will expand/contract the list
|
|
*/
|
|
lui_obj_t* obj_nav_btn_expand = lui_button_create();
|
|
lui_obj_t* obj_nav_btn_text = lui_button_create();
|
|
lui_button_set_label_text(obj_nav_btn_expand, LUI_ICON_CARET_DOWN);
|
|
lui_button_set_label_color(obj_nav_btn_expand, LUI_STYLE_LIST_NAV_LABEL_COLOR);
|
|
lui_button_set_label_color(obj_nav_btn_text, LUI_STYLE_LIST_ITEM_LABEL_COLOR);
|
|
lui_button_set_extra_colors(obj_nav_btn_expand, LUI_STYLE_LIST_NAV_PRESSED_COLOR, LUI_STYLE_LIST_NAV_SELECTION_COLOR);
|
|
lui_button_set_extra_colors(obj_nav_btn_text, LUI_STYLE_LIST_NAV_PRESSED_COLOR, LUI_STYLE_LIST_NAV_SELECTION_COLOR);
|
|
lui_button_set_label_align(obj_nav_btn_text, LUI_ALIGN_LEFT);
|
|
obj_nav_btn_expand->common_style.border_width = 0;
|
|
obj_nav_btn_text->common_style.border_width = 1;
|
|
obj_nav_btn_text->common_style.border_color = LUI_STYLE_LIST_BORDER_COLOR;
|
|
obj_nav_btn_expand->common_style.bg_color = LUI_STYLE_LIST_NAV_BG_COLOR;
|
|
obj_nav_btn_text->common_style.bg_color = LUI_STYLE_LIST_ITEM_BG_COLOR;
|
|
obj_nav_btn_expand->obj_event_cb = obj_nav_btn_text->obj_event_cb = _lui_list_nav_btn_cb;
|
|
_lui_list_add_button_obj(obj, obj_nav_btn_expand);
|
|
_lui_list_add_button_obj(obj, obj_nav_btn_text);
|
|
|
|
}
|
|
|
|
void _lui_list_nav_btn_cb(lui_obj_t* obj_nav_btn)
|
|
{
|
|
uint8_t event = lui_object_get_event(obj_nav_btn);
|
|
lui_list_t* list = (lui_list_t* )obj_nav_btn->parent->obj_main_data;
|
|
lui_obj_t* prev_btn = obj_nav_btn->parent->first_child;
|
|
lui_obj_t* next_btn = prev_btn->next_sibling;
|
|
lui_obj_t* expand_btn = next_btn->next_sibling;
|
|
lui_obj_t* expand_btn2 = expand_btn->next_sibling;
|
|
uint8_t index = list->current_page_index;
|
|
|
|
if (event == LUI_EVENT_PRESSED)
|
|
{
|
|
/* first child is nav_prev btn */
|
|
if (obj_nav_btn == prev_btn && list->current_page_index > 0)
|
|
{
|
|
index--;
|
|
lui_list_set_page_index(obj_nav_btn->parent, index);
|
|
}
|
|
/* 2nd child is nav_nxt button */
|
|
else if (obj_nav_btn == next_btn && list->current_page_index < list->page_count - 1)
|
|
{
|
|
index++;
|
|
lui_list_set_page_index(obj_nav_btn->parent, index);
|
|
}
|
|
else if (obj_nav_btn == expand_btn || obj_nav_btn == expand_btn2)
|
|
{
|
|
list->is_expanded = !list->is_expanded;
|
|
lui_object_set_visibility(next_btn , 0);
|
|
lui_object_set_visibility(prev_btn , 0);
|
|
if (list->page_count > 0 && (list->is_expanded || !list->is_dropdown))
|
|
{
|
|
// draw "next" or "prev" button only if there's a next or previous page
|
|
if (list->current_page_index + 1 < list->page_count)
|
|
lui_object_set_visibility(next_btn, 1);
|
|
if (list->current_page_index - 1 >= 0)
|
|
lui_object_set_visibility(prev_btn, 1);
|
|
}
|
|
if (list->is_expanded)
|
|
{
|
|
lui_button_set_label_text(expand_btn, LUI_ICON_CARET_UP);
|
|
obj_nav_btn->parent->value = obj_nav_btn->parent->layer; // Hack: Using value property to store the layer data temporarily
|
|
lui_object_set_layer(obj_nav_btn->parent, LUI_LAYER_SYSTEM);
|
|
}
|
|
else
|
|
{
|
|
lui_button_set_label_text(expand_btn, LUI_ICON_CARET_DOWN);
|
|
lui_object_set_layer(obj_nav_btn->parent, obj_nav_btn->value);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_SWITCH related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_SWITCH)
|
|
|
|
void lui_switch_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SWITCH) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_switch_t* swtch = (lui_switch_t* )(obj->obj_main_data);
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
uint16_t temp_x = obj->x;
|
|
uint16_t temp_y = obj->y;
|
|
uint16_t temp_height = obj->common_style.height;
|
|
uint16_t temp_width = obj->common_style.width;
|
|
|
|
uint16_t swtch_color;
|
|
if (obj->value == 1)
|
|
{
|
|
swtch_color = swtch->style.knob_on_color;
|
|
}
|
|
else
|
|
{
|
|
swtch_color = swtch->style.knob_off_color;
|
|
}
|
|
if (obj->state == LUI_STATE_SELECTED || obj->state == LUI_STATE_PRESSED)
|
|
{
|
|
swtch_color = swtch->style.selection_color;
|
|
}
|
|
|
|
|
|
lui_gfx_draw_rect_fill(temp_x, temp_y, temp_width, temp_height, obj->common_style.bg_color); // switch bg (color is constant regardless the state)
|
|
if (obj->common_style.border_width)
|
|
lui_gfx_draw_rect(
|
|
temp_x, temp_y, temp_width, temp_height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color); // switch border
|
|
|
|
temp_width = (float)temp_width * 0.3;
|
|
temp_height = (float)temp_height * 0.6;
|
|
temp_x = temp_x + ((obj->common_style.width / 2) - temp_width) / 2;
|
|
if (obj->value == 1)
|
|
temp_x += (obj->common_style.width / 2);
|
|
temp_y = temp_y + (obj->common_style.height - temp_height) / 2;
|
|
|
|
lui_gfx_draw_rect_fill(temp_x, temp_y, temp_width, temp_height, swtch_color);// switch slider
|
|
}
|
|
|
|
lui_obj_t* lui_switch_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
|
|
lui_switch_t* initial_switch = (lui_switch_t* )_lui_mem_alloc(sizeof(*initial_switch));
|
|
if (initial_switch == NULL)
|
|
return NULL;
|
|
|
|
initial_switch->style.knob_off_color = LUI_STYLE_SWITCH_KNOB_OFF_COLOR;
|
|
initial_switch->style.knob_on_color = LUI_STYLE_SWITCH_KNOB_ON_COLOR;
|
|
initial_switch->style.selection_color = LUI_STYLE_SWITCH_SELECTION_COLOR;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_SWITCH;
|
|
obj->enabled = 1;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_SWITCH_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_SWITCH_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_SWITCH_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_SWITCH_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_SWITCH_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_switch;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_switch_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(switch, obj_parent)
|
|
}
|
|
|
|
void lui_switch_set_extra_colors(lui_obj_t* obj, uint16_t knob_off_color, uint16_t knob_on_color, uint16_t selection_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SWITCH) < 0)
|
|
return;
|
|
|
|
lui_switch_t* swtch = (lui_switch_t* )(obj->obj_main_data);
|
|
|
|
if (swtch->style.knob_off_color == knob_off_color && swtch->style.knob_on_color == knob_on_color && swtch->style.selection_color == selection_color)
|
|
return;
|
|
swtch->style.knob_off_color = knob_off_color;
|
|
swtch->style.knob_on_color = knob_on_color;
|
|
swtch->style.selection_color = selection_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
int8_t lui_switch_get_value(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SWITCH) < 0)
|
|
return -1;
|
|
|
|
return obj->value;
|
|
}
|
|
|
|
void lui_switch_set_value(lui_obj_t* obj, uint8_t value)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SWITCH) < 0)
|
|
return;
|
|
|
|
if (obj->value == value)
|
|
return;
|
|
obj->value = value ? 1 : 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_switch_set_on(lui_obj_t* obj)
|
|
{
|
|
lui_switch_set_value(obj, 1);
|
|
}
|
|
|
|
void lui_switch_set_off(lui_obj_t* obj)
|
|
{
|
|
lui_switch_set_value(obj, 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_CHECKBOX related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_CHECKBOX)
|
|
|
|
void lui_checkbox_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_checkbox_t* chkbox = (lui_checkbox_t* )obj->obj_main_data;
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
uint16_t bg_color;
|
|
if (obj->value == 1)
|
|
{
|
|
bg_color = chkbox->style.bg_checked_color;
|
|
}
|
|
else
|
|
{
|
|
bg_color = obj->common_style.bg_color;
|
|
}
|
|
if (obj->state == LUI_STATE_SELECTED || obj->state == LUI_STATE_PRESSED)
|
|
{
|
|
bg_color = chkbox->style.selection_color;
|
|
}
|
|
uint16_t side = obj->common_style.height;
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, side, side, bg_color);
|
|
// draw the tick mark if needed
|
|
if (obj->value == 1)
|
|
{
|
|
uint16_t point_1_x = obj->x + (side * .2), point_1_y = obj->y + (side * .55);
|
|
uint16_t point_2_x = obj->x + (side* .4), point_2_y = obj->y + (side * .75);
|
|
uint16_t point_3_x = obj->x + (side * .75), point_3_y = obj->y + (side * .3);
|
|
|
|
lui_gfx_draw_line(point_1_x, point_1_y, point_2_x, point_2_y, 2, chkbox->style.tick_color);
|
|
lui_gfx_draw_line(point_2_x, point_2_y, point_3_x, point_3_y, 2, chkbox->style.tick_color);
|
|
}
|
|
|
|
// draw the border if needed
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, side, side,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
|
|
/* Draw label if any */
|
|
if (chkbox->label.text)
|
|
{
|
|
uint16_t lbl_bg_color = ~(chkbox->label.style.text_color); // using inverted text color as bg
|
|
uint16_t dim[2] = {0, 0}; // x, y
|
|
lui_gfx_get_string_dimension(chkbox->label.text, chkbox->label.font, g_lui_main->disp_drv->display_hor_res - obj->x + side + 2, dim);
|
|
lui_area_t chkbx_txt_area = {
|
|
.x = obj->x + side + 2,
|
|
.y = obj->y + 1,
|
|
.w = dim[0],
|
|
.h = dim[1]
|
|
};
|
|
|
|
lui_area_t bitmap_crop_area = {
|
|
.x = chkbx_txt_area.x - obj->parent->x,
|
|
.y = chkbx_txt_area.y - obj->parent->y,
|
|
.w = chkbx_txt_area.w,
|
|
.h = chkbx_txt_area.h
|
|
};
|
|
const lui_bitmap_t* bg_img = NULL;
|
|
lui_bitmap_mono_pal_t* mono_palette = NULL;
|
|
if (obj->parent)
|
|
{
|
|
lbl_bg_color = obj->parent->common_style.bg_color;
|
|
/* As panel and scene both have same first elements in the struct,
|
|
* we can use panel even for scene
|
|
*/
|
|
lui_panel_t* panel = (lui_panel_t*)(obj->parent->obj_main_data);
|
|
bg_img = panel->bg_image;
|
|
mono_palette = &panel->img_pal;
|
|
}
|
|
lui_gfx_draw_string_advanced(
|
|
chkbox->label.text,
|
|
&chkbx_txt_area,
|
|
chkbox->label.style.text_color,
|
|
lbl_bg_color,
|
|
bg_img,
|
|
mono_palette,
|
|
&bitmap_crop_area,
|
|
1,
|
|
chkbox->label.font);
|
|
}
|
|
}
|
|
|
|
lui_obj_t* lui_checkbox_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
|
|
lui_checkbox_t* initial_chkbox = (lui_checkbox_t* )_lui_mem_alloc(sizeof(*initial_chkbox));
|
|
if (initial_chkbox == NULL)
|
|
return NULL;
|
|
|
|
initial_chkbox->style.bg_checked_color = LUI_STYLE_CHECKBOX_BG_CHECKED_COLOR;
|
|
initial_chkbox->style.selection_color = LUI_STYLE_CHECKBOX_SELECTION_COLOR;
|
|
initial_chkbox->style.tick_color = LUI_STYLE_CHECKBOX_TICK_COLOR;
|
|
initial_chkbox->label.font = g_lui_main->default_font;
|
|
initial_chkbox->label.text = NULL;
|
|
initial_chkbox->label.style.text_color = LUI_STYLE_CHECKBOX_LABEL_COLOR;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_CHECKBOX;
|
|
obj->enabled = 1;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_CHECKBOX_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_CHECKBOX_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_CHECKBOX_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_CHECKBOX_WIDTH; // Not needed for now. Still present.
|
|
obj->common_style.height = g_lui_main->default_font->bitmap->size_y > LUI_STYLE_CHECKBOX_HEIGHT ? g_lui_main->default_font->bitmap->size_y : LUI_STYLE_CHECKBOX_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_chkbox;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_checkbox_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(checkbox, obj_parent)
|
|
}
|
|
|
|
void lui_checkbox_set_extra_colors(lui_obj_t* obj, uint16_t bg_checked_color, uint16_t tick_color, uint16_t selection_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return;
|
|
|
|
lui_checkbox_t* chkbox = (lui_checkbox_t* )obj->obj_main_data;
|
|
|
|
if (chkbox->style.bg_checked_color == bg_checked_color && chkbox->style.tick_color == tick_color && chkbox->style.selection_color == selection_color)
|
|
return;
|
|
chkbox->style.bg_checked_color = bg_checked_color;
|
|
chkbox->style.tick_color = tick_color;
|
|
chkbox->style.selection_color = selection_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
int8_t lui_checkbox_get_value(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return -1;
|
|
|
|
return obj->value;
|
|
}
|
|
|
|
void lui_checkbox_set_value(lui_obj_t* obj, uint8_t value)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return;
|
|
|
|
if (obj->value == value)
|
|
return;
|
|
obj->value = value ? 1 : 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_checkbox_set_checked(lui_obj_t* obj)
|
|
{
|
|
lui_checkbox_set_value(obj, 1);
|
|
}
|
|
|
|
void lui_checkbox_set_unchecked(lui_obj_t* obj)
|
|
{
|
|
lui_checkbox_set_value(obj, 0);
|
|
}
|
|
|
|
void lui_checkbox_set_label_text(lui_obj_t* obj, const char* text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return;
|
|
|
|
((lui_checkbox_t* )obj->obj_main_data)->label.text = (char*)text;
|
|
uint16_t dim[2];
|
|
lui_gfx_get_string_dimension(
|
|
text,
|
|
((lui_checkbox_t* )obj->obj_main_data)->label.font,
|
|
g_lui_main->disp_drv->display_hor_res - obj->x,
|
|
dim);
|
|
obj->common_style.width = dim[0] + obj->common_style.height;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_checkbox_set_label_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return;
|
|
|
|
((lui_checkbox_t* )obj->obj_main_data)->label.font = font;
|
|
uint16_t dim[2];
|
|
lui_gfx_get_string_dimension(
|
|
((lui_checkbox_t* )obj->obj_main_data)->label.text,
|
|
font,
|
|
g_lui_main->disp_drv->display_hor_res - obj->x,
|
|
dim);
|
|
obj->common_style.height = font->bitmap->size_y > LUI_STYLE_CHECKBOX_HEIGHT ? font->bitmap->size_y : LUI_STYLE_CHECKBOX_HEIGHT;
|
|
obj->common_style.width = dim[0] + obj->common_style.height;
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
|
|
void lui_checkbox_set_label_color(lui_obj_t* obj, uint16_t color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_CHECKBOX) < 0)
|
|
return;
|
|
if (((lui_checkbox_t* )obj->obj_main_data)->label.style.text_color == color)
|
|
return;
|
|
|
|
((lui_checkbox_t* )obj->obj_main_data)->label.style.text_color = color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_SLIDER related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_SLIDER)
|
|
|
|
void lui_slider_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
uint16_t knob_color = slider->style.knob_color;
|
|
if (obj->state == LUI_STATE_SELECTED || obj->state == LUI_STATE_PRESSED)
|
|
{
|
|
knob_color = slider->style.selection_color;
|
|
}
|
|
|
|
uint8_t is_hor = obj->common_style.width >= obj->common_style.height;
|
|
/* For horizontal */
|
|
if (is_hor)
|
|
{
|
|
/* draw the filled region (left) first */
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, slider->knob_center_rel_d, obj->common_style.height, slider->style.bg_filled_color);
|
|
/* draw the remaining region (right) */
|
|
lui_gfx_draw_rect_fill(obj->x + slider->knob_center_rel_d, obj->y, obj->common_style.width - slider->knob_center_rel_d, obj->common_style.height, obj->common_style.bg_color);
|
|
}
|
|
/* For vertical */
|
|
else
|
|
{
|
|
/* draw the remaining region (top) first */
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, obj->common_style.width, slider->knob_center_rel_d, obj->common_style.bg_color);
|
|
/* draw the filled region (bottom) */
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y + slider->knob_center_rel_d, obj->common_style.width, obj->common_style.height - slider->knob_center_rel_d, slider->style.bg_filled_color);
|
|
}
|
|
|
|
// draw the knob
|
|
if (slider->knob_type == LUI_SLIDER_KNOB_TYPE_DEFAULT)
|
|
{
|
|
/* For horizontal */
|
|
if (is_hor)
|
|
lui_gfx_draw_rect_fill(obj->x + slider->knob_center_rel_d - (slider->style.knob_width / 2), obj->y, slider->style.knob_width, obj->common_style.height, knob_color);
|
|
/* For vertical */
|
|
else
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y + slider->knob_center_rel_d - (slider->style.knob_width / 2), obj->common_style.width, slider->style.knob_width, knob_color);
|
|
|
|
}
|
|
else if (slider->knob_type == LUI_SLIDER_KNOB_TYPE_TEXT)
|
|
{
|
|
if (slider->show_value || slider->custom_text)
|
|
{
|
|
char s[64];
|
|
if (slider->custom_text && slider->show_value)
|
|
snprintf(s, 64, "%d %s", obj->value, slider->custom_text);
|
|
else if (slider->show_value)
|
|
snprintf(s, 64, "%d", obj->value);
|
|
else
|
|
snprintf(s, 64, "%s", slider->custom_text);
|
|
uint16_t dim[2];
|
|
lui_gfx_get_string_dimension(s, slider->font, obj->common_style.width, dim);
|
|
uint16_t txt_x = 0, txt_y = 0;
|
|
if (slider->is_progress_bar)
|
|
{
|
|
txt_x = obj->x + (obj->common_style.width - dim[0])/2;
|
|
txt_y = obj->y + (obj->common_style.height - dim[1])/2;
|
|
}
|
|
else
|
|
{
|
|
if (is_hor)
|
|
{
|
|
txt_x = obj->x + slider->knob_center_rel_d;
|
|
if (dim[0] < slider->knob_center_rel_d)
|
|
txt_x = txt_x - dim[0];
|
|
}
|
|
else
|
|
{
|
|
txt_y = obj->y + slider->knob_center_rel_d;
|
|
if (dim[1] < slider->knob_center_rel_d)
|
|
txt_y = txt_y + dim[1];
|
|
}
|
|
}
|
|
|
|
lui_area_t txt_area = {
|
|
.x = txt_x,
|
|
.y = txt_y,
|
|
.w = dim[0],
|
|
.h = dim[1]
|
|
};
|
|
lui_gfx_draw_string_advanced(s, &txt_area, slider->style.knob_color, 0, NULL, NULL, NULL, 0, slider->font);
|
|
}
|
|
}
|
|
|
|
|
|
// draw the border if needed
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, obj->common_style.width,
|
|
obj->common_style.height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
}
|
|
|
|
lui_obj_t* lui_slider_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
|
|
lui_slider_t* initial_slider = (lui_slider_t* )_lui_mem_alloc(sizeof(*initial_slider));
|
|
if (initial_slider == NULL)
|
|
return NULL;
|
|
|
|
initial_slider->style.bg_filled_color = LUI_STYLE_SLIDER_BG_FILLED_COLOR;
|
|
initial_slider->style.knob_color = LUI_STYLE_SLIDER_KNOB_COLOR;
|
|
initial_slider->style.selection_color = LUI_STYLE_SLIDER_SELECTION_COLOR;
|
|
initial_slider->style.knob_width = LUI_STYLE_SLIDER_KNOB_WIDTH;
|
|
initial_slider->range_min = 0;
|
|
initial_slider->range_max = 100;
|
|
initial_slider->knob_center_rel_d = LUI_SLIDER_KNOB_TYPE_DEFAULT ? LUI_STYLE_SLIDER_KNOB_WIDTH / 2 : 0;
|
|
initial_slider->knob_type = LUI_SLIDER_KNOB_TYPE_DEFAULT;
|
|
initial_slider->font = g_lui_main->default_font;
|
|
initial_slider->custom_text = NULL;
|
|
initial_slider->show_value = 0;
|
|
initial_slider->is_progress_bar = 0;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_SLIDER;
|
|
obj->enabled = 1;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_SLIDER_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_SLIDER_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_SLIDER_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_SLIDER_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_SLIDER_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_slider;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_slider_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(slider, obj_parent)
|
|
}
|
|
|
|
void lui_slider_set_extra_colors(lui_obj_t* obj, uint16_t knob_color, uint16_t bg_filled_color, uint16_t selection_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
|
|
if (slider->style.knob_color == knob_color && slider->style.bg_filled_color == bg_filled_color && slider->style.selection_color == selection_color)
|
|
return;
|
|
slider->style.knob_color = knob_color;
|
|
slider->style.bg_filled_color = bg_filled_color;
|
|
slider->style.selection_color = selection_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_slider_set_show_value(lui_obj_t* obj, uint8_t show_val)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
if (slider->show_value == show_val)
|
|
return;
|
|
|
|
slider->show_value = show_val;
|
|
if (show_val)
|
|
{
|
|
lui_slider_set_knob_type(obj, LUI_SLIDER_KNOB_TYPE_TEXT);
|
|
return;
|
|
}
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_slider_set_value(lui_obj_t* obj, int16_t value)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
|
|
|
|
if (value == obj->value)
|
|
return;
|
|
|
|
if (value > slider->range_max)
|
|
{
|
|
obj->value = slider->range_max;
|
|
}
|
|
else if (value < slider->range_min)
|
|
{
|
|
obj->value = slider->range_min;
|
|
}
|
|
else
|
|
{
|
|
obj->value = value;
|
|
}
|
|
|
|
|
|
/* calculate knob's center x position relative to the slider, when value of slider is manually set by user (y is always same) */
|
|
uint16_t knob_w = slider->knob_type == LUI_SLIDER_KNOB_TYPE_DEFAULT ? slider->style.knob_width : 0;
|
|
if (obj->common_style.width > obj->common_style.height)
|
|
slider->knob_center_rel_d = _lui_map_range(obj->value, slider->range_max, slider->range_min, obj->common_style.width - (knob_w / 2), (knob_w / 2));
|
|
else
|
|
/* For vertical slider, relation between y-axis and value is inverse. So, we swap new max and min */
|
|
slider->knob_center_rel_d = _lui_map_range(obj->value, slider->range_max, slider->range_min, (knob_w / 2), obj->common_style.height - (knob_w / 2));
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_slider_set_range(lui_obj_t* obj, int16_t range_min, int16_t range_max)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
|
|
if (range_min == slider->range_min && range_max == slider->range_max)
|
|
return;
|
|
|
|
slider->range_max = range_max;
|
|
slider->range_min = range_min;
|
|
|
|
// limiting within max and min
|
|
obj->value = obj->value > range_max ? range_max : (obj->value < range_min ? range_min : obj->value);
|
|
|
|
// calculate knob's center x position relative to the slider, when value of slider is manually set by user (y is always same)
|
|
uint16_t knob_w = slider->knob_type == LUI_SLIDER_KNOB_TYPE_DEFAULT ? slider->style.knob_width : 0;
|
|
if (obj->common_style.width > obj->common_style.height)
|
|
slider->knob_center_rel_d = _lui_map_range(obj->value, slider->range_max, slider->range_min, obj->common_style.width - (knob_w / 2), (knob_w / 2));
|
|
else
|
|
/* For vertical slider, relation between y-axis and value is inverse. So, we swap new max and min */
|
|
slider->knob_center_rel_d = _lui_map_range(obj->value, slider->range_max, slider->range_min, (knob_w / 2), obj->common_style.height - (knob_w / 2));
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
int16_t lui_slider_get_value(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return -1;
|
|
|
|
return obj->value;
|
|
}
|
|
|
|
int16_t lui_slider_get_min_value(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return -1;
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
|
|
return slider->range_min;
|
|
}
|
|
|
|
int16_t lui_slider_get_max_value(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return -1;
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
|
|
return slider->range_max;
|
|
}
|
|
|
|
void lui_slider_set_progress_bar(lui_obj_t* obj, uint8_t is_progress_bar)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
if (slider->is_progress_bar == is_progress_bar)
|
|
return;
|
|
|
|
slider->is_progress_bar = is_progress_bar;
|
|
if (is_progress_bar)
|
|
{
|
|
obj->enabled = 0;
|
|
lui_slider_set_knob_type(obj, LUI_SLIDER_KNOB_TYPE_TEXT);
|
|
slider->show_value = 1;
|
|
}
|
|
else
|
|
{
|
|
obj->enabled = 1;
|
|
lui_slider_set_knob_type(obj, LUI_SLIDER_KNOB_TYPE_DEFAULT);
|
|
slider->show_value = 0;
|
|
}
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_slider_set_text(lui_obj_t* obj, const char* custom_text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
|
|
((lui_slider_t* )obj->obj_main_data)->custom_text = (char*)custom_text;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_slider_set_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return;
|
|
if (font == NULL)
|
|
return;
|
|
|
|
((lui_slider_t* )obj->obj_main_data)->font = font;
|
|
obj->common_style.height = font->bitmap->size_y > LUI_STYLE_SLIDER_HEIGHT ? font->bitmap->size_y : LUI_STYLE_SLIDER_HEIGHT;
|
|
_lui_object_set_need_refresh(obj);
|
|
|
|
return;
|
|
}
|
|
|
|
int8_t lui_slider_set_knob_type(lui_obj_t* obj, uint8_t knob_type)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SLIDER) < 0)
|
|
return -1;
|
|
if (knob_type > LUI_SLIDER_KNOB_TYPE_TEXT)
|
|
return -1;
|
|
|
|
lui_slider_t* slider = (lui_slider_t* )obj->obj_main_data;
|
|
if (slider->knob_type == knob_type)
|
|
return -1;
|
|
if (slider->is_progress_bar && knob_type == LUI_SLIDER_KNOB_TYPE_DEFAULT)
|
|
return -1;
|
|
slider->knob_type = knob_type;
|
|
uint16_t knob_w = slider->knob_type == LUI_SLIDER_KNOB_TYPE_DEFAULT ? slider->style.knob_width : 0;
|
|
if (obj->common_style.width > obj->common_style.height)
|
|
slider->knob_center_rel_d = _lui_map_range(obj->value, slider->range_max, slider->range_min, obj->common_style.width - (knob_w / 2), (knob_w / 2));
|
|
else
|
|
/* For vertical slider, relation between y-axis and value is inverse. So, we swap new max and min */
|
|
slider->knob_center_rel_d = _lui_map_range(obj->value, slider->range_max, slider->range_min, (knob_w / 2), obj->common_style.height - (knob_w / 2));
|
|
_lui_object_set_need_refresh(obj);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_BTNGRID related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
|
|
void lui_btngrid_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
uint16_t btn_color = btngrid->style.btn_bg_color;
|
|
uint16_t btn_width = 0;
|
|
uint16_t btn_height = 0;
|
|
static int16_t last_act_btn_index = -1;
|
|
|
|
|
|
/* If no event occured yet drawing function is called, that means first time rendering of a btngrid.
|
|
* So, draw the base first
|
|
*/
|
|
if (/* obj->event == LUI_EVENT_NONE && obj->needs_refresh */ btngrid->needs_full_render)
|
|
{
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, obj->common_style.width, obj->common_style.height, obj->common_style.bg_color);
|
|
}
|
|
|
|
uint16_t j = 0;
|
|
for (uint16_t i = 0; i < btngrid->btn_cnt; i++)
|
|
{
|
|
while (btngrid->texts[j][0] == '\n' || btngrid->texts[j][0] == '\0')
|
|
{
|
|
++j;
|
|
}
|
|
|
|
/**
|
|
* Draw a button only if the btngrid needs full render,
|
|
* or when a buttons index matches an active button's index
|
|
*/
|
|
if (btngrid->needs_full_render || (i == btngrid->active_btn_index || i == last_act_btn_index))
|
|
{
|
|
if (!(btngrid->btn_properties[i] & LUI_BTNGRID_MASK_BTN_HIDDEN))
|
|
{
|
|
btn_color = btngrid->style.btn_bg_color;
|
|
if (i == btngrid->active_btn_index)
|
|
{
|
|
if (obj->state == LUI_STATE_SELECTED)
|
|
{
|
|
btn_color = btngrid->style.btn_pressed_color;
|
|
}
|
|
else if (obj->state == LUI_STATE_PRESSED)
|
|
{
|
|
btn_color = btngrid->style.btn_pressed_color;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is to handle when a checkable button lost its focus but check state
|
|
* is changed to "Checked". Or, when manually the check state is set
|
|
*/
|
|
else if (i == last_act_btn_index || btngrid->needs_full_render)
|
|
{
|
|
if (btngrid->btn_properties[i] & LUI_BTNGRID_MASK_BTN_CHECKABLE)
|
|
{
|
|
if (btngrid->btn_properties[i] & LUI_BTNGRID_MASK_BTN_CHECKED)
|
|
{
|
|
btn_color = btngrid->style.btn_pressed_color;
|
|
}
|
|
}
|
|
}
|
|
|
|
btn_width = btngrid->btn_area[i].x2 - btngrid->btn_area[i].x1 + 1;
|
|
btn_height = btngrid->btn_area[i].y2 - btngrid->btn_area[i].y1 + 1;
|
|
lui_gfx_draw_rect_fill(btngrid->btn_area[i].x1, btngrid->btn_area[i].y1, btn_width, btn_height, btn_color);
|
|
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(
|
|
btngrid->btn_area[i].x1, btngrid->btn_area[i].y1,
|
|
btn_width, btn_height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
|
|
uint16_t str_width_height[2];
|
|
lui_gfx_get_string_dimension(btngrid->texts[j], btngrid->font, btn_width, str_width_height);
|
|
|
|
str_width_height[0] = str_width_height[0] > btn_width ? btn_width : str_width_height[0];
|
|
str_width_height[1] = str_width_height[1] > btn_height ? btn_height : str_width_height[1];
|
|
|
|
uint16_t temp_x = btngrid->btn_area[i].x1 + (btn_width - str_width_height[0]) / 2;
|
|
uint16_t temp_y = btngrid->btn_area[i].y1 + (btn_height - str_width_height[1]) / 2;
|
|
|
|
lui_area_t btngrd_btn_area = {
|
|
.x = temp_x,
|
|
.y = temp_y,
|
|
.w = str_width_height[0],
|
|
.h = str_width_height[1]
|
|
};
|
|
lui_gfx_draw_string_advanced(btngrid->texts[j], &btngrd_btn_area, btngrid->style.btn_label_color, 0, NULL, NULL, NULL, 0, btngrid->font);
|
|
}
|
|
}
|
|
|
|
++j;
|
|
}
|
|
last_act_btn_index = btngrid->active_btn_index;
|
|
btngrid->needs_full_render = 0;
|
|
}
|
|
|
|
lui_obj_t* lui_btngrid_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
|
|
lui_btngrid_t* initial_btngrid = (lui_btngrid_t* )_lui_mem_alloc(sizeof(*initial_btngrid));
|
|
if (initial_btngrid == NULL)
|
|
return NULL;
|
|
|
|
initial_btngrid->style.btn_label_color = LUI_STYLE_BTNGRID_LABEL_COLOR;
|
|
initial_btngrid->style.btn_pressed_color = LUI_STYLE_BTNGRID_PRESSED_COLOR;
|
|
initial_btngrid->style.btn_bg_color = LUI_STYLE_BTNGRID_BG_COLOR;
|
|
initial_btngrid->font = g_lui_main->default_font;
|
|
initial_btngrid->texts = NULL;
|
|
initial_btngrid->btn_properties = NULL;
|
|
initial_btngrid->btn_area = NULL;
|
|
initial_btngrid->btn_cnt = 0;
|
|
initial_btngrid->active_btn_index = -1;
|
|
initial_btngrid->style.btn_margin_hor = 2;
|
|
initial_btngrid->style.btn_margin_vert = 2;
|
|
initial_btngrid->needs_full_render = 1;
|
|
#if defined(LUI_USE_KEYBOARD)
|
|
initial_btngrid->kb_data = NULL;
|
|
#endif
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_BTNGRID;
|
|
obj->enabled = 1;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_BTNGRID_BASE_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_BTNGRID_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_BTNGRID_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_BTNGRID_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_BTNGRID_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_btngrid;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_btngrid_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(btngrid, obj_parent)
|
|
}
|
|
|
|
void lui_btngrid_set_textmap(lui_obj_t* obj, const char* texts[])
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
if (texts == NULL)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
uint16_t buttons = 0;
|
|
uint8_t rows = 1;
|
|
|
|
|
|
for (uint16_t i = 0; texts[i][0] != '\0'; i++)
|
|
{
|
|
if (strcmp(texts[i], "\n") != 0)
|
|
{
|
|
++buttons;
|
|
}
|
|
else
|
|
{
|
|
++rows;
|
|
}
|
|
}
|
|
|
|
/* Already btngrid exists and new button count is greater than prev button count */
|
|
if (btngrid->btn_cnt > 0 && buttons > btngrid->btn_cnt)
|
|
{
|
|
return;
|
|
}
|
|
/* Buttongrid may or maynot already exists */
|
|
else
|
|
{
|
|
/* Btngrid doesn't already exists, so, allocate memory for area map and property map */
|
|
if (btngrid->btn_cnt == 0)
|
|
{
|
|
btngrid->btn_area = (_lui_area_priv_t* )_lui_mem_alloc(buttons * sizeof(_lui_area_priv_t));
|
|
if (btngrid->btn_area == NULL)
|
|
return;
|
|
btngrid->btn_properties = (uint8_t* )_lui_mem_alloc(buttons * sizeof(uint8_t));
|
|
if (btngrid->btn_properties == NULL)
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < buttons; i++)
|
|
{
|
|
btngrid->btn_area[i].x1 = 0;
|
|
btngrid->btn_area[i].y1 = 0;
|
|
btngrid->btn_area[i].x2 = 0;
|
|
btngrid->btn_area[i].y2 = 0;
|
|
btngrid->btn_properties[i] = 1;
|
|
}
|
|
|
|
btngrid->btn_cnt = buttons;
|
|
btngrid->row_cnt = rows;
|
|
btngrid->texts = texts;
|
|
btngrid->needs_full_render = 1;
|
|
_lui_object_set_need_refresh(obj);
|
|
|
|
_lui_btngrid_calc_btn_area(obj);
|
|
}
|
|
}
|
|
|
|
void lui_btngrid_set_propertymap(lui_obj_t* obj, const uint8_t properties[])
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
if (properties == NULL)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
if (btngrid->btn_properties == NULL)
|
|
return;
|
|
|
|
memcpy(btngrid->btn_properties, properties, btngrid->btn_cnt);
|
|
btngrid->needs_full_render = 1;
|
|
_lui_object_set_need_refresh(obj);
|
|
_lui_btngrid_calc_btn_area(obj);
|
|
}
|
|
|
|
void lui_btngrid_set_btn_property_bits(lui_obj_t* obj, uint16_t btn_index, uint8_t property_byte)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
if (btngrid->btn_properties == NULL || btn_index >= btngrid->btn_cnt)
|
|
return;
|
|
if (btngrid->btn_properties[btn_index] == property_byte)
|
|
return;
|
|
if ((property_byte & LUI_BTNGRID_MASK_BTN_WIDTH_UNIT) == 0)
|
|
return;
|
|
|
|
btngrid->btn_properties[btn_index] = property_byte;
|
|
_lui_btngrid_calc_btn_area(obj);
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
btngrid->needs_full_render = 1;
|
|
}
|
|
|
|
void lui_btngrid_set_btn_text(lui_obj_t* obj, uint8_t btn_index, char* text)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
if (btngrid->texts == NULL)
|
|
return;
|
|
|
|
uint8_t txt_index = 0;
|
|
for (txt_index = 0; txt_index < btn_index; txt_index++)
|
|
{
|
|
while (btngrid->texts[txt_index][0] == '\n' || btngrid->texts[txt_index][0] == '\0')
|
|
{
|
|
++txt_index;
|
|
}
|
|
}
|
|
btngrid->texts[txt_index] = text;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_btngrid_set_btn_width_unit(lui_obj_t* obj, uint16_t btn_index, uint8_t width_unit)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
uint8_t property = ((lui_btngrid_t* )(obj->obj_main_data))->btn_properties[btn_index] & ~LUI_BTNGRID_MASK_BTN_WIDTH_UNIT;
|
|
lui_btngrid_set_btn_property_bits(obj, btn_index, property | (width_unit & LUI_BTNGRID_MASK_BTN_WIDTH_UNIT));
|
|
}
|
|
|
|
void lui_btngrid_set_btn_hidden(lui_obj_t* obj, uint16_t btn_index, uint8_t hidden)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
uint8_t property = ((lui_btngrid_t* )(obj->obj_main_data))->btn_properties[btn_index] & ~LUI_BTNGRID_MASK_BTN_HIDDEN;
|
|
property = hidden ? (property | LUI_BTNGRID_MASK_BTN_HIDDEN) : property;
|
|
lui_btngrid_set_btn_property_bits(obj, btn_index, property);
|
|
}
|
|
|
|
void lui_btngrid_set_btn_checkable(lui_obj_t* obj, uint16_t btn_index, uint8_t checkable)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
uint8_t property = ((lui_btngrid_t* )(obj->obj_main_data))->btn_properties[btn_index] & ~LUI_BTNGRID_MASK_BTN_CHECKABLE;
|
|
property = checkable ? (property | LUI_BTNGRID_MASK_BTN_CHECKABLE) : property;
|
|
lui_btngrid_set_btn_property_bits(obj, btn_index, property);
|
|
if (checkable)
|
|
{
|
|
((lui_btngrid_t* )(obj->obj_main_data))->needs_full_render = 0; /* if something is set to checkable, no need to redarw entire btngrid */
|
|
}
|
|
}
|
|
|
|
void lui_btngrid_set_btn_checked(lui_obj_t* obj, uint16_t btn_index, uint8_t checked)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
uint8_t property = ((lui_btngrid_t* )(obj->obj_main_data))->btn_properties[btn_index] & ~LUI_BTNGRID_MASK_BTN_CHECKED;
|
|
property = checked ? (property | LUI_BTNGRID_MASK_BTN_CHECKED) : property;
|
|
lui_btngrid_set_btn_property_bits(obj, btn_index, property);
|
|
}
|
|
|
|
int16_t lui_btngrid_get_acive_btn_index(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return -1;
|
|
|
|
return ((lui_btngrid_t* )(obj->obj_main_data))->active_btn_index;
|
|
}
|
|
|
|
const char* lui_btngrid_get_btn_text(lui_obj_t* obj, uint16_t btn_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return NULL;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
uint8_t counter = 0;
|
|
uint8_t txt_index = 0;
|
|
while (counter <= btn_index)
|
|
{
|
|
if ((strcmp(btngrid->texts[txt_index], "\0") == 0))
|
|
{
|
|
return NULL;
|
|
}
|
|
if ((strcmp(btngrid->texts[txt_index], "\n") != 0))
|
|
{
|
|
++counter;
|
|
}
|
|
++txt_index;
|
|
}
|
|
|
|
return btngrid->texts[--txt_index];
|
|
}
|
|
|
|
int8_t lui_btngrid_get_btn_check_status(lui_obj_t* obj, uint8_t btn_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return -1;
|
|
|
|
uint8_t props = ((lui_btngrid_t* )(obj->obj_main_data))->btn_properties[btn_index];
|
|
return (props & LUI_BTNGRID_MASK_BTN_CHECKED) ? 1 : 0;
|
|
}
|
|
|
|
void lui_btngrid_set_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
if (font == NULL)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
btngrid->font = font;
|
|
_lui_object_set_need_refresh(obj);
|
|
btngrid->needs_full_render = 1;
|
|
}
|
|
|
|
void lui_btngrid_set_extra_colors(lui_obj_t* obj, uint16_t btn_color, uint16_t label_color, uint16_t btn_pressed_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
if (btngrid->style.btn_bg_color == btn_color && btngrid->style.btn_label_color == label_color && btngrid->style.btn_pressed_color == btn_pressed_color)
|
|
return;
|
|
btngrid->style.btn_bg_color = btn_color;
|
|
btngrid->style.btn_label_color = label_color;
|
|
btngrid->style.btn_pressed_color = btn_pressed_color;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
btngrid->needs_full_render = 1;
|
|
}
|
|
|
|
void lui_btngrid_set_btn_margin(lui_obj_t* obj, uint8_t margin_x, uint16_t margin_y)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
if (btngrid->style.btn_margin_hor == margin_x && btngrid->style.btn_margin_vert == margin_y)
|
|
return;
|
|
btngrid->style.btn_margin_hor = margin_x;
|
|
btngrid->style.btn_margin_vert = margin_y;
|
|
|
|
_lui_btngrid_calc_btn_area(obj);
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
btngrid->needs_full_render = 1;
|
|
}
|
|
|
|
void _lui_btngrid_calc_btn_area(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
uint8_t units_in_row = 0;
|
|
uint8_t btns_in_row = 0;
|
|
uint8_t btn_index = 0;
|
|
uint8_t unit_index = 0;
|
|
uint16_t raw_height = obj->common_style.height / btngrid->row_cnt;
|
|
|
|
|
|
float w = 0;
|
|
uint16_t h = 0;
|
|
|
|
for (uint16_t i = 0; i < btngrid->btn_cnt + btngrid->row_cnt; i++)
|
|
{
|
|
while (strcmp(btngrid->texts[i], "\n") != 0 && strcmp(btngrid->texts[i], "\0") != 0)
|
|
{
|
|
units_in_row += (btngrid->btn_properties[unit_index++] & LUI_BTNGRID_MASK_BTN_WIDTH_UNIT);
|
|
++btns_in_row;
|
|
++i;
|
|
}
|
|
|
|
float raw_width = (float)(obj->common_style.width) / (float)units_in_row;
|
|
|
|
w = 0.0;
|
|
h += raw_height;
|
|
for (int j = 0; j < btns_in_row; j++)
|
|
{
|
|
_lui_area_priv_t area;
|
|
float this_btn_w = raw_width * (float)(btngrid->btn_properties[btn_index] & LUI_BTNGRID_MASK_BTN_WIDTH_UNIT);
|
|
w += this_btn_w;
|
|
|
|
area.x1 = obj->x + w - this_btn_w + btngrid->style.btn_margin_hor;
|
|
area.x2 = obj->x + w - btngrid->style.btn_margin_hor;
|
|
area.y1 = obj->y + h - raw_height + btngrid->style.btn_margin_vert;
|
|
area.y2 = obj->y + h - btngrid->style.btn_margin_vert;
|
|
|
|
btngrid->btn_area[btn_index++] = area;
|
|
|
|
}
|
|
|
|
btns_in_row = 0;
|
|
units_in_row = 0;
|
|
}
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
((lui_btngrid_t*)(obj->obj_main_data))->needs_full_render = 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_KEYBOARD related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_KEYBOARD) && defined(LUI_USE_BUTTONGRID)
|
|
|
|
static const char* kb_txt_lower_textmap[] =
|
|
{
|
|
"1#", "q", "w", "e", "r", "t", "y","u","i","o","p", LUI_ICON_BACKSPACE,"\n", //buttons: 0-11
|
|
"ABC", "a", "s", "d", "f", "g", "h","j","k","l", LUI_ICON_RETURN_DOWN_BACK,"\n", //buttons: 12-22
|
|
"-","_","z", "x", "c","v","b","n","m",",",".",":","\n", //buttons: 23-34
|
|
LUI_ICON_CLOSE, LUI_ICON_ARROW_BACK, " ", LUI_ICON_ARROW_FORWARD, LUI_ICON_CHECKMARK, "\0"
|
|
};
|
|
static const char* kb_txt_upper_textmap[] =
|
|
{
|
|
"1#", "Q", "W", "E", "R", "T", "Y","U","I","O","P", LUI_ICON_BACKSPACE,"\n", //BUTTONS: 0-11
|
|
"abc", "A", "S", "D", "F", "G", "H","J","K","L", LUI_ICON_RETURN_DOWN_BACK,"\n", //BUTTONS: 12-22
|
|
"-","_","Z", "X", "C","V","B","N","M",",",".",":","\n", //BUTTONS: 23-34
|
|
LUI_ICON_CLOSE, LUI_ICON_ARROW_BACK, " ", LUI_ICON_ARROW_FORWARD, LUI_ICON_CHECKMARK, "\0"
|
|
};
|
|
static const char* kb_spcl_textmap[] =
|
|
{
|
|
"1", "2", "3", "4", "5", "6", "7","8","9","0",".", LUI_ICON_BACKSPACE,"\n", //BUTTONS: 0-11
|
|
"abc", "+", "-", "/", "*", "=", "%","!","?","#", "\\","\n", //BUTTONS: 12-22
|
|
"<",">","(", ")", "{","}","[","]","\"","'",";","@","\n", //BUTTONS: 23-34
|
|
LUI_ICON_CLOSE, LUI_ICON_ARROW_BACK, " ", LUI_ICON_ARROW_FORWARD, LUI_ICON_CHECKMARK, "\0"
|
|
};
|
|
static const uint8_t kb_txt_propertymap[] =
|
|
{
|
|
5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
|
|
6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2, 2, 6, 2, 2
|
|
};
|
|
|
|
lui_obj_t* lui_keyboard_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_keyboard_t* initial_kb = (lui_keyboard_t* )_lui_mem_alloc(sizeof(*initial_kb));
|
|
if (initial_kb == NULL)
|
|
return NULL;
|
|
initial_kb->target_txtbox = NULL;
|
|
initial_kb->keyboard_mode = LUI_KEYBOARD_MODE_TXT_LOWER;
|
|
|
|
lui_obj_t* obj_btngrid = lui_btngrid_create();
|
|
if (obj_btngrid == NULL)
|
|
return NULL;
|
|
((lui_btngrid_t* )(obj_btngrid->obj_main_data))->kb_data = initial_kb;
|
|
uint16_t h = 0.5 * (float)g_lui_main->disp_drv->display_vert_res;
|
|
lui_object_set_position(obj_btngrid, 0, (g_lui_main->disp_drv->display_vert_res - h) - 2);
|
|
lui_object_set_area(obj_btngrid, g_lui_main->disp_drv->display_hor_res, h);
|
|
lui_btngrid_set_textmap(obj_btngrid, kb_txt_lower_textmap);
|
|
lui_btngrid_set_propertymap(obj_btngrid, kb_txt_propertymap);
|
|
lui_object_set_callback(obj_btngrid, lui_keyboard_sys_cb);
|
|
/* For keyboard, by default it's invisible */
|
|
obj_btngrid->visible = 0;
|
|
/* Keyboard sits in the popup layer */
|
|
obj_btngrid->layer = LUI_LAYER_POPUP;
|
|
|
|
return obj_btngrid;
|
|
}
|
|
|
|
lui_obj_t* lui_keyboard_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(keyboard, obj_parent)
|
|
}
|
|
|
|
void lui_keyboard_sys_cb(lui_obj_t* obj_sender)
|
|
{
|
|
if (_lui_verify_obj(obj_sender, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj_sender->obj_main_data);
|
|
if (btngrid->kb_data->target_txtbox == NULL)
|
|
return;
|
|
|
|
int16_t active_btn_id = btngrid->active_btn_index;
|
|
const char* btn_text = lui_keyboard_get_key_text(obj_sender, active_btn_id);
|
|
if (active_btn_id == -1)
|
|
return;
|
|
|
|
|
|
uint16_t caret_index = lui_textbox_get_caret_index(btngrid->kb_data->target_txtbox);
|
|
if (strcmp(btn_text, LUI_ICON_CHECKMARK) == 0)
|
|
{
|
|
btngrid->kb_data->target_txtbox->state = LUI_STATE_IDLE;
|
|
btngrid->kb_data->target_txtbox->event = LUI_EVENT_EXITED;
|
|
if (btngrid->kb_data->target_txtbox->obj_event_cb)
|
|
btngrid->kb_data->target_txtbox->obj_event_cb(btngrid->kb_data->target_txtbox);
|
|
}
|
|
else if (strcmp(btn_text, LUI_ICON_CLOSE) == 0)
|
|
{
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )(btngrid->kb_data->target_txtbox->obj_main_data);
|
|
txtbox->used_chars = 1;
|
|
txtbox->text_buffer[0] = '\0';
|
|
txtbox->caret_index = 0;
|
|
btngrid->kb_data->target_txtbox->needs_refresh = 1;
|
|
}
|
|
else if (strcmp(btn_text, LUI_ICON_ARROW_BACK) == 0)
|
|
{
|
|
lui_textbox_set_caret_index(btngrid->kb_data->target_txtbox, --caret_index);
|
|
}
|
|
else if (strcmp(btn_text, LUI_ICON_ARROW_FORWARD) == 0)
|
|
{
|
|
lui_textbox_set_caret_index(btngrid->kb_data->target_txtbox, ++caret_index);
|
|
}
|
|
else if (strcmp(btn_text, LUI_ICON_BACKSPACE) == 0)
|
|
{
|
|
if (caret_index > 0)
|
|
{
|
|
lui_textbox_set_caret_index(btngrid->kb_data->target_txtbox, --caret_index);
|
|
lui_textbox_delete_char(btngrid->kb_data->target_txtbox);
|
|
}
|
|
}
|
|
else if (strcmp(btn_text, LUI_ICON_RETURN_DOWN_BACK) == 0)
|
|
{
|
|
lui_textbox_insert_char(btngrid->kb_data->target_txtbox, '\n');
|
|
// lui_textbox_set_caret_index(btngrid->kb_data->target_txtbox, ++caret_index);
|
|
}
|
|
else if (strcmp(btn_text, "ABC") == 0 || strcmp(btn_text, "abc") == 0 || strcmp(btn_text, "1#") == 0 )
|
|
{
|
|
if (strcmp(btn_text, "1#") == 0)
|
|
{
|
|
btngrid->kb_data->keyboard_mode = LUI_KEYBOARD_MODE_TXT_SPCL;
|
|
btngrid->texts = kb_spcl_textmap;
|
|
}
|
|
else if (strcmp(btn_text, "ABC") == 0)
|
|
{
|
|
btngrid->kb_data->keyboard_mode = LUI_KEYBOARD_MODE_TXT_UPPER;
|
|
btngrid->texts = kb_txt_upper_textmap;
|
|
}
|
|
else
|
|
{
|
|
btngrid->kb_data->keyboard_mode = LUI_KEYBOARD_MODE_TXT_LOWER;
|
|
btngrid->texts = kb_txt_lower_textmap;
|
|
}
|
|
obj_sender->needs_refresh = 1;
|
|
btngrid->needs_full_render = 1;
|
|
g_lui_needs_render = 1;
|
|
}
|
|
else
|
|
{
|
|
lui_textbox_insert_char(btngrid->kb_data->target_txtbox, btn_text[0]);
|
|
// lui_textbox_set_caret_index(btngrid->kb_data->target_txtbox, lui_textbox_get_caret_index(btngrid->kb_data->target_txtbox) + 1);
|
|
}
|
|
|
|
}
|
|
|
|
void lui_keyboard_set_mode(lui_obj_t* obj, uint8_t mode)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
|
|
if (mode == LUI_KEYBOARD_MODE_TXT_LOWER)
|
|
{
|
|
btngrid->kb_data->keyboard_mode = LUI_KEYBOARD_MODE_TXT_LOWER;
|
|
btngrid->texts = kb_txt_lower_textmap;
|
|
}
|
|
else if (mode == LUI_KEYBOARD_MODE_TXT_UPPER)
|
|
{
|
|
btngrid->kb_data->keyboard_mode = LUI_KEYBOARD_MODE_TXT_UPPER;
|
|
btngrid->texts = kb_txt_upper_textmap;
|
|
}
|
|
else if (mode == LUI_KEYBOARD_MODE_TXT_SPCL)
|
|
{
|
|
btngrid->kb_data->keyboard_mode = LUI_KEYBOARD_MODE_TXT_SPCL;
|
|
btngrid->texts = kb_spcl_textmap;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj->needs_refresh = 1;
|
|
btngrid->needs_full_render = 1;
|
|
g_lui_needs_render = 1;
|
|
|
|
|
|
}
|
|
|
|
void lui_keyboard_set_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
lui_btngrid_set_font(obj, font);
|
|
}
|
|
|
|
const char* lui_keyboard_get_key_text(lui_obj_t* obj, uint8_t btn_index)
|
|
{
|
|
return lui_btngrid_get_btn_text(obj, btn_index);
|
|
}
|
|
|
|
void lui_keyboard_set_target_txtbox(lui_obj_t* obj_kb, lui_obj_t* obj_txtbox)
|
|
{
|
|
if (_lui_verify_obj(obj_kb, LUI_OBJ_BTNGRID) < 0)
|
|
return;
|
|
// type check for textbox
|
|
if (obj_txtbox != NULL && obj_txtbox->obj_type != LUI_OBJ_TEXTBOX)
|
|
return;
|
|
|
|
lui_btngrid_t* btngrid_kb = (lui_btngrid_t* )(obj_kb->obj_main_data);
|
|
static uint16_t txtbox_orig_w = 0, txtbox_orig_h = 0, txtbox_orig_x = 0, txtbox_orig_y = 0;
|
|
static uint8_t txtbox_orig_layer = 0;
|
|
|
|
/* If previous target textbox exists, reset it to its original layer, position, and area. */
|
|
if (btngrid_kb->kb_data->target_txtbox)
|
|
{
|
|
lui_object_set_area(btngrid_kb->kb_data->target_txtbox, txtbox_orig_w, txtbox_orig_h);
|
|
lui_object_set_position(btngrid_kb->kb_data->target_txtbox, txtbox_orig_x, txtbox_orig_y);
|
|
lui_object_set_layer(btngrid_kb->kb_data->target_txtbox, txtbox_orig_layer);
|
|
}
|
|
|
|
/* Now, if new target textbox is not null, store its values in static vars and apply new values */
|
|
if (obj_txtbox)
|
|
{
|
|
txtbox_orig_w = obj_txtbox->common_style.width;
|
|
txtbox_orig_h = obj_txtbox->common_style.height;
|
|
txtbox_orig_x = obj_txtbox->x;
|
|
txtbox_orig_y = obj_txtbox->y;
|
|
txtbox_orig_layer = obj_txtbox->layer;
|
|
|
|
//lui_object_set_area(obj_txtbox, obj_kb->common_style.width, g_lui_main->disp_drv->display_vert_res - obj_kb->common_style.height - 4);
|
|
lui_object_set_area(obj_txtbox, obj_kb->common_style.width, txtbox_orig_h + 2);
|
|
lui_object_set_position(obj_txtbox, 0, obj_kb->y - (txtbox_orig_h + 2));
|
|
lui_object_set_layer(obj_txtbox, obj_kb->layer);
|
|
lui_object_set_visibility(obj_kb, 1);
|
|
|
|
}
|
|
else
|
|
{
|
|
lui_object_set_visibility(obj_kb, 0);
|
|
}
|
|
|
|
btngrid_kb->kb_data->target_txtbox = obj_txtbox;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_TEXTBOX related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_TEXTBOX)
|
|
|
|
void lui_textbox_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
// if no display driver is registered, return
|
|
if (_lui_disp_drv_check() == 0)
|
|
return;
|
|
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )(obj->obj_main_data);
|
|
|
|
if (txtbox->needs_full_render == 1)
|
|
{
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, obj->common_style.width, obj->common_style.height, obj->common_style.bg_color);
|
|
if (obj->common_style.border_width)
|
|
{
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, obj->common_style.width,
|
|
obj->common_style.height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
txtbox->needs_full_render = 0;
|
|
}
|
|
|
|
if (txtbox->text_buffer == NULL)
|
|
return;
|
|
|
|
uint8_t pad = 10;
|
|
uint16_t caret_x = obj->x + pad;
|
|
uint16_t caret_y = obj->y + pad;
|
|
uint8_t caret_h = txtbox->font->bitmap->size_y;
|
|
uint8_t caret_w = 4;
|
|
|
|
lui_area_t txtbx_area = {
|
|
.x = obj->x + pad,
|
|
.y = obj->y + pad,
|
|
.w = obj->common_style.width - 2*pad,
|
|
.h = obj->common_style.height - 2*pad
|
|
};
|
|
lui_gfx_draw_string_advanced(
|
|
txtbox->text_buffer,
|
|
&txtbx_area,
|
|
txtbox->style.text_color,
|
|
obj->common_style.bg_color,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
1,
|
|
txtbox->font);
|
|
|
|
|
|
/* No need to draw caret when textbox is in Idle state */
|
|
if (obj->state == LUI_STATE_IDLE)
|
|
return;
|
|
/* Calculate caret coordinates */
|
|
for (uint16_t i = 0; i < txtbox->caret_index; i++)
|
|
{
|
|
if (txtbox->text_buffer[i] == '\n')
|
|
{
|
|
caret_x = obj->x + pad;
|
|
caret_y += (txtbox->font->bitmap->size_y); //go to next row (row height = height of space)
|
|
}
|
|
else
|
|
{
|
|
// Find the glyph for the char from the font
|
|
const _lui_glyph_t* glyph = _lui_gfx_get_glyph_from_char(txtbox->text_buffer[i], txtbox->font);
|
|
uint8_t glyph_h = txtbox->font->bitmap->size_y;
|
|
uint8_t glyph_w = 0;
|
|
uint8_t glyph_xadv = 0;
|
|
// uint8_t glyph_xoff = 0; // not needed here
|
|
|
|
if (glyph == NULL)
|
|
{
|
|
glyph_w = txtbox->font->bitmap->size_y / 2;
|
|
glyph_xadv = glyph_w;
|
|
}
|
|
/* Width of space is not correct in older font maps, so we calc w based on h */
|
|
else if (glyph->character == ' ' && glyph->x_adv == 0)
|
|
{
|
|
glyph_w = txtbox->font->bitmap->size_y / 4;
|
|
glyph_xadv = glyph_w;
|
|
}
|
|
else
|
|
{
|
|
glyph_w = glyph->width;
|
|
glyph_xadv = _LUI_MAX(glyph->x_adv, glyph_w); // becasue in some rare cases x_adv = 0
|
|
// glyph_xoff = glyph->x_off; // not needed here
|
|
}
|
|
|
|
// check if not enough space available at the right side
|
|
if (caret_x + glyph_xadv > obj->x + obj->common_style.width - pad)
|
|
{
|
|
caret_x = obj->x + pad; //go to first col
|
|
caret_y += glyph_h; //go to next row
|
|
}
|
|
// check if not enough space available at the bottom
|
|
if(caret_y + glyph_h > obj->y + obj->common_style.height - pad)
|
|
{
|
|
break;
|
|
}
|
|
caret_x += glyph_xadv; //next char position
|
|
}
|
|
}
|
|
|
|
/* Whyy???*/
|
|
if (caret_x > obj->x)
|
|
--caret_x;
|
|
|
|
/* Draw the caret now, only if the caret does not go out of the boundary */
|
|
if ((caret_x + caret_w < obj->x + obj->common_style.width - pad) &&
|
|
(caret_y + caret_h - 1 < obj->y + obj->common_style.height))
|
|
{
|
|
lui_gfx_draw_line(caret_x + 2, caret_y + 1, caret_x + 2, caret_y + caret_h - 1, caret_w, ~(obj->common_style.bg_color));
|
|
//lui_gfx_draw_char(txtbox->text_buffer[txtbox->caret_index], caret_x, caret_y, obj->common_style.bg_color, txtbox->style.text_color, 1, txtbox->font);
|
|
}
|
|
}
|
|
|
|
lui_obj_t* lui_textbox_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
|
|
lui_textbox_t* initial_textbox = (lui_textbox_t* )_lui_mem_alloc(sizeof(*initial_textbox));
|
|
if (initial_textbox == NULL)
|
|
return NULL;
|
|
|
|
initial_textbox->style.text_color = LUI_STYLE_TEXTBOX_TEXT_COLOR;
|
|
initial_textbox->text_buffer = NULL;
|
|
initial_textbox->caret_index = 0;
|
|
initial_textbox->font = g_lui_main->default_font;
|
|
initial_textbox->max_len = 0;
|
|
initial_textbox->used_chars = 1;
|
|
initial_textbox->needs_full_render = 1;
|
|
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_TEXTBOX;
|
|
obj->enabled = 1;
|
|
// object common style
|
|
obj->common_style.bg_color = LUI_STYLE_TEXTBOX_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_TEXTBOX_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_TEXTBOX_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_TEXTBOX_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_TEXTBOX_HEIGHT;
|
|
|
|
obj->obj_main_data = (void* )initial_textbox;
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_textbox_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(textbox, obj_parent)
|
|
}
|
|
|
|
void lui_textbox_enter_edit_mode(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
obj->state = LUI_STATE_ENTERED;
|
|
obj->event = LUI_EVENT_ENTERED;
|
|
if (obj->obj_event_cb)
|
|
obj->obj_event_cb(obj);
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_textbox_exit_edit_mode(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
obj->state = LUI_STATE_IDLE;
|
|
obj->event = LUI_EVENT_EXITED;
|
|
if (obj->obj_event_cb)
|
|
obj->obj_event_cb(obj);
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_textbox_set_caret_index(lui_obj_t* obj, uint16_t caret_index)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )obj->obj_main_data;
|
|
|
|
if (txtbox->caret_index == caret_index)
|
|
return;
|
|
|
|
txtbox->caret_index = caret_index;
|
|
if (caret_index > txtbox->used_chars - 1)
|
|
txtbox->caret_index = txtbox->used_chars - 1;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
uint16_t lui_textbox_get_caret_index(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return 0;
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )obj->obj_main_data;
|
|
return txtbox->caret_index;
|
|
}
|
|
|
|
int8_t lui_textbox_insert_char(lui_obj_t* obj, char c)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return -1;
|
|
if (((lui_textbox_t* )obj->obj_main_data)->text_buffer == NULL)
|
|
return -1;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )obj->obj_main_data;
|
|
if (txtbox->used_chars + 1 > txtbox->max_len)
|
|
return -1;
|
|
for (int32_t i = txtbox->used_chars - 1; i >= txtbox->caret_index; i--)
|
|
{
|
|
txtbox->text_buffer[i + 1] = txtbox->text_buffer[i];
|
|
}
|
|
txtbox->text_buffer[txtbox->caret_index] = c;
|
|
++(txtbox->used_chars);
|
|
/* Increase caret index after inserting char */
|
|
++(txtbox->caret_index);
|
|
if (txtbox->caret_index > txtbox->used_chars - 1)
|
|
txtbox->caret_index = txtbox->used_chars - 1;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
return 0;
|
|
}
|
|
|
|
int8_t lui_textbox_delete_char(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return -1;
|
|
if (((lui_textbox_t* )obj->obj_main_data)->text_buffer == NULL)
|
|
return -1;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )obj->obj_main_data;
|
|
if (txtbox->used_chars <= 1)
|
|
return -1;
|
|
for (int32_t i = txtbox->caret_index; i < txtbox->used_chars; i++)
|
|
{
|
|
txtbox->text_buffer[i] = txtbox->text_buffer[i + 1];
|
|
}
|
|
--(txtbox->used_chars);
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
return 0;
|
|
}
|
|
|
|
int8_t lui_textbox_insert_string(lui_obj_t* obj, char* str, uint16_t len)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return -1;
|
|
if (((lui_textbox_t* )obj->obj_main_data)->text_buffer == NULL)
|
|
return -1;
|
|
for (uint16_t i = 0; i < len; i++)
|
|
{
|
|
if (str[i] == '\0')
|
|
return 0;
|
|
if (lui_textbox_insert_char(obj, str[i]) < 0)
|
|
return -1;
|
|
}
|
|
_lui_object_set_need_refresh(obj);
|
|
return 0;
|
|
}
|
|
|
|
void lui_textbox_set_text_buffer(lui_obj_t* obj, char* text_buffer, uint16_t buff_size)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )obj->obj_main_data;
|
|
txtbox->text_buffer = text_buffer;
|
|
txtbox->max_len = buff_size;
|
|
txtbox->text_buffer[0] = '\0';
|
|
txtbox->used_chars = 1;
|
|
txtbox->caret_index = 0;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_textbox_clear_text_buffer(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )(obj->obj_main_data);
|
|
txtbox->used_chars = 1;
|
|
txtbox->text_buffer[0] = '\0';
|
|
txtbox->caret_index = 0;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_textbox_set_text_color(lui_obj_t* obj, uint16_t text_color)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )(obj->obj_main_data);
|
|
txtbox->style.text_color = text_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
txtbox->needs_full_render = 1;
|
|
}
|
|
|
|
void lui_textbox_set_font(lui_obj_t* obj, const lui_font_t* font)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_TEXTBOX) < 0)
|
|
return;
|
|
if (font == NULL)
|
|
return;
|
|
|
|
lui_textbox_t* txtbox = (lui_textbox_t* )(obj->obj_main_data);
|
|
txtbox->font = font;
|
|
_lui_object_set_need_refresh(obj);
|
|
txtbox->needs_full_render = 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_PANEL related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#if defined(LUI_USE_PANEL)
|
|
|
|
/*
|
|
* Initialize a label with default values
|
|
*/
|
|
lui_obj_t* lui_panel_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_panel_t* initial_panel = (lui_panel_t* )_lui_mem_alloc(sizeof(*initial_panel));
|
|
if (initial_panel == NULL)
|
|
return NULL;
|
|
initial_panel->layout.type = LUI_LAYOUT_NONE;
|
|
initial_panel->layout.dim = 0;
|
|
initial_panel->layout.pad_x = 2;
|
|
initial_panel->layout.pad_y = 2;
|
|
initial_panel->bg_image = NULL;
|
|
/* Image palette for mono 1-bpp bitmap image */
|
|
initial_panel->img_pal.fore_color = LUI_RGB(255, 2555, 255);
|
|
initial_panel->img_pal.back_color = 0;
|
|
initial_panel->img_pal.is_backgrnd = 1;
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_PANEL;
|
|
obj->enabled = 1;
|
|
// object comon style
|
|
obj->common_style.bg_color = LUI_STYLE_PANEL_BG_COLOR;
|
|
obj->common_style.border_color = LUI_STYLE_PANEL_BORDER_COLOR;
|
|
obj->common_style.border_width = LUI_STYLE_PANEL_BORDER_THICKNESS;
|
|
obj->common_style.width = LUI_STYLE_PANEL_WIDTH;
|
|
obj->common_style.height = LUI_STYLE_PANEL_HEIGHT;
|
|
obj->obj_main_data = (void* )initial_panel; // will add panel specific main data later
|
|
|
|
return obj;
|
|
}
|
|
|
|
lui_obj_t* lui_panel_create_and_add(lui_obj_t* obj_parent)
|
|
{
|
|
_LUI_CREATE_AND_ADD(panel, obj_parent)
|
|
}
|
|
|
|
void lui_panel_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_PANEL) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_panel_t* panel = (lui_panel_t* )(obj->obj_main_data);
|
|
if (panel->bg_image)
|
|
{
|
|
lui_area_t crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = obj->common_style.width,
|
|
.h = obj->common_style.height
|
|
};
|
|
lui_gfx_draw_bitmap(
|
|
panel->bg_image,
|
|
&panel->img_pal,
|
|
obj->x, obj->y,
|
|
&crop);
|
|
}
|
|
else
|
|
{
|
|
/* Else, just draw background color */
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, obj->common_style.width, obj->common_style.height, obj->common_style.bg_color);
|
|
}
|
|
|
|
/* Draw optional border */
|
|
if (obj->common_style.border_width)
|
|
lui_gfx_draw_rect(
|
|
obj->x, obj->y, obj->common_style.width,
|
|
obj->common_style.height,
|
|
obj->common_style.border_width,
|
|
obj->common_style.border_color);
|
|
}
|
|
|
|
void lui_panel_set_bitmap_image(lui_obj_t* obj, const lui_bitmap_t* bitmap)
|
|
{
|
|
/* As panel and scene both have same first elements in the struct, we can re-use scene's function here */
|
|
lui_scene_set_bitmap_image(obj, bitmap);
|
|
}
|
|
|
|
void lui_panel_set_bitmap_image_mono_palette(lui_obj_t* obj, lui_bitmap_mono_pal_t* palette)
|
|
{
|
|
lui_scene_set_bitmap_image_mono_palette(obj, palette);
|
|
}
|
|
|
|
int8_t lui_panel_layout_set_properties(lui_obj_t* obj, uint8_t type, uint8_t pad_x, uint8_t pad_y)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_PANEL) < 0)
|
|
return -1;
|
|
return _lui_layout_set_properties(obj, type, pad_x, pad_y);
|
|
}
|
|
|
|
int8_t lui_panel_layout_calculate(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_PANEL) < 0)
|
|
return -1;
|
|
return _lui_layout_calculate(obj);
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_SCENE related functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* Create a empty scene with default values and return the scene variable
|
|
*/
|
|
lui_obj_t* lui_scene_create()
|
|
{
|
|
// if total created objects become more than max allowed objects, don't create the object
|
|
if ( g_lui_main->total_created_objects + 1 > LUI_MAX_OBJECTS)
|
|
return NULL;
|
|
g_lui_main->total_created_objects++;
|
|
|
|
lui_scene_t* initial_scene = (lui_scene_t* )_lui_mem_alloc(sizeof(*initial_scene));
|
|
if (initial_scene == NULL)
|
|
return NULL;
|
|
|
|
initial_scene->layout.type = LUI_LAYOUT_NONE;
|
|
initial_scene->layout.dim = 0;
|
|
initial_scene->layout.pad_x = 2;
|
|
initial_scene->layout.pad_y = 2;
|
|
initial_scene->font = g_lui_main->default_font;
|
|
initial_scene->bg_image = NULL;
|
|
/* Color palette for mono 1-bpp bitmap image */
|
|
initial_scene->img_pal.fore_color = LUI_RGB(255, 2555, 255);
|
|
initial_scene->img_pal.back_color = 0;
|
|
initial_scene->img_pal.is_backgrnd = 1;
|
|
lui_obj_t* obj = _lui_object_create();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
// object type
|
|
obj->obj_type = LUI_OBJ_SCENE;
|
|
// object common style
|
|
obj->common_style.width = g_lui_main->disp_drv->display_hor_res;
|
|
obj->common_style.height = g_lui_main->disp_drv->display_vert_res;
|
|
obj->common_style.bg_color = LUI_STYLE_SCENE_BG_COLOR;
|
|
|
|
|
|
//obj->index = g_lui_main->total_scenes;
|
|
obj->obj_main_data = (void* )initial_scene;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
//g_lui_main->scenes[obj->index] = obj;
|
|
g_lui_main->total_scenes++;
|
|
|
|
return obj;
|
|
}
|
|
|
|
void lui_scene_draw(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SCENE) < 0)
|
|
return;
|
|
|
|
if (!(obj->visible))
|
|
return;
|
|
|
|
lui_scene_t* scene = (lui_scene_t* )(obj->obj_main_data);
|
|
if (scene->bg_image)
|
|
{
|
|
lui_area_t crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = obj->common_style.width,
|
|
.h = obj->common_style.height
|
|
};
|
|
lui_gfx_draw_bitmap(
|
|
scene->bg_image,
|
|
&scene->img_pal,
|
|
obj->x, obj->y,
|
|
&crop);
|
|
}
|
|
else
|
|
lui_gfx_draw_rect_fill(obj->x, obj->y, obj->common_style.width, obj->common_style.height, obj->common_style.bg_color);
|
|
}
|
|
|
|
void lui_scene_set_bitmap_image(lui_obj_t* obj, const lui_bitmap_t* bitmap)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
// type check
|
|
/* NOTE: panel and scene both have same first elements in the struct. So, we can use
|
|
* this function for panel too. :)
|
|
*/
|
|
if (obj->obj_type != LUI_OBJ_SCENE && obj->obj_type != LUI_OBJ_PANEL)
|
|
return;
|
|
|
|
lui_scene_t* scene = (lui_scene_t* )(obj->obj_main_data);
|
|
scene->bg_image = bitmap;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_scene_set_bitmap_image_mono_palette(lui_obj_t* obj, lui_bitmap_mono_pal_t* palette)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
// type check
|
|
/* NOTE: panel and scene both have same first elements in the struct. So, we can use
|
|
* this function for panel too. :)
|
|
*/
|
|
if (obj->obj_type != LUI_OBJ_SCENE && obj->obj_type != LUI_OBJ_PANEL)
|
|
return;
|
|
|
|
lui_scene_t* scene = (lui_scene_t* )(obj->obj_main_data);
|
|
|
|
if (palette)
|
|
scene->img_pal = *palette;
|
|
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
|
|
// void lui_scene_set_font(lui_obj_t* obj_scene, const lui_font_t* font)
|
|
// return;
|
|
|
|
// lui_scene_t* scene = (lui_scene_t* )(obj_scene->obj_main_data);
|
|
// scene->font = font;
|
|
|
|
// _lui_object_set_need_refresh(obj_scene);
|
|
// } {
|
|
// if (obj_scene == NULL)
|
|
// return;
|
|
// if (font == NULL)
|
|
// return;
|
|
// // type check
|
|
// if (obj_scene->obj_type != LUI_OBJ_SCENE)
|
|
// return;
|
|
|
|
// lui_scene_t* scene = (lui_scene_t* )(obj_scene->obj_main_data);
|
|
// scene->font = font;
|
|
|
|
// _lui_object_set_need_refresh(obj_scene);
|
|
// }
|
|
|
|
void lui_scene_set_active(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SCENE) < 0)
|
|
return;
|
|
|
|
g_lui_main->active_scene = obj;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
int8_t lui_scene_layout_set_properties(lui_obj_t* obj, uint8_t type, uint8_t pad_x, uint8_t pad_y)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SCENE) < 0)
|
|
return -1;
|
|
return _lui_layout_set_properties(obj, type, pad_x, pad_y);
|
|
}
|
|
|
|
int8_t lui_scene_layout_calculate(lui_obj_t* obj)
|
|
{
|
|
if (_lui_verify_obj(obj, LUI_OBJ_SCENE) < 0)
|
|
return -1;
|
|
return _lui_layout_calculate(obj);
|
|
}
|
|
|
|
lui_obj_t* lui_scene_get_active()
|
|
{
|
|
return g_lui_main->active_scene;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* LUI_OBJECT (generic) functions
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
lui_obj_t* _lui_object_create(void)
|
|
{
|
|
lui_obj_t* obj = (lui_obj_t* )_lui_mem_alloc(sizeof(*obj));
|
|
if (obj == NULL)
|
|
return NULL;
|
|
|
|
obj->x = 0;
|
|
obj->y = 0;
|
|
obj->layer = 0;
|
|
obj->state = LUI_STATE_IDLE;
|
|
obj->event = LUI_EVENT_NONE;
|
|
obj->value = 0;
|
|
obj->obj_event_cb = NULL;
|
|
obj->needs_refresh = 1;
|
|
obj->visible = 1;
|
|
obj->enabled = 0;
|
|
obj->parent = NULL;
|
|
obj->first_child = NULL;
|
|
obj->next_sibling = NULL;
|
|
obj->children_count = 0;
|
|
g_lui_needs_render = 1;
|
|
return obj;
|
|
}
|
|
|
|
void lui_object_add_to_parent(lui_obj_t* obj_child, lui_obj_t* obj_parent)
|
|
{
|
|
if (obj_child == NULL || obj_parent == NULL)
|
|
return;
|
|
// scene cannot be added to any parent, so return
|
|
if (obj_child->obj_type == LUI_OBJ_SCENE)
|
|
return;
|
|
// only panel and scene can be parent, otherwise return
|
|
if (obj_parent->obj_type != LUI_OBJ_PANEL && obj_parent->obj_type != LUI_OBJ_SCENE)
|
|
return;
|
|
|
|
//add the ui element with a new index to scene only if no parent already exists
|
|
if (obj_child->parent != NULL)
|
|
return;
|
|
|
|
|
|
if (obj_parent->first_child == NULL)
|
|
{
|
|
obj_parent->first_child = obj_child;
|
|
}
|
|
else
|
|
{
|
|
lui_obj_t* next_child = obj_parent->first_child;
|
|
|
|
while (next_child->next_sibling != NULL)
|
|
{
|
|
next_child = next_child->next_sibling;
|
|
}
|
|
|
|
next_child->next_sibling = obj_child;
|
|
}
|
|
obj_child->parent = obj_parent;
|
|
lui_object_set_layer(obj_child,obj_child->layer);
|
|
obj_parent->children_count++;
|
|
_lui_object_set_need_refresh(obj_child);
|
|
}
|
|
|
|
void lui_object_remove_from_parent(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
// If item's or parent's index is -1, return
|
|
if (obj->parent == NULL)
|
|
return;
|
|
|
|
// if object is the head (first child)
|
|
if (obj == obj->parent->first_child)
|
|
{
|
|
obj->parent->first_child = obj->next_sibling;
|
|
}
|
|
else
|
|
{
|
|
lui_obj_t* child = obj->parent->first_child;
|
|
while (child->next_sibling != obj)
|
|
{
|
|
child = child->next_sibling;
|
|
}
|
|
child->next_sibling = obj->next_sibling;
|
|
}
|
|
|
|
// common things to do
|
|
obj->parent->children_count--;
|
|
obj->next_sibling = NULL;
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
obj->parent = NULL;
|
|
}
|
|
|
|
lui_obj_t* lui_object_get_child(lui_obj_t* obj_parent, uint16_t child_index)
|
|
{
|
|
if (obj_parent == NULL)
|
|
return NULL;
|
|
if (obj_parent->children_count == 0 || child_index >= obj_parent->children_count)
|
|
return NULL;
|
|
|
|
lui_obj_t* child = obj_parent->first_child;
|
|
|
|
for (uint16_t i = 0; i < child_index; i++)
|
|
{
|
|
child = child->next_sibling;
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
void lui_object_get_position_rel(lui_obj_t* obj, uint16_t pos[2])
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->parent == NULL)
|
|
{
|
|
pos[0] = 0;
|
|
pos[1] = 0;
|
|
}
|
|
else
|
|
{
|
|
pos[0] = obj->x - obj->parent->x;
|
|
pos[1] = obj->y - obj->parent->y;
|
|
}
|
|
}
|
|
|
|
void lui_object_get_position_abs(lui_obj_t* obj, uint16_t pos[2])
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
pos[0] = obj->x;
|
|
pos[1] = obj->y;
|
|
}
|
|
|
|
void lui_object_set_position(lui_obj_t* obj, uint16_t x, uint16_t y)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->obj_type == LUI_OBJ_SCENE)
|
|
return;
|
|
if (obj->x == x && obj->y == y)
|
|
return;
|
|
|
|
uint16_t obj_old_x = obj->x;
|
|
uint16_t obj_old_y = obj->y;
|
|
|
|
if (obj->parent != NULL)
|
|
{
|
|
obj->x = obj->parent->x + x;
|
|
obj->y = obj->parent->y + y;
|
|
}
|
|
else
|
|
{
|
|
obj->x = x;
|
|
obj->y = y;
|
|
}
|
|
|
|
lui_obj_t* child_of_root = obj->first_child;
|
|
while (child_of_root != NULL)
|
|
{
|
|
lui_obj_t* obj_stack[LUI_MAX_OBJECTS] = {NULL};
|
|
uint8_t stack_counter = 0;
|
|
obj_stack[stack_counter++] = child_of_root;
|
|
child_of_root = child_of_root->next_sibling;
|
|
|
|
// loop until stack is empty. in this way all children (and their children too) will be traversed
|
|
while (stack_counter > 0)
|
|
{
|
|
// pop from stack
|
|
lui_obj_t* child = obj_stack[--stack_counter];
|
|
|
|
child->x = child->x + (obj->x - obj_old_x); // offset the child (current obj) based on parent
|
|
child->y = child->y + (obj->y - obj_old_y);
|
|
|
|
// get the child of current object
|
|
child = child->first_child;
|
|
// push all children of current object into stack too
|
|
while (child != NULL)
|
|
{
|
|
// push child to stack
|
|
obj_stack[stack_counter++] = child;
|
|
// get sibling of the child
|
|
child = child->next_sibling;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// object's position is changed, so parent must be redrawn
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
|
|
void lui_object_set_x_pos(lui_obj_t* obj, uint16_t x)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
lui_object_set_position(obj, x, obj->y);
|
|
}
|
|
|
|
void lui_object_set_y_pos(lui_obj_t* obj, uint16_t y)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
lui_object_set_position(obj, obj->x, y);
|
|
}
|
|
|
|
void lui_object_set_area(lui_obj_t* obj, uint16_t width, uint16_t height)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
if (obj->common_style.width == width && obj->common_style.height == height)
|
|
return;
|
|
|
|
if (obj->common_style.width < width && obj->common_style.height < height)
|
|
_lui_object_set_need_refresh(obj);
|
|
else
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
|
|
|
|
obj->common_style.width = width;
|
|
obj->common_style.height = height;
|
|
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
/* If object is an button grid, we need to recalculate the layout when area is changed
|
|
* But, we must check if text map is not null.
|
|
*/
|
|
if (obj->obj_type == LUI_OBJ_BTNGRID && ((lui_btngrid_t* )(obj->obj_main_data))->texts != NULL)
|
|
{
|
|
_lui_btngrid_calc_btn_area(obj);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void lui_object_set_width(lui_obj_t* obj, uint16_t width)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
lui_object_set_area(obj, width, obj->common_style.height);
|
|
}
|
|
|
|
void lui_object_set_height(lui_obj_t* obj, uint16_t height)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
lui_object_set_area(obj, obj->common_style.width, height);
|
|
}
|
|
|
|
void lui_object_set_border_color(lui_obj_t* obj, uint16_t border_color)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->common_style.border_color == border_color)
|
|
return;
|
|
obj->common_style.border_color = border_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_object_set_border_visibility(lui_obj_t* obj, uint8_t is_visible)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->common_style.border_width == is_visible)
|
|
return;
|
|
obj->common_style.border_width = (is_visible == 0) ? 0 : 1;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_object_set_border_width(lui_obj_t* obj, uint8_t width)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->common_style.border_width == width)
|
|
return;
|
|
obj->common_style.border_width = width;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_object_set_bg_color(lui_obj_t* obj, uint16_t bg_color)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->common_style.bg_color == bg_color)
|
|
return;
|
|
obj->common_style.bg_color = bg_color;
|
|
_lui_object_set_need_refresh(obj);
|
|
}
|
|
|
|
void lui_object_set_callback(lui_obj_t* obj, void (*obj_event_cb)(lui_obj_t* ))
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
obj->obj_event_cb = obj_event_cb;
|
|
}
|
|
|
|
int8_t lui_object_get_state(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return -1;
|
|
return obj->state;
|
|
}
|
|
|
|
int8_t lui_object_get_event(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return -1;
|
|
return obj->event;
|
|
}
|
|
|
|
void lui_object_set_visibility(lui_obj_t* obj, uint8_t is_visible)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
// already flag is same as `visible`, no need to waste time in loop. Return.
|
|
if (obj->visible == is_visible)
|
|
return;
|
|
|
|
obj->visible = is_visible;
|
|
|
|
/**
|
|
* @brief When object becomes visible, set needs_refresh bit for itself recursively.
|
|
* When object becomes invisble, set needs_refresh bit for its parent recursively.
|
|
*/
|
|
if (is_visible)
|
|
{
|
|
_lui_object_set_need_refresh(obj);
|
|
if (obj->obj_type == LUI_OBJ_BTNGRID)
|
|
{
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
((lui_btngrid_t* )(obj->obj_main_data))->needs_full_render = 1;
|
|
#endif
|
|
}
|
|
else if (obj->obj_type == LUI_OBJ_TEXTBOX)
|
|
{
|
|
#if defined(LUI_USE_TEXTBOX)
|
|
((lui_textbox_t* )(obj->obj_main_data))->needs_full_render = 1;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
}
|
|
|
|
uint8_t lui_object_set_enable_input(lui_obj_t* obj, uint8_t is_enabled)
|
|
{
|
|
if (obj == NULL)
|
|
return 0;
|
|
|
|
if (obj->obj_type == LUI_OBJ_BUTTON ||
|
|
obj->obj_type == LUI_OBJ_SWITCH ||
|
|
obj->obj_type == LUI_OBJ_CHECKBOX ||
|
|
obj->obj_type == LUI_OBJ_SLIDER)
|
|
{
|
|
obj->enabled = is_enabled;
|
|
}
|
|
else
|
|
{
|
|
obj->enabled = 0;
|
|
}
|
|
return obj->enabled;
|
|
}
|
|
|
|
void lui_object_set_layer(lui_obj_t* obj, uint8_t layer_index)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
if (obj->obj_type == LUI_OBJ_SCENE)
|
|
return;
|
|
|
|
int16_t layer_diff = layer_index - obj->layer;
|
|
if (obj->parent != NULL)
|
|
{
|
|
obj->layer = obj->parent->layer + layer_index;
|
|
if (obj->layer < obj->parent->layer)
|
|
obj->layer = obj->parent->layer;
|
|
}
|
|
else
|
|
{
|
|
obj->layer = layer_index;
|
|
}
|
|
|
|
lui_obj_t* child_of_root = obj->first_child;
|
|
while (child_of_root != NULL)
|
|
{
|
|
lui_obj_t* obj_stack[LUI_MAX_OBJECTS] = {NULL};
|
|
uint8_t stack_counter = 0;
|
|
obj_stack[stack_counter++] = child_of_root;
|
|
child_of_root = child_of_root->next_sibling;
|
|
|
|
// loop until stack is empty. in this way all children (and their children too) will be traversed
|
|
while (stack_counter > 0)
|
|
{
|
|
// pop from stack
|
|
lui_obj_t* child = obj_stack[--stack_counter];
|
|
|
|
child->layer = child->layer + layer_diff;
|
|
|
|
// get the child of current object
|
|
child = child->first_child;
|
|
// push all children of current object into stack too
|
|
while (child != NULL)
|
|
{
|
|
// push child to stack
|
|
obj_stack[stack_counter++] = child;
|
|
// get sibling of the child
|
|
child = child->next_sibling;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// object's layer is changed, so parent must be redrawn
|
|
_lui_object_set_need_refresh(obj->parent);
|
|
}
|
|
|
|
int16_t lui_object_get_type(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return -1;
|
|
|
|
return obj->obj_type;
|
|
}
|
|
|
|
int16_t lui_object_get_layer(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return -1;
|
|
|
|
return obj->layer;
|
|
}
|
|
|
|
int _lui_obj_layer_cmprtr(const void* p1, const void* p2)
|
|
{
|
|
uint8_t l1 = (*((lui_obj_t** )p1))->layer;
|
|
uint8_t l2 = (*((lui_obj_t** )p2))->layer;
|
|
uint8_t t1 = (*((lui_obj_t** )p1))->obj_type;
|
|
uint8_t t2 = (*((lui_obj_t** )p2))->obj_type;
|
|
int16_t ret = 0;
|
|
if (l1 == l2)
|
|
{
|
|
if (t1 == LUI_OBJ_PANEL && t2 != LUI_OBJ_PANEL)
|
|
ret = -1;
|
|
else if (t2 == LUI_OBJ_PANEL && t1 != LUI_OBJ_PANEL)
|
|
ret = 1;
|
|
}
|
|
else
|
|
ret = (l1 - l2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
void _lui_object_render_parent_with_children(lui_obj_t* obj_parent)
|
|
{
|
|
if (obj_parent == NULL)
|
|
return;
|
|
if (!obj_parent->visible)
|
|
return;
|
|
|
|
|
|
lui_obj_t* obj_arr[LUI_MAX_OBJECTS];
|
|
int16_t arr_counter = 0;
|
|
|
|
/* first render the parent, then render all its children in a loop */
|
|
if (obj_parent->layer > 0)
|
|
obj_arr[arr_counter++] = obj_parent;
|
|
else
|
|
_lui_object_render(obj_parent);
|
|
|
|
/*
|
|
* NOTE: objects are added to render stack only if they're visible.
|
|
* That means, if a parent is not visible, its children also won't be
|
|
* added to the render stack, even if those children are visible.
|
|
*/
|
|
|
|
lui_obj_t* child_of_root = obj_parent->first_child;
|
|
while (child_of_root != NULL)
|
|
{
|
|
lui_obj_t* obj_stack[LUI_MAX_OBJECTS] = {NULL};
|
|
uint8_t stack_counter = 0;
|
|
/* Push child in stack, but only if it's visible */
|
|
if (child_of_root->visible)
|
|
{
|
|
obj_stack[stack_counter++] = child_of_root;
|
|
}
|
|
child_of_root = child_of_root->next_sibling;
|
|
|
|
while (stack_counter > 0)
|
|
{
|
|
/* pop from stack */
|
|
lui_obj_t* child = obj_stack[--stack_counter];
|
|
|
|
if (child->layer > 0)
|
|
obj_arr[arr_counter++] = child;
|
|
else
|
|
_lui_object_render(child);
|
|
|
|
/* get the child of current object */
|
|
child = child->first_child;
|
|
/* push all children of current object into stack too */
|
|
while (child != NULL)
|
|
{
|
|
/* push child to stack, but only if it is visible */
|
|
if (child->visible)
|
|
{
|
|
obj_stack[stack_counter++] = child;
|
|
}
|
|
/* get sibling of the child */
|
|
child = child->next_sibling;
|
|
}
|
|
}
|
|
}
|
|
/* Sort the objects based on their layers. bottom -> top */
|
|
qsort((void* )obj_arr, arr_counter, sizeof(obj_arr[0]), _lui_obj_layer_cmprtr);
|
|
|
|
for (uint8_t i = 0; i < arr_counter; i++)
|
|
{
|
|
_lui_object_render(obj_arr[i]);
|
|
}
|
|
|
|
g_lui_needs_render = 0;
|
|
}
|
|
|
|
void _lui_object_render(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
// draw it only if it needs refresh
|
|
if (obj->needs_refresh == 1)
|
|
{
|
|
switch (obj->obj_type)
|
|
{
|
|
case LUI_OBJ_SCENE:
|
|
lui_scene_draw(obj);
|
|
break;
|
|
case LUI_OBJ_PANEL:
|
|
#ifdef LUI_USE_PANEL
|
|
lui_panel_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_BUTTON:
|
|
lui_button_draw(obj);
|
|
break;
|
|
case LUI_OBJ_SWITCH:
|
|
#ifdef LUI_USE_SWITCH
|
|
lui_switch_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_CHECKBOX:
|
|
#ifdef LUI_USE_CHECKBOX
|
|
lui_checkbox_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_SLIDER:
|
|
#ifdef LUI_USE_SLIDER
|
|
lui_slider_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_LABEL:
|
|
lui_label_draw(obj);
|
|
break;
|
|
case LUI_OBJ_LINECHART:
|
|
#ifdef LUI_USE_LINECHART
|
|
lui_linechart_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_LIST:
|
|
#ifdef LUI_USE_LIST
|
|
lui_list_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_BTNGRID:
|
|
#ifdef LUI_USE_BUTTONGRID
|
|
lui_btngrid_draw(obj);
|
|
#endif
|
|
break;
|
|
case LUI_OBJ_TEXTBOX:
|
|
#ifdef LUI_USE_TEXTBOX
|
|
lui_textbox_draw(obj);
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
obj->needs_refresh = 0; // drawing is done, so set need_refresh back to 0
|
|
}
|
|
}
|
|
|
|
void _lui_object_set_need_refresh(lui_obj_t* obj)
|
|
{
|
|
if (obj == NULL)
|
|
return;
|
|
/* Object's visibility is 0, return */
|
|
if (!obj->visible)
|
|
return;
|
|
|
|
g_lui_needs_render = 1;
|
|
/* already flag is 1, no need to waste time in loop. Return. */
|
|
if (obj->needs_refresh == 1)
|
|
return;
|
|
|
|
obj->needs_refresh = 1;
|
|
|
|
/*
|
|
* NOTE: needs_refresh_bit is set to 1 only when object is visible.
|
|
* If a parent is invisible, its children's needs_refresh bit won't be changed too.
|
|
*/
|
|
|
|
lui_obj_t* child_of_root = obj->first_child;
|
|
while (child_of_root != NULL)
|
|
{
|
|
lui_obj_t* obj_stack[LUI_MAX_OBJECTS] = {NULL};
|
|
uint8_t stack_counter = 0;
|
|
// push child to stack, but only if it's visible
|
|
if (child_of_root->visible)
|
|
{
|
|
obj_stack[stack_counter++] = child_of_root;
|
|
}
|
|
child_of_root = child_of_root->next_sibling;
|
|
|
|
while (stack_counter > 0)
|
|
{
|
|
// pop from stack
|
|
lui_obj_t* child = obj_stack[--stack_counter];
|
|
child->needs_refresh = 1;
|
|
|
|
/* When child is either btngrid or textbox, set needs_full_render bit to 1 */
|
|
if (child->obj_type == LUI_OBJ_BTNGRID)
|
|
{
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
((lui_btngrid_t* )(child->obj_main_data))->needs_full_render = 1;
|
|
#endif
|
|
}
|
|
else if (child->obj_type == LUI_OBJ_TEXTBOX)
|
|
{
|
|
#if defined(LUI_USE_TEXTBOX)
|
|
((lui_textbox_t* )(child->obj_main_data))->needs_full_render = 1;
|
|
#endif
|
|
}
|
|
|
|
// get the child of current object
|
|
child = child->first_child;
|
|
// push all children of current object into stack too
|
|
while (child != NULL)
|
|
{
|
|
/* push child to stack, but only if it's visible */
|
|
if (child->visible)
|
|
{
|
|
obj_stack[stack_counter++] = child;
|
|
}
|
|
// get sibling of the child
|
|
child = child->next_sibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* INPUT processing functions
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
lui_obj_t* _lui_process_input_of_act_scene()
|
|
{
|
|
uint8_t input_dev_type = 0;
|
|
|
|
if ( g_lui_main->touch_input_dev != NULL)
|
|
{
|
|
/* Why??!! */
|
|
input_dev_type = LUI_INPUT_TYPE_TOUCH;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t scan_all_objs_flag = 0;
|
|
lui_obj_t* obj_caused_cb = NULL;
|
|
lui_obj_t* last_active_obj = g_lui_main->active_obj;
|
|
// lui_scene_t* scene_main_data = (lui_scene_t* )( g_lui_main->active_scene->obj_main_data);
|
|
lui_touch_input_data_t input_touch_data;
|
|
uint8_t input_is_pressed = 0;
|
|
|
|
|
|
if (input_dev_type == LUI_INPUT_TYPE_TOUCH)
|
|
{
|
|
g_lui_main->touch_input_dev->read_touch_input_cb(&input_touch_data);
|
|
input_is_pressed = input_touch_data.is_pressed;
|
|
|
|
if (g_lui_main->last_touch_data.x == input_touch_data.x &&
|
|
g_lui_main->last_touch_data.y == input_touch_data.y &&
|
|
g_lui_main->last_touch_data.is_pressed == input_touch_data.is_pressed)
|
|
{
|
|
return NULL;
|
|
}
|
|
g_lui_main->last_touch_data.x = input_touch_data.x;
|
|
g_lui_main->last_touch_data.y = input_touch_data.y;
|
|
g_lui_main->last_touch_data.is_pressed = input_touch_data.is_pressed;
|
|
}
|
|
|
|
|
|
/* If previous "pressed" value is 1 and now it's 0, that means a "click" happened */
|
|
if (g_lui_main->input_state_pressed && !input_is_pressed)
|
|
{
|
|
g_lui_main->input_event_clicked = 1;
|
|
}
|
|
else
|
|
{
|
|
g_lui_main->input_event_clicked = 0;
|
|
}
|
|
g_lui_main->input_state_pressed = input_is_pressed;
|
|
|
|
|
|
if (last_active_obj == NULL)
|
|
{
|
|
scan_all_objs_flag = 1;
|
|
}
|
|
|
|
else
|
|
{
|
|
// sets object parameters based on input. also may modify g_lui_main->active_obj
|
|
if ( input_dev_type == LUI_INPUT_TYPE_TOUCH)
|
|
{
|
|
_lui_set_obj_props_on_touch_input(&input_touch_data, last_active_obj);
|
|
}
|
|
|
|
if (last_active_obj->event != LUI_EVENT_NONE)
|
|
{
|
|
if ( g_lui_main->active_obj != last_active_obj /* *state == LUI_STATE_IDLE*/)
|
|
{
|
|
scan_all_objs_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
obj_caused_cb = last_active_obj;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
obj_caused_cb = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
if (scan_all_objs_flag)
|
|
{
|
|
obj_caused_cb = _lui_scan_all_obj_for_input(&input_touch_data, g_lui_main->active_scene, last_active_obj);
|
|
|
|
if (obj_caused_cb == NULL)
|
|
{
|
|
obj_caused_cb = last_active_obj;
|
|
}
|
|
}
|
|
|
|
return obj_caused_cb;
|
|
|
|
}
|
|
|
|
lui_obj_t* _lui_scan_all_obj_for_input(lui_touch_input_data_t* touch_input_data, lui_obj_t* obj_root, lui_obj_t* obj_excluded)
|
|
{
|
|
// Note: This function is made by converting a tail-recursive function to iterative function
|
|
// The simple way is to use a stack.
|
|
// see the answer: https://codereview.stackexchange.com/a/163621
|
|
|
|
if (obj_root->obj_type != LUI_OBJ_SCENE)
|
|
{
|
|
if (obj_root == obj_excluded || !(obj_root->enabled) || !(obj_root->visible))
|
|
return NULL;
|
|
}
|
|
|
|
lui_obj_t* obj_caused_cb = NULL;
|
|
uint8_t popup_layer_exists = 0;
|
|
lui_obj_t* obj_arr[LUI_MAX_OBJECTS];
|
|
int16_t arr_counter = 0;
|
|
|
|
/* obj_arr[arr_counter++] = obj_root;
|
|
if (obj_root->layer == 255)
|
|
popup_layer_exists = 1; */
|
|
|
|
lui_obj_t* child_of_root = obj_root->first_child;
|
|
while (child_of_root != NULL)
|
|
{
|
|
lui_obj_t* obj_stack[LUI_MAX_OBJECTS] = {NULL};
|
|
uint8_t stack_counter = 0;
|
|
if ((child_of_root != obj_excluded) && child_of_root->enabled && child_of_root->visible)
|
|
{
|
|
obj_stack[stack_counter++] = child_of_root;
|
|
}
|
|
child_of_root = child_of_root->next_sibling;
|
|
|
|
// loop until stack is empty. in this way all children (and their children too) will be traversed
|
|
while (stack_counter > 0)
|
|
{
|
|
// pop from stack
|
|
lui_obj_t* child = obj_stack[--stack_counter];
|
|
obj_arr[arr_counter++] = child;
|
|
if (child->layer == LUI_LAYER_POPUP)
|
|
popup_layer_exists = 1;
|
|
|
|
// get the child of current object
|
|
child = child->first_child;
|
|
// push all children of current object into stack too
|
|
while (child != NULL)
|
|
{
|
|
// push child to stack, only if it's not excluded
|
|
if ((child != obj_excluded) && child->enabled && child->visible)
|
|
{
|
|
obj_stack[stack_counter++] = child;
|
|
}
|
|
// get sibling of the child
|
|
child = child->next_sibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Sort the objects based on their layers. bottom -> top */
|
|
qsort((void* )obj_arr, arr_counter, sizeof(obj_arr[0]), _lui_obj_layer_cmprtr);
|
|
|
|
/* Scan for inputs from top layer to bottom layer */
|
|
while(arr_counter--)
|
|
{
|
|
if (popup_layer_exists && obj_arr[arr_counter]->layer < LUI_LAYER_POPUP)
|
|
return NULL;
|
|
obj_caused_cb = _lui_scan_individual_object_for_input(touch_input_data, obj_arr[arr_counter]);
|
|
if (obj_caused_cb != NULL)
|
|
return obj_caused_cb;
|
|
}
|
|
|
|
return obj_caused_cb;
|
|
}
|
|
|
|
lui_obj_t* _lui_scan_individual_object_for_input(lui_touch_input_data_t* touch_input_data, lui_obj_t* obj)
|
|
{
|
|
lui_obj_t* obj_caused_cb = NULL;
|
|
/* if (!(obj->enabled) || !(obj->visible))
|
|
{
|
|
return NULL;
|
|
} */
|
|
|
|
if (touch_input_data != NULL) // Touch input
|
|
{
|
|
// sets object parameters based on input. also, may modify g_lui_main->active_obj
|
|
_lui_set_obj_props_on_touch_input(touch_input_data, obj);
|
|
}
|
|
|
|
if (obj->event != LUI_EVENT_NONE)
|
|
{
|
|
obj_caused_cb = obj;
|
|
}
|
|
else
|
|
{
|
|
obj_caused_cb = NULL;
|
|
}
|
|
|
|
|
|
return obj_caused_cb;
|
|
}
|
|
|
|
uint8_t _lui_check_if_active_obj_touch_input(lui_touch_input_data_t* input, lui_obj_t* obj)
|
|
{
|
|
uint8_t is_active = 0;
|
|
|
|
if (input->x >= obj->x &&
|
|
input->x < obj->x + obj->common_style.width &&
|
|
input->y >= obj->y &&
|
|
input->y < obj->y + obj->common_style.height)
|
|
{
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
if (obj->obj_type == LUI_OBJ_BTNGRID)
|
|
{
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
for (uint16_t i = 0; i < btngrid->btn_cnt; i++)
|
|
{
|
|
if (input->x >= btngrid->btn_area[i].x1 &&
|
|
input->x < btngrid->btn_area[i].x2 &&
|
|
input->y >= btngrid->btn_area[i].y1 &&
|
|
input->y < btngrid->btn_area[i].y2)
|
|
{
|
|
if (!(btngrid->btn_properties[i] & LUI_BTNGRID_MASK_BTN_DISABLED) &&
|
|
!(btngrid->btn_properties[i] & LUI_BTNGRID_MASK_BTN_HIDDEN))
|
|
{
|
|
btngrid->active_btn_index = i;
|
|
g_lui_main->active_obj = obj;
|
|
is_active = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#if defined(LUI_USE_LIST)
|
|
if (obj->obj_type == LUI_OBJ_LIST)
|
|
{
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
if (list->is_expanded)
|
|
{
|
|
is_active = 1;
|
|
uint8_t lim = list->page_first_item_index + list->items_per_page;
|
|
|
|
for (uint8_t i = list->page_first_item_index; i < lim; i++)
|
|
{
|
|
if (i == list->items_cnt)
|
|
break;
|
|
/* Convert items' local coordinates to global */
|
|
uint16_t x1, y1, x2, y2;
|
|
x1 = list->items[i]->area.x1 + obj->x;
|
|
x2 = list->items[i]->area.x2 + obj->x;
|
|
y1 = list->items[i]->area.y1 + obj->y;
|
|
y2 = list->items[i]->area.y2 + obj->y;
|
|
|
|
if (input->x >= x1 &&
|
|
input->x < x2 &&
|
|
input->y >= y1 &&
|
|
input->y < y2)
|
|
{
|
|
list->selected_item_index = i;
|
|
g_lui_main->active_obj = obj;
|
|
is_active = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
g_lui_main->active_obj = obj;
|
|
is_active = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_active == 0)
|
|
{
|
|
// in case input is not on "obj" and previous "active_obj" is same as "obj",
|
|
// set "active_obj" to NULL.
|
|
if (g_lui_main->active_obj == obj)
|
|
{
|
|
g_lui_main->active_obj = NULL;
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
if (obj->obj_type == LUI_OBJ_BTNGRID)
|
|
{
|
|
((lui_btngrid_t* )(obj->obj_main_data))->active_btn_index = -1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return is_active;
|
|
}
|
|
|
|
void _lui_set_obj_props_on_touch_input(lui_touch_input_data_t* input, lui_obj_t* obj)
|
|
{
|
|
uint8_t is_obj_active = _lui_check_if_active_obj_touch_input(input, obj);
|
|
uint8_t new_state = LUI_STATE_IDLE;
|
|
uint8_t old_state = obj->state;
|
|
|
|
if (is_obj_active == 1)
|
|
{
|
|
/* if pressed, then....well, then state = PRESSED */
|
|
if (input->is_pressed == 1)
|
|
{
|
|
new_state = LUI_STATE_PRESSED;
|
|
}
|
|
/* else not pressed, state = SELECTED */
|
|
else
|
|
{
|
|
new_state = LUI_STATE_SELECTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
new_state = LUI_STATE_IDLE;
|
|
}
|
|
if (obj->obj_type == LUI_OBJ_TEXTBOX && obj->state == LUI_STATE_ENTERED)
|
|
{
|
|
new_state = LUI_STATE_ENTERED;
|
|
}
|
|
obj->event = _lui_get_event_against_state(new_state, old_state);
|
|
/* when input is touch input and not mouse pointer, `SELECTION_LOST` event should become `RELEASED` */
|
|
if ((input->x == -1 || input->y == -1) && obj->event == LUI_EVENT_SELECTION_LOST)
|
|
obj->event = LUI_EVENT_RELEASED;
|
|
|
|
|
|
if (obj->event != LUI_EVENT_NONE)
|
|
{
|
|
obj->state = new_state;
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
if (obj->obj_type == LUI_OBJ_BTNGRID)
|
|
{
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
btngrid->btn_properties[btngrid->active_btn_index] |= (new_state & LUI_BTNGRID_MASK_BTN_CHECKED);
|
|
}
|
|
#endif
|
|
|
|
#if defined(LUI_USE_LIST)
|
|
if (obj->obj_type == LUI_OBJ_LIST)
|
|
{
|
|
lui_list_t* list = (lui_list_t* )(obj->obj_main_data);
|
|
if (list->is_dropdown)
|
|
{
|
|
lui_obj_t* nav_btn_text = lui_object_get_child(obj, 3);
|
|
lui_button_set_label_text(nav_btn_text, list->items[list->selected_item_index]->text);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(LUI_USE_PANEL)
|
|
if (obj->obj_type != LUI_OBJ_PANEL)
|
|
_lui_object_set_need_refresh(obj);
|
|
#endif
|
|
|
|
}
|
|
|
|
/* Special case for TextBox. When clicked on a textbox, the state becomes ENTERED
|
|
To EXIT the state, the close or ok button from keyboard must be pressed */
|
|
if (obj->obj_type == LUI_OBJ_TEXTBOX)
|
|
{
|
|
#if defined(LUI_USE_TEXTBOX)
|
|
if (obj->event == LUI_EVENT_PRESSED)
|
|
{
|
|
obj->state = LUI_STATE_ENTERED;
|
|
obj->event = LUI_EVENT_ENTERED;
|
|
}
|
|
else
|
|
{
|
|
obj->state = old_state;
|
|
obj->event = LUI_EVENT_NONE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Special case for checkable buttons, switch, and checkbox: if event is LUI_EVENT_RELEASED,
|
|
then set event to LUI_EVENT_VALUE_CHANGED, and then set the value to `value` property */
|
|
else if (obj->obj_type == LUI_OBJ_SWITCH ||
|
|
obj->obj_type == LUI_OBJ_CHECKBOX ||
|
|
(obj->obj_type == LUI_OBJ_BUTTON && ((lui_button_t*)(obj->obj_main_data))->is_checkable))
|
|
{
|
|
if (obj->event == LUI_EVENT_RELEASED || obj->event == LUI_EVENT_SELECTION_LOST)
|
|
{
|
|
obj->event = LUI_EVENT_VALUE_CHANGED; // for checkable items, being pressed means being toggled, thus value changed
|
|
obj->value = obj->value ? 0 : 1; // toggle the value (1->0 or 0->1)
|
|
obj->needs_refresh = 1;
|
|
g_lui_needs_render = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Special case for slider: If knob is kept pressed and if input pos is not same as knob's current position,
|
|
* set new position to knob and value to slider, also set VALUE_CHANGED event
|
|
*/
|
|
else if (obj->obj_type == LUI_OBJ_SLIDER)
|
|
{
|
|
#if defined(LUI_USE_SLIDER)
|
|
lui_slider_t* slider = (lui_slider_t* )(obj->obj_main_data);
|
|
uint8_t is_hor = obj->common_style.width >= obj->common_style.height;
|
|
uint8_t is_pos_changed = 0;
|
|
if (is_hor)
|
|
is_pos_changed = input->x != (obj->x + slider->knob_center_rel_d);
|
|
else
|
|
is_pos_changed = input->y != (obj->y + slider->knob_center_rel_d);
|
|
|
|
if (obj->state == LUI_STATE_PRESSED && is_pos_changed)
|
|
{
|
|
uint16_t knob_w_by_2 = slider->knob_type == LUI_SLIDER_KNOB_TYPE_DEFAULT ? slider->style.knob_width / 2 : 0;
|
|
|
|
if (is_hor)
|
|
{
|
|
uint16_t max_knob_center_actual_x = obj->x + obj->common_style.width - knob_w_by_2;
|
|
uint16_t min_knob_center_actual_x = obj->x + knob_w_by_2;
|
|
|
|
// cap to minimum/maximum allowed position to prevent the knob from going out of boundary
|
|
if (input->x > max_knob_center_actual_x)
|
|
slider->knob_center_rel_d = max_knob_center_actual_x - obj->x;
|
|
else if (input->x < min_knob_center_actual_x)
|
|
slider->knob_center_rel_d = min_knob_center_actual_x - obj->x;
|
|
else
|
|
slider->knob_center_rel_d = input->x - obj->x;
|
|
|
|
obj->value = _lui_map_range(slider->knob_center_rel_d, obj->common_style.width - (slider->style.knob_width / 2), (slider->style.knob_width / 2), slider->range_max, slider->range_min);
|
|
}
|
|
else
|
|
{
|
|
uint16_t max_knob_center_actual_y = obj->y + obj->common_style.height - knob_w_by_2;
|
|
uint16_t min_knob_center_actual_y = obj->y + knob_w_by_2;
|
|
|
|
// cap to minimum/maximum allowed position to prevent the knob from going out of boundary
|
|
if (input->y > max_knob_center_actual_y)
|
|
slider->knob_center_rel_d = max_knob_center_actual_y - obj->y;
|
|
else if (input->y < min_knob_center_actual_y)
|
|
slider->knob_center_rel_d = min_knob_center_actual_y - obj->y;
|
|
else
|
|
slider->knob_center_rel_d = input->y - obj->y;
|
|
|
|
/* Since the relationship between y-axis pos and value is invserse, we switch max and min for new range */
|
|
obj->value = _lui_map_range(slider->knob_center_rel_d, obj->common_style.height - (slider->style.knob_width / 2), (slider->style.knob_width / 2), slider->range_min, slider->range_max);
|
|
}
|
|
obj->event = LUI_EVENT_VALUE_CHANGED;
|
|
obj->needs_refresh = 1;
|
|
g_lui_needs_render = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* When a different button in a grid is active than the previous one,
|
|
* then the state of btngrid doesn't change and so no event occurs.
|
|
* To handle this, when we detect btn index change, we compare current or old state with STATE_IDLE
|
|
* so we get a proper event.
|
|
* To visualize the difference, comment out the below block and move the mouse really fast in a
|
|
* densely populated btngrid(like qwerty kb). You'll see the previous button remains selected
|
|
* even though mouse is on a different button.
|
|
*/
|
|
else if (obj->obj_type == LUI_OBJ_BTNGRID)
|
|
{
|
|
#if defined(LUI_USE_BUTTONGRID)
|
|
static int16_t last_active_btn = -1;
|
|
lui_btngrid_t* btngrid = (lui_btngrid_t* )(obj->obj_main_data);
|
|
if (btngrid->active_btn_index != last_active_btn)
|
|
{
|
|
uint8_t tmp_new_state, tmp_old_state;
|
|
if (btngrid->active_btn_index == -1)
|
|
{
|
|
tmp_new_state = LUI_STATE_IDLE;
|
|
tmp_old_state = old_state;
|
|
}
|
|
else
|
|
{
|
|
tmp_new_state = new_state;
|
|
tmp_old_state = LUI_STATE_IDLE;
|
|
}
|
|
obj->event = _lui_get_event_against_state(tmp_new_state, tmp_old_state);
|
|
/* when input is touch input and not mouse pointer, `SELECTION_LOST` event should become `RELEASED` */
|
|
if ((input->x == -1 || input->y == -1) && obj->event == LUI_EVENT_SELECTION_LOST)
|
|
obj->event = LUI_EVENT_RELEASED;
|
|
}
|
|
|
|
if (obj->event == LUI_EVENT_PRESSED)
|
|
{
|
|
uint8_t is_checkable = btngrid->btn_properties[btngrid->active_btn_index] & LUI_BTNGRID_MASK_BTN_CHECKABLE;
|
|
if (is_checkable)
|
|
{
|
|
obj->event = LUI_EVENT_CHECK_CHANGED;
|
|
//uint8_t prop = btngrid->btn_properties[btngrid->active_btn_index];
|
|
btngrid->btn_properties[btngrid->active_btn_index] ^= LUI_BTNGRID_MASK_BTN_CHECKED; // toggle check state of the active button
|
|
//prop = btngrid->btn_properties[btngrid->active_btn_index];
|
|
}
|
|
}
|
|
|
|
if (obj->event != LUI_EVENT_NONE)
|
|
{
|
|
obj->needs_refresh = 1;
|
|
g_lui_needs_render = 1;
|
|
}
|
|
last_active_btn = btngrid->active_btn_index;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* DISPLAY DRIVER Callback Functions and Display Properties
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
lui_dispdrv_t* lui_dispdrv_create()
|
|
{
|
|
lui_dispdrv_t* initial_disp_drv = (lui_dispdrv_t* )_lui_mem_alloc(sizeof(*initial_disp_drv));
|
|
if (initial_disp_drv == NULL)
|
|
return NULL;
|
|
|
|
initial_disp_drv->draw_pixels_buff_cb = NULL;
|
|
initial_disp_drv->disp_buff = NULL;
|
|
initial_disp_drv->disp_buff_sz_px = 0;
|
|
initial_disp_drv->display_hor_res = 320; //horizontal 320px default
|
|
initial_disp_drv->display_vert_res = 240; //vertical 240px default
|
|
|
|
return initial_disp_drv;
|
|
}
|
|
|
|
void lui_dispdrv_register(lui_dispdrv_t* dispdrv)
|
|
{
|
|
if (dispdrv == NULL)
|
|
return;
|
|
g_lui_main->disp_drv = dispdrv;
|
|
}
|
|
|
|
lui_dispdrv_t* lui_dispdrv_create_and_register()
|
|
{
|
|
lui_dispdrv_t* dd = lui_dispdrv_create();
|
|
g_lui_main->disp_drv = dd;
|
|
return dd;
|
|
}
|
|
|
|
void lui_dispdrv_set_resolution(lui_dispdrv_t* dispdrv, uint16_t hor_res, uint16_t vert_res)
|
|
{
|
|
if (dispdrv == NULL)
|
|
return;
|
|
dispdrv->display_hor_res = hor_res;
|
|
dispdrv->display_vert_res = vert_res;
|
|
}
|
|
|
|
void lui_dispdrv_set_draw_disp_buff_cb(lui_dispdrv_t* dispdrv, void (*draw_pixels_buff_cb)(uint16_t* disp_buff, lui_area_t* area))
|
|
{
|
|
if (dispdrv == NULL)
|
|
return;
|
|
dispdrv->draw_pixels_buff_cb = draw_pixels_buff_cb;
|
|
}
|
|
|
|
int8_t lui_dispdrv_set_disp_buff(lui_dispdrv_t* dispdrv, uint16_t* disp_buff, uint32_t size_in_px)
|
|
{
|
|
if (dispdrv == NULL)
|
|
return -1;
|
|
if (disp_buff == NULL)
|
|
return -1;
|
|
dispdrv->disp_buff = disp_buff;
|
|
dispdrv->disp_buff_sz_px = size_in_px;
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* INPUT DEVICE Callback Functions and Input properties
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
lui_touch_input_dev_t* lui_touch_inputdev_create()
|
|
{
|
|
lui_touch_input_dev_t* initial_touch_inputdev = (lui_touch_input_dev_t* )_lui_mem_alloc(sizeof(*initial_touch_inputdev));
|
|
if (initial_touch_inputdev == NULL)
|
|
return NULL;
|
|
|
|
initial_touch_inputdev->read_touch_input_cb = NULL;
|
|
// initial_touch_inputdev.touch_data.is_pressed = 0;
|
|
// initial_touch_inputdev.touch_data.x = 0;
|
|
// initial_touch_inputdev.touch_data.y = 0;
|
|
|
|
return initial_touch_inputdev;
|
|
}
|
|
|
|
void lui_touch_inputdev_register (lui_touch_input_dev_t* inputdev)
|
|
{
|
|
if (inputdev == NULL)
|
|
return;
|
|
g_lui_main->touch_input_dev = inputdev;
|
|
}
|
|
|
|
lui_touch_input_dev_t* lui_touch_inputdev_create_and_register()
|
|
{
|
|
lui_touch_input_dev_t* tid = lui_touch_inputdev_create();
|
|
g_lui_main->touch_input_dev = tid;
|
|
return tid;
|
|
}
|
|
|
|
void lui_touch_inputdev_set_read_input_cb(lui_touch_input_dev_t* inputdev, void (*read_touch_input_cb)(lui_touch_input_data_t* inputdata))
|
|
{
|
|
if (inputdev == NULL)
|
|
return;
|
|
inputdev->read_touch_input_cb = read_touch_input_cb;
|
|
}
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Graphics Functions
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
void lui_gfx_draw_string_advanced(const char* str, lui_area_t* area, uint16_t fore_color, uint16_t bg_color, const lui_bitmap_t* bg_bitmap, lui_bitmap_mono_pal_t* palette, lui_area_t* bitmap_crop, uint8_t is_bg, const lui_font_t* font)
|
|
{
|
|
if (str == NULL || area == NULL)
|
|
return;
|
|
|
|
uint16_t x_temp = area->x;
|
|
uint16_t y_temp = area->y;
|
|
const _lui_glyph_t* glyph;
|
|
if (area->w == 0 || area->h == 0)
|
|
{
|
|
uint16_t max_w = area->w == 0 ? g_lui_main->disp_drv->display_hor_res - area->x : area->w;
|
|
uint16_t max_h = area->h == 0 ? g_lui_main->disp_drv->display_vert_res - area->y : area->h;
|
|
uint16_t dim[2] = {0, 0}; // x, y
|
|
|
|
lui_gfx_get_string_dimension(str, font, max_w, dim);
|
|
dim[1] = dim[1] > max_h ? max_h : dim[1];
|
|
|
|
area->w = area->w == 0 ? dim[0] : area->w;
|
|
area->h = area->h == 0 ? dim[1] : area->h;
|
|
}
|
|
/* If the calling function didn't know crop area and set it to 0,
|
|
here we set them to objects's w and h. Don't worry if the actual
|
|
bitmap is smaller than the set crop size, `lui_gfx_draw_bitmap()`
|
|
takes care of it.*/
|
|
if (bitmap_crop)
|
|
{
|
|
if (!bitmap_crop->w ) bitmap_crop->w = area->w;
|
|
if (!bitmap_crop->h ) bitmap_crop->h = area->h;
|
|
}
|
|
if (is_bg)
|
|
{
|
|
if (bg_bitmap)
|
|
lui_gfx_draw_bitmap(bg_bitmap, palette, area->x, area->y, bitmap_crop);
|
|
else
|
|
lui_gfx_draw_rect_fill(area->x, area->y, area->w, area->h, bg_color);
|
|
}
|
|
|
|
// Scan chars one by one from the string
|
|
//char
|
|
while (*str != '\0')
|
|
{
|
|
if (*str == '\n')
|
|
{
|
|
x_temp = area->x; //go to first col
|
|
y_temp += (font->bitmap->size_y); //go to next row (row height = height of space)
|
|
}
|
|
else
|
|
{
|
|
uint8_t glyph_width = 0;
|
|
uint8_t glyph_xadv = 0;
|
|
uint8_t glyph_xoff = 0;
|
|
|
|
glyph = _lui_gfx_get_glyph_from_char(*str, font);
|
|
|
|
if (glyph == NULL)
|
|
{
|
|
glyph_width = font->bitmap->size_y / 2;
|
|
glyph_xadv = glyph_width;
|
|
}
|
|
/* Width of space is not correct in older font maps, so we calc w based on h */
|
|
else if (glyph->character == ' ' && glyph->x_adv == 0)
|
|
{
|
|
glyph_width = font->bitmap->size_y / 4;
|
|
glyph_xadv = glyph_width;
|
|
}
|
|
else
|
|
{
|
|
glyph_width = glyph->width;
|
|
glyph_xadv = _LUI_MAX(glyph->x_adv, glyph_width); // becasue in some rare cases x_adv = 0
|
|
glyph_xoff = glyph->x_off;
|
|
}
|
|
|
|
// check if not enough space available at the right side
|
|
if (x_temp + glyph_xoff + glyph_width > area->x + area->w)
|
|
{
|
|
x_temp = area->x; //go to first col
|
|
y_temp += font->bitmap->size_y; //go to next row
|
|
}
|
|
|
|
/* check if not enough space available at the bottom */
|
|
if(y_temp + font->bitmap->size_y > area->y + area->h)
|
|
return;
|
|
|
|
_lui_gfx_render_char_glyph(x_temp, y_temp, fore_color, 0, 0, glyph, font);
|
|
x_temp += glyph_xadv; //next char position
|
|
}
|
|
|
|
str++;
|
|
}
|
|
}
|
|
|
|
void lui_gfx_draw_string_simple(const char* str, uint16_t x, uint16_t y, uint16_t fore_color, const lui_font_t* font)
|
|
{
|
|
lui_area_t str_area = {
|
|
.x = x,
|
|
.y = y,
|
|
.w = 0,
|
|
.h = 0
|
|
};
|
|
lui_gfx_draw_string_advanced(str, &str_area, fore_color, 0, NULL, NULL, NULL, 0, font);
|
|
}
|
|
|
|
void lui_gfx_draw_char(char c, uint16_t x, uint16_t y, uint16_t fore_color, uint16_t bg_color, uint8_t is_bg, const lui_font_t* font)
|
|
{
|
|
if (c == '\0')
|
|
return;
|
|
const _lui_glyph_t* glyph = _lui_gfx_get_glyph_from_char(c, font);
|
|
_lui_gfx_render_char_glyph(x, y, fore_color, bg_color, is_bg, glyph, font);
|
|
}
|
|
|
|
/*
|
|
* Brehensen's algorithm is used.
|
|
* Draw line between ANY two points.
|
|
* Not necessarily start points has to be less than end points.
|
|
*/
|
|
void lui_gfx_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t line_width, uint16_t color)
|
|
{
|
|
lui_gfx_draw_line_clipped(x0, y0, x1, y1, NULL, line_width, color);
|
|
}
|
|
|
|
void lui_gfx_draw_line_clipped(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, lui_area_t* clip_area, uint8_t line_width, uint16_t color)
|
|
{
|
|
/*
|
|
* Brehensen's algorithm is used.
|
|
* Not necessarily start points has to be less than end points.
|
|
*/
|
|
if (x0 == x1) //vertical line
|
|
{
|
|
lui_gfx_draw_rect_fill_clipped(x0, (y0 < y1 ? y0 : y1), (uint16_t)line_width, (uint16_t)abs(y1 - y0 + 1), clip_area, color);
|
|
}
|
|
else if (y0 == y1) //horizontal line
|
|
{
|
|
lui_gfx_draw_rect_fill_clipped((x0 < x1 ? x0 : x1), y0, (uint16_t)abs(x1 - x0 + 1), (uint16_t)line_width, clip_area, color);
|
|
}
|
|
else
|
|
{
|
|
if (abs(y1 - y0) < abs(x1 - x0))
|
|
{
|
|
if (x0 > x1)
|
|
{
|
|
_LUI_SWAP(uint16_t, x0, x1);
|
|
_LUI_SWAP(uint16_t, y0, y1);
|
|
}
|
|
_lui_gfx_plot_line_low(x0, y0, x1, y1, clip_area, line_width, color);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (y0 > y1)
|
|
{
|
|
_LUI_SWAP(uint16_t, x0, x1);
|
|
_LUI_SWAP(uint16_t, y0, y1);
|
|
}
|
|
_lui_gfx_plot_line_high(x0, y0, x1, y1, clip_area, line_width, color) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: draw rect clipped
|
|
/*
|
|
* Draw a rectangle with a given color and line width
|
|
*/
|
|
void lui_gfx_draw_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t line_width, uint16_t color)
|
|
{
|
|
uint16_t x_new = x+w-1;
|
|
uint16_t y_new = y+h-1;
|
|
lui_gfx_draw_line(x, y, x_new, y, line_width, color); // TL->TR
|
|
lui_gfx_draw_line(x_new-line_width+1, y, x_new-line_width+1, y_new, line_width, color); // TR->BR
|
|
lui_gfx_draw_line(x, y_new-line_width+1, x_new, y_new-line_width+1, line_width, color); // BL->BR
|
|
lui_gfx_draw_line(x, y, x, y_new, line_width, color); // TL->BL
|
|
}
|
|
|
|
/*
|
|
* Fill a rectangular area with a color
|
|
*/
|
|
void lui_gfx_draw_rect_fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)
|
|
{
|
|
lui_gfx_draw_rect_fill_clipped(x, y, w, h, NULL, color);
|
|
}
|
|
|
|
void lui_gfx_draw_rect_fill_clipped(uint16_t x, uint16_t y, uint16_t w, uint16_t h, lui_area_t* clip_area, uint16_t color)
|
|
{
|
|
uint16_t buff_index = 0;
|
|
uint16_t buff_h = 0;
|
|
uint16_t px_cnt = 0;
|
|
uint16_t tmp_x = 0, tmp_y = 0;
|
|
|
|
if (clip_area)
|
|
{
|
|
// Totally outside
|
|
if (x > clip_area->x + clip_area->w - 1 || y > clip_area->y + clip_area->h + 1 ||
|
|
x + w - 1 < clip_area->x || y + h - 1 < clip_area->y)
|
|
return;
|
|
// Clip left
|
|
if (x < clip_area->x)
|
|
{
|
|
w = w - (clip_area->x - x);
|
|
x = clip_area->x;
|
|
}
|
|
// Clip top
|
|
if (y < clip_area->y)
|
|
{
|
|
h = h - (clip_area->y - y);
|
|
y = clip_area->y;
|
|
}
|
|
// Clip right
|
|
if (x + w > clip_area->x + clip_area->w)
|
|
w = clip_area->x + clip_area->w - x;
|
|
// Clip bottom
|
|
if (y + h > clip_area->y + clip_area->h)
|
|
h = clip_area->y + clip_area->h - y;
|
|
}
|
|
|
|
for (tmp_y = y; tmp_y < y + h; ++tmp_y)
|
|
{
|
|
for (tmp_x = x; tmp_x < x + w; ++tmp_x)
|
|
{
|
|
g_lui_main->disp_drv->disp_buff[buff_index++] = color;
|
|
}
|
|
++buff_h;
|
|
px_cnt += w;
|
|
if (px_cnt + w >= g_lui_main->disp_drv->disp_buff_sz_px)
|
|
{
|
|
lui_area_t area = {
|
|
.x = x,
|
|
.y = (tmp_y + 1) - buff_h,
|
|
.w = w,
|
|
.h = buff_h,
|
|
};
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(g_lui_main->disp_drv->disp_buff, &area);
|
|
buff_h = 0;
|
|
px_cnt = 0;
|
|
buff_index = 0;
|
|
}
|
|
}
|
|
if (px_cnt)
|
|
{
|
|
lui_area_t area = {
|
|
.x = x,
|
|
.y = tmp_y - buff_h,
|
|
.w = w,
|
|
.h = buff_h,
|
|
};
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(g_lui_main->disp_drv->disp_buff, &area);
|
|
}
|
|
}
|
|
|
|
uint16_t lui_gfx_get_font_height(const lui_font_t* font)
|
|
{
|
|
if (font == NULL)
|
|
return 0;
|
|
|
|
return font->bitmap->size_y;
|
|
}
|
|
|
|
/*
|
|
* Get the width and height of a string (in pixels).
|
|
* Width: by adding up the width of each glyph (representing a character)
|
|
* Height: Height of any glyph (representing a character)
|
|
*/
|
|
void lui_gfx_get_string_dimension(const char* str, const lui_font_t* font, uint16_t max_w, uint16_t str_dim[2])
|
|
{
|
|
str_dim[0] = 0; // -> width
|
|
str_dim[1] = 0; // -> height
|
|
|
|
if (str == NULL)
|
|
return;
|
|
|
|
uint8_t needs_wrap = 0;
|
|
uint16_t temp_w = 0;
|
|
uint16_t temp_w_highest = 0;
|
|
// height is the height of space
|
|
uint16_t temp_h = font->bitmap->size_y;
|
|
|
|
// Scan chars one by one from the string
|
|
while (*str != '\0')
|
|
{
|
|
if (*str == '\n')
|
|
{
|
|
temp_h += font->bitmap->size_y;
|
|
temp_w = 0;
|
|
}
|
|
else
|
|
{
|
|
const _lui_glyph_t* glyph = _lui_gfx_get_glyph_from_char(*str, font);
|
|
uint8_t glyph_width = 0;
|
|
uint8_t glyph_xadv = 0;
|
|
uint8_t glyph_xoff = 0;
|
|
|
|
if (glyph == NULL)
|
|
{
|
|
glyph_width = font->bitmap->size_y / 2;
|
|
glyph_xadv = glyph_width;
|
|
}
|
|
/* Width of space is not correct in older font maps, so we calc w based on h */
|
|
else if (glyph->character == ' ' && glyph->x_adv == 0)
|
|
{
|
|
glyph_width = font->bitmap->size_y / 4;
|
|
glyph_xadv = glyph_width;
|
|
}
|
|
else
|
|
{
|
|
glyph_width = glyph->width;
|
|
glyph_xadv = _LUI_MAX(glyph->x_adv, glyph_width); // becasue in some rare cases x_adv = 0
|
|
glyph_xoff = glyph->x_off;
|
|
}
|
|
|
|
// Add width of glyphs
|
|
if (temp_w + glyph_xadv > max_w)
|
|
{
|
|
if (temp_w + glyph_xoff + glyph_width > max_w)
|
|
{
|
|
temp_h += font->bitmap->size_y;
|
|
temp_w = 0;
|
|
needs_wrap = 1;
|
|
}
|
|
else
|
|
{
|
|
temp_w += glyph_xoff + glyph_width;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
temp_w += glyph_xadv;
|
|
}
|
|
temp_w_highest = temp_w_highest < temp_w ? temp_w : temp_w_highest;
|
|
}
|
|
|
|
str++;
|
|
}
|
|
str_dim[0] = needs_wrap ? max_w : temp_w_highest;
|
|
str_dim[1] = temp_h;
|
|
}
|
|
|
|
|
|
uint16_t lui_rgb(uint8_t red, uint8_t green, uint8_t blue)
|
|
{
|
|
return LUI_RGB(red, green, blue);
|
|
}
|
|
|
|
const _lui_glyph_t* _lui_gfx_get_glyph_from_char(char c, const lui_font_t* font)
|
|
{
|
|
const _lui_glyph_t* glyph = NULL;
|
|
uint8_t i = 0;
|
|
while (i < font->glyph_count)
|
|
{
|
|
if (font->glyphs[i].character == c)
|
|
{
|
|
glyph = &(font->glyphs[i]);
|
|
break;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
return glyph;
|
|
}
|
|
|
|
/*
|
|
* Draws a character glyph in top-to-bottom, left-to-right order
|
|
* Monochrome fonts generated with lcd-image-converter software are supported only
|
|
* Font must be generated by scanning from left to right
|
|
*
|
|
* Returns the last written pixel's X position
|
|
*/
|
|
void _lui_gfx_render_char_glyph(uint16_t x, uint16_t y, uint16_t fore_color, uint16_t bg_color, uint8_t is_bg, const _lui_glyph_t* glyph, const lui_font_t* font)
|
|
{
|
|
if (font == NULL)
|
|
return;
|
|
|
|
if (glyph == NULL)
|
|
{
|
|
lui_gfx_draw_rect_fill(x, y, font->bitmap->size_y / 2, font->bitmap->size_y, fore_color);
|
|
return;
|
|
}
|
|
|
|
uint16_t temp_x = x + glyph->x_off;
|
|
uint16_t temp_y = y;
|
|
|
|
uint16_t width = 0;
|
|
uint16_t index_offset = 0;
|
|
lui_area_t disp_area = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = 1,
|
|
.h = 1,
|
|
};
|
|
|
|
/* Width of space is not correct in older font maps, so we calc w based on h */
|
|
if (glyph->character == ' ' && glyph->x_adv == 0)
|
|
width = font->bitmap->size_y / 4;
|
|
else
|
|
width = glyph->width;
|
|
index_offset = glyph->payload_index;//((height / 8) + (height % 8 ? 1 : 0)) * x_pos;
|
|
|
|
uint8_t mask = 0x80;
|
|
uint8_t bit_counter = 0;
|
|
for (uint8_t w = 0; w < width; w++)
|
|
{
|
|
bit_counter = 0;
|
|
for (uint8_t h = 0; h < font->bitmap->size_y; h++)
|
|
{
|
|
if (bit_counter >= 8)
|
|
{
|
|
++index_offset;
|
|
bit_counter = 0;
|
|
}
|
|
uint8_t bit = mask & (font->bitmap->payload[index_offset] << bit_counter);
|
|
disp_area.x = temp_x;
|
|
disp_area.y = temp_y;
|
|
/**
|
|
* Nasty hack. Since width of space is calculated from height,
|
|
* we can't render space from bitmap buffer. Hence, we just skip
|
|
* rendering forecolor for space.
|
|
*/
|
|
if (bit && glyph->character != ' ')
|
|
{
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(&fore_color, &disp_area);
|
|
}
|
|
else
|
|
{
|
|
if (is_bg)
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(&bg_color, &disp_area);
|
|
}
|
|
++bit_counter;
|
|
++temp_y;
|
|
}
|
|
++index_offset;
|
|
++temp_x;
|
|
temp_y = y;
|
|
}
|
|
}
|
|
|
|
void lui_gfx_draw_bitmap(const lui_bitmap_t* bitmap, lui_bitmap_mono_pal_t* palette, uint16_t x, uint16_t y, lui_area_t* crop_area)
|
|
{
|
|
if (bitmap == NULL)
|
|
return;
|
|
if (bitmap->bpp != 1 && bitmap->bpp != 8 && bitmap->bpp != 16)
|
|
return;
|
|
|
|
uint16_t color = 0;
|
|
uint16_t tmp_x = x;
|
|
uint16_t tmp_y = y;
|
|
uint8_t mask = 0x80;
|
|
uint8_t bit_counter = 0;
|
|
uint32_t byte_offset = 0;
|
|
uint16_t width = bitmap->size_x;
|
|
uint16_t height = bitmap->size_y;
|
|
uint32_t stride = 0;
|
|
|
|
/* NOTE: Cropping supports only 8bpp and 16bpp bitmaps. NOT for 1-bpp mono */
|
|
if (crop_area && bitmap->bpp != 1)
|
|
{
|
|
/* Crop area start pos can't be higher than bitmap dimension itself */
|
|
if (crop_area->x > bitmap->size_x || crop_area->y > bitmap->size_y)
|
|
return;
|
|
_LUI_BOUNDS(crop_area->w, 1, bitmap->size_x - crop_area->x);
|
|
_LUI_BOUNDS(crop_area->h, 1, bitmap->size_y - crop_area->y);
|
|
|
|
width = crop_area->w;
|
|
height = crop_area->h;
|
|
uint32_t px_offset = bitmap->size_x * crop_area->y + crop_area->x; // Initial pixel offsets for cropping
|
|
byte_offset = px_offset * (bitmap->bpp / 8); // Initial bytes offset
|
|
uint32_t px_skip = bitmap->size_x - crop_area->w; // pixels to skip for cropping, in a loop
|
|
stride = px_skip * (bitmap->bpp / 8); // Bytes to skip, in a loop
|
|
}
|
|
/* For 1-bpp, we must go to next byte when 1 column is finished. */
|
|
else if (bitmap->bpp == 1)
|
|
{
|
|
stride = 1;
|
|
}
|
|
|
|
lui_area_t disp_area = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = 1,
|
|
.h = 1,
|
|
};
|
|
uint16_t buff_index = 0;
|
|
uint16_t buff_h = 0;
|
|
uint16_t px_cnt = 0;
|
|
uint16_t mono_fcol = palette ? palette->fore_color : 0xFFFF;
|
|
uint16_t mono_bcol = palette ? palette->back_color : 0;
|
|
uint16_t mono_transparent = bitmap->bpp == 1 ? (palette ? !palette->is_backgrnd : 0) : 0; // code fart
|
|
for (uint16_t h = 0; h < height; h++)
|
|
{
|
|
bit_counter = 0;
|
|
for (uint16_t w = 0; w < width; w++)
|
|
{
|
|
|
|
if (bitmap->bpp == 1)
|
|
{
|
|
if (bit_counter >= 8)
|
|
{
|
|
++byte_offset;
|
|
bit_counter = 0;
|
|
}
|
|
uint8_t bit = mask & (bitmap->payload[byte_offset] << bit_counter);
|
|
color = bit ? mono_fcol : mono_bcol;
|
|
++bit_counter;
|
|
}
|
|
else if (bitmap->bpp == 8)
|
|
{
|
|
color = LUI_RGB(bitmap->payload[byte_offset], bitmap->payload[byte_offset], bitmap->payload[byte_offset]);
|
|
byte_offset += 1;
|
|
}
|
|
else if (bitmap->bpp == 16)
|
|
{
|
|
color = (uint16_t)(bitmap->payload[byte_offset]) << 8 | (uint16_t)(bitmap->payload[byte_offset+1]);
|
|
byte_offset += 2;
|
|
}
|
|
else if (bitmap->bpp == 32)
|
|
{
|
|
/* 32bpp not supported yet. Only 16-bit colors are supported now */
|
|
// offset += 3;
|
|
}
|
|
|
|
/* If image is mono and it has no bg, we won't buffer it. We'll draw it px by px */
|
|
if (mono_transparent)
|
|
{
|
|
if (color == mono_fcol)
|
|
{
|
|
disp_area.x = tmp_x;
|
|
disp_area.y = tmp_y;
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(&color, &disp_area);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_lui_main->disp_drv->disp_buff[buff_index++] = color;
|
|
}
|
|
++tmp_x;
|
|
}
|
|
byte_offset += stride; // Skip bytes in case of cropping
|
|
tmp_x = x;
|
|
++tmp_y;
|
|
|
|
/* Below section is only for 8-bpp, 16-bpp, and non-transparent 1-bpp */
|
|
if (!mono_transparent)
|
|
{
|
|
++buff_h;
|
|
px_cnt += width;
|
|
if (px_cnt + width > g_lui_main->disp_drv->disp_buff_sz_px)
|
|
{
|
|
disp_area.x = x;
|
|
disp_area.y = tmp_y - buff_h;
|
|
disp_area.w = width;
|
|
disp_area.h = buff_h;
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(g_lui_main->disp_drv->disp_buff, &disp_area);
|
|
buff_h = 0;
|
|
px_cnt = 0;
|
|
buff_index = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (px_cnt)
|
|
{
|
|
disp_area.x = x;
|
|
disp_area.y = tmp_y - buff_h;
|
|
disp_area.w = width;
|
|
disp_area.h = buff_h;
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(g_lui_main->disp_drv->disp_buff, &disp_area);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When dy < 0
|
|
* It's called only by line_draw function. Not for user
|
|
*/
|
|
// TODO: Make thick line render more efficient
|
|
void _lui_gfx_plot_line_low(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, lui_area_t* clip_area, uint8_t line_width, uint16_t color)
|
|
{
|
|
int16_t dx = x1 - x0;
|
|
int16_t dy = y1 - y0;
|
|
int8_t yi = 1;
|
|
if (dy < 0)
|
|
{
|
|
yi = -1;
|
|
dy = -dy;
|
|
}
|
|
|
|
int16_t D = 2*dy - dx;
|
|
uint16_t y = y0;
|
|
uint16_t x = x0;
|
|
lui_area_t disp_area =
|
|
{
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = 1,
|
|
.h = 1,
|
|
};
|
|
while (x <= x1)
|
|
{
|
|
disp_area.x = x;
|
|
disp_area.y = y;
|
|
|
|
for (int8_t i = -line_width / 2; i <= line_width / 2; i++)
|
|
{
|
|
disp_area.y = _LUI_MAX((int32_t)y + i, 0);
|
|
if (clip_area)
|
|
{
|
|
if (disp_area.x >= clip_area->x + clip_area->w || disp_area.x < clip_area->x ||
|
|
disp_area.y >= clip_area->y + clip_area->h || disp_area.y < clip_area->y )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(&color, &disp_area);
|
|
}
|
|
|
|
if (D > 0)
|
|
{
|
|
y = y + yi;
|
|
D = D - 2*dx;
|
|
}
|
|
D = D + 2*dy;
|
|
x++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When dx < 0
|
|
* It's called only by line_draw function. Not for user
|
|
*/
|
|
void _lui_gfx_plot_line_high(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, lui_area_t* clip_area, uint8_t line_width, uint16_t color)
|
|
{
|
|
fflush(stderr);
|
|
int16_t dx = x1 - x0;
|
|
int16_t dy = y1 - y0;
|
|
int8_t xi = 1;
|
|
if (dx < 0)
|
|
{
|
|
xi = -1;
|
|
dx = -dx;
|
|
}
|
|
|
|
int16_t D = 2*dx - dy;
|
|
uint16_t y = y0;
|
|
uint16_t x = x0;
|
|
lui_area_t disp_area =
|
|
{
|
|
.x = 0,
|
|
.y = 0,
|
|
.w = 1,
|
|
.h = 1,
|
|
};
|
|
while (y <= y1)
|
|
{
|
|
disp_area.x = x;
|
|
disp_area.y = y;
|
|
|
|
// Draw multiple pixels vertically for line width
|
|
for (int8_t i = -line_width / 2; i <= line_width / 2; i++)
|
|
{
|
|
disp_area.x = _LUI_MAX((int32_t)x + i, 0);
|
|
if (clip_area)
|
|
{
|
|
if (disp_area.x >= clip_area->x + clip_area->w || disp_area.x < clip_area->x ||
|
|
disp_area.y >= clip_area->y + clip_area->h || disp_area.y < clip_area->y )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
g_lui_main->disp_drv->draw_pixels_buff_cb(&color, &disp_area);
|
|
}
|
|
|
|
if (D > 0)
|
|
{
|
|
x = x + xi;
|
|
D = D - 2*dy;
|
|
}
|
|
D = D + 2*dx;
|
|
y++;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Other Helper Functions
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
// const lui_font_t* _lui_get_font_from_active_scene()
|
|
// {
|
|
// if ( g_lui_main->active_scene == NULL)
|
|
// return NULL;
|
|
// lui_scene_t* act_scene = (lui_scene_t* )( g_lui_main->active_scene->obj_main_data);
|
|
// return (act_scene->font);
|
|
// }
|
|
|
|
uint8_t _lui_get_event_against_state(uint8_t new_state, uint8_t old_state)
|
|
{
|
|
uint8_t event = LUI_EVENT_NONE;
|
|
|
|
if (new_state == old_state)
|
|
{
|
|
event = LUI_EVENT_NONE;
|
|
}
|
|
else
|
|
{
|
|
switch (old_state)
|
|
{
|
|
case LUI_STATE_IDLE: //old
|
|
switch (new_state)
|
|
{
|
|
case LUI_STATE_SELECTED: //new
|
|
event = LUI_EVENT_SELECTED;
|
|
break;
|
|
case LUI_STATE_PRESSED: //new
|
|
event = LUI_EVENT_PRESSED;
|
|
break;
|
|
case LUI_STATE_ENTERED: //new
|
|
event = LUI_EVENT_ENTERED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LUI_STATE_SELECTED: //old
|
|
switch (new_state)
|
|
{
|
|
case LUI_STATE_IDLE: //new
|
|
event = LUI_EVENT_SELECTION_LOST;
|
|
break;
|
|
case LUI_STATE_PRESSED: //new
|
|
event = LUI_EVENT_PRESSED;
|
|
break;
|
|
case LUI_STATE_ENTERED: //new
|
|
event = LUI_EVENT_ENTERED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// PRESSED is only applicable for button
|
|
case LUI_STATE_PRESSED: //old
|
|
switch (new_state)
|
|
{
|
|
case LUI_STATE_IDLE: //new
|
|
event = LUI_EVENT_SELECTION_LOST;
|
|
break;
|
|
case LUI_STATE_SELECTED: //new
|
|
event = LUI_EVENT_RELEASED;
|
|
break;
|
|
// for button, ENTERED will never happen
|
|
case LUI_STATE_ENTERED: //new
|
|
event = LUI_EVENT_NONE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// ENTERED is only applicable for slider
|
|
case LUI_STATE_ENTERED: //old
|
|
switch (new_state)
|
|
{
|
|
case LUI_STATE_IDLE: //new
|
|
event = LUI_EVENT_EXITED;
|
|
break;
|
|
case LUI_STATE_SELECTED: //new
|
|
event = LUI_EVENT_EXITED;
|
|
break;
|
|
// for slider, PRESSED will never happen
|
|
case LUI_STATE_PRESSED: //new
|
|
event = LUI_EVENT_NONE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
/*
|
|
* Map a range of data to a new range of data
|
|
*/
|
|
double _lui_map_range(double old_val, double old_max, double old_min, double new_max, double new_min)
|
|
{
|
|
double new_val = ((((old_val - old_min) * (new_max - new_min)) / (old_max - old_min)) + new_min);
|
|
return new_val;
|
|
}
|
|
|
|
uint8_t _lui_calc_clip_region_code(double x, double y, const lui_area_t* clip_win)
|
|
{
|
|
uint8_t rcode = 0;
|
|
if (x < clip_win->x) rcode |= (1 << 1); //LEFT
|
|
else if (x > clip_win->x + clip_win->w - 1) rcode |= (1 << 2); //RIGHT
|
|
|
|
if (y < clip_win->y) rcode |= (1 << 4); //TOP
|
|
else if (y > clip_win->y + clip_win->h - 1) rcode |= (1 << 3); //BOTTOM
|
|
|
|
return rcode;
|
|
|
|
}
|
|
|
|
/**
|
|
* https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
|
|
* Returns: 0-> unaccepted, 1-> accepted unclipped, 3-> accepted clipped
|
|
*/
|
|
uint8_t _lui_clip_line(double* point_0, double* point_1, const lui_area_t* clip_win)
|
|
{
|
|
if (!point_0 || !point_1 || !clip_win) return 0;
|
|
|
|
// rcode bits
|
|
const uint8_t INSIDE = (1<<0), LEFT = (1<<1), RIGHT = (1<<2), BOTTOM = (1<<3), TOP = (1<<4);
|
|
double x0 = point_0[0], y0 = point_0[1];
|
|
double x1 = point_1[0], y1 = point_1[1];
|
|
|
|
double xmin = clip_win->x, xmax = clip_win->x + clip_win->w - 1;
|
|
// ymin and ymax are swapped because y=0 of screen is at top
|
|
double ymax = clip_win->y, ymin = clip_win->y + clip_win->h - 1;
|
|
|
|
uint8_t rcode_0 = _lui_calc_clip_region_code(x0, y0, clip_win);
|
|
uint8_t rcode_1 = _lui_calc_clip_region_code(x1, y1, clip_win);
|
|
|
|
(void)INSIDE; // suppress `unused ` warning
|
|
uint8_t flag_accept = 0;
|
|
while(1)
|
|
{
|
|
if (!(rcode_0 | rcode_1))
|
|
{
|
|
flag_accept = 1;
|
|
break;
|
|
}
|
|
else if (rcode_0 & rcode_1)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
uint8_t rcode = rcode_0 ? rcode_0 : rcode_1; // at least one code is non-zero, pick that one
|
|
double x, y;
|
|
|
|
// Now find the intersection point;
|
|
// use formulas:
|
|
// slope = (y1 - y0) / (x1 - x0)
|
|
// x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax
|
|
// y = y0 + slope * (xm - x0), where xm is xmin or xmax
|
|
// No need to worry about divide-by-zero because, in each case, the
|
|
// outcode bit being tested guarantees the denominator is non-zero
|
|
|
|
if (rcode & TOP) // point is above the clip window
|
|
{
|
|
x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
|
|
y = ymax;
|
|
}
|
|
else if (rcode & BOTTOM) // point is below the clip window
|
|
{
|
|
x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
|
|
y = ymin;
|
|
}
|
|
else if (rcode & RIGHT) // point is to the right of clip window
|
|
{
|
|
y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
|
|
x = xmax;
|
|
}
|
|
else if (rcode & LEFT) // point is to the left of clip window
|
|
{
|
|
y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
|
|
x = xmin;
|
|
}
|
|
|
|
// Now we move outside point to intersection point to clip
|
|
// and get ready for next pass.
|
|
if (rcode == rcode_0)
|
|
{
|
|
x0 = x;
|
|
y0 = y;
|
|
rcode_0 = _lui_calc_clip_region_code(x0, y0, clip_win);
|
|
}
|
|
else
|
|
{
|
|
x1 = x;
|
|
y1 = y;
|
|
rcode_1 = _lui_calc_clip_region_code(x1, y1, clip_win);
|
|
}
|
|
}
|
|
}
|
|
point_0[0] = x0;
|
|
point_0[1] = y0;
|
|
point_1[0] = x1;
|
|
point_1[1] = y1;
|
|
return flag_accept;
|
|
}
|
|
|
|
int8_t _lui_verify_obj(lui_obj_t* obj, uint8_t obj_type)
|
|
{
|
|
if (obj == NULL)
|
|
return -1;
|
|
if (obj->obj_type != obj_type)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int8_t _lui_layout_set_properties(lui_obj_t* obj, uint8_t layout_type, uint8_t pad_x, uint8_t pad_y)
|
|
{
|
|
if (layout_type == LUI_LAYOUT_VERTICAL || layout_type == LUI_LAYOUT_HORIZONTAL)
|
|
{
|
|
struct _lui_layout_s* layout;
|
|
if (obj->obj_type == LUI_OBJ_SCENE)
|
|
layout = &((lui_scene_t*)(obj->obj_main_data))->layout;
|
|
else
|
|
layout = &((lui_panel_t*)(obj->obj_main_data))->layout;
|
|
|
|
layout->type = layout_type;
|
|
layout->pad_x = pad_x;
|
|
layout->pad_y = pad_y;
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int8_t _lui_layout_calculate(lui_obj_t* obj)
|
|
{
|
|
struct _lui_layout_s* layout;
|
|
uint16_t x, y;
|
|
lui_obj_t* child;
|
|
|
|
if (obj->obj_type == LUI_OBJ_SCENE)
|
|
layout = &((lui_scene_t*)(obj->obj_main_data))->layout;
|
|
else
|
|
layout = &((lui_panel_t*)(obj->obj_main_data))->layout;
|
|
|
|
if (layout->type == LUI_LAYOUT_VERTICAL)
|
|
{
|
|
for (uint8_t i = 0; i < obj->children_count; ++i)
|
|
{
|
|
child = lui_object_get_child(obj, i);
|
|
x = layout->pad_x;
|
|
y = layout->dim + layout->pad_y;
|
|
layout->dim = y + child->common_style.height;
|
|
lui_object_set_position(child, x, y);
|
|
}
|
|
}
|
|
else if (layout->type == LUI_LAYOUT_HORIZONTAL)
|
|
{
|
|
for (uint8_t i = 0; i < obj->children_count; ++i)
|
|
{
|
|
child = lui_object_get_child(obj, i);
|
|
x = layout->dim + layout->pad_x;
|
|
y = layout->pad_y;
|
|
layout->dim = x + child->common_style.width;
|
|
lui_object_set_position(child, x, y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not implemented
|
|
return -69;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if display driver is
|
|
* registered by the stupid user
|
|
*/
|
|
uint8_t _lui_disp_drv_check()
|
|
{
|
|
// if no display driver is registered, return
|
|
if ( g_lui_main->disp_drv == NULL)
|
|
return 0;
|
|
|
|
// If no buffer or no callback function (for drawing) is provided by user, return
|
|
else if (g_lui_main->disp_drv->draw_pixels_buff_cb == NULL || g_lui_main->disp_drv->disp_buff == NULL)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void _lui_mem_init(uint8_t mem_block[], uint32_t size)
|
|
{
|
|
g_lui_mem_block.mem_block = mem_block;
|
|
g_lui_mem_block.block_max_sz = size;
|
|
g_lui_mem_block.mem_allocated = 0;
|
|
}
|
|
|
|
void* _lui_mem_alloc(uint16_t element_size)
|
|
{
|
|
if (g_lui_mem_block.mem_allocated + element_size > g_lui_mem_block.block_max_sz)
|
|
return NULL;
|
|
uint8_t* nxt_addr = g_lui_mem_block.mem_block + g_lui_mem_block.mem_allocated;
|
|
g_lui_mem_block.mem_allocated += element_size;
|
|
return nxt_addr;
|
|
}
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Default Font data
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
/* Create fonts and bitmap images using: https://github.com/abhra0897/LameUI_font_maker */
|
|
/* Default Font data. DON'T EDIT!*/
|
|
/* "ubuntu_regular_17" */
|
|
static const uint8_t default_ubuntu_regular_17_payload[4260] ={
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xCC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x20,0x00,0x04,0x3C,0x00,0x07,0xE0,0x00,0x3C,0x20,0x00,0x04,0x20,0x00,0x04,0x3C,0x00,0x07,0xE0,0x00,0x3C,0x20,
|
|
0x00,0x04,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x08,0x00,0x09,0x04,0x00,0x11,0x04,0x00,0x70,0x87,0x00,0x10,0x84,0x00,0x10,0x44,0x00,0x08,0x48,0x00,
|
|
0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x00,0x21,0x00,0x00,0x21,0x04,0x00,0x21,0x08,0x00,0x1E,0x30,0x00,0x00,0x40,0x00,0x01,0x80,0x00,0x02,
|
|
0x00,0x00,0x0C,0x78,0x00,0x10,0x84,0x00,0x20,0x84,0x00,0x00,0x84,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x1C,0x88,0x00,0x23,0x04,
|
|
0x00,0x20,0x84,0x00,0x21,0x44,0x00,0x22,0x24,0x00,0x1C,0x18,0x00,0x00,0x28,0x00,0x00,0xC4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xF0,0x00,0x0C,0x0C,0x00,0x30,0x03,0x00,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x80,0x30,0x03,0x00,0x0C,
|
|
0x0C,0x00,0x03,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x04,0x80,0x00,0x07,0x00,0x00,0x3C,0x00,0x00,0x07,0x00,0x00,0x04,0x80,0x00,0x08,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x03,0xF8,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0xC0,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x40,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0x10,0x08,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x10,0x08,0x00,0x0F,0xF0,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x3F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x0C,0x00,0x10,0x14,0x00,0x20,
|
|
0x24,0x00,0x20,0x44,0x00,0x20,0x84,0x00,0x11,0x04,0x00,0x0E,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0x00,0x20,0x04,0x00,0x21,0x04,0x00,0x21,0x04,
|
|
0x00,0x21,0x04,0x00,0x22,0x84,0x00,0x12,0x88,0x00,0x0C,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0xA0,0x00,0x03,0x20,0x00,0x04,0x20,0x00,
|
|
0x08,0x20,0x00,0x10,0x20,0x00,0x3F,0xFC,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x3F,0x04,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,
|
|
0x04,0x00,0x21,0x04,0x00,0x20,0x88,0x00,0x20,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xF0,0x00,0x0D,0x08,0x00,0x11,0x04,0x00,0x11,0x04,0x00,0x21,0x04,
|
|
0x00,0x21,0x04,0x00,0x20,0x88,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x1C,0x00,0x20,0xE0,0x00,0x23,0x00,0x00,
|
|
0x24,0x00,0x00,0x28,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x70,0x00,0x12,0x88,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,
|
|
0x04,0x00,0x12,0x88,0x00,0x0C,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x11,0x04,0x00,0x20,0x84,0x00,0x20,0x84,0x00,0x20,0x88,0x00,0x20,0x88,
|
|
0x00,0x10,0xB0,0x00,0x0F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x06,0x0F,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0xA0,0x00,0x00,0xA0,0x00,0x01,0x10,0x00,0x01,0x10,0x00,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
|
|
0x20,0x00,0x01,0x20,0x00,0x01,0x20,0x00,0x01,0x20,0x00,0x01,0x20,0x00,0x01,0x20,0x00,0x01,0x20,0x00,0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x08,
|
|
0x00,0x01,0x10,0x00,0x01,0x10,0x00,0x01,0x10,0x00,0x00,0xA0,0x00,0x00,0xA0,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,
|
|
0x20,0x00,0x00,0x20,0xCC,0x00,0x21,0x00,0x00,0x22,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xF0,0x00,0x0C,0x0C,0x00,0x10,0x02,0x00,0x11,
|
|
0xE2,0x00,0x22,0x11,0x00,0x24,0x09,0x00,0x24,0x09,0x00,0x24,0x09,0x00,0x24,0x09,0x00,0x17,0xF1,0x00,0x10,0x08,0x00,0x0C,0x08,0x00,0x03,0xF0,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x03,0x20,0x00,0x0C,0x20,0x00,0x30,0x20,0x00,0x0C,0x20,0x00,0x03,0x20,0x00,0x00,0xE0,0x00,
|
|
0x00,0x18,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x13,0x04,0x00,0x0C,
|
|
0x88,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x00,0x08,0x10,0x00,0x10,0x08,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,
|
|
0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,
|
|
0x20,0x04,0x00,0x10,0x08,0x00,0x08,0x10,0x00,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x21,
|
|
0x04,0x00,0x21,0x04,0x00,0x20,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x21,0x00,0x00,0x20,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x00,0x08,0x10,0x00,0x10,0x08,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,
|
|
0x10,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x3F,
|
|
0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,
|
|
0x00,0x00,0x08,0x00,0x3F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x01,0x00,0x00,0x02,0x80,0x00,0x04,0x40,0x00,0x08,0x40,0x00,0x08,0x20,0x00,
|
|
0x10,0x10,0x00,0x20,0x08,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,
|
|
0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x3F,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x03,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,
|
|
0x00,0x00,0x60,0x00,0x03,0x80,0x00,0x0C,0x00,0x00,0x10,0x00,0x00,0x3E,0x00,0x00,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x10,0x00,0x00,
|
|
0x0C,0x00,0x00,0x02,0x00,0x00,0x01,0x80,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x3F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x00,0x08,0x10,0x00,0x10,
|
|
0x08,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x10,0x08,0x00,0x08,0x10,0x00,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x3F,0xFC,0x00,0x20,0x40,0x00,0x20,0x40,0x00,0x20,0x40,0x00,0x20,0x40,0x00,0x10,0x80,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x00,
|
|
0x08,0x10,0x00,0x10,0x08,0x00,0x20,0x04,0x00,0x20,0x04,0x00,0x20,0x06,0x00,0x20,0x05,0x00,0x20,0x04,0x80,0x10,0x08,0x80,0x08,0x10,0x80,0x07,0xE0,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0x00,0x20,0x80,0x00,0x20,0x80,0x00,0x20,0x80,0x00,0x20,0x80,0x00,0x20,0xC0,0x00,0x11,0x20,0x00,0x0E,0x18,0x00,0x00,0x04,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x08,0x00,0x12,0x04,0x00,0x21,0x04,0x00,0x21,0x04,0x00,0x20,0x84,0x00,0x10,0x48,0x00,0x00,0x30,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x3F,0xFC,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x3F,0xF0,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x08,0x00,0x3F,0xF0,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0x00,0x00,0xE0,0x00,0x00,0x18,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x03,0x00,0x00,
|
|
0x1C,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x0F,0x00,0x00,0x00,0xF0,0x00,0x00,0x0C,0x00,0x00,0x30,0x00,0x01,0xC0,0x00,0x06,
|
|
0x00,0x00,0x18,0x00,0x00,0x07,0x00,0x00,0x01,0xC0,0x00,0x00,0x30,0x00,0x00,0x0C,0x00,0x00,0xF0,0x00,0x0F,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x20,0x04,0x00,0x10,0x08,0x00,0x08,0x30,0x00,0x06,0x40,0x00,0x01,0x80,0x00,0x01,0x80,0x00,0x06,0x40,0x00,0x08,0x30,0x00,0x10,0x08,0x00,0x20,0x04,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0xFC,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x0C,
|
|
0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x0C,0x00,0x20,0x14,0x00,0x20,0x64,0x00,0x20,0x84,0x00,0x23,0x04,0x00,0x24,0x04,
|
|
0x00,0x28,0x04,0x00,0x30,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x80,0x40,0x00,0x80,0x40,0x00,0x80,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x40,0x00,0x00,0x38,0x00,0x00,0x07,0x00,0x00,0x00,0xC0,0x00,0x00,0x38,0x00,0x00,0x07,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x80,0x40,
|
|
0x00,0x80,0x40,0x00,0x80,0x7F,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x0C,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x10,0x00,
|
|
0x00,0x0C,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
|
|
0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x78,0x00,0x04,0x84,0x00,0x04,0x84,0x00,0x04,0x84,0x00,0x04,0x84,0x00,0x04,0x84,0x00,0x03,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFC,0x00,0x02,0x04,
|
|
0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x02,0x08,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x02,0x08,0x00,
|
|
0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x02,0x08,0x00,0x04,0x04,0x00,0x04,
|
|
0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x02,0x04,0x00,0x7F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x02,0x48,0x00,0x04,0x44,0x00,0x04,0x44,
|
|
0x00,0x04,0x44,0x00,0x04,0x44,0x00,0x02,0x44,0x00,0x01,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFC,0x00,0x24,0x00,0x00,0x44,0x00,0x00,0x44,0x00,0x00,
|
|
0x44,0x00,0x00,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x02,0x08,0x80,0x04,0x04,0x80,0x04,0x04,0x80,0x04,0x04,0x80,0x04,0x04,0x80,0x04,
|
|
0x09,0x00,0x07,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFC,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x02,0x00,
|
|
0x00,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x67,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x67,0xFF,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFC,0x00,0x00,0x40,0x00,0x00,0xA0,0x00,0x01,0x20,0x00,0x01,0x10,0x00,0x02,0x08,0x00,0x04,0x08,0x00,0x00,0x04,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x7F,0xF8,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFC,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,
|
|
0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x03,0xFC,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0xFC,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x07,0xFC,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x01,0xF0,0x00,0x02,0x08,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x02,0x08,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x07,0xFF,0x80,0x04,0x08,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x02,0x08,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x01,0xF0,0x00,0x02,0x08,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x08,0x00,0x07,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x07,
|
|
0xFC,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x04,0x00,0x04,0x84,0x00,0x04,0x44,
|
|
0x00,0x04,0x44,0x00,0x04,0x24,0x00,0x04,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF8,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF0,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x07,0xFC,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x03,0x80,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x03,0x80,0x00,0x04,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x01,0xC0,0x00,0x00,0x30,0x00,0x00,0x0C,0x00,0x00,0x30,0x00,0x01,0xC0,0x00,0x06,0x00,0x00,0x01,0xC0,0x00,
|
|
0x00,0x30,0x00,0x00,0x0C,0x00,0x00,0x30,0x00,0x01,0xC0,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x00,0x02,0x08,0x00,0x01,0x10,0x00,0x00,
|
|
0xA0,0x00,0x00,0x40,0x00,0x00,0xA0,0x00,0x01,0x10,0x00,0x02,0x08,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x80,0x03,0x00,0x80,0x00,0xC0,
|
|
0x80,0x00,0x31,0x00,0x00,0x0E,0x00,0x00,0x38,0x00,0x00,0xC0,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0C,0x00,0x04,0x14,0x00,
|
|
0x04,0x64,0x00,0x04,0x84,0x00,0x05,0x04,0x00,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x3F,0x7F,0x00,0x40,0x00,0x80,0x40,
|
|
0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x80,0x40,0x00,0x80,0x3F,0x7F,0x00,0x00,0x80,0x00,0x00,0x80,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0xFF,0xC0,0x01,0xFF,0xC0,0x03,0xFF,0xC0,0x07,0xFF,0xC0,0x0F,0xFF,0xC0,0x1F,0xFF,0xC0,0x3F,0xF0,0x00,0x7F,
|
|
0xF0,0x00,0x3F,0xF0,0x00,0x1F,0xFF,0xC0,0x3F,0xFF,0xC0,0x3F,0xFF,0xC0,0x3F,0xFF,0xC0,0x03,0xFF,0xC0,0x00,0xFF,0xC0,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x01,0xF0,0x00,0x07,0x1C,0x00,0x0C,0x06,0x00,0x18,0x03,0x00,0x10,0x01,0x00,0x20,0x00,0x80,0x20,0x00,0x80,0x20,0x00,0x80,0x20,0x00,0x80,0x20,0x00,0x80,
|
|
0x30,0x01,0x80,0x11,0x01,0x00,0x1B,0x03,0x00,0x0F,0x06,0x00,0x0F,0x1C,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0xE0,0x00,0x00,0xE0,0x00,0x01,0xF0,0x00,0x03,0xF8,0x00,0x07,0xFC,0x00,0x0F,0xFE,0x00,0x1F,0xFF,
|
|
0x00,0x3F,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x0F,0xC0,0x00,0x1F,0xF0,0x00,0x3F,0xFC,0x00,0x7C,0xFE,0x00,0x78,0x7F,0x80,0x78,0x7F,0xC0,0x78,0x7F,0x80,0x7C,0xFE,0x00,0x3F,0xFC,0x00,0x1F,0xF0,0x00,0x0F,
|
|
0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFC,0x00,0x04,0x04,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,
|
|
0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x07,0xFC,0x00,
|
|
0x07,0xFC,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x38,0x00,0x00,0x1C,0x00,0x00,0x0E,0x00,0x00,
|
|
0x06,0x00,0x00,0x1C,0x00,0x00,0x38,0x00,0x00,0x70,0x00,0x00,0xE0,0x00,0x01,0x80,0x00,0x03,0x00,0x00,0x06,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x70,0x00,0x00,0xE8,0x00,0x00,0xA4,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,
|
|
0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x70,0x00,0x00,0x38,0x00,0x00,0x1C,0x00,0x00,0x0E,0x00,0x00,0x07,0x00,0x1F,0xFF,0x80,0x00,0x07,
|
|
0x00,0x00,0x0E,0x00,0x00,0x1C,0x00,0x00,0x38,0x00,0x00,0x70,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
|
|
0x00,0xE0,0x00,0x03,0xF8,0x00,0x07,0xFC,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0C,0xE6,0x00,0x0E,0x4E,0x00,0x0F,0x1E,0x00,0x0F,0x1E,0x00,0x0E,0x4E,0x00,0x0C,
|
|
0xE6,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x1F,0xFF,0x00,0x1F,0xFF,0x00,0x1F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0x00,0x1F,0xFF,0x00,0x1F,0xFF,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
|
|
0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xF8,0x00,0x07,0xBC,0x00,0x0C,0x06,0x00,0x18,0x03,0x00,0x00,0x01,0x80,
|
|
0x00,0x01,0x80,0x00,0x01,0x80,0x3F,0xC0,0x80,0x00,0x01,0x80,0x00,0x01,0x80,0x00,0x01,0x80,0x18,0x03,0x00,0x0C,0x06,0x00,0x07,0xBC,0x00,0x03,0xF8,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x0C,0x00,0x00,0x1C,0x00,0x00,0x3C,0x00,0x00,0x7C,0x00,0x00,0xFC,0x00,0x03,0xFC,
|
|
0x00,0x07,0xFC,0x00,0x03,0xFC,0x00,0x00,0xFC,0x00,0x00,0x7C,0x00,0x00,0x3C,0x00,0x00,0x1C,0x00,0x00,0x0C,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x03,0xF8,0x00,0x07,0xFC,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x1F,
|
|
0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x04,0x04,0x00,0x07,0xFC,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x40,0x00,0x00,0xE0,0x00,0x01,0xF0,0x00,0x03,0xF8,0x00,0x07,0x5C,0x00,0x0E,0x4E,0x00,0x1C,0x47,0x00,0x18,0x43,0x00,0x00,0x40,0x00,0x00,0x40,0x00,
|
|
0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x04,0x04,0x00,0x0E,0x0E,0x00,0x07,0x1C,0x00,0x03,0xB8,0x00,0x01,0xF0,0x00,0x00,0xE0,0x00,0x01,0xF0,0x00,0x03,0xB8,0x00,0x07,0x1C,0x00,0x0E,0x0E,
|
|
0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x06,0x0C,0x00,0x03,0x18,0x00,0x01,0xB0,0x00,0x00,0xE0,0x00,0x7F,0xFF,0xC0,0x30,0xE1,0x80,0x19,0xB3,0x00,0x0F,0x1E,0x00,0x06,0x0C,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0x80,0x1F,0xFF,
|
|
0x00,0x0F,0xFE,0x00,0x07,0xFC,0x00,0x03,0xF8,0x00,0x01,0xF0,0x00,0x00,0xE0,0x00,0x00,0xE0,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x01,
|
|
0xF0,0x00,0x03,0xF8,0x00,0x07,0xFC,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x1F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x18,0x43,0x00,
|
|
0x1C,0x47,0x00,0x0E,0x4E,0x00,0x07,0x5C,0x00,0x03,0xF8,0x00,0x01,0xF0,0x00,0x00,0xE0,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,
|
|
0xFC,0x00,0x04,0x04,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x05,0xD4,0x00,0x05,0x54,0x00,0x04,0xC4,0x00,0x01,0xF8,0x00,0x06,0x64,0x00,0x01,0x54,0x00,0x05,0x34,
|
|
0x00,0x05,0x74,0x00,0x05,0xF4,0x00,0x05,0xF4,0x00,0x07,0xFC,0x00,0x07,0xFC,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0xC0,0x00,0x01,0xC0,0x00,0x03,0x80,0x00,0x07,0x00,0x00,0x0E,0x00,0x00,0x1C,0x00,0x00,0x3F,0xFF,0x00,0x1C,0x00,0x00,0x0E,0x00,0x00,0x07,0x00,0x00,0x03,
|
|
0x80,0x00,0x01,0xC0,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x06,0x40,0x00,0x06,0xE0,
|
|
0x00,0x0C,0xC0,0x00,0x0D,0x98,0x00,0x09,0xB0,0x00,0x09,0x32,0x00,0x09,0x37,0x00,0x09,0x32,0x00,0x09,0xB0,0x00,0x0D,0x98,0x00,0x0C,0xC0,0x00,0x06,0xE0,0x00,
|
|
0x06,0x40,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x00,0x0F,0xF0,0x00,0x18,0x38,0x00,0x30,0x18,0x00,0x30,
|
|
0x0C,0x00,0x30,0x0C,0x00,0x30,0x0C,0x00,0x30,0x1C,0x00,0x38,0x18,0x00,0x1C,0x78,0x00,0x0F,0xFC,0x00,0x03,0xCE,0x00,0x00,0x07,0x00,0x00,0x03,0x80,0x00,0x01,
|
|
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x07,0x80,0x00,0x1F,0x80,0x00,0x7F,0x80,0x01,0xFF,0x80,0x03,0xFF,0x80,
|
|
0x0F,0xFF,0x80,0x3E,0x05,0x80,0x1F,0xFF,0x80,0x07,0xFF,0x80,0x01,0xFF,0x80,0x00,0x7F,0x80,0x00,0x3F,0x80,0x00,0x0F,0x80,0x00,0x03,0x80,0x00,0x00,0x80,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x02,0x08,0x00,0x0F,0x1E,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x7E,0x0F,0xC0,0x7E,0x0F,0xC0,0x7E,0x0F,
|
|
0xC0,0x7E,0x0F,0xC0,0x7E,0x0F,0xC0,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0x1E,0x00,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x0F,0xFE,0x00,0x00,0x40,0x00,0x00,
|
|
0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFC,0x00,0x04,0x04,
|
|
0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,0x04,0x04,0x00,
|
|
0x04,0x04,0x00,0x04,0x04,0x00,0x07,0xFC,0x00,0x07,0xFC,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x07,
|
|
0x00,0x00,0x07,0x80,0x00,0x07,0xC0,0x00,0x07,0xE0,0x00,0x07,0xF8,0x00,0x07,0xFC,0x00,0x07,0xF8,0x00,0x07,0xE0,0x00,0x07,0xC0,0x00,0x07,0x80,0x00,0x07,0x00,
|
|
0x00,0x06,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
|
|
|
const lui_bitmap_t BITMAP_default_ubuntu_regular_17 = {.size_x=1420, .size_y=19, .payload=default_ubuntu_regular_17_payload};
|
|
|
|
static const _lui_glyph_t FONT_GLYPHS_default_ubuntu_regular_17[] = {
|
|
{ .character=32/* */, .width=3, .payload_index=0 }, { .character=33/*!*/, .width=3, .payload_index=9 },
|
|
{ .character=34/*"*/, .width=6, .payload_index=18 }, { .character=35/*#*/, .width=11, .payload_index=36 },
|
|
{ .character=36/*$*/, .width=10, .payload_index=69 }, { .character=37/*%*/, .width=15, .payload_index=99 },
|
|
{ .character=38/*&*/, .width=12, .payload_index=144 }, { .character=39/*'*/, .width=3, .payload_index=180 },
|
|
{ .character=40/*(*/, .width=6, .payload_index=189 }, { .character=41/*)*/, .width=6, .payload_index=207 },
|
|
{ .character=42/***/, .width=9, .payload_index=225 }, { .character=43/*+*/, .width=9, .payload_index=252 },
|
|
{ .character=44/*,*/, .width=4, .payload_index=279 }, { .character=45/*-*/, .width=6, .payload_index=291 },
|
|
{ .character=46/*.*/, .width=3, .payload_index=309 }, { .character=47/*/*/, .width=9, .payload_index=318 },
|
|
{ .character=48/*0*/, .width=10, .payload_index=345 }, { .character=49/*1*/, .width=6, .payload_index=375 },
|
|
{ .character=50/*2*/, .width=9, .payload_index=393 }, { .character=51/*3*/, .width=10, .payload_index=420 },
|
|
{ .character=52/*4*/, .width=10, .payload_index=450 }, { .character=53/*5*/, .width=10, .payload_index=480 },
|
|
{ .character=54/*6*/, .width=10, .payload_index=510 }, { .character=55/*7*/, .width=10, .payload_index=540 },
|
|
{ .character=56/*8*/, .width=10, .payload_index=570 }, { .character=57/*9*/, .width=10, .payload_index=600 },
|
|
{ .character=58/*:*/, .width=3, .payload_index=630 }, { .character=59/*;*/, .width=4, .payload_index=639 },
|
|
{ .character=60/*<*/, .width=9, .payload_index=651 }, { .character=61/*=*/, .width=10, .payload_index=678 },
|
|
{ .character=62/*>*/, .width=10, .payload_index=708 }, { .character=63/*?*/, .width=8, .payload_index=738 },
|
|
{ .character=64/*@*/, .width=15, .payload_index=762 }, { .character=65/*A*/, .width=13, .payload_index=807 },
|
|
{ .character=66/*B*/, .width=10, .payload_index=846 }, { .character=67/*C*/, .width=12, .payload_index=876 },
|
|
{ .character=68/*D*/, .width=11, .payload_index=912 }, { .character=69/*E*/, .width=9, .payload_index=945 },
|
|
{ .character=70/*F*/, .width=8, .payload_index=972 }, { .character=71/*G*/, .width=11, .payload_index=996 },
|
|
{ .character=72/*H*/, .width=10, .payload_index=1029 }, { .character=73/*I*/, .width=3, .payload_index=1059 },
|
|
{ .character=74/*J*/, .width=9, .payload_index=1068 }, { .character=75/*K*/, .width=11, .payload_index=1095 },
|
|
{ .character=76/*L*/, .width=9, .payload_index=1128 }, { .character=77/*M*/, .width=15, .payload_index=1155 },
|
|
{ .character=78/*N*/, .width=10, .payload_index=1200 }, { .character=79/*O*/, .width=13, .payload_index=1230 },
|
|
{ .character=80/*P*/, .width=9, .payload_index=1269 }, { .character=81/*Q*/, .width=13, .payload_index=1296 },
|
|
{ .character=82/*R*/, .width=11, .payload_index=1335 }, { .character=83/*S*/, .width=9, .payload_index=1368 },
|
|
{ .character=84/*T*/, .width=11, .payload_index=1395 }, { .character=85/*U*/, .width=10, .payload_index=1428 },
|
|
{ .character=86/*V*/, .width=13, .payload_index=1458 }, { .character=87/*W*/, .width=17, .payload_index=1497 },
|
|
{ .character=88/*X*/, .width=12, .payload_index=1548 }, { .character=89/*Y*/, .width=13, .payload_index=1584 },
|
|
{ .character=90/*Z*/, .width=10, .payload_index=1623 }, { .character=91/*[*/, .width=6, .payload_index=1653 },
|
|
{ .character=92/*\*/, .width=9, .payload_index=1671 }, { .character=93/*]*/, .width=6, .payload_index=1698 },
|
|
{ .character=94/*^*/, .width=11, .payload_index=1716 }, { .character=95/*_*/, .width=10, .payload_index=1749 },
|
|
{ .character=96/*`*/, .width=5, .payload_index=1779 }, { .character=97/*a*/, .width=9, .payload_index=1794 },
|
|
{ .character=98/*b*/, .width=10, .payload_index=1821 }, { .character=99/*c*/, .width=9, .payload_index=1851 },
|
|
{ .character=100/*d*/, .width=10, .payload_index=1878 }, { .character=101/*e*/, .width=10, .payload_index=1908 },
|
|
{ .character=102/*f*/, .width=8, .payload_index=1938 }, { .character=103/*g*/, .width=10, .payload_index=1962 },
|
|
{ .character=104/*h*/, .width=10, .payload_index=1992 }, { .character=105/*i*/, .width=3, .payload_index=2022 },
|
|
{ .character=106/*j*/, .width=6, .payload_index=2031 }, { .character=107/*k*/, .width=10, .payload_index=2049 },
|
|
{ .character=108/*l*/, .width=5, .payload_index=2079 }, { .character=109/*m*/, .width=15, .payload_index=2094 },
|
|
{ .character=110/*n*/, .width=10, .payload_index=2139 }, { .character=111/*o*/, .width=10, .payload_index=2169 },
|
|
{ .character=112/*p*/, .width=10, .payload_index=2199 }, { .character=113/*q*/, .width=10, .payload_index=2229 },
|
|
{ .character=114/*r*/, .width=8, .payload_index=2259 }, { .character=115/*s*/, .width=8, .payload_index=2283 },
|
|
{ .character=116/*t*/, .width=7, .payload_index=2307 }, { .character=117/*u*/, .width=10, .payload_index=2328 },
|
|
{ .character=118/*v*/, .width=11, .payload_index=2358 }, { .character=119/*w*/, .width=15, .payload_index=2391 },
|
|
{ .character=120/*x*/, .width=11, .payload_index=2436 }, { .character=121/*y*/, .width=11, .payload_index=2469 },
|
|
{ .character=122/*z*/, .width=8, .payload_index=2502 }, { .character=123/*{*/, .width=7, .payload_index=2526 },
|
|
{ .character=124/*|*/, .width=3, .payload_index=2547 }, { .character=125/*}*/, .width=7, .payload_index=2556 },
|
|
{ .character=126/*~*/, .width=10, .payload_index=2577 },
|
|
{ .character=1/*[01]home.png*/, .width=19, .payload_index=2607 },
|
|
{ .character=2/*[02]reload.png*/, .width=19, .payload_index=2664 }, { .character=3/*[03]caret-back.png*/, .width=19, .payload_index=2721 },
|
|
{ .character=4/*[04]location.png*/, .width=19, .payload_index=2778 }, { .character=5/*[05]battery-full.png*/, .width=19, .payload_index=2835 },
|
|
{ .character=6/*[06]checkmark.png*/, .width=19, .payload_index=2892 }, { .character=7/*[07]return-down-back.png*/, .width=19, .payload_index=2949 },
|
|
{ .character=8/*[08]arrow-down.png*/, .width=19, .payload_index=3006 }, { .character=9/*[09]backspace.png*/, .width=19, .payload_index=3063 },
|
|
{ .character=10/*[0A]pause.png*/, .width=19, .payload_index=3120 }, { .character=11/*[0B]remove.png*/, .width=19, .payload_index=3177 },
|
|
{ .character=12/*[0C]power.png*/, .width=19, .payload_index=3234 }, { .character=13/*[0D]caret-up.png*/, .width=19, .payload_index=3291 },
|
|
{ .character=14/*[0E]volume-medium.png*/, .width=19, .payload_index=3348 }, { .character=15/*[0F]arrow-back.png*/, .width=19, .payload_index=3405 },
|
|
{ .character=16/*[10]close.png*/, .width=19, .payload_index=3462 }, { .character=17/*[11]bluetooth.png*/, .width=19, .payload_index=3519 },
|
|
{ .character=18/*[12]caret-forward.png*/, .width=19, .payload_index=3576 }, { .character=19/*[13]volume-off.png*/, .width=19, .payload_index=3633 },
|
|
{ .character=20/*[14]arrow-forward.png*/, .width=19, .payload_index=3690 }, { .character=21/*[15]battery-charging.png*/, .width=19, .payload_index=3747 },
|
|
{ .character=22/*[16]arrow-up.png*/, .width=19, .payload_index=3804 }, { .character=23/*[17]wifi.png*/, .width=19, .payload_index=3861 },
|
|
{ .character=24/*[18]search.png*/, .width=19, .payload_index=3918 }, { .character=25/*[19]warning.png*/, .width=19, .payload_index=3975 },
|
|
{ .character=26/*[1A]settings.png*/, .width=19, .payload_index=4032 }, { .character=27/*[1B]add.png*/, .width=19, .payload_index=4089 },
|
|
{ .character=28/*[1C]battery-dead.png*/, .width=19, .payload_index=4146 }, { .character=29/*[1D]caret-down.png*/, .width=19, .payload_index=4203 },};
|
|
|
|
const lui_font_t LUI_DEFAULT_FONT = { .bitmap = &BITMAP_default_ubuntu_regular_17, .glyph_count = 124, .glyphs = FONT_GLYPHS_default_ubuntu_regular_17 };
|
|
|
|
|
|
/*-------------------------------------------------------------------------------
|
|
* END
|
|
*-------------------------------------------------------------------------------
|
|
*/
|