freertos_f407/User/system/sgl/widgets/dropdown/sgl_dropdown.c

330 lines
9.8 KiB
C

/* source/widgets/sgl_dropdown.c
*
* MIT License
*
* Copyright(c) 2023-present All contributors of SGL
* Document reference link: https://sgl-docs.readthedocs.io
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <sgl_core.h>
#include <sgl_draw.h>
#include <sgl_math.h>
#include <sgl_log.h>
#include <sgl_mm.h>
#include <sgl_cfgfix.h>
#include <sgl_theme.h>
#include "sgl_dropdown.h"
static const uint8_t dropdown_bitmap[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,
0x0c,0xfa,0x00,0x00,0x00,0x00,0x05,0xee,0x50,
0x0c,0xff,0xa0,0x00,0x00,0x00,0x5e,0xfe,0x30,
0x00,0xcf,0xfa,0x00,0x00,0x05,0xef,0xe3,0x00,
0x00,0x0c,0xff,0xa0,0x00,0x5e,0xfe,0x30,0x00,
0x00,0x00,0xcf,0xfa,0x05,0xef,0xe3,0x00,0x00,
0x00,0x00,0x0c,0xff,0xae,0xfe,0x30,0x00,0x00,
0x00,0x00,0x00,0xcf,0xff,0xe3,0x00,0x00,0x00,
0x00,0x00,0x00,0x0c,0xfe,0x30,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x00,
};
static const sgl_icon_pixmap_t dropdown_icon = {
.bitmap = dropdown_bitmap,
.height = 10,
.width = 18,
};
static void sgl_dropdown_construct_cb(sgl_surf_t *surf, sgl_obj_t* obj, sgl_event_t *evt)
{
sgl_dropdown_t *dropdown = sgl_container_of(obj, sgl_dropdown_t, obj);
int16_t icon_h = dropdown->option_h;
int16_t pos_x = 0, pos_y = 0, icon_y = 0;
sgl_dropdown_option_t *option = dropdown->head;
sgl_rect_t body_area = {
.x1 = obj->coords.x1,
.x2 = obj->coords.x2,
.y1 = obj->coords.y1,
.y2 = obj->coords.y1 + dropdown->option_h,
};
sgl_rect_t icon_area = {
.x1 = obj->coords.x2 - dropdown_icon.width - dropdown->body_desc.radius,
.x2 = obj->coords.x2 - 2,
.y1 = obj->coords.y1,
.y2 = obj->coords.y2,
};
sgl_rect_t text_area = {
.x1 = obj->coords.x1 + 2,
.x2 = obj->coords.x2 - dropdown_icon.width - 5,
.y1 = obj->coords.y1,
.y2 = obj->coords.y2,
};
icon_y = obj->coords.y1 + (icon_h - dropdown_icon.height) / 2;
if (evt->type == SGL_EVENT_DRAW_MAIN) {
sgl_draw_rect(surf, &obj->area, &body_area, &dropdown->body_desc);
if (dropdown->is_open) {
sgl_draw_icon(surf, &obj->area, icon_area.x1, icon_y + 2, dropdown->text_color, dropdown->body_desc.alpha, &dropdown_icon);
}
else {
sgl_draw_icon(surf, &obj->area, icon_area.x1, icon_y, dropdown->text_color, dropdown->body_desc.alpha, &dropdown_icon);
}
for (int i = 0; option != NULL; i++) {
if (i == dropdown->selected) {
break;
}
option = option->next;
}
if (option != NULL) {
pos_x = text_area.x1 + dropdown->body_desc.radius;
pos_y = obj->coords.y1 + (icon_h - dropdown->font->font_height) / 2;
sgl_draw_string(surf, &text_area, pos_x, pos_y, option->text, dropdown->text_color, dropdown->body_desc.alpha, dropdown->font);
}
if (dropdown->is_open) {
pos_y = body_area.y2;
option = dropdown->expend_start;
body_area.y1 = obj->coords.y1 + dropdown->option_h;
body_area.y2 = body_area.y1 + dropdown->expand_h - 1;
sgl_draw_rect(surf, &obj->area, &body_area, &dropdown->body_desc);
for (int i = 0; option != NULL; i++) {
pos_x = text_area.x1 + dropdown->body_desc.radius;
sgl_draw_string(surf, &obj->area, pos_x, pos_y, option->text, dropdown->text_color, dropdown->body_desc.alpha, dropdown->font);
option = option->next;
pos_y += dropdown->font->font_height;
}
}
}
else if (evt->type == SGL_EVENT_MOVE_UP) {
}
else if (evt->type == SGL_EVENT_MOVE_DOWN) {
}
else if (evt->type == SGL_EVENT_PRESSED || evt->type == SGL_EVENT_CLICKED) {
dropdown->expand_h = dropdown->font->font_height * sgl_min(10, dropdown->option_num);
if (dropdown->is_open) {
dropdown->is_open = false;
obj->coords.y2 -= dropdown->expand_h;
SGL_LOG_INFO("sgl_dropdown_construct_cb: selected %d %d %d", evt->pos.y, obj->coords.y2, dropdown->font->font_height);
if (evt->pos.y > obj->coords.y2) {
dropdown->selected = (evt->pos.y - obj->coords.y2) / dropdown->font->font_height;
}
}
else {
dropdown->is_open = true;
obj->coords.y2 += dropdown->expand_h;
}
sgl_obj_set_dirty(obj);
dropdown->expend_start = dropdown->head;
}
else if (evt->type == SGL_EVENT_DRAW_INIT) {
dropdown->option_h = obj->coords.y2 - obj->coords.y1;
}
}
/**
* @brief create a dropdown object
* @param parent parent of the dropdown
* @return pointer to the dropdown object
*/
sgl_obj_t* sgl_dropdown_create(sgl_obj_t* parent)
{
sgl_dropdown_t *dropdown = sgl_malloc(sizeof(sgl_dropdown_t));
if(dropdown == NULL) {
SGL_LOG_ERROR("sgl_dropdown_create: malloc failed");
return NULL;
}
/* set object all member to zero */
memset(dropdown, 0, sizeof(sgl_dropdown_t));
sgl_obj_t *obj = &dropdown->obj;
sgl_obj_init(&dropdown->obj, parent);
obj->construct_fn = sgl_dropdown_construct_cb;
obj->needinit = 1;
sgl_obj_set_border_width(obj, SGL_THEME_BORDER_WIDTH);
sgl_obj_set_clickable(obj);
sgl_obj_set_movable(obj);
dropdown->body_desc.alpha = SGL_THEME_ALPHA;
dropdown->body_desc.color = SGL_THEME_COLOR;
dropdown->body_desc.radius = 5;
dropdown->body_desc.border = 1;
dropdown->body_desc.border_color = SGL_THEME_BORDER_COLOR;
dropdown->font = sgl_get_system_font();
dropdown->head = NULL;
dropdown->is_open = false;
dropdown->selected = -1;
dropdown->clicked = 0;
return obj;
}
/**
* @brief get the selected text of the dropdown
* @param obj pointer to the dropdown object
* @return pointer to the selected text
*/
const char *sgl_dropdown_get_selected_text(sgl_obj_t *obj)
{
sgl_dropdown_t *dropdown = (sgl_dropdown_t*)obj;
sgl_dropdown_option_t *option = dropdown->head;
for (int i = 0; i < dropdown->option_num; i++) {
if (i == dropdown->selected) {
return option->text;
}
option = option->next;
}
return NULL;
}
/**
* @brief add an option to the dropdown
* @param obj pointer to the dropdown object
* @param text pointer to the text
* @return none
*/
void sgl_dropdown_add_option(sgl_obj_t *obj, const char *text)
{
sgl_dropdown_t *dropdown = (sgl_dropdown_t*)obj;
sgl_dropdown_option_t *tail = dropdown->head;
sgl_dropdown_option_t *add = sgl_malloc(sizeof(sgl_dropdown_option_t));
if (add == NULL) {
SGL_LOG_ERROR("sgl_dropdown_add_option: malloc failed");
return;
}
if (tail == NULL) {
dropdown->head = add;
}
else {
while (tail != NULL) {
if (tail->next == NULL) {
tail->next = add;
break;
}
tail = (tail)->next;
}
}
dropdown->option_num ++;
add->text = text;
add->next = NULL;
if (dropdown->selected == -1) {
dropdown->selected = 0;
}
sgl_obj_set_dirty(obj);
}
/**
* @brief delete an option from the dropdown
* @param obj pointer to the dropdown object
* @param text pointer to the text
* @return none
*/
void sgl_dropdown_delete_option_by_text(sgl_obj_t *obj, const char *text)
{
sgl_dropdown_t *dropdown = (sgl_dropdown_t*)obj;
sgl_dropdown_option_t *curr = dropdown->head;
sgl_dropdown_option_t *temp = dropdown->head;
sgl_dropdown_option_t *prev = NULL;
int deleted_index = 0;
if (obj == NULL || text == NULL) {
return;
}
while (curr != NULL) {
if (curr->text && strcmp(curr->text, text) == 0) {
break;
}
prev = curr;
curr = curr->next;
}
if (curr == NULL) {
return;
}
while (temp != curr) {
temp = temp->next;
deleted_index ++;
}
if (prev == NULL) {
dropdown->head = curr->next;
} else {
prev->next = curr->next;
}
sgl_free(curr);
dropdown->option_num--;
sgl_obj_set_dirty(obj);
}
/**
* @brief delete an option from the dropdown
* @param obj pointer to the dropdown object
* @param index index of the option
* @return none
*/
void sgl_dropdown_delete_option_by_index(sgl_obj_t *obj, int index)
{
sgl_dropdown_t *dropdown = (sgl_dropdown_t*)obj;
sgl_dropdown_option_t *curr = dropdown->head;
sgl_dropdown_option_t *prev = NULL;
if (obj == NULL || index < 0 || index >= dropdown->option_num) {
return;
}
for (int i = 0; i < index; i++) {
prev = curr;
curr = curr->next;
}
if (prev == NULL) {
dropdown->head = curr->next;
} else {
prev->next = curr->next;
}
sgl_free(curr);
dropdown->option_num--;
}