From 040b9be6b9632d3b9eb601d542d99f03f7cb2739 Mon Sep 17 00:00:00 2001 From: JiXieShi Date: Thu, 19 Dec 2024 22:49:08 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(sim/oled):=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E6=9C=AA=E4=BD=BF=E7=94=A8=E7=9A=84=E5=A4=B4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=20"graphics.h"=20=E5=92=8C=20"conio.h"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ feat(lib/CMakeLists.txt): 添加 LUI 库支持 ✨ feat(lib/utils/inc/argpase.h): 引入 "stdbool.h" 以支持布尔类型 ✨ feat(sim/display/sim_display.cpp): 重新引入 "graphics.h" 和 "conio.h" 以修复依赖 ✨ feat(sim/lvgl/lv_port_indev.cpp): 移除未使用的头文件 "graphics.h" 和 "easyx.h" ✨ feat(main.c): 添加 LUI 库支持以增强功能 ✨ feat(lib/lui/lame_ui.c): 新增 LUI 库实现以支持 UI 组件 --- demo/lvgl/test.c | 2 +- demo/oled/test.c | 2 +- lib/CMakeLists.txt | 1 + lib/key/inc/key.h | 2 +- lib/lui/inc/lame_ui.h | 3948 ++++++++++++++++++++++ lib/lui/lame_ui.c | 6376 +++++++++++++++++++++++++++++++++++ lib/oled/inc/oled.h | 2 +- lib/utils/inc/argpase.h | 1 + lib/utils/tool.cpp | 1 - main.c | 5 +- sim/Test/sim_test.cpp | 1 - sim/display/sim_display.cpp | 7 +- sim/lvgl/lv_port_indev.cpp | 20 +- sim/oled/oled.cpp | 2 - 14 files changed, 10347 insertions(+), 23 deletions(-) create mode 100644 lib/lui/inc/lame_ui.h create mode 100644 lib/lui/lame_ui.c diff --git a/demo/lvgl/test.c b/demo/lvgl/test.c index 6bf77fb..9eb0b1b 100644 --- a/demo/lvgl/test.c +++ b/demo/lvgl/test.c @@ -41,7 +41,7 @@ int Test_lvgl(void *pVoid) { // lv_port_indev_init(); // lv_example_get_started_1(); - lv_demo_widgets(); + // lv_demo_widgets(); // printf("\nTEST Widgets\n"); while (1) { diff --git a/demo/oled/test.c b/demo/oled/test.c index 1f5d689..cc5d5ec 100644 --- a/demo/oled/test.c +++ b/demo/oled/test.c @@ -105,7 +105,7 @@ OLED_T oled = { .height = 64, .width = 128, .state = IDLE, - .buf = oledbuf, + .buf = oledbuf[0], .cmd = Cmd, .data = Data, .call = Refresh_Call, diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 730f525..3b69dc4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,6 +11,7 @@ set(LIBRARIES HW_LIB_Oled oled oled/inc HW_LIB_Tft tft tft/inc HW_LIB_Flash flash flash/inc + HW_LIB_Lui lui lui/inc ) # 循环浏览库列表以创建它们 diff --git a/lib/key/inc/key.h b/lib/key/inc/key.h index a23a5fe..81a9f71 100644 --- a/lib/key/inc/key.h +++ b/lib/key/inc/key.h @@ -6,7 +6,7 @@ extern "C" { #include #include - +#include #define KEY_TICKS_INTERVAL 5 // 定时器间隔时间,单位为毫秒 #define DEBOUNCE_TICKS 0 // 按键去抖动计数阈值,最大为7(0 ~ 7) #define SHORT_TICKS (300 / KEY_TICKS_INTERVAL) // 短按阈值,单位为定时器间隔的倍数 diff --git a/lib/lui/inc/lame_ui.h b/lib/lui/inc/lame_ui.h new file mode 100644 index 0000000..bb729b3 --- /dev/null +++ b/lib/lui/inc/lame_ui.h @@ -0,0 +1,3948 @@ +/* + * Created on: 02-Apr-2020 + */ +/** + * @file lame_ui.h + * @author Avra Mitra + * @brief One and only header file for LameUI library. + * @version 2.0 + * @date 2023-10-26 + * + * @copyright Copyright (c) 2020-2023 + * + */ + +#ifndef INC_LAME_UI_H_ +#define INC_LAME_UI_H_ + +#include +#include +#include +#include + +/*-------------------------------------------- + * User Config + *-------------------------------------------- + */ + +/** + * @defgroup LUI_USER_CONFIG User Configuration + * @brief User may change configuration using these macros. All these macros + * can be found in `lame_ui.h` file. + * @{ + */ + +#define LUI_MAX_OBJECTS 200 ///< Set number of maximum objects that can be created + +// #define LUI_USE_DARK_THEME 0 ///< Comment out OR set value to 0 for using light theme + +/** + * @defgroup LUI_USE_OBJECT Widgets to use + * @brief Defines which widgets to use. Only if the relevant macro is defined, + * that widget will be compiled. Comment out the object that you don't want to + * use. This helps to save Flash. + * + * @note Some widgets, that are not in this list, are always compiled and can + * not be disabled. + * + * Also see: @ref LUI_OBJ + * @{ + */ +#define LUI_USE_LINECHART ///< Compile Linechart widget +#define LUI_USE_SWITCH ///< Compile Switch widget +#define LUI_USE_CHECKBOX ///< Compile Checkbox widget +#define LUI_USE_SLIDER ///< Compile Slider widget +#define LUI_USE_LIST ///< Compile List widget +#define LUI_USE_PANEL ///< Compile Panel widget +#define LUI_USE_TEXTBOX ///< Compile Textbox widget +#define LUI_USE_BUTTONGRID ///< Compile Buttongrid widget +#if defined(LUI_USE_BUTTONGRID) + #define LUI_USE_KEYBOARD ///< Compile Keyboard widget. (Note: To use keyboard, buttongrid must be used) +#endif +/**@} */ + +/**@} */ + +/*-------------------------------------------- + * End Config + *-------------------------------------------- + */ + +/*---------------------------------------------------- + * ------- !!! Don't edit anything below !!! --------- + *---------------------------------------------------- + */ + +/*-------------------------------------------- + * Macro Definitions + *-------------------------------------------- + */ + +#define LUI_INPUT_TYPE_TOUCH 1 +// #define LUI_INPUT_TYPE_ENCODER 2 + +/** + * @defgroup LUI_STATE LameUI input states + * @brief All input states of LameUI + * + * Also see: @ref LUI_EVENT + * @{ + */ +#define LUI_STATE_IDLE 0 ///< Idle state. Object is not under the pointing device +#define LUI_STATE_SELECTED 1 ///< Object is under the pointing device +#define LUI_STATE_PRESSED 2 ///< Object is under the pointing device and the pointing device button is pressed +#define LUI_STATE_ENTERED 3 ///< Object is in entered state. Example: Text box is clicked +/**@} */ + +/** + * @defgroup LUI_EVENT LameUI input events + * @brief All input events of LameUI. Events depend on previous and current state + * + * Also see: @ref LUI_STATE + * @{ + */ +#define LUI_EVENT_NONE 0 ///< No event occurred +#define LUI_EVENT_SELECTED 1 ///< Object is selected (under the pointing device) +#define LUI_EVENT_SELECTION_LOST 2 ///< Selection of object is lost (no more under the pointing device) +#define LUI_EVENT_PRESSED 3 ///< Object is pressed (object under pointing device and pointing device button is low) +#define LUI_EVENT_RELEASED 4 ///< Object is released (object under pointing device but pointing device button is released) +#define LUI_EVENT_ENTERED 5 ///< Object is entered to edit/input mode. Example: Clicking on a text box +#define LUI_EVENT_EXITED 6 ///< Opposite of above. Example: Exiting edit mode of a text box +#define LUI_EVENT_VALUE_CHANGED 7 ///< Value of a object is changed. Example: a slider is dragged +#define LUI_EVENT_CHECK_CHANGED 7 ///< Check status of an object is changed. Example: checkbox is checked/unchecked +/**@} */ + +/** + * @defgroup LUI_OBJ LameUI object types + * @brief All types of objects (widgets) in LameUI + * @{ + */ +#define LUI_OBJ_NONE 0 ///< No type +#define LUI_OBJ_LABEL 1 ///< Label widget. See: \ref lui_label +#define LUI_OBJ_BUTTON 2 ///< Button widget. See: \ref lui_button +#define LUI_OBJ_SWITCH 3 ///< Switch widget. See: \ref lui_switch +#define LUI_OBJ_LINECHART 4 ///< Line Chart widget. See: \ref lui_linechart +#define LUI_OBJ_PANEL 5 ///< Panel widget. See: \ref lui_panel +#define LUI_OBJ_SCENE 6 ///< Scene widget. See: \ref lui_scene +#define LUI_OBJ_GENERIC 7 ///< Generic widget (user defined widget) +#define LUI_OBJ_LIST 8 ///< List widget. See: \ref lui_list +#define LUI_OBJ_CHECKBOX 9 ///< Checkbox widget. See: \ref lui_checkbox +#define LUI_OBJ_SLIDER 10 ///< Slider widget. See: \ref lui_slider +#define LUI_OBJ_BTNGRID 11 ///< Buttongrid widget. See: \ref lui_btngrid +#define LUI_OBJ_TEXTBOX 12 ///< Textbox widget. See: \ref lui_textbox +/**@} */ + +#define LUI_LAYER_POPUP 255 +#define LUI_LAYER_SYSTEM 254 + +/** + * @defgroup LUI_KEYBOARD_MODE Keyboard modes + * @brief Keyboard modes of LameUI + * @{ + */ +#define LUI_KEYBOARD_MODE_TXT_LOWER 1 ///< lowercase text mode +#define LUI_KEYBOARD_MODE_TXT_UPPER 2 ///< UPPERCASE TEXT MODE +#define LUI_KEYBOARD_MODE_TXT_SPCL 3 ///< $pec!a1 charac+er m0d3; +/**@} */ + + +/** + * @defgroup LUI_ICONS Default built-in icons + * @brief These are default icons used by LameUI. Not to be modified by user. + * + * Icons can be used with strings like this: + * `char* str = LUI_ICON_ARROW_FORWARD "My Home" LUI_ICON_HOME`. + * Range of default icon index is 0x01 (1) to 0x1D (29). This MUST not be changed by user. + * User may add custom icons when creating font. + * Range of user defined icons is 0x80 (128) to 0xFF (255). + * + * Note: Range 0x20 (32) to 0x7F (127) must be kept free for ASCII characters. + * + * @{ + */ +#define LUI_ICON_HOME "\x01" +#define LUI_ICON_RELOAD "\x02" +#define LUI_ICON_CARET_BACK "\x03" +#define LUI_ICON_LOCATION "\x04" +#define LUI_ICON_BATTERY_FULL "\x05" +#define LUI_ICON_CHECKMARK "\x06" +#define LUI_ICON_RETURN_DOWN_BACK "\x07" +#define LUI_ICON_ARROW_DOWN "\x08" +#define LUI_ICON_BACKSPACE "\x09" +#define LUI_ICON_PAUSE "\x0A" +#define LUI_ICON_REMOVE "\x0B" +#define LUI_ICON_POWER "\x0C" +#define LUI_ICON_CARET_UP "\x0D" +#define LUI_ICON_VOLUME_MEDIUM "\x0E" +#define LUI_ICON_ARROW_BACK "\x0F" +#define LUI_ICON_CLOSE "\x10" +#define LUI_ICON_BLUETOOTH "\x11" +#define LUI_ICON_CARET_FORWARD "\x12" +#define LUI_ICON_VOLUME_OFF "\x13" +#define LUI_ICON_ARROW_FORWARD "\x14" +#define LUI_ICON_BATTERY_CHARGING "\x15" +#define LUI_ICON_ARROW_UP "\x16" +#define LUI_ICON_WIFI "\x17" +#define LUI_ICON_SEARCH "\x18" +#define LUI_ICON_WARNING "\x19" +#define LUI_ICON_SETTINGS "\x1A" +#define LUI_ICON_ADD "\x1B" +#define LUI_ICON_BATTERY_DEAED "\x1C" +#define LUI_ICON_CARET_DOWN "\x1D" +/**@} */ + +/** + * @defgroup LUI_BTNGRID_MASKS Buttongrid button property masks + * @brief Property byte masks of a buttongrid. Masks set different properties of + * a button in a buttongrid. + * @{ + */ +#define LUI_BTNGRID_MASK_BTN_DISABLED 0x40 ///< Button disabled mask +#define LUI_BTNGRID_MASK_BTN_HIDDEN 0x20 ///< Button hidden mask +#define LUI_BTNGRID_MASK_BTN_CHECKABLE 0x10 ///< Button checkable mask +#define LUI_BTNGRID_MASK_BTN_CHECKED 0x80 ///< Button checked mask +#define LUI_BTNGRID_MASK_BTN_WIDTH_UNIT 0x0F ///< Button width mask +/**@} */ + +/** + * @defgroup LUI_ALIGNMENT Content alignment flags + * @brief Content alignment flags + * @{ + */ +#define LUI_ALIGN_LEFT 0 ///< Align content to left +#define LUI_ALIGN_CENTER 1 ///< Align content to center +#define LUI_ALIGN_RIGHT 2 ///< Align content to right +/**@} */ + +/** + * @defgroup LUI_LAYOUT Widget layout flags + * @brief Widget layout flags to use with panels and scenes only. + * @{ + */ +#define LUI_LAYOUT_NONE 0 ///< No layout +#define LUI_LAYOUT_HORIZONTAL 1 ///< Horizontal layout +#define LUI_LAYOUT_VERTICAL 2 ///< Vertical layout +/**@} */ + +/** + * @defgroup LUI_SLIDER_KNOB_TYPE Slider widget knob type flag + * @brief Knob type flag for slider widget. It defines how the knob of a slider + * is rendered. + * @{ + */ +#define LUI_SLIDER_KNOB_TYPE_NONE 0 ///< No knob will be rendered +#define LUI_SLIDER_KNOB_TYPE_DEFAULT 1 ///< Default knob (a rectangle) will be rendered +#define LUI_SLIDER_KNOB_TYPE_TEXT 2 ///< Text (value and/or custom text) will be rendered +/**@} */ + +/** + * @defgroup LUI_LINECHART_DRAW_MODE Linechart widget draw mode + * @brief Draw mode flag for linechart widget. It defines how the line is drawn. + * These flags can be ORed. + * @{ + */ +#define LUI_LINECHART_DRAW_MODE_LINE (1 << 0) ///< Draw the lines by connection points +#define LUI_LINECHART_DRAW_MODE_POINT (1 << 1) ///< Draw the chart points/markers +/**@} */ + +/** + * @defgroup LUI_DEFAULT_FONT LameUI default font + * @brief This is default font of LameUI. User can access default font using this macro. + * @{ + */ +#define LUI_DEFAULT_FONT FONT_lui_default +/**@} */ + +/*-------------------------------------------- + * End Macro Definitions + *-------------------------------------------- + */ + +/*-------------------------------------------- + * Typedefs and Structs + *-------------------------------------------- + */ + +#pragma pack(push, 1) + +/** + * @defgroup PublicTypedefs Custom public datatypes. + * @brief Custom public datatypes + */ + +typedef struct +{ + uint8_t* mem_block; + uint32_t block_max_sz; + uint32_t mem_allocated; +} _lui_mem_block_t; + + +/** + * @ingroup PublicTypedefs + * @brief bitmap datatype. Used to render images. Generated using LameUI font tools + * + * Bitmap must be generated with LameUI tools (see docs). Supported + * bits-per-pixel (bpp) are 1, 8, and 16. + * @{ + */ +typedef struct _lui_bitmap_s +{ + const uint8_t* const payload; ///< Bitmap payload + const uint16_t size_x; ///< Bitmap size in x axis + const uint16_t size_y; ///< Bitmap size in y axis + const uint8_t bpp; ///< Bits-per-pixels (1, 8, 16) +} lui_bitmap_t; +/**@} */ + +/* Color palette for 1-bpp mono bitmap image. */ +/** + * @ingroup PublicTypedefs + * @brief Color palette for 1-bpp mono bitmap image. + * + * This color palette has NO effect on 8-bpp grayscale and 16-vpp rgb565 bitmaps. + * @{ + */ +typedef struct _lui_bitmap_mono_pal_s +{ + uint16_t fore_color; ///< foreground color of 1-bpp bitmap + uint16_t back_color; ///< background color of 1-bpp bitmap + uint8_t is_backgrnd; ///< 0: Don't draw any background color, 1: Draw background color +} lui_bitmap_mono_pal_t; +/**@} */ + +/* This is a font glyph description - for now it does not support kerning */ +/* See: https://freetype.org/freetype2/docs/glyphs/glyphs-3.html */ +typedef struct +{ + const uint16_t payload_index; + const char character; // ASCII code + const uint8_t width; // glyph bbox width + const uint8_t x_adv; // glyph advance x + const uint8_t x_off; // glyph left bearing x +} _lui_glyph_t; + +/** + * @ingroup PublicTypedefs + * @brief font datatype. Generated using LameUI font tools. + * + * Users must NOT read/write members directly. All actions are done using + * getter and setter functions + * @{ + */ +typedef struct _lui_font_s +{ + const lui_bitmap_t* const bitmap; + const uint8_t glyph_count; + const _lui_glyph_t* glyphs; // pointer to array of glyph_t elements +} lui_font_t; +/**@} */ + +extern const lui_font_t LUI_DEFAULT_FONT; + +/* !! For internal private use only. This was an old design + * choice and should be replaced with `lui_area_t` in future. + */ +typedef struct _lui_area_priv_s +{ + uint16_t x1; + uint16_t y1; + uint16_t x2; + uint16_t y2; +} _lui_area_priv_t; + +/** + * @ingroup PublicTypedefs + * @brief Area datatype. + * + * lui_area_t is used to define area or size of an item. + * @{ + */ +typedef struct _lui_area_s +{ + uint16_t x; ///< Start position in X axis + uint16_t y; ///< Start position in Y axis + uint16_t w; ///< Width of item + uint16_t h; ///< Height of item +} lui_area_t; +/**@} */ + +struct _lui_layout_s +{ + uint8_t type; + uint8_t pad_x; + uint8_t pad_y; + uint16_t dim; +// uint8_t align; +}; + +struct _lui_common_style_s +{ + uint16_t bg_color; + uint16_t border_color; + uint16_t width; + uint16_t height; + uint8_t border_width; +}; + +struct _lui_label_style_s +{ + uint16_t text_color; + uint8_t is_transparent_bg; +}; +struct _lui_button_style_s +{ + uint16_t label_color; + uint16_t label_pressed_color; + uint16_t pressed_color; + uint16_t selection_color; + uint8_t is_transparent_bg; +}; + +#if defined(LUI_USE_SWITCH) +struct _lui_switch_style_s +{ + uint16_t knob_on_color; + uint16_t knob_off_color; + uint16_t selection_color; +}; +#endif + +#if defined(LUI_USE_CHECKBOX) +struct _lui_checkbox_style_s +{ + uint16_t tick_color; + uint16_t bg_checked_color; + uint16_t selection_color; +}; +#endif + +#if defined(LUI_USE_SLIDER) +struct _lui_slider_style_s +{ + uint16_t knob_color; + uint16_t bg_filled_color; + uint16_t selection_color; + uint8_t knob_width; +}; +#endif + +#if defined(LUI_USE_LINECHART) +struct _lui_linechart_style_s +{ + uint16_t line_color; + uint16_t grid_color; + uint16_t point_color; + uint8_t grid_visible; + uint8_t line_width; + uint8_t point_width; + uint8_t draw_mode; +}; +#endif + +#if defined(LUI_USE_LIST) +struct _lui_list_style_s +{ + uint16_t item_label_color; + uint8_t item_has_border; + uint16_t item_border_color; +}; +#endif + +#if defined(LUI_USE_BUTTONGRID) +struct _lui_btngrid_style_s +{ + uint8_t btn_margin_hor; + uint8_t btn_margin_vert; + uint16_t btn_label_color; + uint16_t btn_pressed_color; + uint16_t btn_bg_color; +}; +#endif + +#if defined(LUI_USE_TEXTBOX) +struct _lui_textbox_style_s +{ + uint16_t text_color; + uint16_t bg_filled_color; +}; +#endif + +/** + * @ingroup PublicTypedefs + * @brief Generic object datatype. Accessed only through getter and setter functions. + * + * lui_obj_t contains all the common data of objects along with address of + * the extended data as per the type of the object + * @{ + */ +typedef struct _lui_obj_s +{ + uint16_t x; ///< Horizontal position (px) + uint16_t y; ///< Vertical position (px) + uint8_t layer; ///< Rendering layer (0 - 128) + struct _lui_common_style_s common_style; ///< Common style properties of objects + uint8_t state; ///< Input state + uint8_t event; ///< Input event + int32_t value; ///< User defined value + void (*obj_event_cb)(struct _lui_obj_s* obj); ///< Input event callback function + + uint8_t needs_refresh; ///< Object refresh flag + uint8_t visible; ///< Object visibility flag + uint8_t enabled; ///< Object input enable flag + uint8_t obj_type; ///< Object type + struct _lui_obj_s* parent; ///< Parent of object + uint8_t children_count; ///< Object's children count + struct _lui_obj_s* first_child; ///< First child of object + struct _lui_obj_s* next_sibling; ///< Next sibling of object + + void *obj_main_data; ///< Main (extended) data of the object +} lui_obj_t; +/**@} */ + +#if defined(LUI_USE_LINECHART) +typedef struct _lui_linechart_s +{ + struct + { + uint8_t hor_count; + uint8_t vert_count; + // uint16_t color; + // uint8_t is_grid; + } grid; + + struct _lui_linechart_style_s style; + + struct + { + double* source; + uint16_t points; + uint8_t auto_scale; + double y_max_value; + double y_min_value; + } data; + + // uint16_t color; + const lui_font_t* font; +} lui_chart_t; +#endif + +typedef struct _lui_label_s +{ + char* text; + const lui_font_t* font; + struct _lui_label_style_s style; +} lui_label_t; + +typedef struct _lui_button_s +{ + struct + { + const lui_font_t* font; + const char* text; + const char* text_pressed; + uint8_t text_align; + } label; + struct _lui_button_style_s style; + const lui_bitmap_t* img_idle; + const lui_bitmap_t* img_pressed; + /* Only when images are mono (1-bpp) */ + lui_bitmap_mono_pal_t img_idle_pal; + lui_bitmap_mono_pal_t img_press_pal; + + uint8_t is_checkable; +} lui_button_t; + +#if defined(LUI_USE_SWITCH) +typedef struct _lui_switch_s +{ + + struct _lui_switch_style_s style; +} lui_switch_t; +#endif + +#if defined(LUI_USE_CHECKBOX) +typedef struct _lui_checkbox_s +{ + struct _lui_label_s label; + struct _lui_checkbox_style_s style; +} lui_checkbox_t; +#endif + +#if defined(LUI_USE_SLIDER) +typedef struct _lui_slider_s +{ + struct _lui_slider_style_s style; + int16_t range_min; + int16_t range_max; + uint8_t knob_type; + // knob's center's distance relative to slider's start position. Measured along x axis for horizontal slider (and y for vertical). + uint16_t knob_center_rel_d; + const char* custom_text; + uint8_t show_value; + const lui_font_t* font; + uint8_t is_progress_bar; +} lui_slider_t; +#endif + +#if defined(LUI_USE_LIST) +struct _lui_list_item +{ + const char* text; + _lui_area_priv_t area; +}; +typedef struct _lui_list_s +{ + struct _lui_list_item** items; + struct _lui_list_style_s style; + const lui_font_t* font; + int16_t selected_item_index; + uint8_t page_count; + uint8_t current_page_index; + uint8_t items_per_page; + uint8_t page_first_item_index; + uint8_t item_min_height; + uint8_t text_align; + uint8_t max_items; + uint8_t items_cnt; + uint8_t is_dropdown; + uint8_t is_expanded; +} lui_list_t; +#endif + +#if defined(LUI_USE_KEYBOARD) +typedef struct _lui_keyboard_s +{ + lui_obj_t* target_txtbox; + uint8_t keyboard_mode; + +} lui_keyboard_t; +#endif + +#if defined(LUI_USE_BUTTONGRID) +typedef struct _lui_btngrid_s +{ + const char* *texts; + uint8_t* btn_properties; + _lui_area_priv_t* btn_area; + uint8_t btn_cnt; + uint8_t row_cnt; + const lui_font_t* font; + int16_t active_btn_index; + uint8_t needs_full_render; + struct _lui_btngrid_style_s style; +#if defined(LUI_USE_KEYBOARD) + struct _lui_keyboard_s *kb_data; +#endif +} lui_btngrid_t; +#endif + +#if defined(LUI_USE_TEXTBOX) +typedef struct _lui_textbox_s +{ + char* text_buffer; + const lui_font_t* font; + uint16_t max_len; + uint16_t caret_index; + uint16_t used_chars; + uint8_t needs_full_render; + struct _lui_textbox_style_s style; +} lui_textbox_t; +#endif + +#if defined(LUI_USE_PANEL) +typedef struct _lui_panel_s +{ + struct _lui_layout_s layout; + const lui_bitmap_t* bg_image; + /* Color palette for only when image is 1-bpp mono bitmap */ + lui_bitmap_mono_pal_t img_pal; +} lui_panel_t; +#endif + +typedef struct _lui_scene_s +{ + struct _lui_layout_s layout; + const lui_bitmap_t* bg_image; + /* Color palette for only when image is 1-bpp mono bitmap */ + lui_bitmap_mono_pal_t img_pal; + const lui_font_t* font; + +} lui_scene_t; + +/** + * @ingroup PublicTypedefs + * @brief Touch Input data + * + * touch input device data. + * + * @{ + */ +typedef struct _lui_touch_input_data_s +{ + uint8_t is_pressed; ///< 0: NOT pressed, 1: Pressed + int16_t x; ///< X position of press/touch. -1 if not Pressed/touched + int16_t y; ///< Y position of press/touch. -1 if not Pressed/touched +} lui_touch_input_data_t; +/**@} */ + +/** + * @ingroup PublicTypedefs + * @brief display driver object. + * + * @{ + */ +typedef struct _lui_disp_drv_s +{ + void (*draw_pixels_buff_cb)(uint16_t* disp_buff, lui_area_t* area); ///< draw pixels buffer callback function. + uint16_t* disp_buff; ///< display buffer. It is an array of uint16_t type. + uint16_t disp_buff_sz_px; ///< size of display buffer in pixel count (NOT in bytes) + uint16_t display_hor_res; ///< display horizontal resolution (along x axis) + uint16_t display_vert_res; ///< display vertical resolution (along y axis) +} lui_dispdrv_t; +/**@} */ + +/** + * @ingroup PublicTypedefs + * @brief touch input device object + * + * The callback function to read input should be implemented by user. + * + * @{ + */ +typedef struct _lui_touch_input_dev_s +{ + void (*read_touch_input_cb)(lui_touch_input_data_t* input); ///< callback function to read input data. + // lui_touch_input_data_t touch_data; +} lui_touch_input_dev_t; +/**@} */ + +typedef struct _lui_main_s +{ + // lui_obj_t* scenes[LUI_MAX_SCENES]; + const lui_font_t* default_font; + lui_obj_t* active_scene; + lui_obj_t* active_obj; + lui_dispdrv_t* disp_drv; + lui_touch_input_dev_t* touch_input_dev; + lui_touch_input_data_t last_touch_data; + uint8_t input_state_pressed; + uint8_t input_event_clicked; + uint8_t total_scenes; + uint8_t total_created_objects; // increases as new objs are created. It never decreases +} _lui_main_t; + +#pragma pack(pop) + +/*-------------------------------------------- + * End Typedefs/Structs + *-------------------------------------------- + */ + +/*-------------------------------------------- + * Function Prototypes + *-------------------------------------------- + */ + +/*------------------------------------------------------------------------------- + * Init and Update functions + *------------------------------------------------------------------------------- + */ + +/** + * @defgroup lui_core Initialization and UI Update API + * @brief These functions are used to initialize the core and to update it. + * - lui_init() is only called once at the very beginning. + * - lui_update() is called periodically in an infinite loop. + * @{ + */ + +/** + * @brief Initialize the LameUI core. Here user provides LameUI some RAM to + * create widgets. If and when RAM is not sufficient, widget creation will fail. + * + * NOTE: This memory does NOT include display buffer memory. Display buffer is + * set seperately using `lui_dispdrv_set_disp_buff()`. + * + * If init fails due to memory allocation problem, returns -1, else returns 0. + * + * @param mem_block an array of uint8_t. this will be used as work area + * @param size size of the alloted memory + * @return 0: Success, -1: Failure + */ +int8_t lui_init(uint8_t mem_block[], uint32_t size); + +/** + * @brief This function updates the UI by reading input and rendering widgets + * that need rendering. + * + * User must call this function periodically (e.g. every 20ms) in a loop so + * no input event is missed. Also, LameUI renders only when widgets need + * rendering. So, draw calls are not wasted. + * + */ +void lui_update(); + +/** + * @brief Sets default font of LameUI. + * + * Objects that are created after calling this, will inherit the new default font. + * But objects created earlier won't update their font automatically. + * + * @param font font + * @return int8_t 0: Success, -1: Fail + */ +int8_t lui_set_default_font(const lui_font_t* font); +/**@}*/ + +/*------------------------------------------------------------------------------- + * LUI_OBJECT (generic) related functions + *------------------------------------------------------------------------------- + */ + +/** + * @defgroup lui_object Generic object related API. Works with all widgets. + * @brief These functions are applicable for all types of objects. + * Only for a few particular types of objects, one or more of these functions have no effect. + * @{ + */ + +/** + * @brief Add a child object to a parent object, thus grouping them together + * + * @code + * // Creating a scene which will be a parent object + * lui_obj_t* parent_scene = lui_scene_create(); + * lui_scene_set_active(parent_scene); + * + * // Creating a label which will be a child of the parent scene + * lui_obj_t* child_label = lui_label_create(); + * lui_label_set_text(child_label, "I am child of a scene"); + * lui_object_add_to_parent(child_label, parent_scene); // Add child to parent + * @endcode + * + * @param obj child object + * @param parent_obj parent object + */ +void lui_object_add_to_parent(lui_obj_t* obj, lui_obj_t* parent_obj); + +/** + * @brief Remove an object from its current parent + * + * @param obj Child object + */ +void lui_object_remove_from_parent(lui_obj_t* obj); + +/** + * @brief Get a particular child by index from a parent + * + * @param obj_parent parent object + * @param child_index index of child + * @return lui_obj_t* pointer to child object. Returns NULL if not found + */ +lui_obj_t* lui_object_get_child(lui_obj_t* obj_parent, uint16_t child_index); + +/** + * @brief Set drawing area of an object + * + * @param obj target object + * @param width width + * @param height height + */ +void lui_object_set_area(lui_obj_t* obj, uint16_t width, uint16_t height); + +/** + * @brief Set width of an object + * + * @param obj target object + * @param width width + */ +void lui_object_set_width(lui_obj_t* obj, uint16_t width); + +/** + * @brief Set height of an object + * + * @param obj target object + * @param height height + */ +void lui_object_set_height(lui_obj_t* obj, uint16_t height); + +/** + * @brief Get object's position relative to its parent + * + * @param obj target object + * @param pos pos array of 2 items to return position ({x, y}) + */ +void lui_object_get_position_rel(lui_obj_t* obj, uint16_t pos[2]); + +/** + * @brief Get object's absolute position on the screen + * + * @param obj target object + * @param pos pos array of 2 items to return position ({x, y}) + */ +void lui_object_get_position_abs(lui_obj_t* obj, uint16_t pos[2]); + +/** + * @brief Set position of an object. This is the relative position of the + * object to its parent. + * + * @param obj target object + * @param x x position (relative) + * @param y y position (relative) + */ +void lui_object_set_position(lui_obj_t* obj, uint16_t x, uint16_t y); + +/** + * @brief Set only x position of an object + * + * @param obj target object + * @param x x position + */ +void lui_object_set_x_pos(lui_obj_t* obj, uint16_t x); + +/** + * @brief Set only y position of an object + * + * @param obj target object + * @param y y position + */ +void lui_object_set_y_pos(lui_obj_t* obj, uint16_t y); + +/** + * @brief Set border color of an object + * + * @param obj target object + * @param border_color border color + */ +void lui_object_set_border_color(lui_obj_t* obj, uint16_t border_color); + +/** + * @brief Deprecated!! Use `lui_object_set_border_width()` instead + * Set border's visibility of an object + * + * @param obj target object + * @param is_visible 1: visible; 0: invisible + */ +void lui_object_set_border_visibility(lui_obj_t* obj, uint8_t is_visible) __attribute__ ((deprecated)); + +/** + * @brief Set border's width or thickness. Set width to 0 to remove border + * + * @param obj target object + * @param width width/thickness value. 0 means invisible + */ +void lui_object_set_border_width(lui_obj_t* obj, uint8_t width); + +/** + * @brief Set background color of an object + * + * @param obj target object + * @param bg_color background color + */ +void lui_object_set_bg_color(lui_obj_t* obj, uint16_t bg_color); + +/** + * @brief Set event call back function for input handling + * + * This function is called when an event occurs against this object + * + * Example: + * @code + * lui_obj_t* my_button; + * void button_callback(lui_obj_t* btn_obj) + * { + * // Do something + * } + * my_button = lui_button_create(); + * lui_object_set_callback(my_button, button_callback); + * @endcode + * + * @param obj target object + * @param obj_event_cb function pointer of the callback function + */ +void lui_object_set_callback(lui_obj_t* obj, void (*obj_event_cb)(lui_obj_t* )); + +/** + * @brief Get the input state of an object + * + * @param obj target object + * @return int8_t state ID + */ +int8_t lui_object_get_state(lui_obj_t* obj); + +/** + * @brief Get input event of an object + * + * @param obj target object + * @return int8_t event ID + */ +int8_t lui_object_get_event(lui_obj_t* obj); + +/** + * @brief Set visibility of an object + * + * @param obj target object + * @param is_visible 1: visible; 0: hidden + */ +void lui_object_set_visibility(lui_obj_t* obj, uint8_t is_visible); + +/** + * @brief Set rendering layer index of an object + * + * Objects in higher layer will be rendered over the objects in lower layer. + * If 2 or more objects are in same layer, they're rendered based on the sequence + * they're added to parent. + * + * @param obj target object + * @param layer_index layer index (0 - 128) + */ +void lui_object_set_layer(lui_obj_t* obj, uint8_t layer_index); + +/** + * @brief Get rendering layer index of an object + * + * @param obj target object + * @return int16_t layer index. Returns -1 if object is NULL + */ +int16_t lui_object_get_layer(lui_obj_t* obj); + +/** + * @brief Get the type of the object + * + * @param obj object + * @return int16_t type of object. -1 if object is NULL + */ +int16_t lui_object_get_type(lui_obj_t* obj); + +/** + * @brief Enable or disble input handling of an object. If disabled, + * object won't cause input event callback + * + * @param obj target object + * @param is_enabled 1: input enabled; 0: input disabled + * @return uint8_t 1: success; 0: failed + */ +uint8_t lui_object_set_enable_input(lui_obj_t* obj, uint8_t is_enabled); + +/* Private functions (User must not call them) */ + +/** + * @private + * @brief Create a generic object with default values + * + * @return lui_obj_t* created object + */ +lui_obj_t* _lui_object_create(void); + +/** + * @private + * @brief Set needs_refresh flag for an object. This flag determines if onject will be redrawn + * + * When setting this flag for an object, flags of children and/or parent might also be set, + * depending on the requirement. + * + * @param obj target object + */ +void _lui_object_set_need_refresh(lui_obj_t* obj); + +/** + * @private + * @brief Render an object along with all its children (if any) + * + * @param obj target object + */ +void _lui_object_render_parent_with_children(lui_obj_t* obj); + +/** + * @private + * @brief Render a single object (and NOT its children) + * + * @param obj target object + */ +void _lui_object_render(lui_obj_t* obj); + +/** + * @private + * @brief Compares two objects' layers and returns (layer1 - layer2) + * + * @param p1 pointer to object 1 + * @param p2 pointer to object 2 + * @return int (layer1 - layer2) + */ +int _lui_obj_layer_cmprtr(const void *p1, const void *p2); +/* Private functions end */ + +/**@}*/ + +/*------------------------------------------------------------------------------- + * END + *------------------------------------------------------------------------------- + */ + +/*------------------------------------------------------------------------------- + * LUI_LABEL related functions + *------------------------------------------------------------------------------- + */ + +/** + * @defgroup lui_label Label widget API + * @brief API for label widgets + * + * @section label_example Example + * @image html docs/widgets_images/label.png "Dark Theme" + * @image html docs/widgets_images/label_2.png "Light Theme" + * @code + * const char* text = "Hi Universe"; + * lui_obj_t* my_label = lui_label_create(); + * lui_label_set_text(my_label, "Hello World"); + * lui_label_set_text_color(my_label, lui_rgb(255, 20, 80)); // Setting text color + * lui_object_set_bg_color(my_label, lui_rgb(10, 10, 10)); // Setting background color + * + * lui_obj_t* my_label2 = lui_label_create_and_add(parent_panel); // Creating and adding to a parent + * lui_label_set_text(my_label2, text); + * lui_label_set_font(my_label2, &FONT_montserrat_32); + * lui_object_set_position(my_label2, 0, 30); + * @endcode + * @{ + */ + +/** + * @brief Create a label with initial values + * + * @return lui_obj_t* Created label object + */ +lui_obj_t* lui_label_create(void); + +/** + * @brief Create a label with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created label object + */ +lui_obj_t* lui_label_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a label widget + * + * @param obj_lbl label object + */ +void lui_label_draw(lui_obj_t* obj_lbl); + +/** + * @brief Set font of a label. + * + * @param obj_lbl label object + * @param font font object. If NULL is passed, default font will be used + */ +void lui_label_set_font(lui_obj_t* obj_lbl, const lui_font_t* font); + +/** + * @brief Set text of a label + * + * @param obj_lbl label object + * @param text char array of text + */ +void lui_label_set_text(lui_obj_t* obj_lbl, const char* text); + +/** + * @brief Set forecolor of a label. + * @note this only changes text color and NOT background color + * + * @param obj_lbl label object + * @param text_color 16-bit color + */ +void lui_label_set_text_color(lui_obj_t* obj_lbl, uint16_t text_color); + +/** + * @brief Set whether the label background is transparent or not. + * When background is set to transparent, parent's background color + * or bitmap is used as background to simulate transparency. + * + * @param obj label object + * @param is_transparent 0: NOT transparent, 1: Transparent + */ +void lui_label_set_bg_transparent(lui_obj_t* obj, uint8_t is_transparent); +/**@}*/ +/*------------------------------------------------------------------------------- + * END + *------------------------------------------------------------------------------- + */ + +/*------------------------------------------------------------------------------- + * LUI_LINECHART related functions + *------------------------------------------------------------------------------- + */ + +#if defined(LUI_USE_LINECHART) +/** + * @defgroup lui_linechart Line Chart widget API + * @brief API for linechart widget + * + * @section linechart_example Example + * @image html docs/widgets_images/linechart.png "Dark Theme" + * @image html docs/widgets_images/linechart_2.png "Light Theme" + * @code + * double fib_data[40] = {0,0, 1,1, 2,1, 3,2, 4,3, 5,5, 6,8, 7,13, 8,21, 9,34}; + * lui_obj_t* my_chart = lui_linechart_create(); + * lui_linechart_set_data_source(my_chart, fib_data, 10); + * lui_linechart_set_grid_count(my_chart, 4, 2); + * lui_linechart_set_line_color(my_chart, lui_rgb(255, 0, 0)); + * // Setting line chart's area is important! + * lui_object_set_area(my_chart, 120, 80); + * @endcode + * @{ + */ + +/** + * @brief Create a linechart with initial values + * + * @return lui_obj_t* Created linechart object + */ +lui_obj_t* lui_linechart_create(void); + +/** + * @brief Create a linechart with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created linechart object + */ +lui_obj_t* lui_linechart_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a linechart + * + * @param obj_linechart linechart object + */ +void lui_linechart_draw(lui_obj_t* obj_linechart); + +/** + * @brief Set number of horizontal and vertical grid lines in a linechart + * + * @param obj_linechart linechart object + * @param hor_lines number of horizontal grid lines + * @param vert_lines number of vertical grid lines + */ +void lui_linechart_set_grid_count(lui_obj_t* obj_linechart, uint16_t hor_lines, uint16_t vert_lines); + +/** + * @brief Set color of grid lines + * + * @param obj_linechart linechart object + * @param color 16-bit color + */ +void lui_linechart_set_grid_color(lui_obj_t* obj_linechart, uint16_t color); + +/** + * @brief Set visibility of grid lines + * + * @param obj_linechart linechart object + * @param state 0: hidden, 1: visible (default) + */ +void lui_linechart_set_grid_visible(lui_obj_t* obj_linechart, uint8_t state); + +/** + * @brief Set color of plot line + * + * @param obj_linechart linechart object + * @param line_color 16-bit color + */ +void lui_linechart_set_line_color(lui_obj_t* obj_linechart, uint16_t line_color); + +/** + * @brief Set width of plot line + * + * @param obj_linechart linechart object + * @param line_width width of line + */ +void lui_linechart_set_line_width(lui_obj_t* obj_linechart, uint8_t line_width); + +/** + * @brief Set color of points/markers of the plot + * + * @param obj_linechart linechart object + * @param point_color 16-bit color + */ +void lui_linechart_set_point_color(lui_obj_t* obj_linechart, uint16_t point_color); + +/** + * @brief Set width of points/markers of plot + * + * @param obj_linechart linechart object + * @param point_width width of points/markers + */ +void lui_linechart_set_point_width(lui_obj_t* obj_linechart, uint8_t point_width); + +/** + * @brief Set draw mode of line chart's plot line + * Sets whether to draw line segments, or only the points/markers, or both. + * + * flags: + * LUI_LINECHART_DRAW_MODE_LINE -> draw line segments by connecting points + * LUI_LINECHART_DRAW_MODE_POINT -> draw points/markers + * + * Flags can be ORed to enable both of them. + * + * @param obj_linechart linechart object + * @param mode_flag ORed drawmode flags. + */ +void lui_linechart_set_draw_mode(lui_obj_t* obj_linechart, uint8_t mode_flag); + +/** + * @brief Set whether to apply automatic scaling on the data source or not. + * If auto scale is enabled, graph will always be in the boundary of drawing area. + * If auto scale is disabled, user must provide manual range + * @param obj_linechart linechart object + * @param auto_scale 1: enabled (default), 0: disabled + */ +void lui_linechart_set_data_auto_scale(lui_obj_t* obj_linechart, uint8_t auto_scale); + +/** + * @brief Set data range of line chart when auto scaling is disabled + * + * @param obj_linechart linechart object + * @param y_min minimum value of y axis data + * @param y_max maximum value of y axis data + */ +void lui_linechart_set_data_range(lui_obj_t* obj_linechart, double y_min, double y_max); + +/** + * @brief Set data source of the line chart + * + * Data source contains both x and y data. Odd index: X, Even index: 1. That + * means, data source array looks like: {x0, y0, x1, y1, ..., xn, yn} + * + * @param obj_linechart linechart object + * @param source array of data points (in x,y format) + * @param points number of data points + */ +void lui_linechart_set_data_source(lui_obj_t* obj_linechart, double* source, uint16_t points); +/**@}*/ +#endif +/*------------------------------------------------------------------------------- + * END + *------------------------------------------------------------------------------- + */ + +/** + * @defgroup lui_button Button widget API + * + * @brief API for button widget + * + * Buttons have 3 important visual properties: + * 1. Background color (bg color) + * 2. Bitmap image + * 3. Text + * + * Each of them can be configured for both pressed state and idle state. + * + * Rendering order is: 1.bg color -> 2.bitmap -> 3.text. + * + * If button is set as transparent bg, then bg color is not rendered, rather parent's + * bg color or bitmap ius rendered as the step 1. + * + * @section button_example1 Example + * @image html docs/widgets_images/button.png "Dark Theme" + * @image html docs/widgets_images/button_2.png "Light Theme" + * @code + * void button_callback(lui_obj_t* btn_obj) + * { + * static uint8_t light_state = 0; + * // Only turn on/off light when button is pressed. For any other event, return. + * if (lui_object_get_event(btn_obj) != LUI_EVENT_PRESSED) + * return; + * light_state = !light_state; + * if (light_state) + * { + * lui_button_set_label_text(my_button, "Turn OFF Light"); + * // turn_on_light(); + * } + * else + * { + * lui_button_set_label_text(my_button, "Turn ON Light"); + * // turn_off_light(); + * } + * + * // Do more stuffs... + * } + * lui_obj_t* my_button = lui_button_create(); + * lui_button_set_label_text(my_button, "Turn ON Light"); + * // Optionally set button area and position + * lui_object_set_area(my_button, 140, 60); + * lui_object_set_position(my_button, 20, 10); + * // set button call back function + * lui_object_set_callback(my_button, button_callback); + * @endcode + * + * @section button_example2 Example of checkable button + * This code creates a button and sets it as checkable. Also, this button changes its + * text and color when pressed. + * @code + * lui_obj_t* check_btn = lui_button_create(); + * + * // Set is as checkable + * lui_button_set_checkable(check_btn, 1); + * // This label text is for both idle and pressed state + * lui_button_set_label_text(check_btn, "Turn ON Light"); + * // Green text fo idle (normal) state + * lui_button_set_label_color(check_btn, lui_rgb(0, 255, 0)); + * // This label text is for pressed state + * lui_button_set_label_text_pressed(check_btn, "Turn OFF Light"); + * // Red text when pressed state + * lui_button_set_label_color(check_btn, lui_rgb(255, 0, 0)); + * + * // Optionally set button area and position + * lui_object_set_area(check_btn, 140, 60); + * lui_object_set_position(check_btn, 20, 10); + * @endcode + * + * @section button_example3 Example of setting background image + * Example of setting a background bitmap image for button + * @code + * #include "warning_symbol.h" // include the image bitmap + * lui_obj_t* img_btn = lui_button_create(); + * // Set area and position of button. + * lui_object_set_area(img_btn, 140, 60); + * lui_object_set_position(img_btn, 20, 10); + * // Now set the bitmap image as background. + * // WE are only setting bitmap for button idle state. No extra bitmap is set for pressed state (NULL passed) + * lui_button_set_bitmap_images(img_btn, &BITMAP_warning_symbol, NULL); + * // NOTE: if we need to set a different bitmap for pressed state too, do this: + * // lui_button_set_bitmap_images(img_btn, &BITMAP_idle, &BITMAP_pressed); + * @endcode + * + * @section button_example4 Example of setting 1-bpp mono background image + * Example of setting a 1-bpp background bitmap image for button. When we set 1-bpp images, + * we should call another function `lui_button_set_bitmap_images_mono_palette()` to set + * rendering colors of bitmaps too. + * + * The following code creates a square shaped button and sets it as checkable. Then + * sets play symbol bitmap for idle state and pause symbol bitmap for pressed state. + * These bitmaps are 1-bpp. + * + * Play button's color set to red(ish) and pause button;s color set to green(ish). + * background color for both bitmaps are ignored due to `is_bg` flag is set to 0. + * + * @code + * #include "play_symbol.h" // 1-bpp mono bitmap + * #include "pause_symbol.h" // 1-bpp mono bitmap + * lui_obj_t* img_btn = lui_button_create(); + * // Make it checkable since it's a play/pause button + * lui_button_set_checkable(img_btn, 1); + * // Set area + * lui_object_set_area(img_btn, 80, 80); + * // Now set 1-bpp mono bitmap images for idle and pressed states + * lui_button_set_bitmap_images(img_btn, &BITMAP_play_symbol, &BITMAP_pause_symbol); + * // Prepare rendering color palettes of both 'idle' and 'pressed' state images + * lui_bitmap_mono_pal_t idle_palette = { + * .fore_color = lui_rgb(255, 10, 10), + * .back_color = 0, + * .is_backgrnd = 0 // don't want to draw background (sets transparent background) + * }; + * lui_bitmap_mono_pal_t pressed_palette = { + * .fore_color = lui_rgb(5, 250, 10), + * .back_color = 0, + * .is_backgrnd = 0 // don't want to draw background (sets transparent background) + * }; + * // Now set those palettes + * lui_button_set_bitmap_images_mono_palette(img_btn, &idle_palette, &pressed_palette); + * @endcode + * + * @{ + */ + +/** + * @brief Create a button with initial values + * + * @return lui_obj_t* created button object + */ +lui_obj_t* lui_button_create(void); + +/** + * @brief Create a button with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created button object + */ +lui_obj_t* lui_button_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a button object + * + * @param obj_btn button object + */ +void lui_button_draw(lui_obj_t* obj_btn); + +/** + * @brief Set the text of the button's label. + * + * Same text is set for idle (normal) state and pressed state. + * + * @param obj_btn button object + * @param text char array of text + */ +void lui_button_set_label_text(lui_obj_t* obj_btn, const char* text); + +/** + * @brief Set text of button's label only for the pressed state + * + * @param obj_btn button object + * @param pressed_text text for pressed state + */ +void lui_button_set_label_text_pressed(lui_obj_t* obj_btn, const char* pressed_text); + +/** + * @brief Set different label texts for idle and pressed states. + * + * Calling this function is same as first calling `lui_button_set_label_text()` + * and then `lui_button_set_label_text_pressed()`. + * + * @param obj_btn button object + * @param idle_text label text when button is idle + * @param pressed_text label text when button is pressed + */ +void lui_button_set_label_texts(lui_obj_t* obj_btn, const char* idle_text, const char* pressed_text); + +/** + * @brief Set alignment of the label text. See: @ref LUI_ALIGNMENT + * + * @param obj_btn button object + * @param alignment Alignment flags. Allowed values: `LUI_ALIGN_LEFT`, `LUI_ALIGN_CENTER`, `LUI_ALIGN_RIGHT` + */ +void lui_button_set_label_align(lui_obj_t* obj_btn, uint8_t alignment); + +/** + * @brief Set text color of button's label. + * + * Same color is set for idle (normal) state and pressed state. + * + * @param obj_btn button object + * @param color 16-bit text color + */ +void lui_button_set_label_color(lui_obj_t* obj_btn, uint16_t color); + +/** + * @brief Set text color of button's label only for the pressed state + * + * @param obj_btn button object + * @param pressed_color 16-bit text color + */ +void lui_button_set_label_color_pressed(lui_obj_t* obj_btn, uint16_t pressed_color); + +/** + * @brief Set different label text-colors for idle and pressed states. + * + * Calling this function is same as first calling `lui_button_set_label_color()` + * and then `lui_button_set_label_color_pressed()`. + * + * @param obj_btn button object + * @param idle_color text color for idle state + * @param pressed_color text color for pressed state + */ +void lui_button_set_label_colors(lui_obj_t* obj_btn, uint16_t idle_color, uint16_t pressed_color); + +/** + * @brief Set font of button's label + * + * @param obj_btn button object + * @param font font object. If NULL is passed, default font will be used + */ +void lui_button_set_label_font(lui_obj_t* obj_btn, const lui_font_t* font); + +/** + * @brief Set background bitmap image for idle (normal) and pressed states of button. + * + * Images are rendered after rendering bg color. If button is set as transparent, then + * button's bg color is not rendered nut images are rendered. + * + * NOTE: Only if the image is 1-bpp monochrome, call `lui_button_set_bitmap_images_mono_palette()` + * too to set colors of the bitmap. Else, default color will be used to render 1-bpp bitmaps. + * + * @param obj_btn button object + * @param bitmap bitmap image object when button is idle (normal situation). Can be NULL + */ +void lui_button_set_bitmap_images(lui_obj_t* obj_btn, const lui_bitmap_t* idle_bitmap, const lui_bitmap_t* pressed_bitmap); + +/** + * @brief Set color palette for 1-bpp mono bitmap images. Has no effect if images are not 1-bpp. + * + * When idle and/or pressed bitmap image is/are 1-bpp mono, this function sets rendering + * colors. Has no effect for 8-bpp grayscale and 16-bpp rgb565 bitmaps. + * + * Set `idle_palette->is_backgrnd` and/or `press_palette->is_backgrnd` to 0 for only drawing the foreground and no background. + * This enables transparent background. `idle_palette->back_color` and/or `press_palette->back_color` is/are ignored in this case. + * + * @param obj_btn button object + * @param idle_palette color palette for button idle state image + * @param press_palette color palette for button pressed state image + */ +void lui_button_set_bitmap_images_mono_palette( + lui_obj_t* obj_btn, + lui_bitmap_mono_pal_t* idle_palette, + lui_bitmap_mono_pal_t* press_palette); + +/** + * @brief Set check value of button. Works only if button is set as checkable + * + * @param obj_btn button object + * @param value 0: Not checked, 1: Checked + */ +void lui_button_set_value(lui_obj_t* obj_btn, uint8_t value); + +/** + * @brief Set button as checked. Only works for checkable button + * + * @param obj_btn button object + */ +void lui_button_set_checked(lui_obj_t* obj_btn); + +/** + * @brief Set button as unchecked. Only works for checkable button + * + * @param obj_btn button object + */ +void lui_button_set_unchecked(lui_obj_t* obj_btn); + +/** + * @brief Set a button as checkable or not. Checkable buttons can be toggled. + * + * @param obj_btn button object + * @param is_checkable 0: NOT checkable, 1: Checkable + */ +void lui_button_set_checkable(lui_obj_t* obj_btn, uint8_t is_checkable); + +/** + * @brief Get checkable property of a button + * + * @param obj_btn button object + * @return uint8_t Returns 0 or 1. 0: NOT checkable, 1: Checkable + */ +uint8_t lui_button_get_checkable(lui_obj_t* obj_btn); + +/** + * @brief Get the check value (status) of a checkable button. + * + * A checkable button fires LUI_EVENET_VALUE CHANGED event on click. Call this + * function to get the current check value of a checkable button. + * + * If a button is not set to checkable using lui_button_set_checkable(), it will + * return 0 always. + * + * @param obj_btn button object + * @return uint8_t Returns 0 or 1. 0: Unchecked, 1: Checked + */ +uint8_t lui_button_get_check_value(lui_obj_t* obj_btn); + +/** + * @brief Set other colors of button object + * + * @param obj_btn button object + * @param pressed_color 16-bit color of button when it's pressed + * @param selection_color 16-bit color of button when it's selected (hovering) + */ +void lui_button_set_extra_colors(lui_obj_t* obj_btn, uint16_t pressed_color, uint16_t selection_color); + +/** + * @brief Set background of the button as transparent. + * + * When background is transparent, button's own bg color is not drawn. + * Rather, the color or bitmap of the parent item is drawn as background to + * simulate transparency. + * + * @param obj_btn button object + * @param is_transparent 0: Transparent background, 1: NOT transparent background + */ +void lui_button_set_bg_transparent(lui_obj_t* obj_btn, uint8_t is_transparent); + + +/**@}*/ + +#if defined(LUI_USE_SWITCH) +/** + * @defgroup lui_switch Switch widget API + * + * @brief API for switch widget + * + * @section switch_example Example + * @image html docs/widgets_images/switch.png "Dark Theme" + * @image html docs/widgets_images/switch_2.png "Light Theme" + * @code + * void switch_callback(lui_obj_t* switch_obj) + * { + * if (lui_object_get_event(switch_obj) != LUI_EVENT_VALUE_CHANGED) + * return; + * int8_t value = lui_switch_get_value(switch_obj); + * if (value == 0) + * { + * // Switch is turned off. Do something now. + * } + * else if (value == 1) + * { + * // Switch is turned on. Do something now. + * } + * } + * lui_obj_t* my_switch = lui_switch_create(); + * // Let's keep the switch ON by default + * lui_switch_set_on(my_switch); + * lui_object_set_position(my_switch, 10, 10); + * // Set a callback function to do stuffs when switch is toggled + * lui_object_set_callback(my_switch, switch_callback) + * + * lui_obj_t* my_switch2 = lui_switch_create(); + * lui_object_set_position(my_switch2, 140, 10); + * @endcode + * @{ + */ + +/** + * @brief Create a switch with initial values + * + * @return lui_obj_t* created switch object + */ +lui_obj_t* lui_switch_create(void); + +/** + * @brief Create a switch with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created switch object + */ +lui_obj_t* lui_switch_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a switch object + * + * @param obj_swtch switch object + */ +void lui_switch_draw(lui_obj_t* obj_swtch); + +/** + * @brief Set extra colors of switch object + * + * @param obj_swtch switch object + * @param knob_off_color 16-bit color of knob when switch is off + * @param knob_on_color 16-bit color of knob when switch is on + * @param selection_color 16-bit color when switch is selected + */ +void lui_switch_set_extra_colors(lui_obj_t* obj_swtch, uint16_t knob_off_color, uint16_t knob_on_color, uint16_t selection_color); + +/** + * @brief Get value of a switch + * + * @param obj_swtch switch object + * @return int8_t value of switch. 0: Off, 1: On, -1: Error. + */ +int8_t lui_switch_get_value(lui_obj_t* obj_swtch); + +/** + * @brief Set value of switch + * + * @param obj_swtch switch object + * @param value 1: switch on, 0: switch off + */ +void lui_switch_set_value(lui_obj_t* obj_swtch, uint8_t value); + +/** + * @brief Set switch to ON (value: 1) + * + * @param obj_swtch switch object + */ +void lui_switch_set_on(lui_obj_t* obj_swtch); + +/** + * @brief Set switch to OFF (value: 0) + * + * @param obj_swtch switch object + */ +void lui_switch_set_off(lui_obj_t* obj_swtch); +/**@}*/ +#endif + +#if defined(LUI_USE_CHECKBOX) +/** + * @defgroup lui_checkbox Checkbox widget API + * + * @brief API for checkbox widget + * + * @section checkbox_example Example + * @image html docs/widgets_images/checkbox.png "Dark Theme" + * @image html docs/widgets_images/checkbox_2.png "Light Theme" + * @code + * lui_obj_t* chkbox_cricket = lui_checkbox_create(); + * lui_checkbox_set_label_text(chkbox_cricket, "Cricket"); + * lui_object_set_position(chkbox_cricket, 0, 0); + * lui_checkbox_set_value(chkbox_cricket, 1); // This checkbox is selected + * + * lui_obj_t* chkbox_hockey = lui_checkbox_create(); + * lui_checkbox_set_label_text(chkbox_hockey, "Hockey"); + * lui_object_set_position(chkbox_hockey, 0, 25); + * + * lui_obj_t* chkbox_football = lui_checkbox_create(); + * lui_checkbox_set_label_text(chkbox_football, "Football"); + * lui_object_set_position(chkbox_football, 0, 50); + * @endcode + * @{ + */ + +/** + * @brief Create a checkbox object with initial values + * + * @return lui_obj_t* + */ +lui_obj_t* lui_checkbox_create(void); + +/** + * @brief Create a checkbox with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created checkbox object + */ +lui_obj_t* lui_checkbox_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a checkbox object + * + * @param obj_checkbox checkbox object + */ +void lui_checkbox_draw(lui_obj_t* obj_checkbox); + +/** + * @brief Set label text of a checkbox + * + * @param obj_checkbox checkbox object + * @param text text + */ +void lui_checkbox_set_label_text(lui_obj_t* obj_checkbox, const char* text); + +/** + * @brief Set label font of a checkbox + * + * @param obj_checkbox checkbox object + * @param font font + */ +void lui_checkbox_set_label_font(lui_obj_t* obj_checkbox, const lui_font_t* font); + +/** + * @brief Set label text color of a checkbox + * + * @param obj_checkbox checkbox object + * @param color 16-bit color + */ +void lui_checkbox_set_label_color(lui_obj_t* obj_checkbox, uint16_t color); + +/** + * @brief Set extra colors of checkbox + * + * @param obj_checkbox checkbox object + * @param bg_checked_color 16-bit background color of checkbox when in checked status + * @param tick_color 16-bit color of the check mark (tick) + * @param selection_color 16-bit color of checkbox when it is selected (hovered) + */ +void lui_checkbox_set_extra_colors(lui_obj_t* obj_checkbox, uint16_t bg_checked_color, uint16_t tick_color, uint16_t selection_color); + +/** + * @brief Get value of checkbox + * + * @param obj_checkbox checkbox object + * @return int8_t Value of checkbox. 0: Unchecked, 1: Checked, -1: Error + */ +int8_t lui_checkbox_get_value(lui_obj_t* obj_checkbox); + +/** + * @brief Set checkbox value + * + * @param obj_checkbox checkbox object + * @param value 0: Unchecked, 1: Checked + */ +void lui_checkbox_set_value(lui_obj_t* obj_checkbox, uint8_t value); + +/** + * @brief Set checkbox status to Checked (value: 1) + * + * @param obj_checkbox checkbox object + */ +void lui_checkbox_set_checked(lui_obj_t* obj_checkbox); + +/** + * @brief Set checkbox status to Unchecked (value: 0) + * + * @param obj_checkbox checkbox object + */ +void lui_checkbox_set_unchecked(lui_obj_t* obj_checkbox); +/**@}*/ +#endif + +#if defined(LUI_USE_SLIDER) +/** + * @defgroup lui_slider Slider widget API + * + * @brief API for slider widget + * + * Sliders can have 3 types of knob: None, Default, Text. + * - `LUI_SLIDER_KNOB_TYPE_NONE`: No knob is rendered. + * - `LUI_SLIDER_KNOB_TYPE_DEFAULT`: A rectangular knob. + * - `LUI_SLIDER_KNOB_TYPE_TEXT`: Text will be rendered in place of knob. This + * text can be value of progress bar, custom text, or both. + * + * Sliders can also act as progress bars. Set the progress bar mode using + * `lui_slider_set_progress_bar()` function. When slider is set as a progress + * bar, knob type automatically becomes Text(`LUI_SLIDER_KNOB_TYPE_TEXT`). + * + * Whether to show slider/progress bar value, can be set using `lui_slider_set_show_value()` + * function. Custom text can be shown using `lui_slider_set_text()`. + * + * @section slider_example1 Example Slider + * Example of sliders with different knob configuration + * @image html docs/widgets_images/slider.png "Dark Theme" + * @image html docs/widgets_images/slider_2.png "Light Theme" + * @code + * void slider_event_handler_cb(lui_obj_t* obj) + * { + * if (lui_object_get_event(obj) == LUI_EVENT_VALUE_CHANGED) + * { + * int16_t val = lui_slider_get_value(obj); + * // Do something with this value.. + * } + * } + * + * // Create a slider object with default knob + * slider_led_brightness = lui_slider_create(); + * // Setting slider's area is important + * lui_object_set_area(slider_led_brightness, 160, 20); + * lui_object_set_position(slider_led_brightness, 0, 5); + * // Set range of slider 0-255 + * lui_slider_set_range(slider_led_brightness, 0, 255); + * // Set default value of slider + * lui_slider_set_value(slider_led_brightness, 50); + * // Set callback function + * lui_object_set_callback(slider_led_brightness, slider_event_handler_cb); + * + * // Create a slider with text knob + * slider_2 = lui_slider_create(); + * lui_object_set_area(slider_2, 160, 20); + * lui_object_set_position(slider_2, 0, 50); + * lui_slider_set_range(slider_2, 0, 100); + * lui_slider_set_value(slider_2, 69); + * // Set knob type as text + * lui_slider_set_knob_type(slider_2, LUI_SLIDER_KNOB_TYPE_TEXT); + * // We'll show slider's value on the knob, and also some custom text + * lui_slider_set_show_value(slider_2, 1); + * lui_slider_set_text(slider_2, " %"); + * @endcode + * + * @section slider_example2 Example Progress Bar + * Example of slider as progress bar + * @image html docs/widgets_images/progress_bar.png "Dark Theme" + * @image html docs/widgets_images/progress_bar_2.png "Light Theme" + * @code + * // Create a slider + * progress_bar = lui_slider_create(); + * lui_object_set_area(progress_bar, 160, 40); + * lui_object_set_position(progress_bar, 10, 20); + * lui_slider_set_range(progress_bar, 0, 100); + * lui_slider_set_value(progress_bar, 85); + * // Set as progress bar + * lui_slider_set_progress_bar(progress_bar, 1); + * // We'll also show some custom text inside progress bar + * lui_slider_set_text(progress_bar, " %"); + * + * // What if we only want to show custom text and no value? + * // lui_slider_set_show_value(progress_bar, 0); + * @endcode + * + * @{ + */ + +/** + * @brief Create a slider object with initial values + * + * @return lui_obj_t* created slider object + */ +lui_obj_t* lui_slider_create(void); + +/** + * @brief Create a slider with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created slider object + */ +lui_obj_t* lui_slider_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw slider object + * + * @param obj_slider slider object + */ +void lui_slider_draw(lui_obj_t* obj_slider); + +/** + * @brief Set extra colors of slider + * + * @param obj_slider slider object + * @param knob_color 16-bit color of slider knob + * @param bg_filled_color 16-bit background color of the filled section of slider + * @param selection_color 16-bit color of slider when it's selected (hovered) + */ +void lui_slider_set_extra_colors(lui_obj_t* obj_slider, uint16_t knob_color, uint16_t bg_filled_color, uint16_t selection_color); + +/** + * @brief Set value of slider + * + * @param obj_slider slider object + * @param value value of slider + */ +void lui_slider_set_value(lui_obj_t* obj_slider, int16_t value); + +/** + * @brief Set whether to show value on the slider or not. + * + * When using as progress bar, this is very helpful. + * + * @param obj_slider slider object + * @param show_val 0: Do NOT show value, 1: Show value + */ +void lui_slider_set_show_value(lui_obj_t* obj_slider, uint8_t show_val); + +/** + * @brief Set minimum and maximum values of slider + * + * @param obj_slider slider object + * @param range_min minimum value of slider + * @param range_max maximum value of slider + */ +void lui_slider_set_range(lui_obj_t* obj_slider, int16_t range_min, int16_t range_max); + +/** + * @brief Get value of slider + * + * @param obj_slider slider object + * @return int16_t value of slider + */ +int16_t lui_slider_get_value(lui_obj_t* obj_slider); + +/** + * @brief Get minimum value of slider's range + * + * @param obj_slider slider object + * @return int16_t minimum value of slider + */ +int16_t lui_slider_get_min_value(lui_obj_t* obj_slider); + +/** + * @brief Get maximum value of slider's range + * + * @param obj_slider slider object + * @return int16_t maximum value of slider + */ +int16_t lui_slider_get_max_value(lui_obj_t* obj_slider); + +/** + * @brief Set custom text to be rendered on the slider. + * + * @param obj_slider slider object + * @param custom_text custom text + */ +void lui_slider_set_text(lui_obj_t* obj_slider, const char* custom_text); + +/** + * @brief Configure the slider as a progress bar. + * + * When a slider becomes progress bar, it does not take touch input. Value of + * the slider is rendered in the center, followed by the custom text (if any). + * + * @param obj_slider slider object + * @param is_progress_bar 1: Set as Progress Bar, 0: Set as normal slider + */ +void lui_slider_set_progress_bar(lui_obj_t* obj_slider, uint8_t is_progress_bar); + +/** + * @brief Set font of the slider + * + * @param obj_slider slider object + * @param font font + */ +void lui_slider_set_font(lui_obj_t* obj_slider, const lui_font_t* font); + +/** + * @brief Set knob type of the slider. See @ref LUI_SLIDER_KNOB_TYPE. + * + * @param obj_slider slider object + * @param knob_type Allowed values: `LUI_SLIDER_KNOB_TYPE_NONE`, `LUI_SLIDER_KNOB_TYPE_DEFAULT`, + * `LUI_SLIDER_KNOB_TYPE_TEXT`, + * @return int8_t 0: Success, -1: Fail + */ +int8_t lui_slider_set_knob_type(lui_obj_t* obj_slider, uint8_t knob_type); +/**@}*/ +#endif + +#if defined(LUI_USE_LIST) +/** + * @defgroup lui_list List widget API + * + * @brief API for list widget + * + * list is a collection of items. List can be dropdown or static. A + * dropdown list can be expanded/contracted while a static list cannot be. + * + * Some important points about list: + * 1. Number of maximum items must be set by calling `lui_list_set_max_items_count()` + * function before items can be added. + * 2. Maximum items count cannot be changed anymore once set. LameUI does NOT + * support memory reallocation. + * 3. After changes are made to a list like changing area/position, addition/removal + * of items etc., `lui_list_prepare()` must be called to prepare the list. + * + * @section list_example Example + * @image html docs/widgets_images/list.png "Dark Theme" + * @image html docs/widgets_images/list_2.png "Light Theme" + * @code + * void my_list_callback(lui_obj_t* list_obj) + * { + * uint8_t event = lui_object_get_event(list_obj); + * if (event == LUI_EVENT_PRESSED) + * { + * int index = lui_list_get_selected_item_index(list_obj); + * char* txt = lui_list_get_item_text(list_obj, index); + * + * // We got both index and text of selected item. + * // Now do something with that information. + * } + * } + * + * lui_obj_t* my_list = lui_list_create(); + * + * // Setting list area is important. Else items won't be properly rendered + * lui_object_set_area(my_list, 110, 160); + * lui_object_set_position(my_list, 50, 5); + * + * // Setting max items count is must. Otherwise we can't add items. + * // Note: This is a one-time process. Max items count cannot be changed later + * lui_list_set_max_items_count(my_list, 20); + * + * // Now, let's add some items. We can not add more than 20 items + * lui_list_add_item(my_list, "--Select--"); + * lui_list_add_item(my_list, "Algerian"); + * lui_list_add_item(my_list, "Amharic "); + * lui_list_add_item(my_list, "Assamese"); + * lui_list_add_item(my_list, "Bavarian"); + * lui_list_add_item(my_list, "Bengali"); + * lui_list_add_item(my_list, "Czech"); + * lui_list_add_item(my_list, "Deccan"); + * lui_list_add_item(my_list, "Dutch"); + * lui_list_add_item(my_list, "English"); + * lui_list_add_item(my_list, "French"); + * lui_list_add_item(my_list, "German"); + * lui_list_add_item(my_list, "Hindi"); + * + * // Now make this list a dropdown list + * lui_list_set_dropdown_mode(my_list, 1); + * + * // Change the selected item to 6th (index = 5) + * lui_list_set_selected_item_index(my_list, 5); + * + * // Set a callback function + * lui_object_set_callback(my_list, my_list_callback); + * + * // IMPORTANT! After everything is done, we must prepare the list. + * // Else nothing will work + * lui_list_prepare(my_list); + * @endcode + * @{ + */ + +/** + * @brief Create a list object with initial values + * + * @return lui_obj_t* created list object + */ +lui_obj_t* lui_list_create(void); + +/** + * @brief Create a list with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created list object + */ +lui_obj_t* lui_list_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Set maximum item count for a list. The list cannot store items more + * than this number. + * + * This function must be called once before adding items to the list. + * Trying to add items before setting max items count will fail. + * Once max items count is set, it can not be changed anymore. Calling + * this function again will fail and return -1. + * + * @param obj list object + * @param max_items_cnt maximum items count + * @return int8_t error code. 0: Success, -1: Fail + */ +int8_t lui_list_set_max_items_count(lui_obj_t* obj, uint8_t max_items_cnt); + +/** + * @brief Draw list object + * + * @param obj_list list object + */ +void lui_list_draw(lui_obj_t* obj_list); + +/** + * @brief Add an item to the end of the list. + * + * Fails if added items exceeds `max items count` set by `lui_list_set_max_items_count()` + * function + * + * @param obj_list list object + * @param text item text + * @return int8_t error code. 0: Success, -1: Fail + */ +int8_t lui_list_add_item(lui_obj_t* obj_list, const char* text); + +/** + * @brief Remove an item from list at a specific index + * + * @param obj list object + * @param item_index item index + * @return int8_t error code. 0: Success, -1: Fail + */ +int8_t lui_list_remove_item(lui_obj_t* obj, uint8_t item_index); + +/** + * @brief Remove all items from a list + * + * @param obj list object + * @return int8_t error code. 0: Success, -1: Fail + */ +int8_t lui_list_remove_all(lui_obj_t* obj); + +/** + * @brief Get index of selected item of a list + * + * @param obj list object + * @return int16_t item index. -1 if failed. + */ +int16_t lui_list_get_selected_item_index(lui_obj_t* obj); + +/** + * @brief Set selected item's index in a list + * + * @param obj list object + * @param item_index item index + * @return int16_t 0: Success, -1: Fail + */ +int16_t lui_list_set_selected_item_index(lui_obj_t* obj, uint8_t item_index); + +/** + * @brief Get text of a list item by its index + * + * @param obj list object + * @param item_index item index + * @return char* text + */ +const char* lui_list_get_item_text(lui_obj_t* obj, uint8_t item_index); + +/** + * @brief Set text of a list item by its index + * + * @param obj list object + * @param text item text + * @param item_index item index + * @return int8_t 0: Success, -1:Fail + */ +int8_t lui_list_set_item_text(lui_obj_t* obj, const char* text, uint8_t item_index); + +/** + * @brief Set if list is a dropdown or not. + * + * When list is dropdown, it can be expanded/contracted by a button. When a list + * is not a dropdown, it is always expanded. + * + * @param obj list object + * @param is_dropdown 0: NOT dropdown, 1: Dropdown + * @return int8_t 0:Success, -1: Fail + */ +int8_t lui_list_set_dropdown_mode(lui_obj_t* obj, uint8_t is_dropdown); + +/** + * @brief Expand a dropdown list manually. Works only if list mode is set as + * dropdown. Otherwise, has no effect. + * + * @param obj list object + * @param is_expanded 0: NOT expanded, 1: Expanded + * @return int8_t 0: Success, -1: Fail + */ +int8_t lui_list_set_dropdown_expand(lui_obj_t* obj, uint8_t is_expanded); + +/** + * @brief Prepares a list for final render. This function must be called by + * the user after creating a list/making changes in a list. Else, those + * changes won't be reflected. + * + * @param obj_list list object + */ +void lui_list_prepare(lui_obj_t* obj_list); + +/** + * @brief Sets minimum height of each item in a list + * + * @param obj_list list object + * @param height minimum height of an item + */ +void lui_list_set_item_min_height(lui_obj_t* obj_list, uint8_t height); + +/** + * @brief Set font of list. If none is set, default font is used + * + * @param obj_list list object + * @param font font of list + */ +void lui_list_set_font(lui_obj_t* obj_list, const lui_font_t* font); + +/** + * @brief Set alignment of texts in a list. + * + * If alignment is changed after the list is already prepared, call `list_prepare() + * function again so new alignment is applied to all the items. + * + * @param obj_list list object + * @param alignment Alignment flag. Allowed values are: LUI_ALIGN_LEFT, LUI_ALIGN_CENTER, LUI_ALIGN_RIGHT + */ +void lui_list_set_text_align(lui_obj_t* obj_list, uint8_t alignment); + +/** + * @brief Set text color of list items + * + * @param obj_list list object + * @param color 16-bit color + */ +void lui_list_set_text_color(lui_obj_t* obj_list, uint16_t color); + +/** + * @brief Set whether list items have border or not + * + * @param obj_list list object + * @param has_border 0: Items have NO border, 1: Items have border + * @param border_color 16-bit border color + */ +void lui_list_set_item_border(lui_obj_t* obj_list, uint8_t has_border, uint16_t border_color); + +/** + * @brief Sets label text color of navigation buttons of a list. Navigation buttons + * are used to change current page of list + * + * @param obj_list list object + * @param color 16-bit color + */ +void lui_list_set_nav_btn_label_color(lui_obj_t* obj_list, uint16_t color); + +/** + * @brief Sets label background color of navigation buttons of a list. Navigation buttons + * are used to change current page of list + * + * @param obj_list list object + * @param color 16-bit color + */ +void lui_list_set_nav_btn_bg_color(lui_obj_t* obj_list, uint16_t color); + +/** + * @brief Sets extra colors of list + * + * @param obj_list list object + * @param pressed_color 16-bit color of list item when pressed + * @param selection_color 16-bit color of list item when selected (hovered) + */ +void lui_list_set_nav_btn_extra_colors(lui_obj_t* obj_list, uint16_t pressed_color, uint16_t selection_color); + +/** + * @brief Sets current page index of a list. The current page is rendered. + * Has no effect if index is out of bound. + * + * @param obj list object + * @param index index of current page + */ +void lui_list_set_page_index(lui_obj_t* obj, uint8_t index); + +/** + * @private + * @brief Adds navigation buttons to list. Only called by the library. + * + * @param obj_list list object + */ +void _lui_list_add_nav_buttons(lui_obj_t* obj_list); + +/** + * @private + * @brief Sets callback function to handle nav button events. Only called by + * the library. + * + * @param obj_list list object + */ +void _lui_list_nav_btn_cb(lui_obj_t* obj_list); + +/** + * @private + * @brief Adds actual button objects that are created during `lui_list_add_item()` call. + * Only called by the library. + * + * @param obj_list list object + * @param obj_btn button object, which is a list item + */ +void _lui_list_add_button_obj(lui_obj_t* obj_list, lui_obj_t* obj_btn); +/**@}*/ +#endif + +#if defined(LUI_USE_BUTTONGRID) +/** + * @defgroup lui_btngrid Buttongrid widget API + * + * @brief API for buttongrid widget + * + * buttongrid is a grid of buttons. It's way more effcient than adding + * individual button objects to forma grid/matrix. For example, if we need 20 + * buttons in a 5x4 grid, creating 20 individual button objects will take lots of + * RAM. Rather using a 5x4 buttongrid will only create a single object and saves + * RAM significantly. + * + * Practical examples of buttongrid are qwerty keyboards, numpads etc.. + * + * @section buttongrid_example1 Example 1 + * Let's create a simple numpad using buttongrid. + * @image html docs/widgets_images/btngrid_simple.png "Dark Theme" + * @image html docs/widgets_images/btngrid_simple_2.png "Light Theme" + * @code + * lui_obj_t* numpad = lui_btngrid_create(); + * // Setting buttongrid area is important. Else items won't be properly rendered + * lui_object_set_area(numpad, 200, 200); + * + * // Text map of the numpad + * const char* numpad_txt_map[] = + * { + * "7", "8", "9", "\n", + * "4", "5", "6", "\n", + * "1", "2", "3", "\n", + * "0", "00", ".", "\n", + * "+", "-", "=", "\0" + * }; + * + * // Property map. Notice how the "=" button has twice the width of "+" and "-". + * const uint8_t numpad_property_map[] = + * { + * 1, 1, 1, + * 1, 1, 1, + * 1, 1, 1, + * 1, 1, 1, + * 1, 1, 2 + * }; + * + * lui_btngrid_set_textmap(numpad, numpad_txt_map); + * lui_btngrid_set_propertymap(numpad, numpad_property_map); + * + * lui_btngrid_set_btn_margin(numpad, 5, 5); + * @endcode + * + * @section buttongrid_example2 Example 2 + * Let's create a simple control panel and use most of the features of buttongrid. + * Also we'll see how to use callback for buttongrid. + * @image html docs/widgets_images/btngrid_advanced.png "Dark Theme" + * @image html docs/widgets_images/btngrid_advanced_2.png "Light Theme" + * @code + * void controlpanel_callback(lui_obj_t* ctrlpanel_btngrid) + * { + * // Get index of the pressed button + * int active_btn_index = lui_btngrid_get_acive_btn_index(ctrlpanel_btngrid); + * if (active_btn_index == -1) return; + * // Get text of the button + * const char* btn_txt = lui_btngrid_get_btn_text(ctrlpanel_btngrid, active_btn_index); + * if (txt == NULL) return; + * + * // We can check which button is pressed either by its index or by its text. + * // In this example we'll use text of the button. + * + * if (strcmp(txt, "Motor 1") == 0) + * { + * // Do something with motor 1 + * } + * else if (strcmp(txt, "Fan 1") == 0) + * { + * // Do something with fan 1 + * } + * else if (strcmp(txt, "Clr T1") == 0) + * { + * // This button is checkable. So, get its check status. + * + * int8_t clr_T1_status = lui_btngrid_get_btn_check_status(ctrlpanel_btngrid, active_btn_index); + * if (clr_T1_status == 1) + * { + * // Checked, do something + * } + * else if (clr_T1_status == 0) + * { + * // Unchecked, do something else + * } + * else + * { + * // Error, return + * return; + * } + * } + * + * //... Check other buttons too .... + * } + * + * lui_obj_t* controlpanel = lui_btngrid_create(); + * // Setting buttongrid area is important. Else items won't be properly rendered + * lui_object_set_area(controlpanel, 640, 240); + * + * // Text map of the controlpanel + * const char* controlpanel_txt_map[] = + * { + * "Motor 1", "Fan 1", "Heater L1", "Heater L2", "\n", + * "Clr T1", "Clr T2","Ring A", "Ring B", "\n", + * "X", "Y", "Z", "Stop", "\n", + * "-", "+", "\0" + * }; + * + * // Property map. 2 buttons are set checkable and 1 button is disabled + * const uint8_t controlpanel_property_map[] = + * { + * 1, 1, 1, 1, + * 1|LUI_BTNGRID_MASK_BTN_CHECKABLE, 1|LUI_BTNGRID_MASK_BTN_CHECKABLE, 1, 1, + * 1, 1, 1, + * 1, 1, 1, 3|LUI_BTNGRID_MASK_BTN_DISABLED, + * 1, 1, 2 + * }; + * + * lui_btngrid_set_textmap(controlpanel, controlpanel_txt_map); + * lui_btngrid_set_propertymap(controlpanel,controlpanel_property_map); + * + * lui_btngrid_set_btn_margin(controlpanel, 2, 5); + * lui_object_set_callback(controlpanel, controlpanel_callback); + * + * @endcode + * @{ + */ + +/** + * @brief Create a buttongrid with initial values + * + * @return lui_obj_t* created buttongrid object + */ +lui_obj_t* lui_btngrid_create(void); + +/** + * @brief Create a buttongrid with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created buttongrid object + */ +lui_obj_t* lui_btngrid_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a buttongrid + * + * @param obj buttongrid object + */ +void lui_btngrid_draw(lui_obj_t* obj); + +/** + * @brief Set texts of all buttons as a map. The grid is created based on this + * text map. Each item in the array makes a column and `\n` (newline) cretaes + * a row. The text map's last item must be a `\0`. This marks the end of the grid + * + * @param obj buttongrid object + * @param texts array of strings to be used as text map. + */ +void lui_btngrid_set_textmap(lui_obj_t* obj, const char* texts[]); + +/** + * @brief Set properties of all buttons as a map. + * + * Each item in the array is a 8-bit value denoting various properties of a + * button of the buttongrid. The size of array must be same as the number of + * buttons in the button grid.The structure of a property byte is as follows: + * + * | Bit | Meaning | Value | + * |-----|----------------------|--------------------------------------------------| + * | 7 | Is button checked? | 0: Unchecked,
1: Checked | + * | 6 | Is button disabled? | 0: NOT disabled (i.e., Enabled),
1: Disabled | + * | 5 | Is button hidden? | 0: NOT hidden (i.e., visible),
1: Hidden | + * | 4 | Is button checkable? | 0: NOT checkable,
1: Checkable | + * | 3:0 | Button width unit | 1-15,
Setting it to 0 has no effect | + * + * Example: To make a checkable button with 3 unit width and to set it to `checked` + * status, property byte should be: (1 << 7) | (1 << 4) | 3 + * + * @param obj buttongrid object + * @param properties array of property bytes + */ +void lui_btngrid_set_propertymap(lui_obj_t* obj, const uint8_t properties[]); + +/** + * @brief Set property byte (8 bits) of a particular button in a buttongrid. + * + * Also see: @ref lui_btngrid_set_propertymap() + * + * @param obj buttongrid object + * @param btn_index index of the button whose property is being set + * @param property_byte 8-bit property value + */ +void lui_btngrid_set_btn_property_bits(lui_obj_t* obj, uint16_t btn_index, uint8_t property_byte); + +/** + * @brief Set text of a particular button in a buttongrid. + * + * @param obj buttongrid object + * @param btn_index index of the button whose text is being set + * @param text text of the button + */ +void lui_btngrid_set_btn_text(lui_obj_t* obj, uint8_t btn_index, char* text); + +/** + * @brief Get text of a particular button in a buttongrid + * + * @param obj buttongrid object + * @param btn_index index of the button + * @return const char* text of the button + */ +const char* lui_btngrid_get_btn_text(lui_obj_t* obj, uint16_t btn_index); + +/** + * @brief Set the width unit of a particular button in a buttongrid + * + * @param obj buttongrid object + * @param btn_index index of the button whose width unit is being set + * @param width_unit width unit. Range is 1-15 + */ +void lui_btngrid_set_btn_width_unit(lui_obj_t* obj, uint16_t btn_index, uint8_t width_unit); + +/** + * @brief Hide/unhide a particular button in the buttongrid + * + * @param obj buttongrid object + * @param btn_index index of the button + * @param hidden 0: Visible, 1: Hidden + */ +void lui_btngrid_set_btn_hidden(lui_obj_t* obj, uint16_t btn_index, uint8_t hidden); + +/** + * @brief Set if a particular button is checkable or not + * + * @param obj buttongrid object + * @param btn_index index of the button + * @param checkable 0: NOT checkable, 1: Checkable + */ +void lui_btngrid_set_btn_checkable(lui_obj_t* obj, uint16_t btn_index, uint8_t checkable); + +/** + * @brief Set the check status of a particular button in button grid + * + * @param obj buttongrid object + * @param btn_index index of the button + * @param checked 0: NOT checked, 1: Checked + */ +void lui_btngrid_set_btn_checked(lui_obj_t* obj, uint16_t btn_index, uint8_t checked); + +/** + * @brief Get the index of currently active button, i.e., the button which has + * the input. + * + * @param obj buttongrid object + * @return int16_t Index of active button. -1 if error. + */ +int16_t lui_btngrid_get_acive_btn_index(lui_obj_t* obj); + +/** + * @brief Get the check status of a particular button in button grid + * + * @param obj buttongrid object + * @param btn_index index of the button + * @return int8_t Check status. 0: NOT checked, 1: Checked, -1: Error + */ +int8_t lui_btngrid_get_btn_check_status(lui_obj_t* obj, uint8_t btn_index); + +/** + * @brief Set font of button grid text + * + * @param obj buttongrid object + * @param font font object + */ +void lui_btngrid_set_font(lui_obj_t* obj, const lui_font_t* font); + +/** + * @brief Set extra colors of button grid + * + * @param obj buttongrid object + * @param btn_color 16-bit color of button + * @param label_color 16-bit color of button texts + * @param btn_pressed_color 16-bit color of pressed buttons + */ +void lui_btngrid_set_extra_colors(lui_obj_t* obj, uint16_t btn_color, uint16_t label_color, uint16_t btn_pressed_color); + +/** + * @brief Set margin of buttons in a button grid + * + * @param obj buttongrid object + * @param margin_x margin in X axis + * @param margin_y margin in Y axis + */ +void lui_btngrid_set_btn_margin(lui_obj_t* obj, uint8_t margin_x, uint16_t margin_y); + +/** + * @private + * @brief Calculate buttongrid area + * + * @param obj buttongrid object + */ +void _lui_btngrid_calc_btn_area(lui_obj_t* obj); +/**@}*/ +#endif + +#if defined(LUI_USE_KEYBOARD) && defined(LUI_USE_BUTTONGRID) +/** + * @defgroup lui_keyboard Keyboard widget API + * + * @brief API for Keyboard widget + * + * keyboard is a special typw of buttongrid. Keyboard has 3 modes. + * See : @ref LUI_KEYBOARD_MODE. + * + * Usually keyboard is used along with a textbox. See: @ref lui_textbox + * + * Steps to use a keyboard with a textbox are: + * 1. Create a keyboard object. Note: Keyboards are hidden by default. + * 2. Create a textbox object. + * 3. Create a callback function for the textbox + * 3.1. If event is LUI_EVENT_ENTERED, set the target keyboard for this textbox. + * This will make the keyboard visible. + * 3.2. If event is LUI_EVENT_EXITED, set target keyboard as NULL. This will hide + * the keyboard again. + * + * @section keyboard_example Example + * This example only creates a keyboard and links it with an existing textbox. + * To see a more complete example, see textbox example section. + * See: @ref lui_textbox + * @image html docs/widgets_images/keyboard.png "Dark Theme" + * @image html docs/widgets_images/keyboard_2.png "Light Theme" + * @code + * lui_obj_t* my_keyboard = lui_keyboard_create(); + * // Keyboard is by default hidden. It is supposed to be made visible when a + * // textbox is clicked on. But for this example, we are making it visible + * // manually. + * lui_object_set_visibility(my_keyboard, 1); + * + * // Set the target textbox + * lui_keyboard_set_target_txtbox(my_keyboard. my_txtbox); + * @endcode + * @{ + */ + +/** + * @brief Create a keyboard object with initial values + * + * @return lui_obj_t* Created keyboard (buttongrid) object + */ +lui_obj_t* lui_keyboard_create(void); + +/** + * @brief Create a keyboard with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created keyboard object + */ +lui_obj_t* lui_keyboard_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Get the text of a key against its index. + * + * @param obj keyboard (buttongrid) object + * @param btn_index index of the key/button + * @return const char* text of the key + */ +const char* lui_keyboard_get_key_text(lui_obj_t* obj, uint8_t btn_index); + +/** + * @brief Set the operation mode of keyboard. + * + * For allowed modes, see: @ref LUI_KEYBOARD_MODE + * + * @param obj keyboard (buttongrid) object + * @param mode mode + */ +void lui_keyboard_set_mode(lui_obj_t* obj, uint8_t mode); + +/** + * @brief Set font of the keyboard + * + * @param obj keyboard (buttongrid) object + * @param font font + */ +void lui_keyboard_set_font(lui_obj_t* obj, const lui_font_t* font); + +/** + * @brief Set the target textbox of the keyboard. When target textbox is set, + * the keyboard becomes visible and only the target textbox receives input. + * To hide the keyboard again, tap/click on the "check/ok" button on the keyboard. + * + * @param obj_kb keyboard (buttongrid) object + * @param obj_txtbox textbox object + */ +void lui_keyboard_set_target_txtbox(lui_obj_t* obj_kb, lui_obj_t* obj_txtbox); + +/** + * @brief Keyboard callback function. This function handles all key presses. + * + * If user needs to write their own callback for keyboard, they must call this + * function from their own callback function. + * + * Example: + * @code + * // User defined custom callback function for keyboard + * void kb_user_callback(lui_obj_t* sender) + * { + * // Keyboard key is pressed. Do something + * // .......... + * // [Mandatory] After all user stuffs are done, must call lui_keyboard_sys_cb() + * lui_keyboard_sys_cb(sender); + * } + * + * lui_obj_t* keyboard = lui_keyboard_create(); + * lui_object_set_callback(keyboard, kb_user_callback); + * @endcode + * + * @param obj_sender sender object + */ +void lui_keyboard_sys_cb(lui_obj_t* obj_sender); +/**@}*/ +#endif + +#if defined(LUI_USE_TEXTBOX) +/** + * @defgroup lui_textbox Textbox widget API + * + * @brief API for textbox widget + + * Usually textbox is used along with a keyboard. See: @ref lui_keyboard + * + * Steps to use a keyboard with a textbox are: + * 1. Create a keyboard object. Note: Keyboards are hidden by default. + * 2. Create a textbox object. + * 3. Create a callback function for the textbox + * 3.1. If event is LUI_EVENT_ENTERED, set the target keyboard for this textbox. + * This will make the keyboard visible. + * 3.2. If event is LUI_EVENT_EXITED, set target keyboard as NULL. This will hide + * the keyboard again. + * + * @section textbox_example Example + * @image html docs/widgets_images/textbox_keyboard.png "Dark Theme" + * @image html docs/widgets_images/textbox_keyboard_2.png "Light Theme" + * @code + * char txtbox_buff[50]; + * lui_obj_t* my_keyboard; + * lui_obj_t* my_textbox; + * + * // This callback is fired when user enters/exists the textbox. + * // Here, we set the target textbox of the keyboard when user enters the textbox. + * // This unhides the keyboard and textbox will receive inputs from the textbox. + * // When user exits the textbox by closing the keyboard, keyboard gets hidden. + * void textbox_callback(lui_obj_t* obj_txtbox) + * { + * int8_t event = lui_object_get_event(obj_txtbox); + * if (event == LUI_EVENT_ENTERED) + * { + * lui_keyboard_set_target_txtbox(my_keyboard, my_textbox); + * } + * else if (event == LUI_EVENT_EXITED) + * { + * lui_keyboard_set_target_txtbox(my_keyboard, NULL); + * } + * } + * + * // Create a textbox object + * my_textbox = lui_textbox_create(); + * // Important to set area of textbox. + * lui_object_set_area(my_textbox, 200, 40); + * //[Mandatory] Must set a buffer where the text will be stored + * lui_textbox_set_text_buffer(my_textbox, txtbox_buff, 40); + * // Let's set an initial string for the testbox. + * // Note: the size of string does NOT include the null (\0) terminating char. + * lui_textbox_insert_string(my_textbox, (char*)"Hello!!", 7); + * //[Important] Set the callback for textbox. In this callback, we will + * // link/unlink this textbox with a keyboard object. That will hide/unhide + * // the keyboard. + * lui_object_set_callback(my_textbox, textbox_callback); + * + * // Create a keyboard + * my_keyboard = lui_keyboard_create(); + * // Keyboard is by default hidden. It is made visible when a + * // textbox is clicked on. + * @endcode + * @{ + */ + +/** + * @brief Create a textbox object with initial values + * + * @return lui_obj_t* created textbox object + */ +lui_obj_t* lui_textbox_create(void); + +/** + * @brief Create a textbox with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created textbox object + */ +lui_obj_t* lui_textbox_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw a textbox + * + */ +void lui_textbox_draw(lui_obj_t* obj); + +/** + * @brief Enter edit mode (input mode) of a textbox + * + * @param obj Textbox object + */ +void lui_textbox_enter_edit_mode(lui_obj_t* obj); + +/** + * @brief Exit edit mode of a textbox + * + * @param obj Textbox object + */ +void lui_textbox_exit_edit_mode(lui_obj_t* obj); + +/** + * @brief Set the index (position) of caret (cursor) in the textbox + * + * Text inputs are inserted at the position of caret. + * + * @param obj textbox object + * @param caret_index caret index + */ +void lui_textbox_set_caret_index(lui_obj_t* obj, uint16_t caret_index); + +/** + * @brief Get the caret index + * + * @param obj textbox object + * @return uint16_t caret index + */ +uint16_t lui_textbox_get_caret_index(lui_obj_t* obj); + +/** + * @brief Insert a character at the position of caret. + * + * This increments the caret index by 1 after the insert operation. + * + * @param obj textbox object + * @param c character + * @return int8_t 0: Success, -1: Error + */ +int8_t lui_textbox_insert_char(lui_obj_t* obj, char c); + +/** + * @brief Insert a string at the position of caret + * + * This increments the caret index by number of inserted chars after the insert operation. + * + * @param obj textbox object + * @param str string (character array) + * @param len length of the string NOT including the null character + * @return int8_t 0: Success, -1: Error + */ +int8_t lui_textbox_insert_string(lui_obj_t* obj, char* str, uint16_t len); + +/** + * @brief Delete a character at the caret index. Does not work when caret is at 0 + * + * @param obj textbox object + * @return int8_t 0: Success, -1: Error + */ +int8_t lui_textbox_delete_char(lui_obj_t* obj); + +/** + * @brief Set the text buffer of a textbox. This is where the text is stored. + * + * @param obj textbox object + * @param text_buffer buffer (character array) + * @param buff_size buffer size + */ +void lui_textbox_set_text_buffer(lui_obj_t* obj, char* text_buffer, uint16_t buff_size); + +/** + * @brief Empty the text buffer and reset caret position + * + * @param obj textbox object + */ + +void lui_textbox_clear_text_buffer(lui_obj_t* obj); +/** + * @brief Set font of the textbox + * + * @param obj textbox object + * @param font font object + */ +void lui_textbox_set_font(lui_obj_t* obj, const lui_font_t* font); +/**@}*/ +#endif + +#if defined(LUI_USE_PANEL) +/** + * @defgroup lui_panel Panel widget API + * + * @brief API for panel widget + * + * A panel is just a container for multiple widgets (objects). This is used for + * grouping widgets together. Panels can act as parent widget and must have a + * scene as its parent. + * + * When widgets are added as children to a panel, their posions are relative to + * the panel. For example, assume a panel is at (X=10, Y=20) position relative to its + * parent scene. Now create a button and set the panel as its parent. Now, the position + * of the button will be realtive to the panel. Setting the button's position to + * (X=5, Y=3) means it global position actually is (X=15, Y=23). + * + * Moving a panel to a new position also moves all its children with it. Making a panel + * hidden also hides all its children with it. Scaling a panel does NOT scales its + * children. But if the new scale is too small to contain all its children, render + * result will be unpredictable. + * + * @note A panel can have layout. All children under the panel will follow the layout. When layout + * is enabled, children's position is set by that layout. Currently only horizontal and vertical layouts are supported. + * + * @section panel_example1 Example of panel + * @code + * + * // Create a panel object + * lui_obj_t* my_panel = lui_panel_create(); + * // Important to set area of panel. + * lui_object_set_area(my_panel, 320, 300); + * // Set position of the panel + * lui_object_set_position(my_panel, 10, 20); + * // Set panel background color + * lui_object_set_bg_color(my_panel, lui_rgb(200, 128, 189)) + * + * // Create a button + * lui_obj_t* my_button_1 = lui_button_create(); + * // .... [ set buttons area, text, callback etc.] ... + * // Now add it to the panel + * lui_object_add_to_parent(my_button_1, my_panel); + * // This position is relative to the panel + * lui_object_set_position(my_button_1, 5, 5); + * + * // Create a label + * lui_obj_t* my_label_1 = lui_label_create(); + * // .... [ set label text, color, area etc.] ... + * // Now add it to the panel + * lui_object_add_to_parent(my_label_1, my_panel); + * // This position is relative to the panel + * lui_object_set_position(my_label_1, 5, 35); + * + * // ... [Keep adding more widgets under the panel] ... + * @endcode + * + * @section panel_example2 Example of setting background image + * Example of setting a background bitmap image + * @code + * #include "sunset_hill.h" // include the image bitmap + * lui_obj_t* img_panel = lui_panel_create(); + * // Important to set area of panel. + * lui_object_set_area(img_panel, 320, 300); + * // Set position of the panel + * lui_object_set_position(img_panel, 10, 20); + * // Now set the bitmap image as background + * lui_panel_set_bitmap_image(img_panel, &BITMAP_sunset_hill); + * @endcode + * + * @section panel_example3 Example of setting color palette for 1-bpp mono bitmap image + * Example of setting color palette for 1-bpp mono bitmap image. Color palettes are applicable + * only if the bitmap image is 1-bpp monochrome. If no palette is set by user, default palette + * is used. + * @code + * #include "some_logo.h" // include the 1-bpp mono image bitmap + * lui_obj_t* img_panel = lui_panel_create(); + * // Important to set area of panel. + * lui_object_set_area(img_panel, 320, 300); + * // Set position of the panel + * lui_object_set_position(img_panel, 10, 20); + * // Now set the bitmap image as background + * lui_panel_set_bitmap_image(img_panel, &BITMAP_some_logo); + * // Prepare the color palette for 1-bpp mono image, and set it + * lui_bitmap_mono_pal_t palette = { + * .fore_color = lui_rgb(5, 250, 10), + * .back_color = lui_rgb(120, 50, 10), + * .is_backgrnd = 1 // Setting to 1 to draw the background with `back_color`. Set it to 0 to NOT draw background of bitmap + * }; + * // Now set those palettes + * lui_panel_set_bitmap_image_mono_palette(img_panel, &palette); + * @endcode + * + * @section panel_example4 Example of using layout + * Example of setting a layout for the panel + * @code + * lui_obj_t* panel1 = lui_panel_create(); + * // Important to set area of panel. + * lui_object_set_area(panel1, 320, 300); + * // Set position of the panel + * lui_object_set_position(panel1, 10, 20); + * // Now add a vertical layout to the panel + * lui_panel_layout_set_properties(panel1, LUI_LAYOUT_VERTICAL, 5, 15); + * + * // Now add a few items. But don't set their positions. + * // Because their position will be set by the layout + * + * lui_obj_t* my_button_1 = lui_button_create_and_add(panel1); + * lui_button_set_label_text(my_button_1, "Button 1"); + * + * lui_obj_t* my_button_2 = lui_button_create_and_add(panel1); + * lui_button_set_label_text(my_button_2, "Button 2"); + * + * lui_obj_t* my_button_3 = lui_button_create_and_add(panel1); + * lui_button_set_label_text(my_button_3, "Button 3"); + * + * lui_obj_t* my_button_4 = lui_button_create_and_add(panel1); + * lui_button_set_label_text(my_button_4, "Button 4"); + * + * lui_obj_t* my_button_5 = lui_button_create_and_add(panel1); + * lui_button_set_label_text(my_button_5, "Button 5"); + * + * // After adding all items to the panel, we'll calculate the layout + * // Without calculating, actual layout won't take effect + * lui_panel_layout_calculate(panel1); + * + * @endcode + * + * @{ + */ + +/** + * @brief Create a panel object with initial values + * + * @return lui_obj_t* Created panel object + */ +lui_obj_t* lui_panel_create(void); + +/** + * @brief Create a panel with initial values and add it to a parent + * + * @param obj_parent parent object + * @return lui_obj_t* Created panel object + */ +lui_obj_t* lui_panel_create_and_add(lui_obj_t* obj_parent); + +/** + * @brief Draw apanel + * + * @param obj_panel panel object + */ +void lui_panel_draw(lui_obj_t* obj_panel); + +/** + * @brief Set background bitmap image of the panel + * + * @param obj_panel panel object + * @param bitmap bitmap image object + */ +void lui_panel_set_bitmap_image(lui_obj_t* obj_panel, const lui_bitmap_t* bitmap); + +/** + * @brief Set color palette for 1-bpp mono bitmap image. + * + * This function has no effect if the bitmap is not 1-bpp. + * + * @param obj_panel panel object + * @param palette color palette for 1-bpp mono bitmap + */ +void lui_panel_set_bitmap_image_mono_palette(lui_obj_t* obj_panel, lui_bitmap_mono_pal_t* palette); + +/** + * @brief Set the layout properties of a panel. + * + * All items added as children to this panel will follow the specified layout + * after calling `lui_panel_layout_calculate()`. + * + * Also see: @ref lui_panel_layout_calculate() + * @ref LUI_LAYOUT + * + * @param obj_panel panel object + * @param type Type of layout. See `LUI_LAYOUT_` macros for supported values + * @param pad_x Padding between children along x-axis + * @param pad_y Padding between children along y-axis + * @return int8_t 0: Success, -1: Failure + */ +int8_t lui_panel_layout_set_properties(lui_obj_t* obj_panel, uint8_t type, uint8_t pad_x, uint8_t pad_y); + +/** + * @brief Calculate the layout after adding all children to the parent panel. + * + * Call this function only after all children are added to the parent. + * @note: Must call `lui_panel_layout_set_properties()` before calling this one. + * + * Also see: @ref lui_panel_layout_set_properties() + * + * @param obj_panel panel object + * @return int8_t int8_t 0: Success, < 0: Failure + */ +int8_t lui_panel_layout_calculate(lui_obj_t* obj_panel); + +/**@}*/ +#endif + +/** + * @defgroup lui_scene Scene widget API + * + * @brief API for scene widget + * + * Scene is the main container of all widgets. It is at the top of hierarchy. + * Scene has no parent and must have at least one child. All widgets must be + * decedents of a scene. + * + * An application may have multiple scenes. Only the active scene is rendered + * and it is set by `lui_scene_set_active(lui_obj_t* obj_scene)` function. + * + * @{ + */ +/** + * @brief Create a scene + * + * @return lui_obj_t* created scene + */ +lui_obj_t* lui_scene_create(); + +/** + * @brief Draw scene + * + * @param obj_scene scene widget + */ +void lui_scene_draw(lui_obj_t* obj_scene); + +/** + * @brief Set the active scene. Only active scene is rendered. + * + * @param obj_scene scene widget + */ +void lui_scene_set_active(lui_obj_t* obj_scene); + +/** + * @brief Get the currently active scene + * + * @return lui_obj_t* active scene widget + */ +lui_obj_t* lui_scene_get_active(); + +/** + * @brief Set background bitmap image of a scene. + * + * See the example of panel background image setting. Same applies for scene too. + * @param obj_scene scene object + * @param bitmap bitmap image object + */ +void lui_scene_set_bitmap_image(lui_obj_t* obj_scene, const lui_bitmap_t* bitmap); + +/** + * @brief Set color palette for 1-bpp mono bitmap image. + * + * This function has no effect if the bitmap is not 1-bpp. + * + * @param obj_scene scene object + * @param palette color palette for 1-bpp mono bitmap + */ +void lui_scene_set_bitmap_image_mono_palette(lui_obj_t* obj_scene, lui_bitmap_mono_pal_t* palette); + +/** + * @brief Set the layout properties of a scene. + * + * All items added as children to this scene will follow the specified layout + * after calling `lui_scene_layout_calculate()`. + * + * Also see: @ref lui_scene_layout_calculate() + * @ref LUI_LAYOUT + * + * @param obj_scene scene object + * @param layout_type Type of layout. See `LUI_LAYOUT_` macros for supported values + * @param pad_x Padding between children along x-axis + * @param pad_y Padding between children along y-axis + * @return int8_t 0: Success, -1: Failure + */ +int8_t lui_scene_layout_set_properties(lui_obj_t* obj_scene, uint8_t type, uint8_t pad_x, uint8_t pad_y); + +/** + * @brief Calculate the layout after adding all children to the parent scene. + * + * Call this function only after all children are added to the parent. + * @note: Must call `lui_scene_layout_set_properties()` before calling this one. + * + * Also see: @ref lui_scene_layout_set_properties() + * + * @param obj_scene scene object + * @return int8_t int8_t 0: Success, < 0: Failure + */ +int8_t lui_scene_layout_calculate(lui_obj_t* obj_scene); + +// /** +// * @brief Set font of a scene +// * +// * @param obj_scene scene widget +// * @param font font +// */ +// void lui_scene_set_font(lui_obj_t* obj_scene, const lui_font_t* font); +/**@}*/ + +/** + * @defgroup lui_dispdrv Display Driver API + * + * @brief API for display driver object + * + * LameUI does NOT have its own display driver. User must supply their own functions + * to draw pixels on a display. The displaydriver object is a virtual driver that + * actually calls user supplied callback functions. + * + * The example below is using TFT_eSPI library on Arduino framework + * Hardware: [MCU = ESP32, Display: ILI9341, Touch IC = XPT2046] + * + * @section displaydriver_touchinput_example Example + * @code + * // This example uses TFT_eSPI library on Arduino framework + * // Hardware: [MCU = ESP32, Display: ILI9341, Touch IC = XPT2046] + * + * #include // Graphics and font library for ILI9341 driver chip + * #include + * // ... [ Include LameUI library and other required headers too] ... + * + * #define HOR_RES 240 + * #define VER_RES 320 + * + * #define DISP_BUFF_PX (HOR_RES * 10) // display buffer pixels count + * #define LAMEUI_MEM_BYTES 4000 // LameUI working memory size in bytes + * + * uint16_t disp_buffer[DISP_BUFF_PX]; + * uint8_t lui_memory[LAMEUI_MEM_BYTES]; + * + * TFT_eSPI tft = TFT_eSPI(); // Invoke library + * + * void draw_pixels_buff_cb(uint16_t* disp_buff, lui_area_t* area) + * { + * tft.setAddrWindow(area->x, area->y, area->w, area->h); + * tft.pushColors(disp_buff, (area->w * area->h), true); // swap_byte is true for ili9341 + * } + * void read_touch_input_cb(lui_touch_input_data_t* inputdata) + * { + * uint16_t x = 0, y = 0; // To store the touch coordinates + * + * // Pressed will be set true is there is a valid touch on the screen + * bool pressed = tft.getTouch(&x, &y); + * inputdata->is_pressed = pressed; + * if (pressed) + * { + * inputdata->x = x; + * inputdata->y = y; + * } + * else + * { + * inputdata->x = -1; + * inputdata->y = -1; + * } + * } + * + * void setup(void) + * { + * // Initilaize tft + * tft.init(); + * tft.setTouch(touch_cal_data); + * + * // Initialize LameUI with some memory + * lui_init(lui_memory, sizeof(lui_memory)); + * + * // Create a display driver object + * lui_dispdrv_t* display_driver = lui_dispdrv_create(); + * lui_dispdrv_register(display_driver); + * lui_dispdrv_set_resolution(display_driver, HOR_RES, VER_RES); + * lui_dispdrv_set_disp_buff(display_driver, disp_buffer, DISP_BUFF_PX); + * lui_dispdrv_set_draw_disp_buff_cb(display_driver, draw_pixels_buff_cb); + * + * // Create touch input device + * lui_touch_input_dev_t* input_device = lui_touch_inputdev_create(); + * lui_touch_inputdev_register(input_device); + * lui_touch_inputdev_set_read_input_cb(input_device, read_touch_input_cb); + * + * + * // ... [Add scene (mandatory) and other widgets] ... + * } + * + * void loop() + * { + * // Must update the UI periodically + * lui_update(); + * + * // ... [Do other stuffs] ... + * } + * @endcode + * + * @{ + */ + +/** + * @brief Create display driver object + * + * @return lui_dispdrv_t* created display driver + */ +lui_dispdrv_t* lui_dispdrv_create(void); + +/** + * @brief Register display driver. + * + * @param dispdrv display driver object + */ +void lui_dispdrv_register(lui_dispdrv_t* dispdrv); + +/** + * @brief Create display driver object and register it + * + * @return lui_dispdrv_t* created display driver + */ +lui_dispdrv_t* lui_dispdrv_create_and_register(void); + +/** + * @brief Set horizontal and vertical resolution of display + * + * @param dispdrv display driver object + * @param hor_res horizontal resolution + * @param vert_res vertical resolution + */ +void lui_dispdrv_set_resolution(lui_dispdrv_t* dispdrv, uint16_t hor_res, uint16_t vert_res); + +/** + * @brief Set the display buffer. This buffer is provided by user and filled + * with colors by library. Then user passes this buffer to the display. + * + * Display buffer is an array of uint16_t. Array size must be multiple of + * horizontal resolution of display. If display's horizontal res is 320px, + * minimum buffer size should be 320. It's recommended to use at least 10 times + * horizontal resolution as the buffer size. + * + * @param dispdrv display driver object + * @param disp_buff display buffer of type uint16_t + * @param size_in_px buffer size (in pixel count) + * + * @return int8_t -1: Failure, 0: Success + */ +int8_t lui_dispdrv_set_disp_buff(lui_dispdrv_t* dispdrv, uint16_t* disp_buff, uint32_t size_in_px); + +/** + * @brief Set callback function for drawing an area of pixels with a display buffer. + * + * @param dispdrv display driver object + * @param draw_pixels_buff_cb callback function pointer + */ +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)); + +/** + * @private + * @brief Check if display driver and pixel drawing callback function are properly registered + * + * @return uint8_t 0: Not registered, 1: Registered + */ +uint8_t _lui_disp_drv_check(); +/**@}*/ + +/** + * @defgroup lui_input Touch Input Device API + * + * @brief API for touch input device object + * + * LameUI does NOT have its own Touch Input driver. User must supply their own functions + * to read touch input. Touch input data is stored in `lui_touch_input_data_t`: + * ```C + * typedef struct _lui_touch_input_data_s + * { + * uint8_t is_pressed; + * int16_t x; + * int16_t y; + * }lui_touch_input_data_t; + * ``` + * @note When touch input is NOT pressed, x and y value must be -1. + * + * @section touchinput_example Example + * Example code for touch input can be found inside the display driver example code. + * + * See: @ref displaydriver_touchinput_example + * + * @{ + */ + +/** + * @brief Create touch input device object + * + * @return lui_touch_input_dev_t* Created touch input device object + */ +lui_touch_input_dev_t* lui_touch_inputdev_create(void); + +/** + * @brief Register a touch input device + * + * @param touch_inputdev touch input device object + */ +void lui_touch_inputdev_register(lui_touch_input_dev_t* touch_inputdev); + +/** + * @brief Create touch input device object and register it + * + * @return lui_touch_input_dev_t* Created touch input device object + */ +lui_touch_input_dev_t* lui_touch_inputdev_create_and_register(void); + +/** + * @brief + * + * @param touch_inputdev + * @param read_touch_input_cb + */ +void lui_touch_inputdev_set_read_input_cb(lui_touch_input_dev_t* touch_inputdev, void (*read_touch_input_cb)(lui_touch_input_data_t* touch_inputdata)); +/**@}*/ + +//------------------------------------------------------------------------------- +//-------------------------------- HELPER FUNCTIONS ----------------------------- +//------------------------------------------------------------------------------- +void _lui_mem_init(uint8_t mem_block[], uint32_t size); +void *_lui_mem_alloc(uint16_t element_size); +double _lui_map_range(double old_val, double old_max, double old_min, double new_max, double new_min); +uint8_t _lui_clip_line(double* point_0, double* point_1, const lui_area_t* clip_win); +uint8_t _lui_calc_clip_region_code(double x, double y, const lui_area_t* clip_win); +lui_obj_t* _lui_process_input_of_act_scene(); +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); +lui_obj_t* _lui_scan_individual_object_for_input(lui_touch_input_data_t* touch_input_data, lui_obj_t* obj); +void _lui_set_obj_props_on_touch_input(lui_touch_input_data_t* input_data, lui_obj_t* obj); +uint8_t _lui_check_if_active_obj_touch_input(lui_touch_input_data_t* input_data, lui_obj_t* obj); +// const lui_font_t* _lui_get_font_from_active_scene(); +uint8_t _lui_get_event_against_state(uint8_t new_state, uint8_t old_state); +int8_t _lui_verify_obj(lui_obj_t* obj, uint8_t obj_type); +int8_t _lui_layout_set_properties(lui_obj_t* obj, uint8_t layout_type, uint8_t pad_x, uint8_t pad_y); +int8_t _lui_layout_calculate(lui_obj_t* obj); + +/** + * @defgroup lui_gfx Graphics related API (for drawing shapes and text) + * + * @brief API for various graphics related functions like drawing line, rectangle, text etc. + * User does NOT require to use them. But if needed for some special reason, user may + * call these functions. + * + * Note: `lui_update()` function will overwrite any manual gfx calls made by user. + * So, user must call gfx functions after the update function in a loop. + * + * @{ + */ + +/** + * @brief Advanced function to draw a string. For internal use only, but user + * may call it if needed. Most of the time `draw_string_simple` is enough for user. + * + * This function lets user draw a string with specified fore color and background + * color. The background can be a bitmap image instead of color. + * + * `area` sets string bounding box. When the area of the string is not known, + * width and height of area (`area.w` and `area.h`) can be set to 0. In this case, + * the function calculates the area. Width and height can be found using + * `lui_gfx_get_string_dimension()`. + * + * When the `is_bg` arg is 0, background color/image is not rendered, creating a + * tranaparent background. But the downside is, if the text is cahnged later, it + * cannot clear the previous pixels when `is_bg` is 0. + * + * When `is_bg` is 1 and `bg_bitmap` is not NULL, the bitmap is rendered as background + * instead of `bg_color`. Set bitmap to NULL for rendering `bg_color`. + * + * `bitmap_crop` argument is useful when the backgrounf bitmap image is bigger than + * the text area. + * + * @param str text string + * @param area area of the string. It sets drawing start positions and the dimension. + * @param fore_color text color + * @param bg_color text background color + * @param bg_bitmap text background bimap. + * @param palette color palette for 1-bpp mono bitmap. Has no effect if bitmap is NOT 1-bpp. + * @param bitmap_crop crop area of the bitmap image. + * @param is_bg flag decides whether to render background color/image or not + * @param font font of the text + */ +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); + +/** + * @brief Simplified function to draw a string + * + * Note: This function does not render the background + * + * @param str text + * @param x start X position + * @param y start Y position + * @param fore_color text color + * @param font tetx font + */ +void lui_gfx_draw_string_simple(const char* str, uint16_t x, uint16_t y, uint16_t fore_color, const lui_font_t* font); + +/** + * @brief Draw a single character. + * + * @param c character + * @param x postion X + * @param y position Y + * @param fore_color character color + * @param bg_color background color + * @param is_bg flag decides whether to draw bg_color or not + * @param font character 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); + +/** + * @brief Get height of the font + * + * @param font font + * @return uint16_t height of font in pixels + */ +uint16_t lui_gfx_get_font_height(const lui_font_t* font); + +/** + * @brief Get dimension of a string along x and y axis. + * + * `max_w` (max width) sets the text's bounding box width. Past this width, text + * is wrapped and goes to next line. This value must not be bigger than the width + * of the display. + * + * It does not return any value, rather modifies the `str_dim` array. + * + * @param str text + * @param font_obj font + * @param max_w maximum allowed width + * @param str_dim array of 2 items to return string dimension ({w, h}) + */ +void lui_gfx_get_string_dimension(const char* str, const lui_font_t* font_obj, uint16_t max_w, uint16_t str_dim[2]); + +/** + * @brief Draw a line + * + * @param x0 start X + * @param y0 start Y + * @param x1 end X + * @param y1 end Y + * @param line_width width in px + * @param color line color + */ +void lui_gfx_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t line_width, uint16_t color); + +/** + * @brief Draw a line within a clipping area + * + * @param x0 start X + * @param y0 start Y + * @param x1 end X + * @param y1 end Y + * @param clip_area pointer to the clipping area + * @param line_width width in px + * @param color line 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); + +/** + * @brief Draw a rectangle + * + * @param x start X + * @param y start Y + * @param w width + * @param h height + * @param line_width width of line in px + * @param color color of line + */ +void lui_gfx_draw_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t line_width, uint16_t color); + +/** + * @brief Draw a filled rectangle + * + * @param x start X + * @param y start Y + * @param w width + * @param h height + * @param color fill color + */ +void lui_gfx_draw_rect_fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); + +/** + * @brief Draw a filled rectangle within a clipping area/window + * + * @param x start X + * @param y start Y + * @param w width + * @param h height + * @param clip_area pointer to the clipping area + * @param color fill 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); + +/** + * @brief Draws a bitmap at given x,y position. Crop area can be NULL if cropping + * is not needed. `palette` parameter is only applicable if image is 1-bpp mono bitmap. + * + * Set `pallete` to NULL if the `bitmap` image is snot 1-bpp. + * + * @param bitmap pointer to bitmap data + * @param palette color palette for 1-bpp mono bitmap. Has no effect for 8-bpp and 16-bpp bitmaps. + * @param x start X position + * @param y start Y position + * @param crop_area pointer to crop area. Set NULL for no cropping. + */ +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); + +/** + * @brief Create 16-bit RGB565 color using R, G, and B values (each 8-bit) + * + * Since LameUI uses RGB565 (16-bit) internally, this function is needed when + * when setting color for any item/object. + * + * @param red 8-bit red color + * @param green 8-bit green color + * @param blue 8-bit blue color + * @return uint16_t 16-bit color + */ +uint16_t lui_rgb(uint8_t red, uint8_t green, uint8_t blue); +/**@}*/ +const _lui_glyph_t* _lui_gfx_get_glyph_from_char(char c, const lui_font_t* font); +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); +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); +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); +/*-------------------------------------------- + * End Function Prototypes + *-------------------------------------------- + */ + +/*-------------------------------------------- + * Themes (Dark/Light) + *-------------------------------------------- + */ + +#define _LUI_R_POS_RGB 11 // Red last bit position for RGB display +#define _LUI_G_POS_RGB 5 // Green last bit position for RGB display +#define _LUI_B_POS_RGB 0 // Blue last bit position for RGB display + +#define LUI_RGB(R, G, B) (((uint16_t)(R >> 3) << _LUI_R_POS_RGB) | \ + ((uint16_t)(G >> 2) << _LUI_G_POS_RGB) | \ + ((uint16_t)(B >> 3) << _LUI_B_POS_RGB)) + +/*------------------------------------------------------------------------------------ + * Dark and Light Theme. User may modify here if needed + *------------------------------------------------------------------------------------ + */ +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_BUTTON_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_BUTTON_PRESSED_COLOR LUI_RGB(91, 160, 235) +#define LUI_STYLE_BUTTON_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_BUTTON_BG_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_BUTTON_BORDER_COLOR LUI_RGB(75, 81, 92) +#else +#define LUI_STYLE_BUTTON_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_BUTTON_PRESSED_COLOR LUI_RGB(91, 160, 235) +#define LUI_STYLE_BUTTON_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_BUTTON_BG_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_BUTTON_BORDER_COLOR LUI_RGB(75, 81, 92) +#endif +#define LUI_STYLE_BUTTON_BORDER_THICKNESS 0 +#define LUI_STYLE_BUTTON_WIDTH 40 +#define LUI_STYLE_BUTTON_HEIGHT 30 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_LABEL_TEXT_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_LABEL_BG_COLOR LUI_RGB(23, 33, 43) +#define LUI_STYLE_LABEL_BORDER_COLOR LUI_RGB(74, 129, 188) +#else +#define LUI_STYLE_LABEL_TEXT_COLOR LUI_RGB(0, 0, 0) +#define LUI_STYLE_LABEL_BG_COLOR LUI_RGB(255, 255, 255) +#define LUI_STYLE_LABEL_BORDER_COLOR LUI_RGB(74, 129, 188) +#endif +#define LUI_STYLE_LABEL_BORDER_THICKNESS 0 +#define LUI_STYLE_LABEL_WIDTH 0 /*40*/ +#define LUI_STYLE_LABEL_HEIGHT 0 /*30*/ + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_SWITCH_SELECTION_COLOR LUI_RGB(0, 170, 179) +#define LUI_STYLE_SWITCH_KNOB_OFF_COLOR LUI_RGB(57, 62, 70) +#define LUI_STYLE_SWITCH_KNOB_ON_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_SWITCH_BG_COLOR LUI_RGB(23, 33, 43) +#define LUI_STYLE_SWITCH_BORDER_COLOR LUI_RGB(74, 129, 188) +#else +#define LUI_STYLE_SWITCH_SELECTION_COLOR LUI_RGB(0, 170, 179) +#define LUI_STYLE_SWITCH_KNOB_OFF_COLOR LUI_RGB(150, 150, 150) +#define LUI_STYLE_SWITCH_KNOB_ON_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_SWITCH_BG_COLOR LUI_RGB(255, 255, 255) +#define LUI_STYLE_SWITCH_BORDER_COLOR LUI_RGB(74, 129, 188) +#endif +#define LUI_STYLE_SWITCH_BORDER_THICKNESS 1 +#define LUI_STYLE_SWITCH_WIDTH 40 +#define LUI_STYLE_SWITCH_HEIGHT 20 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_CHECKBOX_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_CHECKBOX_TICK_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_CHECKBOX_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_CHECKBOX_BG_COLOR LUI_RGB(23, 33, 43) +#define LUI_STYLE_CHECKBOX_BG_CHECKED_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_CHECKBOX_BORDER_COLOR LUI_RGB(74, 129, 188) +#else +#define LUI_STYLE_CHECKBOX_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_CHECKBOX_TICK_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_CHECKBOX_LABEL_COLOR LUI_RGB(0, 0, 0) +#define LUI_STYLE_CHECKBOX_BG_COLOR LUI_RGB(255, 255, 255) +#define LUI_STYLE_CHECKBOX_BG_CHECKED_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_CHECKBOX_BORDER_COLOR LUI_RGB(74, 129, 188) +#endif +#define LUI_STYLE_CHECKBOX_BORDER_THICKNESS 1 +#define LUI_STYLE_CHECKBOX_WIDTH 20 +#define LUI_STYLE_CHECKBOX_HEIGHT LUI_STYLE_CHECKBOX_WIDTH + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_SLIDER_SELECTION_COLOR LUI_RGB(0, 170, 179) +#define LUI_STYLE_SLIDER_KNOB_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_SLIDER_BG_COLOR LUI_RGB(57, 62, 70) +#define LUI_STYLE_SLIDER_BG_FILLED_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_SLIDER_BORDER_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_SLIDER_BORDER_THICKNESS 0 +#else +// TODO: Improve light theme +#define LUI_STYLE_SLIDER_SELECTION_COLOR LUI_RGB(0, 170, 179) +#define LUI_STYLE_SLIDER_KNOB_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_SLIDER_BG_COLOR LUI_RGB(150, 150,150) +#define LUI_STYLE_SLIDER_BG_FILLED_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_SLIDER_BORDER_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_SLIDER_BORDER_THICKNESS 1 +#endif +#define LUI_STYLE_SLIDER_KNOB_WIDTH 20 +#define LUI_STYLE_SLIDER_WIDTH 80 +#define LUI_STYLE_SLIDER_HEIGHT 20 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_LINECHART_LINE_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_LINECHART_POINT_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_LINECHART_GRID_COLOR LUI_RGB(75, 81, 92) +#define LUI_STYLE_LINECHART_BG_COLOR LUI_RGB(35, 46, 60) +#define LUI_STYLE_LINECHART_BORDER_COLOR LUI_RGB(74, 129, 188) +#else +#define LUI_STYLE_LINECHART_LINE_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_LINECHART_POINT_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_LINECHART_GRID_COLOR LUI_RGB(150, 150, 150) +#define LUI_STYLE_LINECHART_BG_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_LINECHART_BORDER_COLOR LUI_RGB(74, 129, 188) +#endif +#define LUI_STYLE_LINECHART_GRID_VISIBLE 1 +#define LUI_STYLE_LINECHART_BORDER_THICKNESS 1 +#define LUI_STYLE_LINECHART_WIDTH 40 +#define LUI_STYLE_LINECHART_HEIGHT 20 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_LIST_NAV_BG_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_LIST_NAV_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_LIST_NAV_PRESSED_COLOR LUI_RGB(91, 160, 235) +#define LUI_STYLE_LIST_NAV_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_LIST_ITEM_BG_COLOR LUI_RGB(57, 62, 70) +#define LUI_STYLE_LIST_ITEM_SELECTION_COLOR LUI_RGB(84, 91, 102) +#define LUI_STYLE_LIST_ITEM_PRESSED_COLOR LUI_RGB(109, 118, 133) +#define LUI_STYLE_LIST_ITEM_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_LIST_ITEM_BORDER_COLOR LUI_RGB(75, 81, 92) +#define LUI_STYLE_LIST_BORDER_THICKNESS 0 +#else +#define LUI_STYLE_LIST_NAV_BG_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_LIST_NAV_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_LIST_NAV_PRESSED_COLOR LUI_RGB(91, 160, 235) +#define LUI_STYLE_LIST_NAV_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_LIST_ITEM_BG_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_LIST_ITEM_SELECTION_COLOR LUI_RGB(109, 118, 133) +#define LUI_STYLE_LIST_ITEM_PRESSED_COLOR LUI_RGB(137, 173, 232) +#define LUI_STYLE_LIST_ITEM_LABEL_COLOR LUI_RGB(0, 0, 0) +#define LUI_STYLE_LIST_ITEM_BORDER_COLOR LUI_RGB(75, 81, 92) +#define LUI_STYLE_LIST_BORDER_THICKNESS 1 +#endif +#define LUI_STYLE_LIST_ITEM_BORDER_THICKNESS 0 +#define LUI_STYLE_LIST_ITEM_MIN_HEIGHT 30 +#define LUI_STYLE_LIST_BORDER_COLOR LUI_RGB(74, 129, 188) +#define LUI_STYLE_LIST_WIDTH 40 +#define LUI_STYLE_LIST_HEIGHT 60 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_BTNGRID_BASE_BG_COLOR LUI_RGB(23, 33, 43) +#define LUI_STYLE_BTNGRID_LABEL_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_BTNGRID_PRESSED_COLOR LUI_RGB(91, 160, 235) +#define LUI_STYLE_BTNGRID_BG_COLOR LUI_RGB(39, 55, 71) +#define LUI_STYLE_BTNGRID_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_BTNGRID_BORDER_COLOR LUI_RGB(75, 81, 92) +#define LUI_STYLE_BTNGRID_BORDER_THICKNESS 0 + +#else +#define LUI_STYLE_BTNGRID_BASE_BG_COLOR LUI_RGB(255, 255, 255) +#define LUI_STYLE_BTNGRID_LABEL_COLOR LUI_RGB(0, 0, 0) +#define LUI_STYLE_BTNGRID_PRESSED_COLOR LUI_RGB(91, 160, 235) +#define LUI_STYLE_BTNGRID_BG_COLOR LUI_RGB(200, 200, 200) +#define LUI_STYLE_BTNGRID_SELECTION_COLOR LUI_RGB(82, 143, 209) +#define LUI_STYLE_BTNGRID_BORDER_COLOR LUI_RGB(150, 150, 150) +#define LUI_STYLE_BTNGRID_BORDER_THICKNESS 1 +#endif +#define LUI_STYLE_BTNGRID_WIDTH 300 +#define LUI_STYLE_BTNGRID_HEIGHT 180 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_TEXTBOX_TEXT_COLOR LUI_RGB(238, 238, 238) +#define LUI_STYLE_TEXTBOX_BG_COLOR LUI_RGB(45, 56, 70) +#define LUI_STYLE_TEXTBOX_BORDER_COLOR LUI_RGB(74, 129, 188) +#else +#define LUI_STYLE_TEXTBOX_TEXT_COLOR LUI_RGB(0, 0, 0) +#define LUI_STYLE_TEXTBOX_BG_COLOR LUI_RGB(255, 255, 255) +#define LUI_STYLE_TEXTBOX_BORDER_COLOR LUI_RGB(74, 129, 188) +#endif +#define LUI_STYLE_TEXTBOX_BORDER_THICKNESS 1 +#define LUI_STYLE_TEXTBOX_WIDTH 200 +#define LUI_STYLE_TEXTBOX_HEIGHT 20 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_PANEL_BG_COLOR LUI_RGB(23, 33, 43) +#define LUI_STYLE_PANEL_BORDER_COLOR LUI_RGB(74, 129, 188) +#else +#define LUI_STYLE_PANEL_BG_COLOR LUI_RGB(255, 255, 255) +#define LUI_STYLE_PANEL_BORDER_COLOR LUI_RGB(74, 129, 188) +#endif +#define LUI_STYLE_PANEL_BORDER_THICKNESS 1 +#define LUI_STYLE_PANEL_WIDTH 100 +#define LUI_STYLE_PANEL_HEIGHT 100 + +#if LUI_USE_DARK_THEME == 1 +#define LUI_STYLE_SCENE_BG_COLOR LUI_RGB(23, 33, 43) +#else +#define LUI_STYLE_SCENE_BG_COLOR LUI_RGB(255, 255, 255) +#endif + +/*-------------------------------------------- + * End Themes (Dark/Light) + *-------------------------------------------- + */ + +/*-------------------------------------------- + * Helper Macros + *-------------------------------------------- + */ + +#ifndef NULL +#define NULL ((void *)0) +#endif +// TODO: Inspect the code and use MIN, MAX, and BOUND where needed +#define _LUI_MIN(A,B) ({ (A) < (B) ? (A) : (B); }) +#define _LUI_MAX(A,B) ({ (A) < (B) ? (B) : (A); }) + +#define _LUI_BOUNDS(x, low, high) ({\ + (x) > (high) ? (high) : ((x) < (low) ? (low) : (x));\ +}) + +#define _LUI_SWAP(type, a, b) ({ type tmp = (a); (a) = (b); (b) = tmp; }) + +#define _LUI_CREATE_AND_ADD(type, parent) \ + lui_obj_t* obj = lui_##type##_create(); \ + lui_object_add_to_parent(obj, parent); \ + return obj; + +#endif /* INC_LAME_UI_H_ */ diff --git a/lib/lui/lame_ui.c b/lib/lui/lame_ui.c new file mode 100644 index 0000000..584aa28 --- /dev/null +++ b/lib/lui/lame_ui.c @@ -0,0 +1,6376 @@ +/* + * 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 + *------------------------------------------------------------------------------- + */ diff --git a/lib/oled/inc/oled.h b/lib/oled/inc/oled.h index 6fdb975..c7be0c3 100644 --- a/lib/oled/inc/oled.h +++ b/lib/oled/inc/oled.h @@ -4,7 +4,7 @@ #ifdef __cplusplus extern "C" { #endif - +#include "stdbool.h" #include "stdint.h" diff --git a/lib/utils/inc/argpase.h b/lib/utils/inc/argpase.h index 3c37fb1..7720b19 100644 --- a/lib/utils/inc/argpase.h +++ b/lib/utils/inc/argpase.h @@ -3,6 +3,7 @@ #ifdef __cplusplus extern "C" { #endif +#include "stdbool.h" typedef char OptId; typedef struct Option { diff --git a/lib/utils/tool.cpp b/lib/utils/tool.cpp index bd32d38..abfcf62 100644 --- a/lib/utils/tool.cpp +++ b/lib/utils/tool.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "tool.h" float Mapping(float val, float I_Min, float I_Max, float O_Min, float O_Max) { diff --git a/main.c b/main.c index 3282fac..9bd736a 100644 --- a/main.c +++ b/main.c @@ -12,7 +12,7 @@ #include "t_tft.h" #include "tool.h" #include "sim_test.h" - +#include "t_lui.h" #include #include #include @@ -160,10 +160,11 @@ int main(int argc, char *argv[]) { // Test_Run("List", Test_List,NULL); // Test_RunTime("Key", Test_Key); // Test_RunTime("Queue", Test_Queue); - Test_RunTime("Hash", Test_Hash); + // Test_RunTime("Hash", Test_Hash); // Test_RunTime("Task", Test_task); // Test_RunTime("OLED", Test_OLED); // Test_RunTime("LVGL", Test_lvgl); // Test_RunTime("TFT", Test_tft); + Test_RunTime("LUI", Test_lui); return 0; } diff --git a/sim/Test/sim_test.cpp b/sim/Test/sim_test.cpp index b87d58c..e7055b4 100644 --- a/sim/Test/sim_test.cpp +++ b/sim/Test/sim_test.cpp @@ -4,7 +4,6 @@ #include #include -#include #include "sim_test.h" void Test_RunTime(char *name, int (*pFunction)(void *)) { diff --git a/sim/display/sim_display.cpp b/sim/display/sim_display.cpp index c84bd11..9da650e 100644 --- a/sim/display/sim_display.cpp +++ b/sim/display/sim_display.cpp @@ -1,6 +1,5 @@ #include "sim_display.h" -#include "graphics.h" -#include + #define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) @@ -20,6 +19,10 @@ uint32_t RGB565_to_ARGB8888(uint16_t rgb565, bool isBGR) { } } #ifndef USER_SDL3 + +#include "graphics.h" +#include + static uint32_t pixelColor, backgroundColor; static int scaleFactor, w, h; uint8_t border; diff --git a/sim/lvgl/lv_port_indev.cpp b/sim/lvgl/lv_port_indev.cpp index 2e89114..d35e8bf 100644 --- a/sim/lvgl/lv_port_indev.cpp +++ b/sim/lvgl/lv_port_indev.cpp @@ -12,8 +12,6 @@ *********************/ #include "lv_port_indev.h" #include "lvgl.h" -#include "graphics.h" -#include /********************* * DEFINES *********************/ @@ -253,8 +251,8 @@ static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) static bool mouse_is_pressed(void) { /*Your code comes here*/ - ExMessage msg;peekmessage(&msg, EM_MOUSE); - if(msg.message==WM_LBUTTONDOWN) return true; + // ExMessage msg;peekmessage(&msg, EM_MOUSE); + // if(msg.message==WM_LBUTTONDOWN) return true; return false; } @@ -262,13 +260,13 @@ static bool mouse_is_pressed(void) static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y) { /*Your code comes here*/ - ExMessage msg; - if(peekmessage(&msg, EM_MOUSE)) - { - (*x) = msg.x; - (*y) = msg.y; - printf("\nX:%d,Y:%d",*x,*y); - } + // ExMessage msg; + // if(peekmessage(&msg, EM_MOUSE)) + // { + // (*x) = msg.x; + // (*y) = msg.y; + // printf("\nX:%d,Y:%d",*x,*y); + // } } /*------------------ diff --git a/sim/oled/oled.cpp b/sim/oled/oled.cpp index f79e3c4..7b48193 100644 --- a/sim/oled/oled.cpp +++ b/sim/oled/oled.cpp @@ -1,6 +1,4 @@ #include "sim_oled.h" -#include "graphics.h" -#include static SIM_Display_t oled_display; void SIM_OLED_INIT(int width, int height, uint32_t pixcolor, uint32_t backcolor, int scale, uint8_t b) {