lvgl_cpp/lvgl/extra/widgets/spinbox/lv_spinbox.c

518 lines
15 KiB
C
Raw Normal View History

2023-03-14 11:57:25 +00:00
/**
* @file lv_spinbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinbox.h"
#if LV_USE_SPINBOX
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_spinbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_spinbox_updatevalue(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_spinbox_class = {
.constructor_cb = lv_spinbox_constructor,
.event_cb = lv_spinbox_event,
.width_def = LV_DPI_DEF,
.instance_size = sizeof(lv_spinbox_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.base_class = &lv_textarea_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param obj pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(i > spinbox->range_max) i = spinbox->range_max;
if(i < spinbox->range_min) i = spinbox->range_min;
spinbox->value = i;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox rollover function
* @param spinbox pointer to spinbox
* @param b true or false to enable or disable (default)
*/
void lv_spinbox_set_rollover(lv_obj_t * obj, bool b)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->rollover = b;
}
/**
* Set spinbox digit format (digit count and decimal format)
* @param spinbox pointer to spinbox
* @param digit_count number of digit excluding the decimal separator and the sign
* @param separator_position number of digit before the decimal point. If 0, decimal point is not
* shown
*/
void lv_spinbox_set_digit_format(lv_obj_t * obj, uint8_t digit_count, uint8_t separator_position)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) digit_count = LV_SPINBOX_MAX_DIGIT_COUNT;
if(separator_position >= digit_count) separator_position = 0;
if(separator_position > LV_SPINBOX_MAX_DIGIT_COUNT) separator_position = LV_SPINBOX_MAX_DIGIT_COUNT;
if(digit_count < LV_SPINBOX_MAX_DIGIT_COUNT) {
int64_t max_val = lv_pow(10, digit_count);
if(spinbox->range_max > max_val - 1) spinbox->range_max = max_val - 1;
if(spinbox->range_min < - max_val + 1) spinbox->range_min = - max_val + 1;
}
spinbox->digit_count = digit_count;
spinbox->dec_point_pos = separator_position;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement
*/
void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->step = step;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox value range
* @param spinbox pointer to spinbox
* @param range_min maximum value, inclusive
* @param range_max minimum value, inclusive
*/
void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->range_max = range_max;
spinbox->range_min = range_min;
if(spinbox->value > spinbox->range_max) spinbox->value = spinbox->range_max;
if(spinbox->value < spinbox->range_min) spinbox->value = spinbox->range_min;
lv_spinbox_updatevalue(obj);
}
/**
* Set cursor position to a specific digit for edition
* @param spinbox pointer to spinbox
* @param pos selected position in spinbox
*/
void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * lv_pow(10, pos);
if(pos <= 0) spinbox->step = 1;
else if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Set direction of digit step when clicking an encoder button while in editing mode
* @param spinbox pointer to spinbox
* @param direction the direction (LV_DIR_RIGHT or LV_DIR_LEFT)
*/
void lv_spinbox_set_digit_step_direction(lv_obj_t * obj, lv_dir_t direction)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->digit_step_dir = direction;
lv_spinbox_updatevalue(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the spinbox numeral value (user has to convert to float according to its digit format)
* @param obj pointer to spinbox
* @return value integer value of the spinbox
*/
int32_t lv_spinbox_get_value(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->value;
}
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param obj pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->step;
}
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition
* @param obj pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t new_step = spinbox->step / 10;
if((new_step) > 0)
spinbox->step = new_step;
else
spinbox->step = 1;
lv_spinbox_updatevalue(obj);
}
/**
* Select next higher digit for edition
* @param obj pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * 10;
if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Get spinbox rollover function status
* @param obj pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->rollover;
}
/**
* Increment spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value + spinbox->step <= spinbox->range_max) {
/*Special mode when zero crossing*/
if((spinbox->value + spinbox->step) > 0 && spinbox->value < 0) spinbox->value = -spinbox->value;
spinbox->value += spinbox->step;
}
else {
// Rollover?
if((spinbox->rollover) && (spinbox->value == spinbox->range_max))
spinbox->value = spinbox->range_min;
else
spinbox->value = spinbox->range_max;
}
lv_spinbox_updatevalue(obj);
}
/**
* Decrement spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value - spinbox->step >= spinbox->range_min) {
/*Special mode when zero crossing*/
if((spinbox->value - spinbox->step) < 0 && spinbox->value > 0) spinbox->value = -spinbox->value;
spinbox->value -= spinbox->step;
}
else {
/*Rollover?*/
if((spinbox->rollover) && (spinbox->value == spinbox->range_min))
spinbox->value = spinbox->range_max;
else
spinbox->value = spinbox->range_min;
}
lv_spinbox_updatevalue(obj);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_LOG_TRACE("begin");
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
/*Initialize the allocated 'ext'*/
spinbox->value = 0;
spinbox->dec_point_pos = 0;
spinbox->digit_count = 5;
spinbox->step = 1;
spinbox->range_max = 99999;
spinbox->range_min = -99999;
spinbox->rollover = false;
spinbox->digit_step_dir = LV_DIR_RIGHT;
lv_textarea_set_one_line(obj, true);
lv_textarea_set_cursor_click_pos(obj, true);
lv_spinbox_updatevalue(obj);
LV_LOG_TRACE("Spinbox constructor finished");
}
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = LV_RES_OK;
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(code == LV_EVENT_RELEASED) {
/*If released with an ENCODER then move to the next digit*/
lv_indev_t * indev = lv_indev_get_act();
if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) {
if(lv_group_get_editing(lv_obj_get_group(obj))) {
if(spinbox->digit_count > 1) {
if(spinbox->digit_step_dir == LV_DIR_RIGHT) {
if(spinbox->step > 1) {
lv_spinbox_step_next(obj);
}
else {
/*Restart from the MSB*/
spinbox->step = lv_pow(10, spinbox->digit_count - 2);
lv_spinbox_step_prev(obj);
}
}
else {
if(spinbox->step < lv_pow(10, spinbox->digit_count - 1)) {
lv_spinbox_step_prev(obj);
}
else {
/*Restart from the LSB*/
spinbox->step = 10;
lv_spinbox_step_next(obj);
}
}
}
}
}
/*The cursor has been positioned to a digit.
* Set `step` accordingly*/
else {
const char * txt = lv_textarea_get_text(obj);
size_t txt_len = strlen(txt);
if(txt[spinbox->ta.cursor.pos] == '.') {
lv_textarea_cursor_left(obj);
}
else if(spinbox->ta.cursor.pos == (uint32_t)txt_len) {
lv_textarea_set_cursor_pos(obj, txt_len - 1);
}
else if(spinbox->ta.cursor.pos == 0 && spinbox->range_min < 0) {
lv_textarea_set_cursor_pos(obj, 1);
}
size_t len = spinbox->digit_count - 1;
uint16_t cp = spinbox->ta.cursor.pos;
if(spinbox->ta.cursor.pos > spinbox->dec_point_pos && spinbox->dec_point_pos != 0) cp--;
uint32_t pos = len - cp;
if(spinbox->range_min < 0) pos++;
spinbox->step = 1;
uint16_t i;
for(i = 0; i < pos; i++) spinbox->step *= 10;
}
}
else if(code == LV_EVENT_KEY) {
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_increment(obj);
else
lv_spinbox_step_next(obj);
}
else if(c == LV_KEY_LEFT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_decrement(obj);
else
lv_spinbox_step_prev(obj);
}
else if(c == LV_KEY_UP) {
lv_spinbox_increment(obj);
}
else if(c == LV_KEY_DOWN) {
lv_spinbox_decrement(obj);
}
else {
lv_textarea_add_char(obj, c);
}
}
}
static void lv_spinbox_updatevalue(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8];
lv_memset_00(buf, sizeof(buf));
char * buf_p = buf;
uint8_t cur_shift_left = 0;
if(spinbox->range_min < 0) { // hide sign if there are only positive values
/*Add the sign*/
(*buf_p) = spinbox->value >= 0 ? '+' : '-';
buf_p++;
}
else {
/*Cursor need shift to left*/
cur_shift_left++;
}
int32_t i;
char digits[LV_SPINBOX_MAX_DIGIT_COUNT + 4];
/*Convert the numbers to string (the sign is already handled so always covert positive number)*/
lv_snprintf(digits, sizeof(digits), "%" LV_PRId32, LV_ABS(spinbox->value));
/*Add leading zeros*/
int lz_cnt = spinbox->digit_count - (int)strlen(digits);
if(lz_cnt > 0) {
for(i = (uint16_t)strlen(digits); i >= 0; i--) {
digits[i + lz_cnt] = digits[i];
}
for(i = 0; i < lz_cnt; i++) {
digits[i] = '0';
}
}
int32_t intDigits;
intDigits = (spinbox->dec_point_pos == 0) ? spinbox->digit_count : spinbox->dec_point_pos;
/*Add the decimal part*/
for(i = 0; i < intDigits && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
if(spinbox->dec_point_pos != 0) {
/*Insert the decimal point*/
(*buf_p) = '.';
buf_p++;
for(/*Leave i*/; i < spinbox->digit_count && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
}
/*Refresh the text*/
lv_textarea_set_text(obj, (char *)buf);
/*Set the cursor position*/
int32_t step = spinbox->step;
uint8_t cur_pos = (uint8_t)spinbox->digit_count;
while(step >= 10) {
step /= 10;
cur_pos--;
}
if(cur_pos > intDigits) cur_pos++; /*Skip the decimal point*/
cur_pos -= cur_shift_left;
lv_textarea_set_cursor_pos(obj, cur_pos);
}
#endif /*LV_USE_SPINBOX*/