This commit is contained in:
snow 2024-02-05 17:39:56 +08:00
commit eaaf17b1d8
1064 changed files with 670361 additions and 0 deletions

279
CMakeLists.txt Normal file
View File

@ -0,0 +1,279 @@
# Copyright (C) 2020 QUECTEL Technologies Limited and/or its affiliates("QUECTEL").
# All rights reserved.
#
add_subdirectory_if_exist(init)
add_subdirectory_if_exist(nw)
add_subdirectory_if_exist(peripheral)
add_subdirectory_if_exist(osi)
add_subdirectory_if_exist(dev)
add_subdirectory_if_exist(power)
if(QL_APP_FEATURE_USB)
add_subdirectory_if_exist(usb)
endif()
add_subdirectory_if_exist(qnewlib)
if(QL_APP_FEATURE_FILE)
add_subdirectory_if_exist(fs)
endif()
if(QL_APP_FEATURE_AUDIO)
add_subdirectory_if_exist(audio)
if(QL_APP_FEATURE_TTS)
add_subdirectory_if_exist(tts)
endif()
endif()
if(QL_APP_FEATURE_BT)
add_subdirectory_if_exist(bt)
endif()
if(QL_APP_FEATURE_GNSS)
add_subdirectory_if_exist(gnss)
endif()
if(QL_APP_FEATURE_FTP)
add_subdirectory_if_exist(ftp)
endif()
if(QL_APP_FEATURE_HTTP)
add_subdirectory_if_exist(http)
endif()
if(QL_APP_FEATURE_MMS)
add_subdirectory_if_exist(mms)
endif()
if(QL_APP_FEATURE_MQTT)
add_subdirectory_if_exist(mqtt)
endif()
if(QL_APP_FEATURE_FIREWALL)
add_subdirectory_if_exist(firewall)
endif()
if(QL_APP_FEATURE_SSL)
add_subdirectory_if_exist(ssl)
endif()
if(QL_APP_FEATURE_STK)
add_subdirectory_if_exist(stk)
endif()
if(QL_APP_FEATURE_PING)
add_subdirectory_if_exist(ping)
endif()
if(QL_APP_FEATURE_NTP)
add_subdirectory_if_exist(ntp)
endif()
if(QL_APP_FEATURE_LBS)
add_subdirectory_if_exist(lbs)
endif()
if(QL_APP_FEATURE_QTHSDK)
add_subdirectory_if_exist(qthsdk)
endif()
if(QL_APP_FEATURE_CTSREG)
add_subdirectory_if_exist(ctsreg)
endif()
if(QL_APP_FEATURE_SOCKET)
add_subdirectory_if_exist(socket)
endif()
if(QL_APP_FEATURE_LCD)
add_subdirectory_if_exist(lcd)
endif()
if(QL_APP_FEATURE_MIPI_LCD)
add_subdirectory_if_exist(mipi_lcd)
endif()
if(QL_APP_FEATURE_LVGL)
add_subdirectory_if_exist(EC600U_lvgl_lib)
add_subdirectory_if_exist(EC600U_lvgl)
endif()
if(QL_APP_FEATURE_CAMERA)
add_subdirectory_if_exist(camera)
endif()
if(QL_APP_FEATURE_WIFISCAN)
add_subdirectory_if_exist(wifi_scan)
endif()
if(QL_APP_FEATURE_SIM)
add_subdirectory_if_exist(sim)
endif()
if(QL_APP_FEATURE_VSIM_ADAPT)
add_subdirectory_if_exist(vsim_adapt)
endif()
if(QL_APP_FEATURE_SMS)
add_subdirectory_if_exist(sms)
endif()
if(QL_APP_FEATURE_VOICE_CALL)
add_subdirectory_if_exist(voice_call)
endif()
if(QL_APP_FEATURE_VOLTE)
add_subdirectory_if_exist(volte)
endif()
if(QL_APP_FEATURE_SPI)
add_subdirectory_if_exist(spi)
endif()
if(QL_APP_FEATURE_SPI_NOR_FLASH)
add_subdirectory_if_exist(spi_nor_flash)
endif()
if(QL_APP_FEATURE_SPI4_EXT_NOR_SFFS)
add_subdirectory_if_exist(spi4_ext_nor_sffs)
endif()
if(QL_APP_FEATURE_SPI6_EXT_NOR)
add_subdirectory_if_exist(spi6_ext_nor_flash)
endif()
if(QL_APP_FEATURE_SPI_NAND_FLASH)
add_subdirectory_if_exist(spi_nand_flash)
endif()
if(QL_APP_FEATURE_HTTP_FOTA)
add_subdirectory_if_exist(http_fota)
endif()
if(QL_APP_FEATURE_FTP_FOTA)
add_subdirectory_if_exist(ftp_fota)
endif()
if(QL_APP_FEATURE_DECODER)
add_subdirectory_if_exist(decoder)
endif()
if(QL_APP_FEATURE_APP_IMG_AT)
add_subdirectory_if_exist(app_img_at)
endif()
if(QL_APP_FEATURE_RTC)
add_subdirectory_if_exist(rtc)
endif()
if(QL_APP_FEATURE_VIRT_AT)
add_subdirectory_if_exist(virt_at)
endif()
if(QL_APP_FEATURE_ALI_LINKSDK)
add_subdirectory_if_exist(LinkSDK)
endif()
if(QL_APP_FEATURE_QCLOUD_IOT)
add_subdirectory_if_exist(qcloud-iot)
endif()
if(QL_APP_FEATURE_I2C)
add_subdirectory_if_exist(i2c)
endif()
if(QL_APP_FEATURE_SDMMC)
add_subdirectory_if_exist(sdmmc)
endif()
if(QL_APP_FEATURE_USBNET)
add_subdirectory_if_exist(usbnet)
endif()
if(QL_APP_FEATURE_FS_NAND_FLASH)
add_subdirectory_if_exist(fs_nand_flash)
endif()
if(QL_APP_FEATURE_FS_NOR_FLASH)
add_subdirectory_if_exist(fs_nor_flash)
endif()
if(QL_APP_FEATURE_FILE_ZIP)
add_subdirectory_if_exist(zip)
endif()
if(QL_APP_FEATURE_SFTP)
add_subdirectory_if_exist(sftp)
endif()
if(QL_APP_FEATURE_MXML)
add_subdirectory_if_exist(mxml)
endif()
if(QL_APP_FEATURE_CLOUDOTA)
add_subdirectory_if_exist(cloud_ota)
endif()
if(QL_APP_FEATURE_HILINK)
add_subdirectory_if_exist(HiLinkSDK)
endif()
if(QL_APP_FEATURE_EMBED_NOR_FLASH)
add_subdirectory_if_exist(embed_nor_flash)
endif()
if(QL_APP_FEATURE_GPRS_DATA_TRANSFER)
add_subdirectory_if_exist(gprs_data_transfer)
endif()
if(QL_APP_FEATURE_TP)
add_subdirectory_if_exist(tp)
endif()
if(QL_APP_FEATURE_ALIOTSMARTCARD)
add_subdirectory_if_exist(aliotsmartcard)
endif()
if (QL_APP_FEATURE_ALIPAY_IOT_SDK)
add_subdirectory_if_exist(alipay_iot_sdk)
endif()
if(QL_APP_FEATURE_NTRIP_RTK)
add_subdirectory_if_exist(ntrip_rtk)
endif()
if(QL_APP_FEATURE_LWM2M)
add_subdirectory_if_exist(lwm2m)
endif()
if(QL_APP_FEATURE_WEBSOCKET)
add_subdirectory_if_exist(libwebsockets)
endif()
if(QL_APP_FEATURE_SS)
add_subdirectory_if_exist(ss)
endif()
if(QL_APP_FEATURE_ETHERNET)
add_subdirectory_if_exist(ethernet)
endif()
if(QL_APP_FEATURE_WIFI)
add_subdirectory_if_exist(wifi)
endif()
# add_subdirectory_if_exist(EC600U_rtk+t2n)
add_subdirectory_if_exist(EC600U_lora)
add_subdirectory_if_exist(EC600U_rtk)
add_subdirectory_if_exist(EC600U_t2n)
add_subdirectory_if_exist(EC600U_can)
add_subdirectory_if_exist(EC600U_bl0939)
add_subdirectory_if_exist(EC600U_soft_enc)
add_subdirectory_if_exist(EC600U_rs485)
add_subdirectory_if_exist(EC600U_uart1)
add_subdirectory_if_exist(esp32_iap)

View File

@ -0,0 +1,46 @@
# Copyright (C) 2020 QUECTEL Technologies Limited and/or its affiliates("QUECTEL").
# All rights reserved.
#
# set(target ql_app_t2n)
# add_library(${target} STATIC)
# set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
# target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QUEC)
# target_include_directories(${target} PUBLIC inc)
# #target_link_libraries(${target} PRIVATE ql_api_common)
# target_sources(${target}
# # cfg.c
# # cJSON.c
# # md5.c
# modbus-tcp.c
# modbus-udp.c
# ModbusS.c
# network_ql.c
# t2n.c
# )
# target_sources_if(QL_APP_FEATURE_KEYPAD THEN ${target} PRIVATE keypad_demo.c)
# target_sources_if(QL_APP_FEATURE_RS485 THEN ${target} PRIVATE rs485_demo.c)
# relative_glob(srcs include/*.h src/*.c inc/*.h)
# beautify_c_code(${target} ${srcs})
set(target ac_current)
add_library(${target} STATIC)
set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
# target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QUEC)
target_include_directories(${target} PUBLIC inc)
include_directories(${SOURCE_TOP_DIR}/components/app/t2n/include)
target_link_libraries(${target} PUBLIC)
target_sources(${target} PUBLIC
ac_current.c
)
relative_glob(srcs include/*.h src/*.c inc/*.h)
beautify_c_code(${target} ${srcs})

328
EC600U_bl0939/ac_current.c Normal file
View File

@ -0,0 +1,328 @@
#include <stdint.h>
#include <string.h>
#include "osi_api.h"
#include "ql_api_osi.h"
#include "ql_api_spi.h"
#include "osi_compiler.h"
//#include "cfg.h"
#include "../EC600U_t2n/include/t2n.h"
#include "ql_log.h"
#define AC_CURRENT_REG_ADDR 24
#define ENC_VALUE_REG_ADDR 28
// #define LOGD(msg, ...) QL_LOG(QL_LOG_LEVEL_DEBUG, "bl0939", msg, ##__VA_ARGS__)
// #define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "bl0939", msg, ##__VA_ARGS__)
// #define LOGW(msg, ...) QL_LOG(QL_LOG_LEVEL_WARN, "bl0939", msg, ##__VA_ARGS__)
// #define LOGE(msg, ...) QL_LOG(QL_LOG_LEVEL_ERROR, "bl0939", msg, ##__VA_ARGS__)
#define LOGD(msg, ...) QL_LOG(QL_LOG_LEVEL_VERBOSE, "bl0939", msg, ##__VA_ARGS__)
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_VERBOSE, "bl0939", msg, ##__VA_ARGS__)
#define LOGW(msg, ...) QL_LOG(QL_LOG_LEVEL_WARN, "bl0939", msg, ##__VA_ARGS__)
#define LOGE(msg, ...) QL_LOG(QL_LOG_LEVEL_ERROR, "bl0939", msg, ##__VA_ARGS__)
extern int enc_value;
// static ql_task_t *current_task_handle = NULL;
#define BL0939_READ_TIMER 0x8000
osiThread_t *bl_thread_handle = NULL;
osiTimer_t *bl_timer = NULL;
static int spi_wait_write_read = 0;
ql_sem_t spi_write_semaphore;
ql_sem_t spi_read_semaphore;
#define QL_SPI_WAIT_NONE 0
#define QL_SPI_WAIT_WRITE 1
#define QL_SPI_WAIT_READ 2
extern uint16_t gWordVar[];
static const uint8_t bl0939_cmd[36] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x55, 0x00, 0, 0, 0, 0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x55, 0x07, 0, 0, 0, 0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x55, 0x06, 0, 0, 0, 0};
static uint8_t spi_txbuf[36] OSI_CACHE_LINE_ALIGNED;
static uint8_t spi_rxbuf[36] OSI_CACHE_LINE_ALIGNED;
static void spi_cb_handler(ql_spi_irq_s cause)
{
if (cause.rx_dma_done == 1 && spi_wait_write_read == QL_SPI_WAIT_READ)
{
spi_wait_write_read = 0;
ql_rtos_semaphore_release(spi_read_semaphore);
}
if (cause.tx_dma_done == 1 && spi_wait_write_read == QL_SPI_WAIT_WRITE)
{
spi_wait_write_read = 0;
ql_rtos_semaphore_release(spi_write_semaphore);
}
LOGD("spi_cb_handler rx_dma_done=%d tx_dma_done=%d\r\n", cause.rx_dma_done, cause.tx_dma_done);
}
static int spi_write(int fd, uint8_t *inbuf, int len)
{
int ret;
spi_wait_write_read = QL_SPI_WAIT_WRITE;
ql_spi_request_sys_clk(fd);
ret = ql_spi_write(fd, inbuf, len);
ql_rtos_semaphore_wait(spi_write_semaphore, 10);
LOGD("spi_write inbuf=%p,len=%d,ret=%d", inbuf, len, ret);
return ret;
}
static int spi_write_read(int fd, uint8_t *outbuf, uint8_t *inbuf, int len)
{
int ret;
spi_wait_write_read = QL_SPI_WAIT_READ;
ret = ql_spi_write_read(fd, inbuf, outbuf, len);
ql_rtos_semaphore_wait(spi_read_semaphore, 10);
LOGD("spi_write_read inbuf=%p,outbuf=%p,len=%d,ret=%d", inbuf, outbuf, len, ret);
return ret;
}
static int check_sum_ch(uint8_t *resp, uint8_t *cmd, int offset)
{
uint8_t sum = ~(0x55 + cmd[offset - 1] + resp[offset] + resp[offset + 1] + resp[offset + 2]);
if (resp[offset + 3] == sum)
{
return 1;
}
return 0;
}
uint32_t readUint24LE(uint8_t *buf, int offset)
{
return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
}
uint32_t readUint24BE(uint8_t *buf, int offset)
{
return buf[offset] << 16 | buf[offset + 1] << 8 | buf[offset + 2];
}
float ac_current_coef[3] = {
1.218f / (324004.f * 0.225 * 1000 / 2000) * 100.f,
1.218f / (324004.f * 0.23 * 1000 / 2000) * 100.f,
1.218f / (79931.f * 0.002 * 500) * 100.f};
// uint8_t bl0939_rxbuf[36];
int32_t ac_ad_values[3] = {0};
static void bl_read_data_timer(void *parm)
{
osiTimerStart(bl_timer, 20);
ql_event_t event;
event.id = BL0939_READ_TIMER;
if (bl_thread_handle)
{
ql_rtos_event_send(bl_thread_handle, &event);
}
// LOGD("bl_read_data_timer\r\n");
}
static uint32_t bl0939_read_reg(int fd, uint8_t reg)
{
spi_txbuf[0] = 0x55;
spi_txbuf[1] = reg;
spi_txbuf[2] = 0;
spi_txbuf[3] = 0;
spi_txbuf[4] = 0;
spi_txbuf[5] = 0;
// bl0939_spi_reset();
spi_write_read(fd, spi_txbuf, spi_rxbuf, 6);
spi_wait_write_read = QL_SPI_WAIT_READ;
ql_rtos_semaphore_wait(spi_read_semaphore, 10);
if (spi_rxbuf[5] == (uint8_t) ~(0x55 + reg + spi_rxbuf[2] + spi_rxbuf[3] + spi_rxbuf[4]))
{
return (uint32_t)spi_rxbuf[2] << 16 |
(uint32_t)spi_rxbuf[3] << 8 |
(uint32_t)spi_rxbuf[4] << 0;
}
return 0;
}
void bl0939_spi_reset(int fd)
{
spi_txbuf[0] = 0xff;
spi_txbuf[1] = 0xff;
spi_txbuf[2] = 0xff;
spi_txbuf[3] = 0xff;
spi_txbuf[4] = 0xff;
spi_txbuf[5] = 0xff;
spi_write(fd, spi_txbuf, 6);
}
uint32_t r_temp = 0;
static int bl0939_write_reg(int fd, uint8_t reg, uint32_t val, int check)
{
int try_times = 0;
uint8_t h = val >> 16;
uint8_t m = val >> 8;
uint8_t l = val >> 0;
do
{
// bl0939_spi_reset();
spi_txbuf[0] = 0xA5;
spi_txbuf[1] = reg;
spi_txbuf[2] = h;
spi_txbuf[3] = m;
spi_txbuf[4] = l;
spi_txbuf[5] = ~(0XA5 + reg + h + m + l);
spi_write_read(fd, spi_txbuf, spi_rxbuf, 6);
ql_rtos_semaphore_wait(spi_read_semaphore, 10);
if (0 == check)
return 0;
r_temp = bl0939_read_reg(fd, reg);
if (r_temp == val)
return 0;
ql_rtos_task_sleep_ms(100);
} while (try_times++ < 5);
return 1;
}
void bl0939_init(int fd)
{
bl0939_spi_reset(fd);
bl0939_write_reg(fd, 0x19, 0x005a5a5a, 0); // <20><>λ<EFBFBD>û<EFBFBD><C3BB>Ĵ<EFBFBD><C4B4><EFBFBD>
bl0939_write_reg(fd, 0x1a, 0x00000055, 1); // <20><><EFBFBD>д<EFBFBD><D0B4><EFBFBD><EFBFBD>
bl0939_write_reg(fd, 0x10, 0xffff, 0); // Threshold A
bl0939_write_reg(fd, 0x1E, 0xffff, 1); // Threshold B
bl0939_write_reg(fd, 0x18, 0x00002000, 1); // cf
bl0939_write_reg(fd, 0x1B, 0x000047ff, 0); // cf
bl0939_write_reg(fd, 0x1a, 0x00000000, 1); // д<><D0B4><EFBFBD><EFBFBD>
}
void bl0939_task(void *data)
{
int ret;
ql_spi_config_s spi_config = {0};
int spi_no = QL_SPI_PORT1;
spi_config.port = spi_no;
spi_config.spiclk = QL_SPI_CLK_781_25KHZ;
spi_config.framesize = 8;
spi_config.input_mode = QL_SPI_INPUT_TRUE;
spi_config.cs_polarity0 = QL_SPI_CS_ACTIVE_LOW;
spi_config.cs_polarity1 = QL_SPI_CS_ACTIVE_LOW;
spi_config.cpol = QL_SPI_CPOL_LOW;
spi_config.cpha = QL_SPI_CPHA_2Edge; // mode 1
spi_config.input_sel = QL_SPI_DI_1;
spi_config.transmode = QL_SPI_DMA_IRQ;
spi_config.cs = QL_SPI_CS0;
spi_config.clk_delay = QL_SPI_CLK_DELAY_0;
LOGD("bl0939_task entry");
ql_rtos_task_sleep_ms(5000);
ql_spi_init_ext(spi_config);
ql_spi_cs_auto(spi_no);
// uint32_t addr = (uint32_t)__spi_dma_out_buf;
// addr += 32 - addr % 32;
// spi_txbuf = (uint8_t *)(addr);
// LOGD("spi_txbuf=%p",spi_txbuf);
// addr = (uint32_t)__spi_dma_in_buf;
// addr += 32 - addr % 32;
// spi_rxbuf = (uint8_t *)(addr);
// LOGD("spi_rxbuf=%p", spi_rxbuf);
LOGD("ql_spi_init done");
ql_rtos_semaphore_create(&spi_read_semaphore, 0);
ql_rtos_semaphore_create(&spi_write_semaphore, 0);
LOGD("semaphore create done");
ql_spi_irq_s mask = {
.rx_dma_done = 1,
.tx_dma_done = 1,
};
ql_spi_set_irq(spi_no, mask, spi_cb_handler);
LOGD("ql_spi_set_irq done");
bl0939_init(spi_no);
bl_thread_handle = osiThreadCurrent();
bl_timer = osiTimerCreate(bl_thread_handle, bl_read_data_timer, (void *)spi_no); // 创建定时器
if (NULL == bl_timer)
{
LOGE("bl_timer create failed\n");
osiThreadExit();
}
ret = osiTimerStart(bl_timer, 20);
if (ret)
{
LOGE("osiTimerStart failed ret=%d\n", ret);
}
LOGD("bl0939_task init done");
ret = ql_pin_set_func(95, 5); // set pin 95 to gpio10
if (ret != QL_GPIO_SUCCESS)
{
LOGE("ql_pin_set_func 95 err ret=%d", ret);
}
ql_gpio_set_direction(GPIO_10, GPIO_INPUT);
ret = ql_pin_set_func(98, 5); // set pin 95 to gpio9
if (ret != QL_GPIO_SUCCESS)
{
LOGE("ql_pin_set_func 98 err ret=%d", ret);
}
ql_gpio_set_direction(GPIO_9, GPIO_INPUT);
ret = ql_pin_set_func(99, 5); // set pin 98 to gpio8
if (ret != QL_GPIO_SUCCESS)
{
LOGE("ql_pin_set_func 99 err ret=%d", ret);
}
ql_gpio_set_direction(GPIO_8, GPIO_OUTPUT);
ret = ql_pin_set_func(100, 5); // set pin 95 to gpio12
if (ret != QL_GPIO_SUCCESS)
{
LOGE("ql_pin_set_func 100 err ret=%d", ret);
}
ql_gpio_set_direction(GPIO_8, GPIO_OUTPUT);
while (1)
{
ql_event_t event;
ret = ql_event_wait(&event, 1000);
if (ret == 0)
{
switch (event.id)
{
case BL0939_READ_TIMER:
memcpy(spi_txbuf, bl0939_cmd, sizeof(bl0939_cmd));
ret = spi_write_read(spi_no, spi_txbuf, spi_rxbuf, 36);
if (ret == 0)
{
for (int ch = 0; ch < 3; ch++)
{
if (check_sum_ch(spi_rxbuf, spi_txbuf, 8 + ch * 12))
{
ac_ad_values[ch] = readUint24BE(spi_rxbuf, 8 + ch * 12);
LOGI("channel : %d ac_ad_values: %d\n ", ch, ac_ad_values[ch]);
gWordVar[AC_CURRENT_REG_ADDR + ch] = ac_ad_values[ch] * ac_current_coef[ch];
}
else
{
int value = readUint24BE(spi_rxbuf, 8 + ch * 12);
LOGW("check bed ch: %d ac_ad_values: %d\n ", ch, value);
}
// ac_ad_values[ch] = readUint24BE(bl0939_rxbuf,8+ch*12);
// ESP_LOGI(TAG, "channel : %d ac_ad_values: %d\n ", ch, ac_ad_values[ch]);
}
}
else
{
LOGE("spi_write_read err ret=%d\r\n", ret);
}
gWordVar[ENC_VALUE_REG_ADDR] = enc_value & 0xffff;
gWordVar[ENC_VALUE_REG_ADDR + 1] = enc_value >> 16;
ql_LvlMode gpio_value9;
ql_LvlMode gpio_value10;
ql_gpio_get_level(GPIO_9, &gpio_value9);
ql_gpio_get_level(GPIO_10, &gpio_value10);
gWordVar[ENC_VALUE_REG_ADDR + 2] = gpio_value9 | (gpio_value10 << 1);
break;
}
}
else
{
LOGE("timer not work ??", ret);
osiTimerStart(bl_timer, 20);
}
}
}

47
EC600U_can/CMakeLists.txt Normal file
View File

@ -0,0 +1,47 @@
# Copyright (C) 2020 QUECTEL Technologies Limited and/or its affiliates("QUECTEL").
# All rights reserved.
#
# set(target ql_app_t2n)
# add_library(${target} STATIC)
# set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
# target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QUEC)
# target_include_directories(${target} PUBLIC inc)
# #target_link_libraries(${target} PRIVATE ql_api_common)
# target_sources(${target}
# # cfg.c
# # cJSON.c
# # md5.c
# modbus-tcp.c
# modbus-udp.c
# ModbusS.c
# network_ql.c
# t2n.c
# )
# target_sources_if(QL_APP_FEATURE_KEYPAD THEN ${target} PRIVATE keypad_demo.c)
# target_sources_if(QL_APP_FEATURE_RS485 THEN ${target} PRIVATE rs485_demo.c)
# relative_glob(srcs include/*.h src/*.c inc/*.h)
# beautify_c_code(${target} ${srcs})
set(target can)
add_library(${target} STATIC)
set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
# target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QUEC)
target_include_directories(${target} PUBLIC inc)
include_directories(${SOURCE_TOP_DIR}/components/app/t2n/include)
target_link_libraries(${target} PUBLIC)
target_sources(${target} PUBLIC
app_can.c
mcp2515.c
)
relative_glob(srcs include/*.h src/*.c inc/*.h)
beautify_c_code(${target} ${srcs})

256
EC600U_can/app_can.c Normal file
View File

@ -0,0 +1,256 @@
#include <stdint.h>
#include <string.h>
#include "osi_api.h"
#include "ql_api_osi.h"
#include "ql_api_spi.h"
#include "mcp2515.h"
// #include "hw.h"
// #include "cfg.h"
#include "../EC600U_t2n/include/t2n.h"
#include "ql_log.h"
#define LOGD(msg, ...) QL_LOG(QL_LOG_LEVEL_DEBUG, "can", msg, ##__VA_ARGS__)
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "can", msg, ##__VA_ARGS__)
#define LOGW(msg, ...) QL_LOG(QL_LOG_LEVEL_WARN, "can", msg, ##__VA_ARGS__)
#define LOGE(msg, ...) QL_LOG(QL_LOG_LEVEL_ERROR, "can", msg, ##__VA_ARGS__)
#define QL_SPI_WAIT_NONE 0
#define QL_SPI_WAIT_WRITE 1
#define QL_SPI_WAIT_READ 2
osiThread_t *can_task_handle = NULL;
extern uint16_t gWordVar[];
struct can_status *can_status[2] = {(struct can_status *)&gWordVar[512], (struct can_status *)&gWordVar[520]};
// struct can_dev_cfg *can_dev_cfg = (struct can_dev_cfg *)&gWordVar[0x1000];
// ql_sem_t spi_write_semaphore;
// ql_sem_t spi_read_semaphore;
// static int spi_wait_write_read;
// static void spi_cb_handler(ql_spi_irq_s cause)
// {
// if (cause.rx_dma_done == 1 && spi_wait_write_read == QL_SPI_WAIT_READ)
// {
// spi_wait_write_read = 0;
// ql_rtos_semaphore_release(spi_read_semaphore);
// }
// if (cause.tx_dma_done == 1 && spi_wait_write_read == QL_SPI_WAIT_WRITE)
// {
// spi_wait_write_read = 0;
// ql_rtos_semaphore_release(spi_write_semaphore);
// }
// // LOGD("spi_cb_handler rx_dma_done=%d tx_dma_done=%d\r\n", cause.rx_dma_done, cause.tx_dma_done);
// }
int spi_write(int fd, uint8_t *outbuf, int len)
{
int ret;
// spi_wait_write_read = QL_SPI_WAIT_WRITE;
// ql_spi_request_sys_clk(fd);
ret = ql_spi_write(fd, outbuf, len);
// ql_rtos_semaphore_wait(spi_write_semaphore, 10);
// LOGD("spi_write inbuf=%p,len=%d,ret=%x", outbuf, len, ret);
return ret;
}
int spi_write_read(int fd, uint8_t *outbuf, uint8_t *inbuf, int len)
{
int ret;
// spi_wait_write_read = QL_SPI_WAIT_READ;
ret = ql_spi_write_read(fd, inbuf, outbuf, len);
// ql_rtos_semaphore_wait(spi_read_semaphore, 10);
// LOGD("spi_write_read inbuf=%p,outbuf=%p,len=%d,ret=%x", inbuf, outbuf, len, ret);
return ret;
}
static void gpio_mcp_int_cb(void *ctx)
{
ql_event_t event;
event.id = 1000;
if (can_task_handle)
{
ql_rtos_event_send(can_task_handle, &event);
}
// LOGI("gpio_mcp_int_cb");
}
osiTimer_t *can_led_timer = NULL;
void can_led_timer_cb(void *param)
{
ql_event_t event;
event.id = 1001;
if (can_task_handle)
{
ql_rtos_event_send(can_task_handle, &event);
}
}
void can_task(void *data)
{
int ret;
// ret = ql_pin_set_func(QL_CUR_SPI2_CS_PIN, QUEC_PIN_SPI2_FUNC);
// if (ret != QL_GPIO_SUCCESS)
// {
// LOGE("set pin err");
// }
// ret = ql_pin_set_func(QL_CUR_SPI2_CLK_PIN, QUEC_PIN_SPI2_FUNC);
// if (ret != QL_GPIO_SUCCESS)
// {
// LOGE("set pin err");
// }
// ret = ql_pin_set_func(QL_CUR_SPI2_DO_PIN, QUEC_PIN_SPI2_FUNC);
// if (ret != QL_GPIO_SUCCESS)
// {
// LOGE("set pin err");
// }
// ret = ql_pin_set_func(QL_CUR_SPI2_DI_PIN, QUEC_PIN_SPI2_FUNC);
// if (ret != QL_GPIO_SUCCESS)
// {
// LOGE("set pin err");
// }
// ret = ql_pin_set_func(GPIO_10, 0); // set pin4 sp1_cs to gpio fun
// if (ret != QL_GPIO_SUCCESS)
// {
// LOGE("set pin err");
// }
// ql_gpio_deinit(GPIO_10);
// ql_gpio_init(GPIO_10, GPIO_INPUT, PULL_UP, LVL_HIGH);
ql_spi_config_s spi_config = {0};
int spi_no = QL_SPI_PORT2;
spi_config.port = spi_no;
spi_config.spiclk = QL_SPI_CLK_6_25MHZ;
spi_config.framesize = 8;
spi_config.input_mode = QL_SPI_INPUT_TRUE;
spi_config.cs_polarity0 = QL_SPI_CS_ACTIVE_LOW;
spi_config.cs_polarity1 = QL_SPI_CS_ACTIVE_LOW;
spi_config.cpol = QL_SPI_CPOL_LOW;
spi_config.cpha = QL_SPI_CPHA_1Edge;
spi_config.input_sel = QL_SPI_DI_1;
spi_config.transmode = QL_SPI_DIRECT_POLLING;
spi_config.cs = QL_SPI_CS0;
spi_config.clk_delay = QL_SPI_CLK_DELAY_0;
ql_spi_init_ext(spi_config);
ql_spi_cs_auto(spi_no);
// ql_rtos_semaphore_create(&spi_read_semaphore, 0);
// ql_rtos_semaphore_create(&spi_write_semaphore, 0);
// ql_spi_irq_s mask = {
// .rx_dma_done = 1,
// .tx_dma_done = 1,
// };
// ql_spi_set_irq(spi_no, mask, spi_cb_handler);
can_config_t can_cfg;
memset(&can_cfg, 0, sizeof(can_cfg));
can_cfg.baud_rate = 500000;
can_cfg.can_mode = MODE_NORMAL;
can_cfg.rx_ctrl[0] = RXB_RX_ANY;
can_cfg.rx_ctrl[1] = RXB_RX_ANY;
can_cfg.rx_mask[0] = 0x1fffffff | CAN_EFF_FLAG;
can_cfg.rx_mask[1] = 0x1fffffff | CAN_EFF_FLAG;
can_cfg.rx_filter[0] = 0x18FEEE00 | CAN_EFF_FLAG;
can_cfg.rx_filter[1] = 0x18FEEF00 | CAN_EFF_FLAG;
can_cfg.rx_filter[2] = 0X18FEE500 | CAN_EFF_FLAG;
ret = mcp_can_init(spi_no, &can_cfg);
if (ret != 0)
{
LOGE("mcp_can_init fail ret=%d\n", ret);
}
// ql_rtos_task_get_current_ref(&can_task_handle);
can_task_handle = osiThreadCurrent();
// ql_pin_set_func(15, 4); // set GPIO22
ql_int_register(GPIO_22, EDGE_TRIGGER, DEBOUNCE_DIS, EDGE_FALLING, PULL_UP, gpio_mcp_int_cb, NULL);
ql_int_enable(GPIO_22);
int tx_led_status = 0;
int rx_led_status = 0;
// can_led_timer = osiTimerCreate(can_task_handle, can_led_timer_cb, NULL); // 创建定时器
// if (NULL == can_led_timer)
// {
// LOGE("can_led_timer create failed\n");
// osiThreadExit();
// }
// ret = osiTimerStart(can_led_timer, 100);
// if (ret != TRUE)
// {
// LOGE("can_led_timer start failed\n");
// }
while (1)
{
ql_event_t event;
ql_LvlMode int_value;
if (ql_event_wait(&event, 100) != 0) // 中断有时会丢失,所以超时也读取接收缓冲区
{
event.id = 1000;
ql_gpio_get_level(GPIO_22, &int_value);
}
else
{
int_value = 0;
}
switch (event.id)
{
case 1000: {
if (int_value == 0)
{
LED0_ON(spi_no);
rx_led_status = 1;
do
{
CanRxMsg RxMsg;
if (can_read_rxbuf(spi_no, &RxMsg))
{
// LOGI("CAN0: Id=0x%x,IDE=%d,DLC=%d,Data=[%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x]", RxMsg.Id, RxMsg.IDE, RxMsg.DLC, RxMsg.Data[0], RxMsg.Data[1], RxMsg.Data[2], RxMsg.Data[3], RxMsg.Data[4], RxMsg.Data[5], RxMsg.Data[6], RxMsg.Data[7]);
// if (can_ops[0] != NULL && can_ops[0]->parse)
// {
// can_status[0]->rx_cnt++;
// can_ops[0]->parse(&RxMsg);
// }
}
ql_gpio_get_level(GPIO_22, &int_value);
} while (int_value == 0);
LED0_OFF(spi_no);
}
break;
}
break;
// case 1001:
// if (rx_led_status == 1)
// {
// LED0_OFF(spi_no);
// rx_led_status = 2;
// }
// else if(rx_led_status == 2)
// {
// rx_led_status = 0;
// }
// if (tx_led_status == 1)
// {
// LED1_OFF(spi_no);
// tx_led_status = 2;
// }
// else if(rx_led_status == 2)
// {
// rx_led_status = 0;
// }
// osiTimerStart(can_led_timer, 100);
// break;
case 0: {
CanTxMsg TxPack = {
.Id = 0x182,
.IDE = CAN_ID_STD,
.DLC = 8,
.Data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
LED1_ON(spi_no);
tx_led_status = 1;
can_tx_pack(spi_no, &TxPack);
can_status[0]->bus_status = get_mcp_status(spi_no);
}
break;
}
}
}

475
EC600U_can/mcp2515.c Normal file
View File

@ -0,0 +1,475 @@
#include <stdint.h>
#include "mcp2515.h"
#include "ql_api_spi.h"
#include "ql_api_osi.h"
// #include "hw.h"
#include "ql_log.h"
#define LOGD(msg, ...) QL_LOG(QL_LOG_LEVEL_DEBUG, "mcp2515", msg, ##__VA_ARGS__)
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "mcp2515", msg, ##__VA_ARGS__)
#define LOGW(msg, ...) QL_LOG(QL_LOG_LEVEL_WARN, "mcp2515", msg, ##__VA_ARGS__)
#define LOGE(msg, ...) QL_LOG(QL_LOG_LEVEL_ERROR, "mcp2515", msg, ##__VA_ARGS__)
extern struct can_status *can_status[2];
#define mcp_cs(fd) // ql_spi_cs_low(fd)
#define mcp_ncs(fd) // ql_spi_cs_high(fd)
static uint8_t spi_txbuf[32] __attribute__((aligned(32)));
static uint8_t spi_rxbuf[32] __attribute__((aligned(32)));
unsigned char spi_put(unsigned char);
void mcp_wr_can_eid(int fd, unsigned char add, unsigned long can_id)
{
unsigned char txbuf[4];
uint16_t canid;
canid = (uint16_t)can_id;
txbuf[EID0] = canid & 0xff;
txbuf[EID8] = (canid >> 8) & 0xff;
canid = (uint16_t)(can_id >> 16);
txbuf[SIDL] = canid & 0x03;
canid = canid << 3;
txbuf[SIDL] |= (canid & 0xe0);
txbuf[SIDL] |= 0x8;
txbuf[SIDH] = canid >> 8;
mcp_wrs(fd, add, txbuf, 4);
}
unsigned long mcp_rd_can_eid(int fd, unsigned char add)
{
unsigned char rcbuf[4];
uint32_t can_id;
uint16_t canid;
mcp_rds(fd, add, rcbuf, 4);
can_id = rcbuf[EID0] + (rcbuf[EID8] << 8);
canid = rcbuf[SIDL] + (rcbuf[SIDH] << 8);
canid = canid >> 3;
canid |= (rcbuf[SIDL] & 0x03);
can_id |= (canid << 16);
return can_id;
}
void mcp_wr_can_id(int fd, unsigned char add, unsigned int CanId)
{
unsigned char txbuf[4];
uint16_t sid;
uint32_t eid;
if (CanId & CAN_EFF_FLAG) // eid
{
sid = (CanId >> 18) & 0x7ff;
eid = CanId & 0x3ffff;
txbuf[SIDH] = (uint8_t)(sid >> 3);
txbuf[SIDL] = (sid << 5) & 0xe0;
txbuf[SIDL] |= (1 << 3);
txbuf[SIDL] |= (eid >> 16);
txbuf[EID8] = (eid >> 8) & 0xff;
txbuf[EID0] = eid & 0xff;
}
else
{
sid = CanId & 0x7ff;
txbuf[SIDH] = (uint8_t)(sid >> 3);
txbuf[SIDL] = (sid << 5) & 0xe0;
txbuf[EID8] = 0;
txbuf[EID0] = 0;
}
mcp_wrs(fd, add, txbuf, 4);
}
unsigned int mcp_rd_can_id(int fd, unsigned char add)
{
unsigned int CanId;
unsigned char rcbuf[4];
mcp_rds(fd, add, rcbuf, 4);
CanId = (rcbuf[SIDL] + (rcbuf[SIDH] << 8));
CanId >>= 5;
CanId &= 0x7ff;
if (rcbuf[SIDL] & (1 << 3))
{
unsigned int eid;
eid = rcbuf[EID0] | (rcbuf[EID8] << 8);
eid |= (rcbuf[SIDL] & 0x03) << 16;
CanId <<= 18;
CanId |= eid;
CanId |= CAN_EFF_FLAG;
}
return CanId;
}
void mcp_reset(int fd)
{
mcp_cs(fd);
spi_txbuf[0] = RESET;
spi_write(fd, spi_txbuf, 1);
mcp_ncs(fd);
}
// 单字节写
void mcp_wr(int fd, unsigned char add, unsigned char DAT)
{
spi_txbuf[0] = WRITE;
spi_txbuf[1] = add;
spi_txbuf[2] = DAT;
mcp_cs(fd);
spi_write(fd, spi_txbuf, 3);
mcp_ncs(fd);
}
// 多字节写
void mcp_wrs(int fd, unsigned char add, unsigned char *wr_dat, unsigned char length)
{
spi_txbuf[0] = WRITE;
spi_txbuf[1] = add;
mcp_cs(fd);
for (int i = 0; i < length; i++)
{
spi_txbuf[i + 2] = wr_dat[i];
}
spi_write(fd, spi_txbuf, length + 2);
mcp_ncs(fd);
}
// 单字节读
unsigned char mcp_rd(int fd, unsigned char add)
{
spi_txbuf[0] = READ;
spi_txbuf[1] = add;
spi_txbuf[2] = 0;
mcp_cs(fd);
spi_write_read(fd, spi_txbuf, spi_rxbuf, 3);
mcp_ncs(fd);
return (spi_rxbuf[2]);
}
// 多字节读
void mcp_rds(int fd, unsigned char add, unsigned char *DAT, unsigned char length)
{
spi_txbuf[0] = READ;
spi_txbuf[1] = add;
for (int i = 0; i < length; i++)
{
spi_txbuf[2 + i] = 0;
}
mcp_cs(fd);
spi_write_read(fd, spi_txbuf, spi_rxbuf, length + 2);
for (int i = 0; i < length; i++)
{
*DAT++ = spi_rxbuf[2 + i];
}
mcp_ncs(fd);
}
// 发送请求
void rts(int fd, unsigned char ch)
{
ch &= 0x7;
ch |= 0x80;
spi_txbuf[0] = ch;
mcp_cs(fd);
spi_write(fd, spi_txbuf, 1);
mcp_ncs(fd);
}
// 状态读
unsigned char get_mcp_status(int fd)
{
spi_txbuf[0] = STATUS;
spi_txbuf[1] = 0;
mcp_cs(fd);
spi_write_read(fd, spi_txbuf, spi_rxbuf, 2);
mcp_ncs(fd);
return (spi_rxbuf[1]);
}
void mcp_wr_bit(int fd, unsigned char add, unsigned char DAT, unsigned char mask)
{
spi_txbuf[0] = 0x05;
spi_txbuf[1] = add;
spi_txbuf[2] = mask;
spi_txbuf[3] = DAT;
mcp_cs(fd);
spi_write(fd, spi_txbuf, 4);
mcp_ncs(fd);
}
void mcp_rd_rxbuf(int fd, unsigned char buf, unsigned char *DAT, unsigned char length)
{
buf <<= 1;
buf &= 0x06;
buf |= 0x90;
spi_txbuf[0] = buf;
for (int i = 0; i < length; i++)
{
spi_txbuf[i + 1] = 0;
}
mcp_cs(fd);
spi_write_read(fd, spi_txbuf, spi_rxbuf, length + 1);
for (int i = 0; i < length; i++)
{
*DAT++ = spi_rxbuf[1 + i];
}
mcp_ncs(fd);
}
void mcp_ld_txbuf(int fd, unsigned char buf, unsigned char *DAT, unsigned char length)
{
mcp_cs(fd);
buf &= 0x07;
buf |= 0x40;
spi_txbuf[0] = buf;
for (int i = 0; i < length; i++)
{
spi_txbuf[i + 1] = 0;
}
spi_write_read(fd, spi_txbuf, spi_rxbuf, length + 1);
for (int i = 0; i < length; i++)
{
*DAT++ = spi_rxbuf[i + 1];
}
mcp_ncs(fd);
}
unsigned char get_mcp_rxstatus(int fd)
{
spi_txbuf[0] = 0xb0;
spi_txbuf[1] = 0;
mcp_cs(fd);
spi_write_read(fd, spi_txbuf, spi_rxbuf, 2);
mcp_ncs(fd);
return spi_rxbuf[1];
}
int mcp_can_init(int ch, can_config_t *cfg)
{
// uint8_t value;
int can_clock = 24000000;
uint8_t brp;
int bit_time;
mcp_reset(ch);
ql_rtos_task_sleep_ms(20);
mcp_wr(ch, CANCTRL, MODE_CONFIG);
if (mcp_rd(ch, CANCTRL) != MODE_CONFIG)
{
return -1;
}
bit_time = can_clock / 2 / cfg->baud_rate;
if (bit_time % 12 == 0) // ±ê×¼²¨ÌØÂÊ 125,250,500,10000
{
mcp_wr(ch, CNF2, BTLMODE_CNF3 + SEG3 * 8 + SEG5);
mcp_wr(ch, CNF3, SEG3);
brp = bit_time / 12 - 1;
if (brp > 7)
{
return -2;
}
mcp_wr(ch, CNF1, SJW1 + brp);
}
else if (bit_time % 15 == 0) // 100,200,400,800
{
mcp_wr(ch, CNF2, BTLMODE_CNF3 + SEG4 * 8 + SEG6);
mcp_wr(ch, CNF3, SEG4);
brp = bit_time / 15 - 1;
if (brp > 7)
{
return -2;
}
mcp_wr(ch, CNF1, SJW1 + brp);
}
else if (bit_time % 16 == 0) // 750,325
{
mcp_wr(ch, CNF2, BTLMODE_CNF3 + SEG5 * 8 + SEG6);
mcp_wr(ch, CNF3, SEG4);
brp = bit_time / 16 - 1;
if (brp > 7)
{
return -2;
}
mcp_wr(ch, CNF1, SJW1 + brp);
}
else // ²»Ö§³ÖµÄ²¨ÌØÂÊ
{
return -2;
}
mcp_wr(ch, RXB0CTRL, cfg->rx_ctrl[0]); // Â˲¨Æ÷0
mcp_wr_can_id(ch, RXM0SIDH, cfg->rx_mask[0]);
mcp_wr_can_id(ch, RXF0SIDH, cfg->rx_filter[0]);
mcp_wr(ch, RXB1CTRL, cfg->rx_ctrl[1]); // Â˲¨Æ÷1
mcp_wr_can_id(ch, RXF1SIDH, cfg->rx_filter[1]);
mcp_wr_can_id(ch, RXM1SIDH, cfg->rx_mask[1]);
mcp_wr_can_id(ch, RXF2SIDH, cfg->rx_filter[2]);
mcp_wr_can_id(ch, RXF3SIDH, cfg->rx_filter[3]);
mcp_wr_can_id(ch, RXF4SIDH, cfg->rx_filter[4]);
mcp_wr_can_id(ch, RXF5SIDH, cfg->rx_filter[5]);
mcp_wr(ch, CANINTE, 0x03);
mcp_wr(ch, CANINTF, 0x00);
mcp_wr_bit(ch, TXRTSCTRL, 0x0, 0xff);
mcp_wr_bit(ch, BFPCTRL, 0x3c, 0xff);
mcp_wr(ch, CANCTRL, (cfg->can_mode & 0xf0));
LED0_ON(ch);
LED1_ON(ch);
LOGD("[%s] can%d init ok", __FUNCTION__, ch);
return 0;
}
int can_read_rxbuf(int ch, CanRxMsg *RxMsg)
{
unsigned char status;
uint32_t can_id;
status = get_mcp_rxstatus(ch);
if (status & 0x40)
{
RxMsg->Id = mcp_rd_can_id(ch, 0x61);
if (RxMsg->Id & (1 << 31))
{
RxMsg->Id &= 0x1fffffff;
RxMsg->IDE = CAN_Id_Extended;
}
else
{
RxMsg->Id &= 0x7ff;
RxMsg->IDE = CAN_Id_Standard;
}
RxMsg->DLC = mcp_rd(ch, 0x65);
if (RxMsg->DLC & 0x40)
{
RxMsg->DLC = 0;
RxMsg->RTR = 1;
goto ret1; // Ë͵½rtr
}
RxMsg->RTR = 0;
if (RxMsg->DLC > 8)
{
RxMsg->DLC = 8;
}
mcp_rd_rxbuf(ch, 0x1, RxMsg->Data, RxMsg->DLC);
ret1:
mcp_wr_bit(ch, CANINTF, 0x00, 0x01);
return 1;
}
if (status & 0x80)
{
RxMsg->Id = mcp_rd_can_id(ch, 0x71);
if (can_id & (1 << 31))
{
RxMsg->Id &= 0x1fffffff;
RxMsg->IDE = CAN_Id_Extended;
}
else
{
RxMsg->Id &= 0x7ff;
RxMsg->IDE = CAN_Id_Standard;
}
RxMsg->DLC = mcp_rd(ch, 0x75);
if (RxMsg->DLC & 0x40)
{
RxMsg->DLC = 0;
RxMsg->RTR = 1;
goto ret2; // ÊÕµ½rtr
}
RxMsg->RTR = 0;
if (RxMsg->DLC > 8)
{
RxMsg->DLC = 8;
}
mcp_rd_rxbuf(ch, 0x3, RxMsg->Data, RxMsg->DLC);
ret2:
mcp_wr_bit(ch, CANINTF, 0x00, 0x02);
return 2;
}
return 0;
}
int can_tx_pack(int ch, CanTxMsg *TxPack)
{
// #ifdef _CAN_DEBUG_
// char msg[256];
// int i;
// int n = sprintf(msg, "CAN%d Tx: Id=0x%x,IDE=%x,DLC=%d,Data=[", ch, TxPack->Id, TxPack->IDE, TxPack->DLC);
// for (i = 0; i < TxPack->DLC; i++)
// {
// n += sprintf(&msg[n], "%02x ", TxPack->Data[i]);
// }
// msg[n - 1] = ']';
// eat_trace(msg);
// #endif
can_status[0]->tx_cnt++;
if (!(mcp_rd(ch, 0x30) & 0x08))
{ // ·¢ËÍ»º³åÇø0¿Õ£¿
LED1_ON(ch);
if (TxPack->IDE == CAN_Id_Extended)
{
TxPack->Id |= (1 << 31);
}
else
{
TxPack->Id &= 0x7ff;
}
mcp_wr_can_id(ch, 0x31, TxPack->Id);
if (TxPack->RTR)
{
mcp_wr(ch, 0x35, 0x40); // ·¢ËÍRTR
}
else
{
mcp_wr(ch, 0x35, TxPack->DLC);
mcp_ld_txbuf(ch, 0x1, (uint8_t *)&TxPack->Data[0], TxPack->DLC);
}
rts(ch, 1);
LED1_OFF(ch);
return 0;
}
if (!(mcp_rd(ch, 0x40) & 0x08))
{ // ·¢ËÍ»º³åÇø1¿Õ£¿
LED1_ON(ch);
if (TxPack->IDE == CAN_Id_Extended)
{
TxPack->Id |= (1 << 31);
}
else
{
TxPack->Id &= 0x7ff;
}
mcp_wr_can_id(ch, 0x41, TxPack->Id);
if (TxPack->RTR)
{
mcp_wr(ch, 0x45, 0x40); // ·¢ËÍRTR
}
else
{
mcp_wr(ch, 0x45, TxPack->DLC);
mcp_ld_txbuf(ch, 0x3, (uint8_t *)&TxPack->Data[0], TxPack->DLC);
}
rts(ch, 2);
LED1_OFF(ch);
return 0;
}
if (!(mcp_rd(ch, 0x50) & 0x08))
{ // ·¢ËÍ»º³åÇø2¿Õ£¿
LED1_ON(ch);
if (TxPack->IDE == CAN_Id_Extended)
{
TxPack->Id |= (1 << 31);
}
else
{
TxPack->Id &= 0x7ff;
}
mcp_wr_can_id(ch, 0x51, TxPack->Id);
if (TxPack->RTR)
{
mcp_wr(ch, 0x55, 0x40); // ·¢ËÍRTR
}
else
{
mcp_wr(ch, 0x55, TxPack->DLC);
mcp_ld_txbuf(ch, 0x5, (uint8_t *)&TxPack->Data[0], TxPack->DLC);
}
rts(ch, 4);
LED1_OFF(ch);
return 0;
}
if (mcp_rd(ch, EFLG) & (1 << 5))
{
mcp_wr(ch, TEC, 0);
}
return 1; // ·¢ËÍ»º³åÇøÂú,·¢ËÍʧ°Ü
}

354
EC600U_can/mcp2515.h Normal file
View File

@ -0,0 +1,354 @@
#ifndef _2515_H
#define _2515_H
/*
** Register offsets into the transmit buffers.
*/
#define TXBnCTRL 0
#define TXBnSIDH 1
#define TXBnSIDL 2
#define TXBnEID8 3
#define TXBnEID0 4
#define TXBnDLC 5
#define TXBnD0 6
#define TXBnD1 7
#define TXBnD2 8
#define TXBnD3 9
#define TXBnD4 10
#define TXBnD5 11
#define TXBnD6 12
#define TXBnD7 13
#define CANSTAT 14
#define CANCTRL 15
#define SIDH 0
#define SIDL 1
#define EID8 2
#define EID0 3
/*
** Register offsets into the receive buffers.
*/
#define RXBnCTRL 0
#define RXBnSIDH 1
#define RXBnSIDL 2
#define RXBnEID8 3
#define RXBnEID0 4
#define RXBnDLC 5
#define RXBnD0 6
#define RXBnD1 7
#define RXBnD2 8
#define RXBnD3 9
#define RXBnD4 10
#define RXBnD5 11
#define RXBnD6 12
#define RXBnD7 13
/*
** Bits in the TXBnCTRL registers.
*/
#define TXB_TXBUFE_M 0x80
#define TXB_ABTF_M 0x40
#define TXB_MLOA_M 0x20
#define TXB_TXERR_M 0x10
#define TXB_TXREQ_M 0x08
#define TXB_TXIE_M 0x04
#define TXB_TXP10_M 0x03
#define DLC_MASK 0x0F
#define RTR_MASK 0x40
#define TXB0CTRL 0x30
#define TXB0SIDH 0x31
#define TXB1CTRL 0x40
#define TXB1SIDH 0x41
#define TXB2CTRL 0x50
#define TXB2SIDH 0x51
#define TXPRIOHIGH 0x03
#define TXPRIOHIGHLOW 0x02
#define TXPRIOLOWHIGH 0x01
#define TXPRIOLOW 0x00
#define TXB_EXIDE_M 0x08 // In TXBnSIDL
#define TXB_RTR_M 0x40 // In TXBnDLC
#define RXB_IDE_M 0x08 // In RXBnSIDL
#define RXB_RTR_M 0x40 // In RXBnDLC
#define BFPCTRL 0x0C
#define B2RTS 0x20
#define B1RTS 0x10
#define B0RTS 0x08
#define B2RTSM 0x04
#define B1RTSM 0x02
#define B0RTSM 0x01
#define TEC 0x1C
#define REC 0x1D
#define CLKCTRL CANCTRL
#define RXF0SIDH 0
#define RXF0SIDL 1
#define RXF0EID8 2
#define RXF0EID0 3
#define RXF1SIDH 4
#define RXF1SIDL 5
#define RXF1EID8 6
#define RXF1EID0 7
#define RXF2SIDH 8
#define RXF2SIDL 9
#define RXF2EID8 10
#define RXF2EID0 11
#define RXF3SIDH 16
#define RXF3SIDL 17
#define RXF3EID8 18
#define RXF3EID0 19
#define RXF4SIDH 20
#define RXF4SIDL 21
#define RXF4EID8 22
#define RXF4EID0 23
#define RXF5SIDH 24
#define RXF5SIDL 25
#define RXF5EID8 26
#define RXF5EID0 27
#define RXF_EXIDE_M 0x08
#define RXM0SIDH 0x20
#define RXM1SIDH 0x24
#define CNF3 0x28
#define CNF2 0x29
#define CNF1 0x2A
#define CANINTE 0x2B
#define CANINTF 0x2C
#define EFLG 0x2D
#define TXRTSCTRL 0x0D
#define EFLG_RX1OVR 0x80
#define EFLG_RX0OVR 0x40
#define EFLG_TXBO 0x20
#define EFLG_TXEP 0x10
#define EFLG_RXEP 0x08
#define EFLG_TXWAR 0x04
#define EFLG_RXWAR 0x02
#define EFLG_EWARN 0x01
#define SJW1 0x00
#define SJW2 0x40
#define SJW3 0x80
#define SJW4 0xC0
#define BTLMODE_CNF3 0x80
#define SAMP1 0x00
#define SAMP3 0x40
#define SEG1 0x00
#define SEG2 0x01
#define SEG3 0x02
#define SEG4 0x03
#define SEG5 0x04
#define SEG6 0x05
#define SEG7 0x06
#define SEG8 0x07
#define BRP1 0x00
#define BRP2 0x01
#define BRP3 0x02
#define BRP4 0x03
#define BRP5 0x04
#define BRP6 0x05
#define BRP7 0x06
#define BRP8 0x07
#define IVRIE 0x80
#define WAKIE 0x40
#define ERRIE 0x20
#define TX2IE 0x10
#define TX1IE 0x08
#define TX0IE 0x04
#define RX1IE 0x02
#define RX0IE 0x01
#define NO_IE 0x00
#define IVRINT 0x80
#define WAKINT 0x40
#define ERRINT 0x20
#define TX2INT 0x10
#define TX1INT 0x08
#define TX0INT 0x04
#define RX1INT 0x02
#define RX0INT 0x01
#define NO_INT 0x00
#define RXB0CTRL 0x60
#define RXB1CTRL 0x70
#define RXB_RXRDY 0x80
#define RXB_RXM1 0x40
#define RXB_RXM0 0x20
#define RXB_RX_ANY 0x60
#define RXB_RX_EXT 0x40
#define RXB_RX_STD 0x20
#define RXB_RX_STDEXT 0x00
#define RXB_RXMx_M 0x60
// #define RXB_RXIE_M 0x10
#define RXB_RXRTR 0x08 // In RXBnCTRL
#define RXB_BUKT 0x04
#define RXB_BUKT_RO 0x02
#define RXB_FILHIT 0x01
#define RXB_FILHIT2 0x04
#define RXB_FILHIT1 0x02
#define RXB_FILHIT_M 0x07
#define RXB_RXF5 0x05
#define RXB_RXF4 0x04
#define RXB_RXF3 0x03
#define RXB_RXF2 0x02
#define RXB_RXF1 0x01
#define RXB_RXF0 0x00
#define CLKEN 0x04
#define CLK1 0x00
#define CLK2 0x01
#define CLK4 0x02
#define CLK8 0x03
#define MODE_NORMAL 0x00
#define MODE_SLEEP 0x20
#define MODE_LOOPBACK 0x40
#define MODE_LISTENONLY 0x60
#define MODE_CONFIG 0xE0
#define ABORT 0x10
#define RECEIVE_BUFFER(x) (0x60 + 0x10 * (x))
#define TRANSMIT_BUFFER(x) (0x30 + 0x10 * (x))
#define RESET 0xc0 // Reset internal registers to default state, set conf mode
#define RTS 0x80 // Trigg transmission
#define RD_STAT 0xA0 // Start reading status
#define BIT_MOD 0x05 // Bit modify command data == MASK, BITS
#define READ 0x03 // Read data from memory
#define WRITE 0x02 // Write data to memory
#define STATUS 0xa0
#define LED0_ON(fd) mcp_wr_bit(fd, BFPCTRL, 0Xff, 0x10)
#define LED1_ON(fd) mcp_wr_bit(fd, BFPCTRL, 0Xff, 0x20)
#define LED0_OFF(fd) mcp_wr_bit(fd, BFPCTRL, 0X00, 0x10)
#define LED1_OFF(fd) mcp_wr_bit(fd, BFPCTRL, 0X00, 0x20)
typedef struct
{
uint32_t Id; /*!< Specifies the standard identifier.
This parameter can be a value between 0 to 0x7FF. */
uint8_t IDE; /*!< Specifies the type of identifier for the message that
will be received. This parameter can be a value of
@ref CAN_identifier_type */
uint8_t RTR; /*!< Specifies the type of frame for the received message.
This parameter can be a value of
@ref CAN_remote_transmission_request */
uint8_t DLC; /*!< Specifies the length of the frame that will be received.
This parameter can be a value between 0 to 8 */
uint8_t FMI; /*!< Specifies the index of the filter the message stored in
the mailbox passes through. This parameter can be a
value between 0 to 0xFF */
uint8_t Data[8]; /*!< Contains the data to be received. It ranges from 0 to
0xFF. */
} CanRxMsg;
typedef struct
{
uint32_t Id; /*!< Specifies the standard identifier.
This parameter can be a value between 0 to 0x7FF. */
uint8_t IDE; /*!< Specifies the type of identifier for the message that
will be transmitted. This parameter can be a value
of @ref CAN_identifier_type */
uint8_t RTR; /*!< Specifies the type of frame for the message that will
be transmitted. This parameter can be a value of
@ref CAN_remote_transmission_request */
uint8_t DLC; /*!< Specifies the length of the frame that will be
transmitted. This parameter can be a value between
0 to 8 */
uint8_t Rsv;
uint8_t Data[8]; /*!< Contains the data to be transmitted. It ranges from 0
to 0xFF. */
} CanTxMsg;
#define CAN_Id_Standard ((uint32_t)0x00000000) /*!< Standard Id */
#define CAN_Id_Extended ((uint32_t)0x00000004) /*!< Extended Id */
#define CAN_ID_STD CAN_Id_Standard
#define CAN_ID_EXT CAN_Id_Extended
#define CAN_EFF_FLAG 0x80000000U // 扩展帧的标识
#define CAN_RTR_FLAG 0x40000000U // 远程帧的标识
#define CAN_ERR_FLAG 0x20000000U // 错误帧的标识,用于错误检查
// extern void mcp_cs(int fd);
// extern void mcp_ncs(int fd);
int spi_write(int fd, uint8_t *spi_txbuf, int len);
int spi_write_read(int fd, uint8_t *spi_txbuf, uint8_t *spi_rxbuf, int len);
// void mcp_rd_can ( unsigned char buffer, unsigned char * ext, unsigned long* can_id,unsigned char * dlc, unsigned char * rtr, unsigned char * data );
// void mcp_wr_can_id ( unsigned char mcp_addr,unsigned long);
// unsigned long mcp_rd_can_id ( unsigned char mcp_addr);
void mcp_wr_can_id(int fd, unsigned char, unsigned int);
unsigned int mcp_rd_can_id(int fd, unsigned char);
void mcp_reset(int fd);
unsigned char mcp_rd(int fd, unsigned char);
void mcp_rds(int fd, unsigned char, unsigned char *, unsigned char);
void mcp_wr(int fd, unsigned char, unsigned char);
void mcp_wrs(int fd, unsigned char, unsigned char *, unsigned char);
unsigned char mcp_rd(int fd, unsigned char);
void mcp_write_tbuf(int fd, unsigned char value);
// void mcp_write_can (unsigned char buffer, unsigned char ext, unsigned long can_id,
// unsigned char dlc, unsigned char rtr, const unsigned char * data );
void mcp_transmit(int fd, unsigned char buffer);
// void mcp_init(int fd);
void mcp_write(int fd, unsigned char MCPaddr, const unsigned char *writedata, unsigned char length);
void mcp_ld_txbuf(int fd, unsigned char buf, unsigned char *DAT, unsigned char length);
void rts(int fd, unsigned char);
void mcp_wr_bit(int fd, unsigned char add, unsigned char DAT, unsigned char mask);
unsigned char get_mcp_rxstatus(int fd);
void mcp_rd_rxbuf(int fd, unsigned char buf, unsigned char *DAT, unsigned char length);
// uint8_t spi_write(uint8_t *data, uint8_t length);
// uint8_t spi_read(uint8_t *data, uint8_t length);
// uint8_t spi_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, uint8_t rlen);
typedef struct can_config
{
uint32_t baud_rate;
uint8_t rx_ctrl[2];
uint16_t can_mode;
uint32_t rx_mask[2];
uint32_t rx_filter[6];
} can_config_t;
struct can_status
{
uint8_t bus_status;
uint8_t status;
uint16_t rx_cnt;
uint16_t tx_cnt;
};
int mcp_can_init(int ch, can_config_t *cfg);
int can_tx_pack(int ch, CanTxMsg *TxPack);
int can_read_rxbuf(int ch, CanRxMsg *RxMsg);
unsigned char get_mcp_status(int fd);
#endif

View File

@ -0,0 +1,19 @@
set(target ec600u_lora)
add_library(${target} STATIC)
set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
# target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QUEC)
target_include_directories(${target} PUBLIC inc)
include_directories(${SOURCE_TOP_DIR}/components/app/t2n/include)
target_link_libraries(${target} PUBLIC)
target_sources(${target} PUBLIC
lora_app.c
lora_test.c
lora_test_function.c
user.c
lora.c
)
relative_glob(srcs include/*.h src/*.c inc/*.h)
beautify_c_code(${target} ${srcs})

27
EC600U_lora/inc/lora.h Normal file
View File

@ -0,0 +1,27 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/*获取同步字*/
uint16_t lora_get_syncword(void);
/*设置standby模式*/
void lora_set_standby(uint8_t data);
/*lora模块初始化函数*/
int lora_init(void);
/*启动lora通讯*/
int lora_start(void);
/*设置radio参数*/
int lora_radioConfig(void);
/*获取中断状态*/
uint16_t lora_get_irq_status(void);
/*清除中断状态*/
void lora_clear_irq_status(uint16_t irq_status);

View File

@ -0,0 +1 @@
void lora_app_init(void);

View File

@ -0,0 +1,57 @@
/*!
* \brief Represents all possible opcode understood by the radio
*/
//cmd
#define RADIO_GET_STATUS 0xC0
#define RADIO_WRITE_REGISTER 0x0D
#define RADIO_READ_REGISTER 0x1D
#define RADIO_WRITE_BUFFER 0x0E
#define RADIO_READ_BUFFER 0x1E
#define RADIO_SET_SLEEP 0x84
#define RADIO_SET_STANDBY 0x80
#define RADIO_SET_FS 0xC1
#define RADIO_SET_TX 0x83
#define RADIO_SET_RX 0x82
#define RADIO_SET_RXDUTYCYCLE 0x94
#define RADIO_SET_CAD 0xC5
#define RADIO_SET_TXCONTINUOUSWAVE 0xD1
#define RADIO_SET_TXCONTINUOUSPREAMBLE 0xD2
#define RADIO_SET_PACKETTYPE 0x8A
#define RADIO_GET_PACKETTYPE 0x11
#define RADIO_SET_RFFREQUENCY 0x86
#define RADIO_SET_TXPARAMS 0x8E
#define RADIO_SET_PACONFIG 0x95
#define RADIO_SET_CADPARAMS 0x88
#define RADIO_SET_BUFFERBASEADDRESS 0x8F
#define RADIO_SET_MODULATIONPARAMS 0x8B
#define RADIO_SET_PACKETPARAMS 0x8C
#define RADIO_GET_RXBUFFERSTATUS 0x13
#define RADIO_GET_PACKETSTATUS 0x14
#define RADIO_GET_RSSIINST 0x15
#define RADIO_GET_STATS 0x10
#define RADIO_RESET_STATS 0x00
#define RADIO_CFG_DIOIRQ 0x08
#define RADIO_GET_IRQSTATUS 0x12
#define RADIO_CLR_IRQSTATUS 0x02
#define RADIO_CALIBRATE 0x89
#define RADIO_CALIBRATEIMAGE 0x98
#define RADIO_SET_REGULATORMODE 0x96
#define RADIO_GET_ERROR 0x17
#define RADIO_CLR_ERROR 0x07
#define RADIO_SET_TCXOMODE 0x97
#define RADIO_SET_TXFALLBACKMODE 0x93
#define RADIO_SET_RFSWITCHMODE 0x9D
#define RADIO_SET_STOPRXTIMERONPREAMBLE 0x9F
#define RADIO_SET_LORASYMBTIMEOUT 0xA0
//寄存器地址
#define LORA_SYNCWORD 0x0740
//standby mode
#define STDBY_RC 0x00
#define STDBY_XOSC 0x01
void lora_test_init(void);

View File

@ -0,0 +1,8 @@
#include "ql_api_spi.h"
#include "lora_test.h"
bool wait_on_busy(void);
void llcc68_wakeup(void);
uint16_t llcc68_get_syncword(void);
void llcc68_set_standby(uint8_t mode);
void llcc68_set_rfswitchmode(uint8_t enable);

126
EC600U_lora/inc/user.h Normal file
View File

@ -0,0 +1,126 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "ql_gpio.h"
#define SPI_CLK_PIN 1
#define SPI_CLK_IO GPIO_9
#define SPI_MISO_PIN 2
#define SPI_MISO_IO GPIO_12
#define SPI_MOSI_PIN 3
#define SPI_MOSI_IO GPIO_11
#define SPI_CS_PIN 4
#define SPI_CS_IO GPIO_10
#define SPI_RST_PIN 59 //spi2-mosi
#define SPI_RST_IO GPIO_2
#define SPI_INT_PIN 60 //spi2-miso
#define SPI_INT_IO GPIO_3
#define SPI_BUSY_PIN 61 //spi2-clk
#define SPI_BUSY_IO GPIO_0
#define LORA_SYNCWORD_1 0x3444
#define LORA_SYNCWORD_2 0x1444
/*cmd*/
#define LORA_SET_REGULATORMODE 0x96
#define LORA_SET_RFSWITCHMODE 0x9D
#define LORA_SET_CALIBRATE 0x89
#define LORA_SET_BUFFERBASEADDRESS 0x8F
#define LORA_SET_PACONFIG 0x95
#define LORA_SET_TX_PARAMS 0x8E
#define LORA_SET_CALIBRATEIMAGE 0x98
#define LORA_SET_RFFREQ 0x86
#define LORA_SET_STOPRXTIMERONPREAMBLE 0x9F
#define LORA_SET_SYMBNUMTIMEOUT 0xA0
#define LORA_SET_PACKETTYPE 0x8A
#define LORA_SET_MODULATIONPARAMS 0x8B
#define LORA_SET_PACKETPARAMS 0x8C
#define LORA_SET_DIO_IRQ_CONFIG 0x08
#define LORA_SET_RX 0x82 //将芯片设置为接收模式
#define LORA_GET_STATUS 0xC0 //返回设备当前状态
#define LORA_GET_IRQSTATUS 0x12 //获取中断状态
#define LORA_CLEAR_IRQSTATUS 0x02 //清除中断状态
/*register addr*/
#define LORA_SET_OCP 0x08E7
#define LORA_SYNCWORD 0x0740
/*参数宏*/
//CALIBRATE
#define CALIBRATE_IMAGE_OFF 0b00000000 // 6 6 image calibration: disabled
#define CALIBRATE_IMAGE_ON 0b01000000 // 6 6 enabled
#define CALIBRATE_ADC_BULK_P_OFF 0b00000000 // 5 5 ADC bulk P calibration: disabled
#define CALIBRATE_ADC_BULK_P_ON 0b00100000 // 5 5 enabled
#define CALIBRATE_ADC_BULK_N_OFF 0b00000000 // 4 4 ADC bulk N calibration: disabled
#define CALIBRATE_ADC_BULK_N_ON 0b00010000 // 4 4 enabled
#define CALIBRATE_ADC_PULSE_OFF 0b00000000 // 3 3 ADC pulse calibration: disabled
#define CALIBRATE_ADC_PULSE_ON 0b00001000 // 3 3 enabled
#define CALIBRATE_PLL_OFF 0b00000000 // 2 2 PLL calibration: disabled
#define CALIBRATE_PLL_ON 0b00000100 // 2 2 enabled
#define CALIBRATE_RC13M_OFF 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled
#define CALIBRATE_RC13M_ON 0b00000010 // 1 1 enabled
#define CALIBRATE_RC64K_OFF 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled
#define CALIBRATE_RC64K_ON 0b00000001 // 0 0 enabled
//standby
#define LORA_STANDBY_RC 0x00
#define LORA_STANDBY_XOSC 0x01
//regulator
#define REGULATOR_DCDC 0x01
#define REGULATOR_LDO 0x00
//freq
#define XTAL_FREQ (double)(32000000)
#define FREQ_DIV (double)(33554432) //2的25次方
#define FREQ_STEP (double)(XTAL_FREQ/FREQ_DIV)
#define RF_FREQ (uint32_t)470000000
//irq
typedef enum
{
IRQ_RADIO_NONE = 0x0000,
IRQ_TX_DONE = 0x0001,
IRQ_RX_DONE = 0x0002,
IRQ_PREAMBLE_DETECTED = 0x0004,
IRQ_SYNCWORD_VALID = 0x0008,
IRQ_HEADER_VALID = 0x0010,
IRQ_HEADER_ERROR = 0x0020,
IRQ_CRC_ERROR = 0x0040,
IRQ_CAD_DONE = 0x0080,
IRQ_CAD_ACTIVITY_DETECTED = 0x0100,
IRQ_RX_TX_TIMEOUT = 0x0200,
IRQ_RADIO_ALL = 0xFFFF,
}RadioIrqMasks_t;
/*初始化spi总线*/
int lora_spi_init(void);
/*初始化lora所需的gpio口*/
int lora_gpio_init(void);
/*初始化收发数组内存进行32字节对齐*/
void transfer_buf_init(void);
/*复位lora模块*/
void lora_reset(void);
/*等待busy被拉低*/
int wait_busy_low(void);
/*写命令*/
int lora_write_cmd(uint8_t cmd, uint8_t *data, uint8_t len);
/*读命令*/
int lora_read_cmd(uint8_t cmd, uint8_t len);
/*写寄存器*/
int lora_wrtie_register(uint16_t add,uint8_t *data,uint8_t len);
/*读寄存器*/
int lora_read_register(uint16_t add, uint8_t size);
/*写buffer*/
void lora_write_buffer(uint8_t *data, uint8_t len);
/*读buffer*/
void lora_read_buffer(uint8_t offset, uint8_t len);

321
EC600U_lora/lora.c Normal file
View File

@ -0,0 +1,321 @@
#include "./inc/user.h"
#include "./inc/lora.h"
#include "ql_api_osi.h"
#include "ql_log.h"
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "lora", msg, ##__VA_ARGS__)
extern unsigned char * write_buf;
extern unsigned char * read_buf;
uint16_t syncword;
uint8_t PacketParams[6] = {0};
uint16_t lora_get_syncword(void)
{
if(lora_read_register(LORA_SYNCWORD,2)){
uint16_t syncword = read_buf[4] << 8 | read_buf[5];
return syncword;
}
return 65535;
}
void lora_set_standby(uint8_t data)
{
lora_write_cmd(0x80,&data,1);
}
void lora_set_RfSwitchCtrl(uint8_t enable)
{
lora_write_cmd(LORA_SET_RFSWITCHMODE,&enable,1);
}
void lora_set_calibrate(uint8_t param)
{
lora_write_cmd(LORA_SET_CALIBRATE,&param,1);
}
void lora_set_regulator(uint8_t param)
{
lora_write_cmd(LORA_SET_REGULATORMODE,&param,1);
}
void lora_set_bufferBaseAddress(uint8_t tx_base_add,uint8_t rx_base_add)
{
uint8_t data[2] = {tx_base_add,rx_base_add};
lora_write_cmd(LORA_SET_BUFFERBASEADDRESS,data,2);
}
void lora_set_PaConfig(uint8_t paDutyCycle,uint8_t hpMax,uint8_t deviceSel,uint8_t paLut)
{
uint8_t data[4] = {paDutyCycle,hpMax,deviceSel,paLut};
lora_write_cmd(LORA_SET_PACONFIG,data,4);
}
void lora_set_OverCurrentProtection(float max_current)
{
if((max_current >= 0.0)&&(max_current <= 140.0)){
uint8_t data = (uint8_t)(max_current*2/5);
lora_wrtie_register(LORA_SET_OCP,&data,1);
}
}
void lora_set_TX(uint8_t power,uint8_t rampTime)
{
if(power > 22) power = 22;
if(power < -3) power = -3;
uint8_t data[2] = {power,rampTime};
lora_write_cmd(LORA_SET_TX_PARAMS,data,2);
}
void lora_set_calibrateImage(uint32_t freq)
{
uint8_t calFreq[2] = {0,0};
if( freq > 900000000 ){
calFreq[0] = 0xE1;
calFreq[1] = 0xE9;
}else if( freq > 850000000 ){
calFreq[0] = 0xD7;
calFreq[1] = 0xDB;
}else if( freq > 770000000 ){
calFreq[0] = 0xC1;
calFreq[1] = 0xC5;
}else if( freq > 460000000 ){
calFreq[0] = 0x75;
calFreq[1] = 0x81;
}else if( freq > 425000000 ){
calFreq[0] = 0x6B;
calFreq[1] = 0x6F;
}
lora_write_cmd(LORA_SET_CALIBRATEIMAGE,calFreq,2);
}
void lora_set_RfFreq(uint32_t freq)
{
lora_set_calibrateImage(freq);
uint32_t frequence = (uint32_t)((double)freq/(double)FREQ_STEP);
uint8_t data[4] = {0,0,0,0};
data[0] = (uint8_t)((frequence>>24)&0xff);
data[1] = (uint8_t)((frequence>>16)&0xff);
data[2] = (uint8_t)((frequence>>8)&0xff);
data[3] = (uint8_t)(frequence&0xff);
lora_write_cmd(LORA_SET_RFFREQ,data,4);
}
void lora_stop_rxTimerRonpreamble(uint8_t enable)
{
//取反
enable = !enable;
lora_write_cmd(LORA_SET_STOPRXTIMERONPREAMBLE,&enable,1);
}
void lora_set_symbNumTimeout(uint8_t symbNum)
{
lora_write_cmd(LORA_SET_SYMBNUMTIMEOUT,&symbNum,1);
}
void lora_set_packetType(uint8_t type)
{
lora_write_cmd(LORA_SET_PACKETTYPE,&type,1);
}
void lora_set_modulationParams(uint8_t spreadingFactor,uint8_t bandwidth,uint8_t codingRate,uint8_t lowDataRateOptimize)
{
uint8_t data[4] = {spreadingFactor,bandwidth,codingRate,lowDataRateOptimize};
lora_write_cmd(LORA_SET_MODULATIONPARAMS,data,4);
}
uint8_t* lora_get_packetParams(uint16_t preambleLength,uint8_t payloadLen,uint8_t crcOn,uint8_t invertIrq)
{
PacketParams[0] = (preambleLength >> 8)&0xFF;
PacketParams[1] = preambleLength;
if(payloadLen){
PacketParams[2] = 0x01;
PacketParams[3] = payloadLen;
}else{
PacketParams[2] = 0x00;
PacketParams[3] = 0xFF;
}
if(crcOn){
PacketParams[4] = 0x01; //inverted
}else{
PacketParams[4] = 0x00; //standard
}
if(invertIrq){
PacketParams[5] = 0x01;
}else{
PacketParams[5] = 0x00;
}
return PacketParams;
}
void lora_set_IQ_Polarity(uint8_t iqConfig)
{
//获取当前寄存器数据
lora_read_register(0x0736,1);
uint8_t iqConfigCurrent = read_buf[0];
if(iqConfig == 0x01){//inverted
iqConfigCurrent &= 0xFB;
}else{
iqConfigCurrent |= 0x04; //standard
}
lora_wrtie_register(0x0736,&iqConfigCurrent,1);
}
void lora_set_packetParams(uint8_t * packetParams)
{
lora_write_cmd(LORA_SET_PACKETPARAMS,packetParams,6);
}
void lora_set_DioRiqConfig(uint16_t irqMask, uint16_t dio1, uint16_t dio2, uint16_t dio3)
{
uint8_t buf[8] = {0};
buf[0] = (uint8_t)((irqMask >>8)&0x00FF);
buf[1] = (uint8_t)(irqMask & 0x00FF);
buf[2] = (uint8_t)((dio1 >>8)&0x00FF);
buf[3] = (uint8_t)(dio1 & 0x00FF);
buf[4] = (uint8_t)((dio2 >>8)&0x00FF);
buf[5] = (uint8_t)(dio2 & 0x00FF);
buf[6] = (uint8_t)((dio3 >>8)&0x00FF);
buf[7] = (uint8_t)(dio3 & 0x00FF);
lora_write_cmd(LORA_SET_DIO_IRQ_CONFIG,buf,8);
}
uint8_t lora_get_status(void)
{
lora_read_cmd(LORA_GET_STATUS,1);
return (uint8_t)read_buf[0];
}
int lora_set_Rx(uint32_t timeout)
{
lora_set_standby(0x00);
uint8_t buf[3] = {0};
buf[0] = (uint8_t)((timeout >> 16) & 0xFF);
buf[1] = (uint8_t)((timeout >> 8) & 0xFF);
buf[2] = (uint8_t)(timeout & 0xFF);
lora_write_cmd(LORA_SET_RX,buf,3);
for(int i = 0; i < 10; i++){
if((lora_get_status()&0x70) == 0x50){ //01110000,
return 1; //01010000 所以状态为0x20
}
ql_delay_us(1000);
}
return 0;
}
uint16_t lora_get_irq_status(void)
{
lora_read_cmd(LORA_GET_IRQSTATUS,3);
return (uint16_t)((read_buf[2] << 8) | read_buf[3]);
}
void lora_clear_irq_status(uint16_t irq_status)
{
uint8_t buf[2] = {0};
buf[0] = (uint8_t)((irq_status >>8)&0x00FF);
buf[1] = (uint8_t)(irq_status & 0x00FF);
lora_write_cmd(LORA_CLEAR_IRQSTATUS,buf,2);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int lora_init(void)
{
transfer_buf_init();
int err = lora_gpio_init();
if(err != 1){
LOGI("lora_gpio_init fail,err = %d\n",err);
return 0;
}
err = lora_spi_init();
if(err != 1){
LOGI("lora_spi_init fail,err = %d\n",err);
return 0;
}
lora_reset();
return 1;
}
int lora_start(void)
{
/*获取同步字并判断*/
syncword = lora_get_syncword();
if(syncword != LORA_SYNCWORD_1 && syncword != LORA_SYNCWORD_2){
LOGI("syncword 0x%02X is error\n",syncword);
return 0;
}
/*设置standby为RC模式*/
lora_set_standby(LORA_STANDBY_RC);
/*启用RF开关控制*/
lora_set_RfSwitchCtrl(1);
/*设置射频校准*/
lora_set_calibrate(0b01111111);
/*设置电源为DCDC模式*/
lora_set_regulator(REGULATOR_DCDC);
/*设置缓冲区基地址*/
lora_set_bufferBaseAddress(0,0);
/*配置功率放大器*/
lora_set_PaConfig(0x04,0x07,0x00,0x01); //+22dBM
/*设置过流保护*/
lora_set_OverCurrentProtection(60.0);
/*设置发送参数*/
lora_set_TX(20,0x04);//默认20发送功率渐变200us
/*设置射频频率*/
lora_set_RfFreq(RF_FREQ);
return 1;
}
int lora_radioConfig(void)
{
/*不禁用接收定时器*/
lora_stop_rxTimerRonpreamble(0);
/*设置超时参数*/
lora_set_symbNumTimeout(0);
/*设置数据包类型*/
lora_set_packetType(0x01);//lora模式
/*设置射频调制参数*/
lora_set_modulationParams(7,4,1,0);
/*配置数据包参数*/
uint8_t *packetParams = lora_get_packetParams(8,0,1,0);
/*设置射频接收IQ极性*/
lora_set_IQ_Polarity(packetParams[5]);
/*设置数据包参数*/
lora_set_packetParams(packetParams);
/*配置数字输入输出中断*/
lora_set_DioRiqConfig(1023,2,0,0);//0b1111111111,0b0000000010
/*设置接收模式为RX连续模式*/
if(lora_set_Rx(0xFFFFFF) == 0) return 0;
return 1;
}

144
EC600U_lora/lora_app.c Normal file
View File

@ -0,0 +1,144 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "ql_api_osi.h"
#include "ql_api_spi.h"
//#include "ql_gpio.h"
#include "ql_log.h"
#include "./inc/lora.h"
#include "./inc/user.h"
#include "lora_app.h"
/*宏定义*/
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "lora_app", msg, ##__VA_ARGS__)
#define USE_TEST 1
/*全局变量*/
int flag;
ql_task_t lora_app_task;
extern unsigned char *write_buf;
extern unsigned char *read_buf;
int count = 1;
/*函数*/
#if USE_TEST
void lora_send_test(void)
{
//uint16_t syncword = lora_get_syncword(); //00011101 00000111 01000000
//LOGI("syncword = 0x%04X\n",syncword);
//lora_set_standby(0x00);
LOGI("lora_send_test start");
if(wait_busy_low()){
write_buf[0] = 0x1D; //00011101
write_buf[1] = 0x07; //00000111
write_buf[2] = 0x40; //01000000
ql_errcode_spi_e spi_err = ql_spi_write_read(QL_SPI_PORT1, read_buf,write_buf, 3);
LOGI("ql_spi_write_read spi_err = %d",spi_err);
LOGI("read_buf 0 = 0x%02X, read_buf 1 = 0x%02X\n",read_buf[0],read_buf[1]);
LOGI("read_buf 2 = 0x%02X, read_buf 3 = 0x%02X\n",read_buf[2],read_buf[3]);
LOGI("read_buf 4 = 0x%02X, read_buf 5 = 0x%02X\n",read_buf[4],read_buf[5]);
}
else{
LOGI("lora_send_test wait_busy_low outtime");
}
LOGI("lora_send_test end");
}
#endif
void lora_receive_mode(void)
{
/*获取中断状态*/
uint16_t irq_status = lora_get_irq_status();
/*清除中断状态*/
lora_clear_irq_status(irq_status);
/*根据中断状态进行操作*/
if((irq_status & IRQ_TX_DONE) == IRQ_TX_DONE){
}
if( ( irq_status & IRQ_RX_DONE ) == IRQ_RX_DONE ){
}
if( ( irq_status & IRQ_CAD_DONE ) == IRQ_CAD_DONE ){
}
if( ( irq_status & IRQ_RX_TX_TIMEOUT ) == IRQ_RX_TX_TIMEOUT ){
}
if( ( irq_status & IRQ_PREAMBLE_DETECTED ) == IRQ_PREAMBLE_DETECTED ){
}
if( ( irq_status & IRQ_SYNCWORD_VALID ) == IRQ_SYNCWORD_VALID ){
}
if( ( irq_status & IRQ_HEADER_VALID ) == IRQ_HEADER_VALID ){
}
if( ( irq_status & IRQ_HEADER_ERROR ) == IRQ_HEADER_ERROR ){
}
}
void lora_app_thread(void *param)
{
LOGI("lora_app_thread start");
lora_init();
#if !USE_TEST
lora_start();
lora_radioConfig();
#endif
while(1){
LOGI("enter while in %d times\n",count);
count++;
// wait_busy_low();
// write_buf[0] = 0x1D; //00011101
// write_buf[1] = ((0x0740 & 0xff00) >> 8);//00000111
// write_buf[2] = (0x0740 & 0xff);//01000000
// write_buf[3] = 0;
// write_buf[4] = 0;
// LOGI("write_buf[0] = 0x%x, write_buf[1] = 0x%02x, write_buf[2] = 0x%x, write_buf[3] = 0x%x, write_buf[4] = 0x%x",write_buf[0],write_buf[1],write_buf[2],write_buf[3],write_buf[4]);
// LOGI("ql_spi_write_read err = %d",ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,5));
// wait_busy_low();
// LOGI("read_buf[0] = 0x%x, read_buf[1] = 0x%x, read_buf[2] = 0x%x, read_buf[3] = 0x%02x, read_buf[4] = 0x%02x",read_buf[0],read_buf[1],read_buf[2],read_buf[3],read_buf[4]);
/*唤醒*/
write_buf[0] = 0xC0; //返回设备当前状态命令
write_buf[1] = 0x00;
LOGI(" write 0xC0 0x00 err = %d",ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,2));
LOGI(" read_buf[0] = 0x%02x, read_buf[1] = 0x%02x,",read_buf[0],read_buf[1]);
wait_busy_low();
ql_rtos_task_sleep_ms(5);
}
}
void lora_app_init(void)
{
QlOSStatus err = ql_rtos_task_create(&lora_app_task, 4096, APP_PRIORITY_NORMAL,"lora",lora_app_thread,NULL,1);
if(err != 0){
LOGI("create lora_demo_task failed\n");
}
}
/*
0001 1101
0100 0101
rst推挽输出低电平10ms后改为上拉输入模式持续10ms
nss低
192
0
nss高
busy引脚由高变低
*/

161
EC600U_lora/lora_test.c Normal file
View File

@ -0,0 +1,161 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ql_api_spi.h"
#include "ql_api_osi.h"
#include "ql_gpio.h"
#include "ql_log.h"
#include "./inc/lora_test.h"
#include "./inc/lora_test_function.h"
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "lora_test", msg, ##__VA_ARGS__)
#define RST_PIN 59
#define INT_PIN 60
#define BUSY_PIN 61
#define SWITCH_PIN 16
#define RST_GPIO GPIO_2
#define INT_GPIO GPIO_3
#define BUSY_GPIO GPIO_0
#define SWITCH_GPIO GPIO_19
ql_task_t lora_test_task;
ql_errcode_gpio gpio_err;
unsigned char *out_buf = NULL;
unsigned char *out_mal_buf = NULL;
unsigned char *in_buf = NULL;
unsigned char *in_mal_buf = NULL;
unsigned short buf_len = 512;
static void int_cb(void *ctx)
{
LOGI(" enter int cb");
}
void lora_test_thread(void *param)
{
LOGI("lora_test_thread start");
/*初始化收发buffer32字节对齐*/
out_mal_buf = (unsigned char *)malloc(QL_SPI_DMA_ADDR_ALIN + buf_len);
in_mal_buf = (unsigned char *)malloc(QL_SPI_DMA_ADDR_ALIN + buf_len);
out_buf = (unsigned char *)OSI_ALIGN_UP(out_mal_buf, QL_SPI_DMA_ADDR_ALIN);
in_buf = (unsigned char *)OSI_ALIGN_UP(in_mal_buf, QL_SPI_DMA_ADDR_ALIN);
memset(out_buf, 0x00, buf_len);
memset(in_buf, 0x00, buf_len);
/*初始化引脚功能*/
LOGI("ql_pin_set_gpio(RST_PIN) err = %d", ql_pin_set_gpio(RST_PIN));
LOGI("ql_pin_set_gpio(INT_PIN) err = %d", ql_pin_set_gpio(INT_PIN));
LOGI("ql_pin_set_gpio(BUSY_PIN) err = %d", ql_pin_set_gpio(BUSY_PIN));
/*开启电平开关*/
LOGI("ql_pin_set_gpio(16) err = %d", ql_pin_set_gpio(SWITCH_PIN));
LOGI("GPIO_19 init err = %d", ql_gpio_init(SWITCH_GPIO, GPIO_OUTPUT, PULL_DOWN, LVL_LOW));
/*初始化busyIO为上拉输入*/
LOGI("BUSY_GPIO GPIO_INPUT err = %d", ql_gpio_set_direction(BUSY_GPIO, GPIO_INPUT));
LOGI("BUSY_GPIO PULL_UP err = %d", ql_gpio_set_pull(BUSY_GPIO, PULL_UP));
/*初始化SPI*/
ql_spi_config_s lora_spi_config = {0};
lora_spi_config.input_mode = QL_SPI_INPUT_TRUE;
lora_spi_config.port = QL_SPI_PORT1;
lora_spi_config.framesize = 8;
lora_spi_config.spiclk = QL_SPI_CLK_6_25MHZ;
lora_spi_config.cs_polarity0 = QL_SPI_CS_ACTIVE_LOW;
lora_spi_config.cs_polarity1 = QL_SPI_CS_ACTIVE_LOW;
lora_spi_config.cpol = QL_SPI_CPOL_LOW;
lora_spi_config.cpha = QL_SPI_CPHA_2Edge;
lora_spi_config.input_sel = QL_SPI_DI_1;
lora_spi_config.transmode = QL_SPI_DMA_IRQ;
lora_spi_config.cs = QL_SPI_CS0;
lora_spi_config.clk_delay = QL_SPI_CLK_DELAY_0;
lora_spi_config.release_flag = QL_SPI_NOT_RELEASE;
LOGI("ql_spi_init_ext err = %d", ql_spi_init_ext(lora_spi_config));
//LOGI("SPI1 cs auto err = %d", ql_spi_cs_auto(QL_SPI_PORT1));
LOGI("SPI1 request_sys_clk err = %d", ql_spi_request_sys_clk(QL_SPI_PORT1));
/*复位,rst引脚推挽输出低电平20ms后拉高10ms改为上拉输入*/
ql_delay_us(10000);
LOGI("RST_GPIO GPIO_OUTPUT err = %d", ql_gpio_set_direction(RST_GPIO, GPIO_OUTPUT));
LOGI("RST_GPIO LVL_LOW err = %d", ql_gpio_set_level(RST_GPIO, LVL_LOW));
ql_delay_us(20000);
LOGI("RST_GPIO LVL_HIGH err = %d", ql_gpio_set_level(RST_GPIO, LVL_HIGH));//官方例程中没有拉高,但是不拉高就测不到波形
ql_delay_us(10000);
LOGI("RST_GPIO GPIO_INPUT err = %d", ql_gpio_set_direction(RST_GPIO, GPIO_INPUT));
LOGI("RST_GPIO PULL_UP err = %d", ql_gpio_set_pull(RST_GPIO, PULL_UP));
/*配置中断int初始化为下拉输入上升沿中断*/
LOGI("ql_int_register err = %d", ql_int_register(INT_GPIO, EDGE_TRIGGER, DEBOUNCE_EN, EDGE_RISING, PULL_DOWN, int_cb, NULL));
LOGI("ql_int_enable err = %d", ql_int_enable(INT_GPIO));
/*唤醒*/
//llcc68_wakeup();
/*获取同步字*/
LOGI("syncword = 0x%04x",llcc68_get_syncword());
// llcc68_set_standby(STDBY_RC);
// llcc68_set_rfswitchmode(true);
while (1)
{
out_buf[0] = RADIO_READ_REGISTER;
out_buf[1] = ((LORA_SYNCWORD & 0xff00) >> 8);
out_buf[2] = (LORA_SYNCWORD & 0xff);
out_buf[3] = 0x00;
out_buf[4] = 0x00;
out_buf[5] = 0x00;
// LOGI("SPI1 request_sys_clk err = %d", ql_spi_request_sys_clk(QL_SPI_PORT1));
wait_on_busy();
LOGI("SPI1 ql_spi_cs_low err = %d",ql_spi_cs_low(QL_SPI_PORT1));
LOGI("SPI1 ql_spi_write_read err = %d",ql_spi_write_read(QL_SPI_PORT1,in_buf,out_buf,6));
LOGI("SPI1 ql_spi_cs_low err = %d",ql_spi_cs_high(QL_SPI_PORT1));
wait_on_busy();
// LOGI("SPI1 ql_spi_release_sys_clk err = %d", ql_spi_release_sys_clk(QL_SPI_PORT1));
LOGI("in_buf 0 = 0x%02x",in_buf[0]);
LOGI("in_buf 1 = 0x%02x",in_buf[1]);
LOGI("in_buf 2 = 0x%02x",in_buf[2]);
LOGI("in_buf 3 = 0x%02x",in_buf[3]);
LOGI("in_buf 4 = 0x%02x",in_buf[4]);
LOGI("in_buf 5 = 0x%02x",in_buf[5]);
memset(out_buf, 0x00, buf_len);
memset(in_buf, 0x00, buf_len);
ql_rtos_task_sleep_ms(20);
}
}
void lora_test_init(void)
{
QlOSStatus err = ql_rtos_task_create(&lora_test_task, 4096, APP_PRIORITY_NORMAL, "loratest", lora_test_thread, NULL, 1);
if (err != 0)
{
LOGI("create lora_demo_task failed\n");
}
}
// 测试引脚功能
// uint8_t func = 255;
// ql_pin_get_func(66,&func);
// LOGI("66 func = %d",func);
// ql_pin_get_func(RST_PIN,&func);
// LOGI("RST_PIN func = %d",func);
// ql_pin_get_func(INT_PIN,&func);
// LOGI("INT_PIN func = %d",func);
// ql_pin_get_func(BUSY_PIN,&func);
// LOGI("BUSY_PIN func = %d",func);
//测试中断引脚方向
// ql_GpioDir dir = 1;
// LOGI("ql_gpio_get_direction err = %d",ql_gpio_get_direction(INT_GPIO,&dir));
// LOGI("INT_GPIO dir = %d",dir);

View File

@ -0,0 +1,78 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ql_api_spi.h"
#include "ql_api_osi.h"
#include "ql_gpio.h"
#include "ql_log.h"
#include "./inc/lora_test.h"
#include "./inc/lora_test_function.h"
#define BUSY_GPIO GPIO_0
extern unsigned char *out_buf;
extern unsigned char *in_buf;
bool wait_on_busy(void)
{
ql_LvlMode level = LVL_HIGH;
for(uint32_t i = 0; i < 1000; i++){
ql_gpio_get_level(GPIO_0,&level);
if(level == LVL_LOW){
return true;
}
ql_delay_us(1000);
}
return false;
}
// void llcc68_write_cmd(enum RadioCommands_e cmd)
// {
// out_buf[0] = cmd;
// ql_spi_write(QL_SPI_PORT1,out_buf,1);
// wait_on_busy();
// }
void llcc68_wakeup(void)
{
out_buf[0] = RADIO_GET_STATUS;
out_buf[1] = 0x00;
ql_spi_write(QL_SPI_PORT1,out_buf,2);
wait_on_busy();
}
uint16_t llcc68_get_syncword(void)
{
wait_on_busy();
ql_spi_cs_low(QL_SPI_PORT1);
out_buf[0] = RADIO_READ_REGISTER;
out_buf[1] = ((LORA_SYNCWORD & 0xff00) >> 8);
out_buf[2] = (LORA_SYNCWORD & 0xff);
out_buf[3] = 0x00;
out_buf[4] = 0x00;
out_buf[5] = 0x00;
ql_spi_write_read(QL_SPI_PORT1,in_buf,out_buf,6);
// ql_spi_write(QL_SPI_PORT1,out_buf,4);
// ql_spi_read(QL_SPI_PORT1,in_buf,2);
ql_spi_cs_high(QL_SPI_PORT1);
wait_on_busy();
return (in_buf[4] << 8 | in_buf[5]);
}
void llcc68_set_standby(uint8_t mode)
{
out_buf[0] = RADIO_SET_STANDBY;
out_buf[1] = mode;
ql_spi_write(QL_SPI_PORT1,out_buf,2);
wait_on_busy();
}
void llcc68_set_rfswitchmode(uint8_t enable)
{
out_buf[0] = RADIO_SET_RFSWITCHMODE;
out_buf[1] = enable;
ql_spi_write(QL_SPI_PORT1,out_buf,2);
wait_on_busy();
}

212
EC600U_lora/user.c Normal file
View File

@ -0,0 +1,212 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "ql_gpio.h"
#include "ql_api_spi.h"
#include "ql_api_osi.h"
#include "ql_log.h"
#include "./inc/user.h"
#define LOGI(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "user", msg, ##__VA_ARGS__)
/*全局变量*/
ql_errcode_gpio gpio_err;
ql_errcode_spi_e spi_err;
ql_LvlMode busy_level = LVL_HIGH;
unsigned char * write_buf = NULL;
unsigned char * write_mal_buf = NULL;
unsigned char * read_buf = NULL;
unsigned char * read_mal_buf = NULL;
unsigned short write_len = 512;
unsigned short read_len = 512;
/*函数*/
int wait_busy_low(void)
{
int retry = 10000;
while(retry--){
ql_gpio_get_level(SPI_BUSY_IO,&busy_level);
if(busy_level == LVL_LOW) return 1;
}
return 0;
}
int lora_wrtie_register(uint16_t add,uint8_t *data,uint8_t len)
{
wait_busy_low();
write_buf[0] = 0x0D;
write_buf[1] = ((add & 0xff00) >> 8);
write_buf[2] = (add & 0xff);
for(int i =0;i<len;i++){
write_buf[i+3] = data[i];
}
ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,len+3);
return wait_busy_low();
}
int lora_read_register(uint16_t add, uint8_t len)
{
wait_busy_low();
write_buf[0] = 0x1D;
write_buf[1] = ((add & 0xff00) >> 8);
write_buf[2] = (add & 0xff);
for(int i = 0; i <= len; i++){
write_buf[i+1+len] = 0;
}
ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,len+4);
return wait_busy_low();
}
int lora_write_cmd(uint8_t cmd, uint8_t *data, uint8_t len)
{
if(wait_busy_low() == 0) return 0;
write_buf[0] = cmd;
for(int i = 0; i < len; i++){
write_buf[i+1] = data[i];
}
spi_err = ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,len+1);
if(spi_err !=0) return 0;
//if(wait_busy_low() == 0) return 0;
return wait_busy_low();
}
int lora_read_cmd(uint8_t cmd, uint8_t len)
{
wait_busy_low();
write_buf[0] = cmd;
for(int i = 0; i < len; i++){
write_buf[i+1] = 0;
}
ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,len+1);
return wait_busy_low();
}
void lora_write_buffer(uint8_t *data, uint8_t len)
{
wait_busy_low();
write_buf[0] = 0x0E;
write_buf[1] = 0x00;
for(int i = 0; i < len; i++){
write_buf[i+2] = data[i];
}
ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,len+2);
wait_busy_low();
}
void lora_read_buffer(uint8_t offset, uint8_t len)
{
wait_busy_low();
write_buf[0] = 0x1E;
write_buf[1] = offset;
for(int i = 0; i < len; i++){
write_buf[i+2] = 0;
}
ql_spi_write_read(QL_SPI_PORT1,read_buf,write_buf,len+2);
wait_busy_low();
}
void transfer_buf_init(void)
{
write_mal_buf = (unsigned char *)malloc(QL_SPI_DMA_ADDR_ALIN+write_len);
read_mal_buf = (unsigned char *)malloc(QL_SPI_DMA_ADDR_ALIN+read_len);
//32对齐
write_buf = (unsigned char *)OSI_ALIGN_UP(write_mal_buf, QL_SPI_DMA_ADDR_ALIN);
read_buf = (unsigned char *)OSI_ALIGN_UP(read_mal_buf, QL_SPI_DMA_ADDR_ALIN);
//清零
memset(write_buf, 0x00, write_len);
memset(read_buf, 0x00, read_len);
}
void lora_reset(void)
{
ql_gpio_set_level(SPI_RST_IO,LVL_LOW);
ql_delay_us(1000);
ql_gpio_set_level(SPI_RST_IO,LVL_HIGH);
wait_busy_low();
}
int lora_spi_init(void)
{
ql_spi_config_s lora_spi_config = {0};
lora_spi_config.input_mode = QL_SPI_INPUT_TRUE;
lora_spi_config.port = QL_SPI_PORT1;
lora_spi_config.framesize = 8;
lora_spi_config.spiclk = QL_SPI_CLK_6_25MHZ;
lora_spi_config.cs_polarity0 = QL_SPI_CS_ACTIVE_LOW;
lora_spi_config.cs_polarity1 = QL_SPI_CS_ACTIVE_LOW;
lora_spi_config.cpol = QL_SPI_CPOL_LOW;
lora_spi_config.cpha = QL_SPI_CPHA_2Edge;
lora_spi_config.input_sel = QL_SPI_DI_1;
lora_spi_config.transmode = QL_SPI_DMA_IRQ;
lora_spi_config.cs = QL_SPI_CS0;
lora_spi_config.clk_delay = QL_SPI_CLK_DELAY_0;
lora_spi_config.release_flag = QL_SPI_NOT_RELEASE;
spi_err = ql_spi_init_ext(lora_spi_config); if(spi_err != 0) return 2;
spi_err = ql_spi_cs_auto(QL_SPI_PORT1); if(spi_err != 0) return 3;
spi_err = ql_spi_request_sys_clk(QL_SPI_PORT1); if(spi_err != 0) return 4;
return 1;
}
static void int_cb(void *ctx)
{
}
int lora_gpio_init(void)
{
/*将引脚设置为spi功能*/
gpio_err = ql_pin_set_func(SPI_CS_PIN,1); if(gpio_err != 0) return 2;
gpio_err = ql_pin_set_func(SPI_CLK_PIN,1); if(gpio_err != 0) return 3;
gpio_err = ql_pin_set_func(SPI_MOSI_PIN,1); if(gpio_err != 0) return 4;
gpio_err = ql_pin_set_func(SPI_MISO_PIN,1); if(gpio_err != 0) return 5;
/*将busyintrst引脚设置为gpio功能*/
gpio_err = ql_pin_set_gpio(SPI_BUSY_PIN); if(gpio_err != 0) return 6;
gpio_err = ql_pin_set_gpio(SPI_RST_PIN); if(gpio_err != 0) return 7;
gpio_err = ql_pin_set_gpio(SPI_INT_PIN); if(gpio_err != 0) return 8;
/*拉低电平转换器开关电平使能B端电压*/
gpio_err = ql_pin_set_gpio(16); if(gpio_err != 0) return 14;
gpio_err = ql_gpio_set_direction(GPIO_19,GPIO_OUTPUT); if(gpio_err != 0) return 15;
gpio_err = ql_gpio_set_level(GPIO_19,LVL_LOW); if(gpio_err != 0) return 16;
/*将busy引脚同一io口的引脚设置为非gpio功能防止冲突*/
gpio_err = ql_pin_set_func(66,0); if(gpio_err != 0) return 9;
// /*busy初始化为上拉输入*/
// ql_gpio_set_direction(SPI_BUSY_IO,GPIO_INPUT);
// ql_gpio_set_pull(SPI_BUSY_IO,PULL_UP);
// /*rst推挽输出低电平20ms后转为输入再延时10ms*/
// ql_gpio_set_direction(SPI_RST_IO,GPIO_OUTPUT);
// ql_gpio_set_level(SPI_RST_IO,LVL_LOW);
// ql_rtos_task_sleep_ms(20);
// ql_gpio_set_direction(SPI_RST_IO,GPIO_INPUT);
// ql_rtos_task_sleep_ms(10);
// /*int引脚初始化为下拉输入注册上升沿触发中断*/
// ql_gpio_set_direction(SPI_INT_IO,GPIO_INPUT);
// ql_gpio_set_pull(SPI_INT_IO,PULL_DOWN);
// ql_int_register(SPI_INT_IO,EDGE_TRIGGER,DEBOUNCE_EN,EDGE_RISING,PULL_DOWN,int_cb,NULL);
/*设置busyintrst的gpio*/
gpio_err = ql_gpio_init(SPI_BUSY_IO,GPIO_INPUT,PULL_UP,LVL_LOW); if(gpio_err != 0) return 10;
//gpio_err = ql_gpio_set_direction(SPI_INT_IO,GPIO_INPUT); if(gpio_err != 0) return 11;
if(ql_int_register(SPI_INT_IO,EDGE_TRIGGER,DEBOUNCE_EN,EDGE_RISING,PULL_DOWN,int_cb,NULL)!=0) return 11;
ql_int_enable(SPI_INT_IO);
gpio_err = ql_gpio_set_direction(SPI_RST_IO,GPIO_OUTPUT); if(gpio_err != 0) return 12;
gpio_err = ql_gpio_set_level(SPI_RST_IO,LVL_HIGH); if(gpio_err != 0) return 13;
return 1;
}

View File

@ -0,0 +1,21 @@
set(target ql_app_lvgl)
add_library(${target} STATIC)
set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QUEC)
target_include_directories(${target} PUBLIC inc)
#target_link_libraries(${target} PRIVATE kernel driver hal ql_api_common lvgl)
target_link_libraries(${target} PRIVATE lvgl)
target_sources(${target} PRIVATE
lvgl_demo.c
./ui/ui.c
./ui/ui_screen1.c
./ui/ui_screen2.c
./ui/ui_screen3.c
./ui/ui_screen3_chart.c
./ui/ui_screen4.c
./ui/fs_function.c
)
relative_glob(srcs include/*.h src/*.c inc/*.h)
beautify_c_code(${target} ${srcs})

View File

@ -0,0 +1,42 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TP_nRST_pin (137)
#define nTP_INT_pin (62)
#define TP_nRST_GPIO_num GPIO_4
#define nTP_INT_GPIO_num GPIO_5
#define X_RESOLUTION 480
#define Y_RESOLUTION 320
#define MAX_TOUCH_NUMBER 1
#define GT911_ADDRESS 0x5D //从机地址
#define GT911_REG_WRITE_ADDRESS 0xBA //从机写地址
#define GT911_REG_READ_ADDRESS 0xBB //从机读地址
#define GT911_CTRL_ADDRESS 0x8040 //控制寄存器地址
#define GT911_CFG_ADDRESS 0x8047 //配置寄存器起始地址
#define GT911_CHECK_REG 0X80FF //校验和寄存器地址
#define GT911_TPD_Sta 0X8150 //触摸点起始数据地址
#define GT911_STATE_REG 0X814E //触摸状态寄存器, 第7位是触摸标志位低4位是触摸点数个数
#define GT911_TP1_REG 0X8150 //第一个触摸点数据地址
#define GT911_TP2_REG 0X8158 //第二个触摸点数据地址
#define GT911_TP3_REG 0X8160 //第三个触摸点数据地址
#define GT911_TP4_REG 0X8168 //第四个触摸点数据地址
#define GT911_TP5_REG 0X8170 //第五个触摸点数据地址
typedef struct{
uint16_t X_Resolution;
uint16_t Y_Resolution;
uint8_t Number_Of_Touch_Support;
int ReverseX;
int ReverseY;
int SwithX2Y;
int SoftwareNoiseReduction;
}gt911_t;
void lvgl_demo_init(void);

View File

@ -0,0 +1,42 @@
#include "../../EC600U_lvgl_lib/lvgl/lvgl.h"
#include "time.h"
#include "ql_fs.h"
#define FILE_LETTER 'SD' //盘符
#define CHAR_READ_LENGTH 80 //每次读取文件的长度
struct Device_Data
{
int id; //设备桩点号
int start_hour;
int start_min;
int start_second;
int end_hour;
int end_min;
int end_second;
float max_depth; //设备所处的深度
float max_V; //设备当前喷浆量
float average_I; //设备在两次记录之间的平均电流
int count; //该id出现的次数
int offset; //该id第一次出现时的字节偏移量
};
struct Return_Data {
int id_count;
struct Device_Data dataArr[64]; // 假设数组长度为10
};
#define SAME_ID_MAX_NUM 20
struct ChartData{
float depth[SAME_ID_MAX_NUM];
float V[SAME_ID_MAX_NUM];
float I[SAME_ID_MAX_NUM];
char time[SAME_ID_MAX_NUM][10];
int same_id_count;
};
int8_t findDataFile(uint16_t year,uint8_t month,uint8_t day);
void dealdata(char*buffer,int last_file,int last_i,struct Return_Data *newStruct);
void readDataFromFile(QFILE fd, struct Return_Data * data);
void setDataToTables(struct Return_Data* dataArray,int page,int page_count);
void setDataToChart(QFILE fd, int offset ,int current_id,struct ChartData* chart_data);

11
EC600U_lvgl/inc/ui/ui.h Normal file
View File

@ -0,0 +1,11 @@
#include "lvgl.h"
//screen4
#define TEST 0
extern lv_obj_t * ui_Screen4;
extern lv_obj_t * ui_Screen4_left_btn;
extern lv_obj_t * ui_Screen4_right_btn;
/************************************************************************************************************/
/************************************************************************************************************/
/************************************************************************************************************/
void ui_init(void);

View File

@ -0,0 +1,133 @@
#include "../../EC600U_lvgl_lib/lvgl/lvgl.h"
#define LEFT_BUTTON_X (0)
#define LEFT_BUTTON_Y (0)
#define RIGHT_BUTTON_X (450)
#define RIGHT_BUTTON_Y (0)
#define START_BUTTON_X (440)
#define START_BUTTON_Y (190)
#define STOP_BUTTON_X (440)
#define STOP_BUTTON_Y (240)
/************************************************************************************************************/
/* 自定义图标集合 */
#define ICON_GRAPH_USER_14 "\xEE\xA0\x8F " // 0xe80f 曲线图图标
#define ICON_INCLINATION_USER_14 "\xEE\x98\x88 " // 0xe608 倾角测量图标
#define ICON_RECORD_USER_14 "\xEE\x98\xB0 " // 0xe630 记录图标
#define ICON_OPERATION_USER_14 "\xEE\x99\x82 " // 0xe642 作业图标
#define ICON_LIGHT_20 "\xEE\x98\x93 " // 0xe613 亮度图标
#define ICON_SET_SYS_24 "\xEE\x98\x99 " // 0xe619 系统图标
#define ICON_SET_ABOUT_24 "\xEE\x98\x80 " // 0xe600 关于图标
#define ICON_SET_USER_24 "\xEE\x98\xBD " // 0xe63d 用户图标
#define ICON_SET_FLOW_24 "\xEE\x98\xA5 " // 0xe625 流量计图标
#define ICON_SET_DEPTH_24 "\xEE\xA2\xA9 " // 0xe8a9 深度计图标
#define ICON_SET_CAL_24 "\xEE\x9A\x8F " // 0xe68f 校准图标
#define ICON_INC_STATE_10 "\xEE\x9B\xA6 " // 0xe68f 倾角目标图标
#define ICON_GPS_ON_STATE_24 "\xEE\x98\x80 " // 0xe600 GPS开启图标
#define ICON_GPS_OFF_STATE_24 "\xEE\x98\x81 " // 0xe601 GPS关闭图标
#define ICON_SIM_OFF_STATE_24 "\xEE\x9A\x98 " // 0xe698 sim卡未填充图标
#define ICON_SIM_WARN_STATE_24 "\xEE\x9A\xA9 " // 0xe6a9 sim卡故障图标
#define ICON_SIM_ON_STATE_24 "\xEE\x9A\xAA " // 0xe6aa sim卡已填充图标
#define ICON_NET_ON_STATE_24 "\xEE\x98\xA3 " // 0xe623 连接网络图标
#define ICON_NET_OFF_STATE_24 "\xEE\x98\xA4 " // 0xe624 断开网络图标
#define ICON_NET_WARN_STATE_24 "\xEE\x99\x93 " // 0xe653 网络故障图标
#define ICON_SERVER_CON_ON_STATE_24 "\xEE\x9A\xB8 " // 0xe6b8 服务器连接状态图标
#define ICON_SERVER_CON_DOWNLOAD_STATE_24 "\xEE\x98\x82 " // 0xe602 服务器数据下载状态图标
#define ICON_SERVER_CON_UPLOAD_STATE_24 "\xEE\x98\x83 " // 0xe603 服务器数据上传状态图标
#define ICON_SERVER_CON_NO_STATE_24 "\xEE\x98\x84 " // 0xe604 服务器无连接连接状态图标
#define ICON_GPS_CSQ_5_24 "\xEE\x98\xAB " // 0xe62b GPS信号5格图标
#define ICON_GPS_CSQ_4_24 "\xEE\x98\xB4 " // 0xe634 GPS信号4格图标
#define ICON_GPS_CSQ_3_24 "\xEE\x98\xB5 " // 0xe635 GPS信号3格图标
#define ICON_GPS_CSQ_2_24 "\xEE\x98\xB6 " // 0xe636 GPS信号2格图标
#define ICON_GPS_CSQ_1_24 "\xEE\x98\xB7 " // 0xe637 GPS信号1格图标
#define ICON_GPS_CSQ_24 "\xEE\x9A\x80 " // 0xe680 GPS信号图标
#define ICON_GNET_CSQ_4_24 "\xEE\x9A\x81 " // 0xe681 4G信号4格图标
#define ICON_GNET_CSQ_3_24 "\xEE\x98\x93 " // 0xe613 4G信号3格图标
#define ICON_GNET_CSQ_2_24 "\xEE\x98\x91 " // 0xe611 4G信号2格图标
#define ICON_GNET_CSQ_1_24 "\xEE\x98\x94 " // 0xe614 4G信号1格图标
#define ICON_GNET_CSQ_0_24 "\xEE\x98\x92 " // 0xe612 4G信号0格图标
#define THEME_COLOR_LIGHT_MODE_DEF 0xFFFFFF
#define THEME_COLOR_DARK_MODE_DEF 0x292831
#define THEME_COLOR_DODER_BLUE_DEF 0x1E90FF
#define THEME_COLOR_WHITE_DEF 0xFFFFFF
#define THEME_COLOR_BLACK_DEF 0x000000
#define THEME_COLOR_SLATE_GREY_DEF 0x708090
#define THEME_COLOR_SPRING_GREEN_DEF 0x00FF7F
#define THEME_COLOR_FIRE_BRICK1_DEF 0xFF3030
#define THEME_COLOR_FIRE_BRICK3_DEF 0xCD2626
#define THEME_COLOR_YELLOW_DEF 0xFFFF00
#define THEME_COLOR_GRAY81_DEF 0xCFCFCF
#define THEME_COLOR_SEA_GREEN1_DEF 0x54FF9F
#define THEME_COLOR_BTN_BLUE_DEF 0x2196F3
#define THEME_COLOR_SNOW3_DEF 0xCDC9C9
#define THEME_COLOR_SNOW4_DEF 0x8B8989
#define THEME_COLOR_SEAGREEN2_DEF 0x4EEE94
#define THEME_COLOR_GOLD3_DEF 0xCDAD00
/************************************************************************************************************/
// 字体合集
extern const lv_font_t ui_font_12;
extern const lv_font_t lv_font_montserrat_16;
extern const lv_font_t lv_font_montserrat_20;
extern const lv_font_t lv_font_montserrat_42;
extern const lv_font_t system_status_icon_24;
extern lv_obj_t *ui_Screen1; // 用于切屏
extern lv_obj_t *current_bar; // 显示电流的bar
extern lv_obj_t *SIM_card_label; // sim卡状态
extern lv_obj_t *gnet_dsc_label; // 4G描述label
extern lv_obj_t *g_gnet_label; // 4G状态label
extern lv_obj_t *gps_level_label; // gps状态
extern lv_obj_t *net_connect_label; // 网络连接状态
extern lv_obj_t *server_connect_label; // 服务器连接状态
extern lv_obj_t *screen1_led; // 用于更改led颜色
extern lv_obj_t *screen1_play_btn_label;
extern lv_obj_t *screen1_measurements_label1;
extern lv_obj_t *screen1_measurements_label2;
extern lv_obj_t *screen1_measurements_label3;
extern lv_obj_t *screen1_measurements_label4;
extern lv_obj_t *screen1_measurements_label5;
extern lv_obj_t *screen1_measurements_label6;
extern lv_obj_t *screen1_measurements_label7;
extern lv_obj_t *screen1_measurements_label8;
extern lv_obj_t *screen1_measurements_label9;
#pragma pack(2)
typedef struct
{
short enc_val; // 编码器原始值 无效
short ss_1; // 1通道瞬时流量
short ss_2;
short flow_10cm_1; // 1通道瞬时流量
short flow_10cm_2;
uint16_t accumulate1;
uint16_t accumulate2;
uint16_t one_pile_work_time;
int ll_1; // 1通道累计流量
int ll_2;
short speed; // 速度
short depth; // 深度
unsigned short Ia; // a通道电流值
unsigned short Ib;
unsigned short Ic;
unsigned short cnt; // 计数 无效
short angle_x; // x轴角度
short angle_y;
short angle_z;
int dx; // 经度
int dy; // 维度
unsigned short id; // 桩点号
} user_data_t;
#pragma pack() /*取消指定对齐,恢复缺省对齐*/
#pragma pack(1)
typedef enum
{
WORK = 0x0200,
PAUSE = 0x0100,
STOP = 0x0000,
} MACHINE_WORK_STATE;
#pragma pack()
void ui_screen1_init(void);

View File

@ -0,0 +1,144 @@
#include "../../EC600U_lvgl_lib/lvgl/lvgl.h"
#define THEME_COLOR_BTN_BLUE_DEF 0x2196F3
#define THEME_COLOR_WHITE_DEF 0xFFFFFF
extern const lv_font_t ui_font_16;
extern lv_obj_t * ui_Screen2;
struct Dropdown_t{
int dropdown_size[2];
int dropdown_align[2];
char dropdown_options[64];
char dropdown_dsc[32];
};
struct Textarea_t{
int num;
int label_size[14][2];
int label_align[14][2];
char label_text[14][32];
int textarea_size[14][2];
int textarea_align[14][2];
char textarea_default_data[14][10];
};
struct Btn_t{
lv_obj_t * btn_p[3];
int btn_align[3][2];
void (*set_cb)(lv_event_t * e);
void (*reset_cb)(lv_event_t * e);
void (*default_cb)(lv_event_t * e);
};
/**********************************************************************************************************/
struct _ch
{
uint16_t ad_4ma;
uint16_t ad_20ma;
};
typedef struct _cal_4_20ma_t
{
uint16_t magic;
struct _ch ch[2];
}cal_4_20ma_t;
/**********************************************************************************************************/
struct ad_flow_cal_t
{
int16_t flow_min;
int16_t flow_max;
}; //存储ad转换的最大流量和最小流量
typedef struct _flow_config_t
{
uint16_t magic; //可能是一个用于标识流量配置的特殊字段或标志?
uint16_t input_type; // 1 : 4~20ma 2: 0~3.6K
int16_t min_flow[2]; // 小流量切除
struct ad_flow_cal_t ad_cal[2];
uint16_t pulse_coef[2]; //可能代表了脉冲输出的系数或倍率?
uint16_t rsv[6]; //可能是预留给未来扩展使用的保留字段?
}flow_config_t;
/**********************************************************************************************************/
typedef struct _depth_config_t
{
uint16_t magic;
uint8_t input_type; // 0正交 1:正交反向 2:方向脉冲 3:方向脉冲反向
uint8_t port; // 编码器端口
uint16_t N; //编码器系数分子
uint16_t M; //编码器系数分母
int16_t min_depth; // 最小深度 mm
int16_t max_depth; // 最大深度 mm
int16_t sample_depth; // 采样深度 mm
int16_t depth_offset; // 默认深度偏移
int16_t min_valid_depth; // 最小有效深度
int16_t inc_pile_depth; // 允许换桩深度
uint16_t current_on_threshold; // 行走电机开启电流
uint16_t current_off_threshold; // 行走电机关闭电流
uint16_t move_on_duration; // 持续时间
uint16_t move_off_duration; // 持续时间
uint16_t move_current_channel; //行走电流通道
}depth_config_t;
typedef struct _check_t
{
uint8_t id;
uint16_t addr;//该数据在modbus寄存器中的地址
uint8_t type;//该数据类型
uint8_t decimal_places;//如果该数据是浮点型有几位小数
uint16_t max_num;//该数据的最大值
int16_t min_num;
}check_t;
#define TYPE_INT8 0
#define TYPE_UINT8 1
#define TYPE_INT16 2
#define TYPE_UINT16 3
#define TYPE_FLOAT 4 //数据类型为浮点型
#define SAVE_CMD (0x55aa)
#define CAL_4_20MA_ADDR (384)
#define CAL1_AD_4MA_ADDR (CAL_4_20MA_ADDR + 1)
#define CAL1_AD_20MA_ADDR (CAL_4_20MA_ADDR + 2)
#define CAL2_AD_4MA_ADDR (CAL_4_20MA_ADDR + 3)
#define CAL2_AD_20MA_ADDR (CAL_4_20MA_ADDR + 4)
#define FLOW_CONFIG_ADDR (CAL_4_20MA_ADDR + (sizeof(cal_4_20ma_t) + 15)/16*8)
#define FLOW_INPUT_TYPE_ADDR (FLOW_CONFIG_ADDR + 1)
#define FLOW_MIN_FLOW1_ADDR (FLOW_CONFIG_ADDR + 2)
#define FLOW_MIN_FLOW2_ADDR (FLOW_CONFIG_ADDR + 3)
#define FLOW_AD_CAL1_FLOW_MIN_ADDR (FLOW_CONFIG_ADDR + 4)
#define FLOW_AD_CAL1_FLOW_MAX_ADDR (FLOW_CONFIG_ADDR + 5)
#define FLOW_AD_CAL2_FLOW_MIN_ADDR (FLOW_CONFIG_ADDR + 6)
#define FLOW_AD_CAL2_FLOW_MAX_ADDR (FLOW_CONFIG_ADDR + 7)
#define FLOW_PAUSE_COEF1_ADDR (FLOW_CONFIG_ADDR + 8)
#define FLOW_PAUSE_COEF2_ADDR (FLOW_CONFIG_ADDR + 9)
#define DEPTH_CONFIG_ADDR (FLOW_CONFIG_ADDR + (sizeof(flow_config_t) + 15)/16*8)
#define DEPTH_INPUT_TYPE_ADDR (DEPTH_CONFIG_ADDR + 1) //高八位
#define DEPTH_PORT_ADDR (DEPTH_CONFIG_ADDR + 1) //低八位
#define DEPTH_ENC_N_ADDR (DEPTH_CONFIG_ADDR + 2)
#define DEPTH_ENC_M_ADDR (DEPTH_CONFIG_ADDR + 3)
#define DEPTH_MIN_DEPTH_ADDR (DEPTH_CONFIG_ADDR + 4)
#define DEPTH_MAX_DEPTH_ADDR (DEPTH_CONFIG_ADDR + 5)
#define DEPTH_SAMPLE_DEPTH_ADDR (DEPTH_CONFIG_ADDR + 6)
#define DEPTH_OFFSET_ADDR (DEPTH_CONFIG_ADDR + 7)
#define DEPTH_MIN_VALID_ADDR (DEPTH_CONFIG_ADDR + 8)
#define DEPTH_INC_PILE_ADDR (DEPTH_CONFIG_ADDR + 9)
#define DEPTH_CUR_ON_ADDR (DEPTH_CONFIG_ADDR + 10)
#define DEPTH_CUR_OFF_ADDR (DEPTH_CONFIG_ADDR + 11)
#define DEPTH_MOVE_ON_DUR_ADDR (DEPTH_CONFIG_ADDR + 12)
#define DEPTH_MOVE_OFF_DUR_ADDR (DEPTH_CONFIG_ADDR + 13)
#define DEPTH_MOVE_CUR_CH_ADDR (DEPTH_CONFIG_ADDR + 14)
#define FLOW_INPUT_TYPE_MODE1 (1) //4-20ma
#define FLOW_INPUT_TYPE_MODE2 (2) //0-3.6k
#define DEPTH_INPUT_TYPE_MODE1 (0)
#define DEPTH_INPUT_TYPE_MODE2 (0b100000000) //1<<8
#define DEPTH_INPUT_TYPE_MODE3 (0b1000000000) //2<<8
#define DEPTH_INPUT_TYPE_MODE4 (0b1100000000) //3<<8
void ui_screen2_init(void);

View File

@ -0,0 +1,26 @@
#include "../../EC600U_lvgl_lib/lvgl/lvgl.h"
#define THEME_COLOR_WHITE_DEF 0xFFFFFF
#define THEME_COLOR_BTN_BLUE_DEF 0x2196F3
extern const lv_font_t lv_font_chinese_12_name;
extern const lv_font_t lv_font_montserrat_24;
extern lv_obj_t * ui_Screen3;//用来切屏
extern lv_obj_t * screen3_calender_dsc_label;
extern lv_obj_t * screen3_calender;
extern lv_obj_t * screen3_spinbox_dsc_label;
extern lv_obj_t * btn_page_plus;
extern lv_obj_t * btn_page_reduce;
extern lv_obj_t * screen3_spinbox;
extern lv_obj_t * scrren3_spinbox_btn;
extern lv_obj_t * data_table1;
extern lv_obj_t * data_table2;
extern lv_obj_t * data_table3;
extern lv_obj_t * data_table4;
extern lv_obj_t * data_table5;
extern lv_obj_t * data_table6;
extern lv_obj_t * data_table7;
extern lv_obj_t * data_table8;
void ui_screen3_init(void);

View File

@ -0,0 +1,5 @@
#include "../../EC600U_lvgl_lib/lvgl/lvgl.h"
extern const lv_font_t lv_font_montserrat_24;
void ui_screen3_chart_init(int num);

View File

@ -0,0 +1,53 @@
#include "../../EC600U_lvgl_lib/lvgl/lvgl.h"
#define THEME_COLOR_WHITE_DEF 0xFFFFFF
#define THEME_COLOR_BTN_BLUE_DEF 0x2196F3
extern const lv_font_t lv_font_montserrat_16;
typedef enum{
LEVEL_INSTUMENT_PANEL_ALL = 0,
LEVEL_INSTUMENT_PANEL_RED,
LEVEL_INSTUMENT_PANEL_YELLOW,
LEVEL_INSTUMENT_PANEL_GREEN,
LEVEL_INSTRUMENT_LED,
LEVEL_INSTRUMENT_CROSSHAIR
} level_instrument_part_t;
typedef struct {
lv_obj_t * red_bg;
lv_obj_t * yellow_bg;
lv_obj_t * green_bg;
lv_obj_t * horizontal_line;
lv_obj_t * vertical_line;
}my_panel_t;
typedef struct {
my_panel_t * panel;
lv_obj_t * led;
int size;
int x;
int y;
}level_instrument_t;
extern level_instrument_t * level_instrument;
void ui_screen4_init(void);
//设置控件对应部件大小
void level_instrument_set_size(level_instrument_t * level_instrument, lv_coord_t size, level_instrument_part_t part);
//设置光标在面板上的位置(光标以坐标轴方向为准,基于原点偏移)
void level_instrument_set_cursor_pos(level_instrument_t * level_instrument,lv_coord_t x_offset, lv_coord_t y_offset);
//设置光标颜色
void level_instrument_set_cursor_color(level_instrument_t * level_instrument,lv_color_t color);
//设置光标亮度
void level_instrument_set_cursor_brightness(level_instrument_t * level_instrument,uint8_t bright);
//设置控件整体的对齐
void level_instrument_set_align(level_instrument_t * level_instrument,lv_align_t align,lv_coord_t x_offset,lv_coord_t y_offset);
//设置面板透明度
void level_instrument_set_opa(level_instrument_t * level_instrument,lv_opa_t opa);

369
EC600U_lvgl/lvgl_demo.c Normal file
View File

@ -0,0 +1,369 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ql_api_osi.h"
#include "ql_lcd.h"
#include "ql_i2c.h"
#include "ql_gpio.h"
#include "ql_keypad.h"
#include "ql_log.h"
#include "../peripheral/inc/ql_pin_cfg.h"
#include "ql_fs.h"
#include "../../ql-kernel/inc/ql_sdmmc.h"
#include "../EC600U_lvgl_lib/lvgl/lvgl.h"
#include "lvgl_demo.h"
#include "./inc/ui/ui.h"
#include "./inc/ui/ui_screen1.h"
#include "./inc/ui/fs_function.h"
#include "../../kernel/include/osi_api.h"
#define LOG_INFO(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "lvgldemo", msg, ##__VA_ARGS__)
#define MY_DISP_HOR_RES 480
#define MY_DISP_VER_RES 320
#define KEY1 QL_KEY_MAP_18 //切换到上一个屏幕
#define KEY2 QL_KEY_MAP_19 //切换到下一个屏幕
#define KEY3 QL_KEY_MAP_16 //开始/暂停
#define KEY4 QL_KEY_MAP_17 //停止
ql_task_t lvgl_test_task;
osiThread_t *lvgl_thread = NULL;// 后台ui事件处理线程
static lv_color_t * buf_1 = NULL;
static lv_disp_t * disp = NULL;
lv_indev_t * indev_touchpad = NULL;
lv_indev_t * indev_button = NULL;
uint8_t act_screen_id = 1;//记录当前屏幕id
extern user_data_t g_ui_user_data;
uint8_t gt911_array[184] = {
0x81, 0x00, 0x04, 0x58, 0x02, 0x0A, 0x0C, 0x20, 0x01, 0x08, 0x28, 0x05, 0x50, // 0x8047 - 0x8053
0x3C, 0x0F, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x8054 - 0x8060
0x00, 0x89, 0x2A, 0x0B, 0x2D, 0x2B, 0x0F, 0x0A, 0x00, 0x00, 0x01, 0xA9, 0x03, // 0x8061 - 0x806D
0x2D, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, // 0x806E - 0x807A
0x59, 0x94, 0xC5, 0x02, 0x07, 0x00, 0x00, 0x04, 0x93, 0x24, 0x00, 0x7D, 0x2C, // 0x807B - 0x8087
0x00, 0x6B, 0x36, 0x00, 0x5D, 0x42, 0x00, 0x53, 0x50, 0x00, 0x53, 0x00, 0x00, // 0x8088 - 0x8094
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x8095 - 0x80A1
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80A2 - 0x80AD
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, // 0x80AE - 0x80BA
0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 0x80BB - 0x80C7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80C8 - 0x80D4
0x02, 0x04, 0x06, 0x08, 0x0A, 0x0F, 0x10, 0x12, 0x16, 0x18, 0x1C, 0x1D, 0x1E, // 0x80D5 - 0x80E1
0x1F, 0x20, 0x21, 0x22, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // 0x80E2 - 0x80EE
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80EF - 0x80FB
0x00, 0x00, // 0x80FC - 0x80FD
};
/******************************************************回调函数**********************************************************/
static void dispflush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
LOG_INFO("dispflush area x1 = %d, x2 = %d, y1 = %d, y2 = %d",area->x1,area->x2,area->y1,area->y2);
ql_lcd_write((uint16_t*)color_p, area->x1 , area->y1, area->x2 , area->y2);
lv_disp_flush_ready(disp);
}
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
//LOG_INFO("enter touchpad_read");
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*检测是否被触摸*/
uint8_t state = 255;
ql_I2cRead_16bit_addr(i2c_2,0x5D,GT911_STATE_REG,&state,1);
uint8_t STATE = state;
uint8_t touch_number = (state & 0x0f);
uint8_t touch_state = (state & 0x80);
/*代表被触摸了*/
if((touch_state == 0x80) && (touch_number >= 1) && (STATE != 255)){
/*获取触摸坐标,lvgl只支持单点触摸因此只读取第一个坐标数据*/
//LOG_INFO("touch_number = %d, touch_state = %d",touch_number,touch_state);
uint8_t low8 = 0;
uint8_t high8 = 0;
ql_I2cRead_16bit_addr(i2c_2,0x5D,(GT911_TP1_REG),&low8,1);
ql_I2cRead_16bit_addr(i2c_2,0x5D,(GT911_TP1_REG + 1),&high8,1);
last_y = (low8 | (high8 << 8));
ql_I2cRead_16bit_addr(i2c_2,0x5D,(GT911_TP1_REG + 2),&low8,1);
ql_I2cRead_16bit_addr(i2c_2,0x5D,(GT911_TP1_REG + 3),&high8,1);
last_x = (low8 | (high8 << 8));
//LOG_INFO("x = %d, y = %d",last_x,320- last_y);
data->state = LV_INDEV_STATE_PR;
} else {
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
data->point.x = last_x;
data->point.y = (320 - last_y);
//清除数据标志位
uint8_t flag = 0;
ql_I2cWrite_16bit_addr(i2c_2,0x5D,GT911_STATE_REG,&flag,1);
}
void ql_keypad_callback(ql_keymatrix_t keymatrix)
{
//LOG_INFO("keymap:%d keyout:%d keyin:%d pressd:%d", keymatrix.keymap, keymatrix.keyout, keymatrix.keyin, keymatrix.keystate);
}
static int8_t button_get_pressed_id(void)
{
uint8_t i;
uint8_t KEYS[4] = {KEY1,KEY2,KEY3,KEY4};
for(i = 0; i < 4; i++) {
uint32_t press_state = 0;
ql_keypad_state(&press_state,KEYS[i]);
if(press_state == 1){
return i;
}
}
return -1;
}
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
//LOG_INFO("enter button_read");
static uint8_t last_btn = 0;
int8_t btn_act = button_get_pressed_id();
if((btn_act == 2) || (btn_act == 3)){//当点击按键为后两个时
if(act_screen_id == 3){//如果当前活动屏幕id为第三个屏幕则返回控制spinbox按键的id
btn_act += 2;
}
else if((act_screen_id == 2) || (act_screen_id == 4)){//当前活动屏幕为第二个或者第四个的时候不对后两个按键做出反应
return;
}
}
if(btn_act >= 0) {
data->state = LV_INDEV_STATE_PR;
last_btn = btn_act;
} else {
data->state = LV_INDEV_STATE_REL;
}
data->btn_id = last_btn;
}
/*************************************************************************************************************************/
void gt911_init(void)
{
ql_I2cInit(i2c_2,STANDARD_MODE);
ql_pin_set_gpio(TP_nRST_pin);
ql_pin_set_gpio(nTP_INT_pin);
ql_gpio_init(TP_nRST_GPIO_num,GPIO_OUTPUT,PULL_NONE,LVL_LOW);
ql_gpio_init(nTP_INT_GPIO_num,GPIO_OUTPUT,PULL_NONE,LVL_LOW);
//设置从机地址0xBA
ql_rtos_task_sleep_ms(10);
ql_gpio_set_level(TP_nRST_GPIO_num,LVL_HIGH);
ql_rtos_task_sleep_ms(10);
//int转为悬浮输入
ql_gpio_init(nTP_INT_GPIO_num,GPIO_INPUT,PULL_NONE,LVL_LOW);
ql_rtos_task_sleep_ms(10);
//复位
uint8_t temp = 2;
ql_I2cWrite_16bit_addr(i2c_2,GT911_ADDRESS,GT911_CTRL_ADDRESS,&temp,1);
//更新配置数组
gt911_t config = {
.X_Resolution = X_RESOLUTION,
.Y_Resolution = Y_RESOLUTION,
.Number_Of_Touch_Support = MAX_TOUCH_NUMBER,
.ReverseX = false,
.ReverseY = false,
.SoftwareNoiseReduction = true,
.SwithX2Y = false,
};
gt911_array[1] = config.X_Resolution & 0x00FF;
gt911_array[2] = (config.X_Resolution >> 8) & 0x00FF;
gt911_array[3] = config.Y_Resolution & 0x00FF;
gt911_array[4] = (config.Y_Resolution >> 8) & 0x00FF;
gt911_array[5] = config.Number_Of_Touch_Support;
gt911_array[6] = 0;
gt911_array[6] |= config.ReverseY << 7;
gt911_array[6] |= config.ReverseX << 6;
gt911_array[6] |= config.SwithX2Y << 3;
gt911_array[6] |= config.SoftwareNoiseReduction << 2;
//计算校验和
uint8_t buf[2] = {0,0};//第一个下标是校验位第二个是模式0代表掉电不保存
for(uint8_t i = 0; i < 184; i++){
buf[0] += gt911_array[i];
}
buf[0] = (~buf[0])+1;
//往0x8047写入配置参数
ql_I2cWrite_16bit_addr(i2c_2,GT911_ADDRESS,GT911_CFG_ADDRESS,(uint8_t*)gt911_array,184);
//往0x80FF写入校验和
ql_I2cWrite_16bit_addr(i2c_2,GT911_ADDRESS,GT911_CHECK_REG,(uint8_t*)buf,2);
//结束复位
temp = 0;
ql_I2cWrite_16bit_addr(i2c_2,GT911_ADDRESS,GT911_CTRL_ADDRESS,&temp,1);
}
void button_init(void)
{
ql_keypad_out_e row[QL_KEYPAD_ROW_LENGTH] = {QL_KP_OUT0, QL_KP_OUT1, QL_KP_OUT2, QL_KP_OUT3, QL_KP_OUT4, QL_KP_OUT5};
ql_keypad_in_e col[QL_KEYPAD_COL_LENGTH] = {QL_KP_IN0, QL_KP_IN1,QL_KP_IN2, QL_KP_IN3, QL_KP_IN_NO_VALID, QL_KP_IN_NO_VALID};
ql_keypad_init(ql_keypad_callback, row, col);
}
/*************************************************************************************************************************/
//接收到数据时对相关结构体进行处理
void ui_event_process(const uint32_t event_id, const char *param)
{
/*判断是否为有效id*/
if(event_id < 9){
LOG_INFO("receive wrong event id, %d",event_id);
return;
}
/*根据id进行处理*/
switch (event_id)
{
case 9: //屏1
{
#if !TEST
user_data_t * u_data = (user_data_t *)param;
memcpy(&g_ui_user_data, u_data, sizeof(g_ui_user_data));
#endif
break;
}
default:
break;
}
}
static void lvgl_demo_thread(void *param)
{
/*初始化ST7796SLCD显示屏*/
ql_lcd_init();
/*初始化GT911触摸屏*/
gt911_init();
/*初始化EC600U按键*/
button_init();
/*初始化lvgl*/
lv_init();
LOG_INFO("lv_init end");
/*注册disp驱动*/
static lv_disp_draw_buf_t draw_buf_dsc_1;
//static lv_color_t buf_1[MY_DISP_HOR_RES * 64];
buf_1 = (lv_color_t*)malloc(480 * 320 * sizeof(lv_color_t));
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 320);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
disp_drv.flush_cb = dispflush;
disp_drv.draw_buf = &draw_buf_dsc_1;
disp = lv_disp_drv_register(&disp_drv);
LOG_INFO("disp init end");
/*注册touchpad驱动*/
static lv_indev_drv_t touchpad_drv;
lv_indev_drv_init(&touchpad_drv);
touchpad_drv.type = LV_INDEV_TYPE_POINTER;
touchpad_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&touchpad_drv);
LOG_INFO("touchpad init end");
/*注册button驱动*/
static lv_indev_drv_t button_drv;
lv_indev_drv_init(&button_drv);
button_drv.type = LV_INDEV_TYPE_BUTTON;
button_drv.read_cb = button_read;
indev_button = lv_indev_drv_register(&button_drv);
/*Assign buttons to points on the screen*/
static const lv_point_t btn_points[6] = {
{0, 0}, //切到上一个界面
{450, 0}, //切刀下一个界面
{420, 170}, //screen1 开始/暂停
{420, 240}, //screen1 停止
{300, 0}, //screen3 减少页数btn
{410, 0}, //screen3 增加页数btn
};
lv_indev_set_button_points(indev_button, btn_points);
LOG_INFO("button init end");
/*demo*/
ui_init();
LOG_INFO("ui_init end");
lvgl_thread = osiThreadCurrent();
while (1)
{
osiEvent_t waitevent;
lv_timer_handler();
lv_tick_inc(10);
if(osiEventTryWait(lvgl_thread, &waitevent,20)){
LOG_INFO("waitevent id = %d",waitevent.id);
char *context = (char *)waitevent.param1;
ui_event_process(waitevent.id, context);
}
ql_rtos_task_sleep_ms(10);
}
}
void lvgl_demo_init(void)
{
ql_pin_set_func(QL_PIN_SDMMC_CMD , QL_PIN_SDMMC_MODE_FUNC);
ql_pin_set_func(QL_PIN_SDMMC_DATA_0 , QL_PIN_SDMMC_MODE_FUNC);
ql_pin_set_func(QL_PIN_SDMMC_DATA_1 , QL_PIN_SDMMC_MODE_FUNC);
ql_pin_set_func(QL_PIN_SDMMC_DATA_2 , QL_PIN_SDMMC_MODE_FUNC);
ql_pin_set_func(QL_PIN_SDMMC_DATA_3 , QL_PIN_SDMMC_MODE_FUNC);
ql_pin_set_func(QL_PIN_SDMMC_CLK , QL_PIN_SDMMC_MODE_FUNC);
LOG_INFO("ql_sdmmc_mount err = %d",ql_sdmmc_mount());
QlOSStatus err = ql_rtos_task_create(&lvgl_test_task, 1024*10, 12, "lvgldemo", lvgl_demo_thread, NULL, 5);
if (err != QL_OSI_SUCCESS){
LOG_INFO("lvgl demo task created failed");
}
}
// LOG_INFO("findDataFile err = %d",findDataFile(2024,1,10));
// LOG_INFO("findDataFile err = %d",findDataFile(2024,1,1));
// LOG_INFO("findDataFile err = %d",findDataFile(2024,1,12));
// LOG_INFO("findDataFile err = %d",findDataFile(2023,1,10));
// int fd = ql_fopen("SD:2024/01/10.txt","rb+");
// struct Return_Data data = {0};
// readDataFromFile(fd,&data);
// LOG_INFO("id count = %d",data.id_count);
// if (data.id_count > 0) {
// for (uint8_t i = 0; i < data.id_count; i++) {
// LOG_INFO("id = %d", data.dataArr[i].id);
// LOG_INFO("start_time = %02d:%02d:%02d", data.dataArr[i].start_hour, data.dataArr[i].start_min, data.dataArr[i].start_second);
// LOG_INFO("end_time = %02d:%02d:%02d", data.dataArr[i].end_hour, data.dataArr[i].end_min, data.dataArr[i].end_second);
// LOG_INFO("max_depth = %.2f", data.dataArr[i].max_depth);
// LOG_INFO("max_V = %.2f", data.dataArr[i].max_V);
// LOG_INFO("average_I = %.2f", data.dataArr[i].average_I);
// LOG_INFO("count = %d", data.dataArr[i].count);
// LOG_INFO("offset = %d", data.dataArr[i].offset);
// }
// }
// ql_fclose(fd);

View File

@ -0,0 +1,265 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ql_log.h"
#include "../inc/ui/ui_screen3.h"
#include "../inc/ui/fs_function.h"
#define LOG_INFO(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "fsfunction", msg, ##__VA_ARGS__)
#define LOG 1
struct Return_Data global_file_data_p;
int end_count = 0;
int current_id = 0;
/**
* func:
*/
int8_t findDataFile(uint16_t year,uint8_t month,uint8_t day)
{
/*查询目录路径*/
char dir_path[32];
sprintf(dir_path,"SD:%d/%02d",year,month);
#if LOG
LOG_INFO("dir_path = %s",dir_path);
#endif
QDIR *dir = ql_opendir(dir_path);
if(dir == NULL){
#if LOG
LOG_INFO("ql_opendir fail");
#endif
return -1;
}
ql_closedir(dir);
/*查询文件路径*/
char file_path[32];
sprintf(file_path,"SD:%d/%02d/%02d.txt",year,month,day);
#if LOG
LOG_INFO("file_path = %s",file_path);
#endif
QFILE file_fd = ql_fopen(file_path,"rb");
if(file_fd <= 0){
#if LOG
LOG_INFO("ql_fopen fail");
#endif
return -2;
}
/*查询文件是否为空*/
if(ql_fseek(file_fd,0,QL_SEEK_END) <= 0){
#if LOG
LOG_INFO("ql_fseek fail");
#endif
ql_fclose(file_fd);
return -3;
}
/*存在该目录下的非空文件*/
ql_fclose(file_fd);
return 1;
}
/**
*
*/
void dealdata(char*buffer,int last_file,int last_i,struct Return_Data *newStruct)
{
int id = 0 ;
time_t utc_time = 0;
struct tm *tm_now;
float max_depth = 0;
float max_V = 0;
float average_I = 0;
sscanf(buffer + last_i,"%d,%lld,%f,%f,%f",&id,&utc_time,&max_depth,&max_V,&average_I);
#if LOG
LOG_INFO("id = %d,utc_time = %lld,max_depth = %f,max_V = %f,average_I = %f",id,utc_time,max_depth,max_V,average_I);
#endif
tm_now = gmtime(&utc_time);
//处理数据
if(current_id != id){
current_id = id;
//LV_LOG_USER("新的id\n");
newStruct->dataArr[end_count].count ++;
newStruct->dataArr[end_count].id = id;
newStruct->dataArr[end_count].max_depth = max_depth;
newStruct->dataArr[end_count].max_V = max_V;
newStruct->dataArr[end_count].average_I = average_I;
newStruct->dataArr[end_count].start_hour = tm_now ->tm_hour;
newStruct->dataArr[end_count].start_min = tm_now ->tm_min;
newStruct->dataArr[end_count].start_second = tm_now ->tm_sec;
newStruct->dataArr[end_count].end_hour= tm_now ->tm_hour;
newStruct->dataArr[end_count].end_min = tm_now ->tm_min;
newStruct->dataArr[end_count].end_second = tm_now ->tm_sec;
newStruct->dataArr[end_count].offset = last_file;
//LV_LOG_USER("当前id在文件中的指针偏移量为%d\n",newStruct->dataArr[end_count].offset);
newStruct->id_count = end_count+1;
end_count++;
}else if(current_id == id){
//LV_LOG_USER("相同id\n");
newStruct->dataArr[end_count-1].max_depth = (max_depth > newStruct->dataArr[end_count-1].max_depth) ? max_depth : newStruct->dataArr[end_count-1].max_depth;
newStruct->dataArr[end_count-1].max_V = (max_V > newStruct->dataArr[end_count-1].max_V) ? max_V : newStruct->dataArr[end_count-1].max_V;
newStruct->dataArr[end_count-1].average_I = (newStruct->dataArr[end_count-1].average_I * newStruct->dataArr[end_count-1].count + average_I)/(newStruct->dataArr[end_count-1].count +1);
newStruct->dataArr[end_count-1].count += 1;
if(newStruct->dataArr[end_count-1].end_hour != tm_now ->tm_hour) newStruct->dataArr[end_count-1].end_hour = tm_now ->tm_hour;
if(newStruct->dataArr[end_count-1].end_min != tm_now ->tm_min) newStruct->dataArr[end_count-1].end_min = tm_now ->tm_min;
if(newStruct->dataArr[end_count-1].end_second != tm_now ->tm_sec) newStruct->dataArr[end_count-1].end_second = tm_now ->tm_sec;
}
}
/**
*
*/
void readDataFromFile(QFILE fd, struct Return_Data * data)
{
char buffer[CHAR_READ_LENGTH];
int last_file = 0;
int char_length = 0;
int last_i = 0;
ql_fseek(fd,0,QL_SEEK_END);
int end_pos = ql_ftell(fd);
ql_fseek(fd,0,QL_SEEK_SET);
while(1){
ql_fread(buffer + char_length, CHAR_READ_LENGTH - char_length, 1, fd);//int br = 0; lv_fs_read(file,buffer+char_length,CHAR_READ_LENGTH-char_length,&br);
#if LOG
LOG_INFO("buffer: %s",buffer);
#endif
last_i = 0;
for(int i = 0; i < CHAR_READ_LENGTH; i++){
if(buffer[i]=='\n'){
dealdata(buffer,last_file,last_i,data);
last_file = last_file + i - last_i + 2;//当前数据在文件中的偏移量=本身+上一行数据的数据长度
last_i = i + 1;//下一次读取的起始位置
}
}
if(buffer[CHAR_READ_LENGTH-1] != '\n'){
char_length = CHAR_READ_LENGTH-last_i;
int pos = ql_ftell(fd);// int pos = 0; lv_fs_tell(file,&pos);
last_file = pos - char_length;
strncpy(buffer,buffer + last_i, CHAR_READ_LENGTH - last_i);
}
// if (feof(file->file_d)) { // 如果已经读完文件,则退出循环
// break;
// }
if(ql_ftell(fd) == end_pos){
return;
}
}
}
/**
* 8table上
*/
void setDataToTables(struct Return_Data* dataArray, int page, int page_count)
{
char max_V[10];
char max_depth[10];
char average_I[10];
int end_pos = 0;
lv_obj_t * data_tables_in_func[] = {data_table1, data_table2, data_table3, data_table4,data_table5, data_table6, data_table7, data_table8 };
if(page<page_count){
end_pos = 8;
}else if(page == page_count){
end_pos = dataArray->id_count - 8*(page-1);
//清空8个table
for(int m=0;m<8;m++){
for(int n=0;n<6;n++){
lv_table_set_cell_value_fmt(data_tables_in_func[m],0,n,"");
}
}
}
for(int i = 0; i < end_pos; i++){
sprintf(max_V,"%.3f",dataArray->dataArr[(page-1)*8+i].max_V);
sprintf(max_depth,"%.3f",dataArray->dataArr[(page-1)*8+i].max_depth);
sprintf(average_I,"%.2f",dataArray->dataArr[(page-1)*8+i].average_I);
lv_table_set_cell_value_fmt(data_tables_in_func[i],0,0,"%d",dataArray->dataArr[(page-1)*8+i].id);
lv_table_set_cell_value_fmt(data_tables_in_func[i],0,1,"%s",max_V);
lv_table_set_cell_value_fmt(data_tables_in_func[i],0,2,"%s",max_depth);
lv_table_set_cell_value_fmt(data_tables_in_func[i],0,3,"%s",average_I);
lv_table_set_cell_value_fmt(data_tables_in_func[i],0,4,"%d:%d:%d",
dataArray->dataArr[(page-1)*8+i].start_hour,
dataArray->dataArr[(page-1)*8+i].start_min,
dataArray->dataArr[(page-1)*8+i].start_second);
lv_table_set_cell_value_fmt(data_tables_in_func[i],0,5,"%d:%d:%d",
dataArray->dataArr[(page-1)*8+i].end_hour,
dataArray->dataArr[(page-1)*8+i].end_min,
dataArray->dataArr[(page-1)*8+i].end_second);
}
}
/**
* chart上
*/
void setDataToChart(QFILE fd, int offset ,int current_id,struct ChartData* chart_data)
{
char buffer[CHAR_READ_LENGTH];//存储每次读到的字符串
int char_length = 0;//记录不完整字符串的长度
int same_id_count = 0;
/*获取长度*/
ql_fseek(fd,0,QL_SEEK_END);
int end_pos = ql_ftell(fd);
ql_fseek(fd,offset,LV_FS_SEEK_SET);//lv_fs_seek(file,offset,LV_FS_SEEK_SET);
int last_i = 0;
while(1){
last_i = 0;
ql_fread(buffer + char_length, CHAR_READ_LENGTH - char_length, 1, fd);//lv_fs_read(file,buffer+char_length,CHAR_READ_LENGTH-char_length,NULL);
//buffer[CHAR_READ_LENGTH] = '\0';
for(int i = 0; i < CHAR_READ_LENGTH; i++){
if(buffer[i]=='\n'){
int id = 0 ;
time_t utc_time = 0;
struct tm *tm_now;
float max_depth = 0;
float max_V = 0;
float average_I = 0;
sscanf(buffer + last_i,"%d,%lld,%f,%f,%f",&id,&utc_time,&max_depth,&max_V,&average_I);
tm_now = gmtime(&utc_time);
if(current_id == id){
if(same_id_count < SAME_ID_MAX_NUM){
chart_data->depth[same_id_count] = max_depth;
chart_data->V[same_id_count] = max_V;
chart_data->I[same_id_count] = average_I;
sprintf(chart_data->time[same_id_count], "%d:%d:%d", tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);
chart_data->same_id_count = same_id_count;
same_id_count++;
}
last_i = i + 1;
}else{
return;
}
}
}
if(buffer[CHAR_READ_LENGTH-1] != '\n'){
char_length = CHAR_READ_LENGTH-last_i;
strncpy(buffer,buffer+last_i,CHAR_READ_LENGTH-last_i);
}
if(ql_ftell(fd) >= end_pos){
return;
}
}
return;
}

255
EC600U_lvgl/ui/ui.c Normal file
View File

@ -0,0 +1,255 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "time.h"
#include "osi_api.h"
#include "lvgl.h"
#include "../inc/ui/ui.h"
#include "../inc/ui/ui_screen1.h"
#include "../inc/ui/ui_screen2.h"
#include "../inc/ui/ui_screen3.h"
#include "../inc/ui/ui_screen4.h"
#include "../inc/ui/fs_function.h"
#include "ql_fs.h"
#include "ql_log.h"
#define LOG_INFO(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "ui", msg, ##__VA_ARGS__)
extern const lv_font_t ui_font_16;
extern struct Return_Data global_file_data_p; //存储table数据
extern int current_id;
extern int end_count;
extern uint8_t act_screen_id;
extern lv_timer_t * ui_screen1_data_timer;
#if !TEST
extern user_data_t g_ui_user_data;
#endif
extern MACHINE_WORK_STATE current_wort_state;
void screen1_left_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen4 != NULL)) {
lv_scr_load_anim(ui_Screen4, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
if(current_wort_state == WORK){
lv_timer_pause(ui_screen1_data_timer);
}
act_screen_id = 4;
}
}
void screen1_right_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen2 != NULL)) {
lv_scr_load_anim(ui_Screen2, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
if(current_wort_state == WORK){
lv_timer_pause(ui_screen1_data_timer);
}
act_screen_id = 2;
}
}
void screen2_left_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen1 != NULL)) {
#if !TEST
memset(&g_ui_user_data, 0x00, sizeof(g_ui_user_data));
#endif
lv_scr_load_anim(ui_Screen1, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
act_screen_id = 1;
if(current_wort_state == WORK){
lv_timer_resume(ui_screen1_data_timer);
}
}
}
void screen2_right_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen3 != NULL)) {
lv_scr_load_anim(ui_Screen3, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
act_screen_id = 3;
}
}
void screen3_left_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen2 != NULL)) {
lv_scr_load_anim(ui_Screen2, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
act_screen_id = 2;
}
}
void screen3_right_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen4 != NULL)) {
lv_scr_load_anim(ui_Screen4, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
act_screen_id = 4;
}
}
void screen4_left_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen3 != NULL)) {
lv_scr_load_anim(ui_Screen3, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
act_screen_id = 3;
}
}
void screen4_right_btn_cb(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
if((event_code == LV_EVENT_CLICKED) && (ui_Screen1 != NULL)) {
#if !TEST
memset(&g_ui_user_data, 0x00, sizeof(g_ui_user_data));
#endif
lv_scr_load_anim(ui_Screen1, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
act_screen_id = 1;
if(current_wort_state == WORK){
lv_timer_resume(ui_screen1_data_timer);
}
}
}
void ui_screens_create(void)
{
ui_Screen1 = lv_obj_create(NULL);
ui_Screen2 = lv_obj_create(NULL);
ui_Screen3 = lv_obj_create(NULL);
ui_Screen4 = lv_obj_create(NULL);
lv_obj_clear_flag(ui_Screen1,LV_OBJ_FLAG_SCROLLABLE);
lv_obj_clear_flag(ui_Screen2,LV_OBJ_FLAG_SCROLLABLE);
lv_obj_clear_flag(ui_Screen3,LV_OBJ_FLAG_SCROLLABLE);
lv_obj_clear_flag(ui_Screen4,LV_OBJ_FLAG_SCROLLABLE);
lv_disp_load_scr(ui_Screen4);
lv_obj_t * screen1_left_btn = lv_btn_create(ui_Screen1);
lv_obj_t * screen1_right_btn = lv_btn_create(ui_Screen1);
lv_obj_t * screen2_left_btn = lv_btn_create(ui_Screen2);
lv_obj_t * screen2_right_btn = lv_btn_create(ui_Screen2);
lv_obj_t * screen3_left_btn = lv_btn_create(ui_Screen3);
lv_obj_t * screen3_right_btn = lv_btn_create(ui_Screen3);
lv_obj_t * screen4_left_btn = lv_btn_create(ui_Screen4);
lv_obj_t * screen4_right_btn = lv_btn_create(ui_Screen4);
lv_obj_t * btns[8] = {screen1_left_btn,screen1_right_btn,screen2_left_btn,screen2_right_btn,screen3_left_btn,screen3_right_btn,screen4_left_btn,screen4_right_btn};
lv_obj_t * label = NULL;
for(uint8_t i = 0; i < 8; i++){
label = lv_label_create(btns[i]);
lv_obj_set_style_text_color(label,lv_color_hex(THEME_COLOR_WHITE_DEF),LV_PART_MAIN);
lv_obj_center(label);
//设置btn
lv_obj_set_size(btns[i],30,30);
lv_obj_set_style_radius(btns[i], 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btns[i],lv_color_hex(THEME_COLOR_BTN_BLUE_DEF),LV_PART_MAIN);
if(i%2){
lv_obj_set_pos(btns[i],450,0);
lv_label_set_text(label,LV_SYMBOL_RIGHT);
}
else{
lv_obj_set_pos(btns[i],0,0);
lv_label_set_text(label,LV_SYMBOL_LEFT);
}
}
lv_obj_add_event_cb(screen1_left_btn,screen1_left_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen1_right_btn,screen1_right_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen2_left_btn,screen2_left_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen2_right_btn,screen2_right_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen3_left_btn,screen3_left_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen3_right_btn,screen3_right_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen4_left_btn,screen4_left_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_add_event_cb(screen4_right_btn,screen4_right_btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_t * screens[4] = {ui_Screen1,ui_Screen2,ui_Screen3,ui_Screen4};
const char * text[4] = {"作业界面","参数设置","历史数据","水平偏移"};
for(uint8_t i = 0; i < 4; i++){
label = lv_label_create(screens[i]);
lv_obj_set_style_text_font(label, &ui_font_16, 0);
lv_label_set_text(label,text[i]);
lv_obj_align(label,LV_ALIGN_TOP_MID,0,10);
}
}
void ui_init(void)
{
ui_screens_create();//创建4个活动屏幕并添加可以切屏的btn
ui_screen1_init();
ui_screen2_init();
ui_screen3_init();
ui_screen4_init();
// /////////////////////////////////////////////////////设置数据////////////////////////////////////////////////////////////
#if 0
/*获取当天日期并显示到日历label上*/
time_t t = time(NULL);
struct tm *local_time = localtime(&t);
uint16_t year = local_time->tm_year + 1900; // 年份需要加上1900
uint8_t month = local_time->tm_mon + 1; // 月份从0开始需要加1
uint8_t day = local_time->tm_mday;
char calendar_text_str[32];
sprintf(calendar_text_str,"%d.%02d.%02d",year,month,day);
lv_label_set_text(screen3_calender_dsc_label,calendar_text_str);
/*将日历设置为对应日期*/
lv_calendar_set_showed_date(screen3_calender,year,month);
lv_calendar_set_today_date(screen3_calender,year,month,day);
int8_t result = findDataFile(year,month,day);
if(result){
/*存在该目录下的非空文件*/
char filename[32];
sprintf(filename,"%c:/%d/%02d/%02d.txt",FILE_LETTER,year,month,day);
QFILE file_fd = ql_fopen(filename,"rb");
readDataFromFile(file_fd, &global_file_data_p);
//重置全局变量“当前id”“结束计数”, 关闭文件
ql_fclose(file_fd);
current_id =0;
end_count = 0;
/*根据数据设置控件*/
//计算总页数设置spinbox页数label以及spinbox范围
uint8_t page_count = global_file_data_p.id_count/8 + 1;
lv_label_set_text_fmt(screen3_spinbox_dsc_label,"/ %d",page_count);
lv_spinbox_set_range(screen3_spinbox, 1,page_count);
//LV_LOG_USER("different id num is: %d, page_count = %d\n", global_file_data_p.id_count,page_count);
//根据当前页数和总页数设置label的数据
uint8_t current_page = lv_spinbox_get_value(screen3_spinbox);
setDataToTables(&global_file_data_p,current_page,page_count);
}
else{
LOG_INFO(" findDataFile result = %d",result);
//将显示最大页数的label设为NA
lv_label_set_text_fmt(screen3_spinbox_dsc_label,"N/A");
//取消spinbox翻页btn和手动设置页数btn的可点击flag
lv_obj_clear_flag(btn_page_plus,LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(btn_page_reduce,LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(scrren3_spinbox_btn,LV_OBJ_FLAG_CLICKABLE);
lv_obj_t * tables[8] = {data_table1,data_table2,data_table3,data_table4,data_table5,data_table6,data_table7,data_table8};
for(int i = 0; i < 8; i++){
lv_obj_clear_flag(tables[i],LV_OBJ_FLAG_CLICKABLE);
}
}
#endif /*#if 1*/
}

464
EC600U_lvgl/ui/ui_screen1.c Normal file
View File

@ -0,0 +1,464 @@
#include "../inc/ui/ui_screen1.h"
#include "stdio.h"
#include "stdlib.h"
#include "osi_api.h"
#include "../../EC600U_uart1/ModbusM.h"
#include "ql_log.h"
#include "time.h"
#include "../inc/ui/ui.h"
#define LOG_INFO(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "ui_screen1", msg, ##__VA_ARGS__)
#define DEPTH_REG_ADDR (12)
#define gWORD_SIZE (8192)
lv_obj_t * ui_Screen1;//用于切屏
lv_obj_t * current_bar; //显示电流的bar
lv_obj_t * current_value;
lv_obj_t * SIM_card_label; //sim卡状态
lv_obj_t * gnet_dsc_label; //4G描述label
lv_obj_t * g_gnet_label; //4G状态label
lv_obj_t * gps_level_label;//gps状态
lv_obj_t * net_connect_label;//网络连接状态
lv_obj_t * server_connect_label;//服务器连接状态
lv_obj_t * screen1_led;//用于更改led颜色
lv_obj_t * screen1_play_btn_label;//用于更改按钮的图标
lv_obj_t * screen1_measurements_label1;//速度
lv_obj_t * screen1_measurements_label2;//时间(未知)
lv_obj_t * screen1_measurements_label3;//深度
lv_obj_t * screen1_measurements_label4;//瞬时1
lv_obj_t * screen1_measurements_label5;//瞬时2
lv_obj_t * screen1_measurements_label6;//10cm1未知
lv_obj_t * screen1_measurements_label7;//10cm2未知
lv_obj_t * screen1_measurements_label8;//累计1
lv_obj_t * screen1_measurements_label9;//累计2
extern lv_obj_t * ui_Screen2;
extern lv_obj_t * ui_Screen4;
#if !TEST
extern uint16_t gWordVar[];
user_data_t g_ui_user_data;
#endif
MACHINE_WORK_STATE current_wort_state = STOP;
/*********************************************************************************************/
lv_timer_t * ui_screen1_data_timer;
lv_timer_t * ui_screen1_led_timer;
int8_t led_state = 1;//1:on -1:off
static void ui_screen1_led_timer_cb(lv_timer_t *e)
{
if(current_wort_state == WORK){
lv_led_toggle(screen1_led);
led_state = led_state * (-1);
}
else{
if(led_state == -1){
lv_led_on(screen1_led);
}
}
}
uint8_t test_data = 0;
static void ui_screen1_data_timer_cb(lv_timer_t *e)
{
#if TEST
test_data ++;
lv_label_set_text_fmt(screen1_measurements_label1, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label2, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label3, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label4, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label5, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label6, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label7, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label8, "%d", test_data);
lv_label_set_text_fmt(screen1_measurements_label9, "%d", test_data);
#else
char buf[12] = {0};
memset(buf, 0, 12);
sprintf(buf, "%.1f", g_ui_user_data.speed / 10.0);
lv_label_set_text_fmt(screen1_measurements_label1, "%s", buf);
/*时间*/
memset(buf, 0, 12);
sprintf(buf, "%d", g_ui_user_data.one_pile_work_time);
lv_label_set_text_fmt(screen1_measurements_label2, "%s", buf);
memset(buf, 0, 12);
sprintf(buf, "%.3f", g_ui_user_data.depth / 1000.0);
lv_label_set_text_fmt(screen1_measurements_label3, "%s", buf); // mm转换成cm
memset(buf, 0, 12);
sprintf(buf, "%.2f", g_ui_user_data.ss_1 / 100.0); // 1通道瞬时流量
lv_label_set_text_fmt(screen1_measurements_label4, "%s", buf);
memset(buf, 0, 12);
sprintf(buf, "%.2f", g_ui_user_data.ss_2 / 100.0); // 2通道瞬时流量
lv_label_set_text_fmt(screen1_measurements_label5, "%s", buf);
/*10cm流量1&2*/
memset(buf, 0, 12);
sprintf(buf, "%.2f", g_ui_user_data.accumulate1 / 100.0); // 10cm累计流量1
lv_label_set_text_fmt(screen1_measurements_label6, "%s", buf);
memset(buf, 0, 12);
sprintf(buf, "%.2f", g_ui_user_data.accumulate2 / 100.0);
lv_label_set_text_fmt(screen1_measurements_label7, "%s", buf);
memset(buf, 0, 12);
sprintf(buf, "%.1f", g_ui_user_data.ll_1 / 100.0); //1通道累计流量
lv_label_set_text_fmt(screen1_measurements_label8, "%s", buf);
memset(buf, 0, 12);
sprintf(buf, "%.1f", g_ui_user_data.ll_2 / 100.0);
lv_label_set_text_fmt(screen1_measurements_label9, "%s", buf);
lv_bar_set_value(current_bar,g_ui_user_data.Ia,LV_ANIM_OFF);
memset(buf, 0, 12);
sprintf(buf, "%.2f", g_ui_user_data.Ia / 10.0);
lv_label_set_text_fmt(current_value,"%s",buf);
#endif
}
/*********************************************************************************************/
bool start_pause_flag = 0;
void play_btn_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("play btn clicked");
/*stop -> work*/
if((current_wort_state == STOP) && (start_pause_flag == 0)){
LOG_INFO("(current_wort_state == STOP) && (start_pause_flag == 0)");
/*将数据全部置0*/
lv_obj_t * obj[9] = {
screen1_measurements_label1,
screen1_measurements_label2,
screen1_measurements_label3,
screen1_measurements_label4,
screen1_measurements_label5,
screen1_measurements_label6,
screen1_measurements_label7,
screen1_measurements_label8,
screen1_measurements_label9,
};
for(uint8_t i = 0; i < 9; i++){
lv_label_set_text(obj[i],"0");
}
#if !TEST
/*发送启动命令*/
gWordVar[DEPTH_REG_ADDR] = ((gWordVar[DEPTH_REG_ADDR] & 0x00ff) | WORK);
LOG_INFO("zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1) res = %d",zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1));
#else
test_data = 0;
#endif
/*启动定时器,开始显示监测数据*/
lv_timer_resume(ui_screen1_data_timer);
/*更新当前工作状态*/
current_wort_state = WORK;
/*开始按键变成暂停按键,并更新状态*/
start_pause_flag = 1;
lv_led_set_color(screen1_led,lv_color_hex(THEME_COLOR_SPRING_GREEN_DEF));
lv_label_set_text(screen1_play_btn_label,LV_SYMBOL_PAUSE);
}
/*stop -> pause*/
else if((current_wort_state == STOP) && (start_pause_flag == 1)){
/*不做反应*/
}
/*pause -> work*/
else if((current_wort_state == PAUSE) && (start_pause_flag == 0)){
LOG_INFO("(current_wort_state == PAUSE) && (start_pause_flag == 0)");
#if !TEST
/*发送启动命令*/
gWordVar[DEPTH_REG_ADDR] = ((gWordVar[DEPTH_REG_ADDR] & 0x00ff) | WORK);
LOG_INFO("zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1) res = %d",zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1));
#endif
/*启动定时器,继续监测数据*/
lv_timer_resume(ui_screen1_data_timer);
/*更新当前工作状态*/
current_wort_state = WORK;
/*开始按键变成暂停按键,并更新状态标志*/
lv_led_set_color(screen1_led,lv_color_hex(THEME_COLOR_SPRING_GREEN_DEF));
lv_label_set_text(screen1_play_btn_label,LV_SYMBOL_PAUSE);
start_pause_flag = 1;
}
/*pause -> pause*/
else if((current_wort_state == PAUSE) && (start_pause_flag == 1)){
/*不做反应*/
}
/*work -> pause*/
else if((current_wort_state == WORK) && (start_pause_flag == 1)){
LOG_INFO("(current_wort_state == WORK) && (start_pause_flag == 1)");
#if !TEST
/*发送暂停命令*/
gWordVar[DEPTH_REG_ADDR] = ((gWordVar[DEPTH_REG_ADDR] & 0x00ff) | PAUSE);
LOG_INFO("zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1) res = %d",zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1));
#endif
/*暂停定时器*/
lv_timer_pause(ui_screen1_data_timer);
/*更新当前工作状态*/
current_wort_state = PAUSE;
/*暂停按键变成开始按键,并更新状态标志*/
lv_led_set_color(screen1_led,lv_palette_main(LV_PALETTE_YELLOW));
lv_label_set_text(screen1_play_btn_label,LV_SYMBOL_PLAY);
start_pause_flag = 0;
}
/*work -> work*/
else if((current_wort_state == WORK) && (start_pause_flag == 0)){
/*不做反应*/
}
}
}
void stop_btn_event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("stop btn clicked");
/*stop -> stop*/
if(current_wort_state == STOP){
/*不做处理*/
}
else{
LOG_INFO("current_wort_state != STOP");
lv_timer_pause(ui_screen1_data_timer);
lv_led_set_color(screen1_led,lv_palette_main(LV_PALETTE_RED));
lv_label_set_text(screen1_play_btn_label,LV_SYMBOL_PLAY);
start_pause_flag = 0;
#if !TEST
/*发送终止信号*/
gWordVar[DEPTH_REG_ADDR] = ((gWordVar[DEPTH_REG_ADDR] & 0x00ff) | STOP);
LOG_INFO("zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1) res = %d",zb_ModBusWordWriteHook(DEPTH_REG_ADDR,1));
/*清空全局变量*/
memset(&g_ui_user_data, 0x00, sizeof(g_ui_user_data));
#endif
current_wort_state = STOP;
}
}
}
/*********************************************************************************************/
void ui_screen1_init(void)
{
#if !TEST
memset(&g_ui_user_data, 0x00, sizeof(g_ui_user_data));
#endif
//sim卡标识
SIM_card_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(SIM_card_label, &system_status_icon_24, 0);
lv_label_set_text(SIM_card_label, ICON_SIM_OFF_STATE_24);
lv_obj_set_pos(SIM_card_label,35,5);
//4G
gnet_dsc_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(gnet_dsc_label, &lv_font_montserrat_14, 0);
lv_label_set_text(gnet_dsc_label, "4G");
lv_obj_set_pos(gnet_dsc_label,65,10);
g_gnet_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(g_gnet_label, &system_status_icon_24, 0);
lv_label_set_text(g_gnet_label, ICON_GNET_CSQ_4_24);
lv_obj_set_pos(g_gnet_label,90,7);
//网络连接
net_connect_label = lv_label_create(ui_Screen1);
lv_label_set_text(net_connect_label, ICON_NET_WARN_STATE_24);
lv_obj_set_style_text_font(net_connect_label, &system_status_icon_24, 0);
lv_obj_set_pos(net_connect_label,120,5);
//服务器连接
server_connect_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(server_connect_label, &system_status_icon_24, 0);
lv_label_set_text(server_connect_label, ICON_SERVER_CON_NO_STATE_24);
lv_obj_set_pos(server_connect_label,345,5);
//gps
gps_level_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(gps_level_label, &system_status_icon_24, 0);
lv_label_set_text(gps_level_label, ICON_GPS_CSQ_24);
lv_obj_set_pos(gps_level_label,415,5);
lv_obj_t * screen1_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(screen1_label, &lv_font_montserrat_14, 0);
lv_label_set_text(screen1_label, "GPS");
lv_obj_set_pos(screen1_label,380,10);
//电流bar
current_bar = lv_bar_create(ui_Screen1);
lv_obj_set_size(current_bar, 32, 234 - 50);
lv_bar_set_mode(current_bar, LV_BAR_MODE_RANGE);
lv_obj_align(current_bar,LV_ALIGN_LEFT_MID, 10, 0);
lv_obj_set_style_pad_all(current_bar, 0, 0);
lv_bar_set_range(current_bar, 0, 2000);
lv_bar_set_value(current_bar, 150, LV_ANIM_OFF);
screen1_label = lv_label_create(ui_Screen1);
lv_label_set_text(screen1_label,"电流");
lv_obj_set_style_text_font(screen1_label, &ui_font_12, 0);
lv_obj_align_to(screen1_label, current_bar, LV_ALIGN_OUT_TOP_MID, 0, 0);
current_value = lv_label_create(ui_Screen1);
lv_label_set_text(screen1_label, "150.0");
lv_obj_set_style_text_font(screen1_label, &lv_font_montserrat_16, 0);
lv_obj_align_to(screen1_label, current_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
//显示实时数据区域
int size[9][2] = {
{100, 67},{130,67},{130,67},
{180,67},{180,67},
{180,67},{180,67},
{180,67},{180,67}
};
int align[9][2] = {
{-140,33},{-22,33},{110,33},
{-100,103},{85,103},
{-100,173},{85,173},
{-100,243},{85,243}
};
lv_obj_t * obj_i = NULL;
const char *g_measurements_dsc[9] = {
"速度(m/min)", "时间(min:s)" , "深度(m)",
"瞬时流量1(L/m)","瞬时流量2(L/m)",
"10CM流量1(L)", "10CM流量2(L)",
"累计流量1(L)", "累计流量2(L)"
};
for(int i = 0; i < 9; i++){
//创建子区域
obj_i = lv_obj_create(ui_Screen1);
lv_obj_clear_flag(obj_i, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_align(obj_i, LV_ALIGN_TOP_MID, align[i][0], align[i][1]);
lv_obj_set_size(obj_i,size[i][0],size[i][1]);
//创建描述label
screen1_label = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_label, &ui_font_12, 0);
lv_label_set_text(screen1_label, g_measurements_dsc[i]);
lv_obj_align(screen1_label, LV_ALIGN_TOP_MID, 0, -12);
//创建数据label
if(i == 0){
screen1_measurements_label1 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label1, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label1, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label1, "0");
}
else if(i == 1){
screen1_measurements_label2 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label2, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label2, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label2, "0");
}
else if(i == 2){
screen1_measurements_label3 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label3, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label3, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label3, "0");
}
else if(i == 3){
screen1_measurements_label4 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label4, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label4, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label4, "0");
}
else if(i == 4){
screen1_measurements_label5 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label5, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label5, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label5, "0");
}
else if(i == 5){
screen1_measurements_label6 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label6, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label6, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label6, "0");
}
else if(i == 6){
screen1_measurements_label7 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label7, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label7, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label7, "0");
}
else if(i == 7){
screen1_measurements_label8 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label8, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label8, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label8, "0");
}
else if(i == 8){
screen1_measurements_label9 = lv_label_create(obj_i);
lv_obj_set_style_text_font(screen1_measurements_label9, &lv_font_montserrat_42, 0);
lv_obj_align(screen1_measurements_label9, LV_ALIGN_CENTER, 0, 12);
lv_label_set_text(screen1_measurements_label9, "0");
}
}
//创建右侧按钮与状态灯
screen1_led = lv_led_create(ui_Screen1);
lv_led_set_color(screen1_led,lv_palette_main(LV_PALETTE_GREY));
lv_obj_set_size(screen1_led,45,45);
lv_obj_align(screen1_led,LV_ALIGN_TOP_RIGHT,0,40);
// 添加停止按钮
lv_obj_t * screen1_stop_btn = lv_btn_create(ui_Screen1);
lv_obj_set_size(screen1_stop_btn,58,58);
lv_obj_set_style_bg_color(screen1_stop_btn,lv_color_hex(0xffffff),0);
lv_obj_set_pos(screen1_stop_btn,420,240);
screen1_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(screen1_label, &lv_font_montserrat_20, 0);
lv_label_set_text(screen1_label, LV_SYMBOL_STOP);
lv_obj_set_pos(screen1_label,440,260);
// 添加play按钮
lv_obj_t * screen1_play_btn = lv_obj_create(ui_Screen1);
lv_obj_set_size(screen1_play_btn,58,58);
lv_obj_set_style_bg_color(screen1_play_btn,lv_color_hex(0xffffff),0);
lv_obj_set_pos(screen1_play_btn,420,170);
screen1_play_btn_label = lv_label_create(ui_Screen1);
lv_obj_set_style_text_font(screen1_play_btn_label, &lv_font_montserrat_20, 0);
lv_label_set_text(screen1_play_btn_label, LV_SYMBOL_PLAY);
lv_obj_set_pos(screen1_play_btn_label,440,190);
//点击切换状态灯颜色
lv_obj_add_event_cb(screen1_stop_btn, stop_btn_event_cb, LV_EVENT_ALL, NULL);//停止btn
lv_obj_add_event_cb(screen1_play_btn, play_btn_event_cb, LV_EVENT_ALL, NULL);//开始btn
//创建定时器,定时刷新数据
if (screen1_measurements_label1 && screen1_measurements_label2 &&
screen1_measurements_label3 && screen1_measurements_label4 &&
screen1_measurements_label5 && screen1_measurements_label6 &&
screen1_measurements_label7 && screen1_measurements_label8 &&
screen1_measurements_label9)
{
ui_screen1_data_timer = lv_timer_create(ui_screen1_data_timer_cb,100,NULL);
lv_timer_pause(ui_screen1_data_timer);
} else {
LOG_INFO(" create data timer cb fail");
}
/*led闪烁定时器*/
ui_screen1_led_timer = lv_timer_create(ui_screen1_led_timer_cb,100,NULL);
}

587
EC600U_lvgl/ui/ui_screen2.c Normal file
View File

@ -0,0 +1,587 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../inc/ui/ui.h"
#include "../inc/ui/ui_screen2.h"
#include "../../EC600U_uart1/ModbusM.h"
#include "ql_log.h"
#define LOG_INFO(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "ui_screen2", msg, ##__VA_ARGS__)
/****************************************************************************************************************************/
lv_obj_t *ui_Screen2; // 用来切屏
lv_obj_t *screen2_keyboard; // 用来创建btnm
lv_obj_t *screen2_btnm_textarea; // 用来创建textarea
lv_obj_t *screen2_cal_textareas[4] = {NULL, NULL, NULL, NULL}; // 1通道4ma1通道20ma2通道4ma2通道20ma
lv_obj_t *screen2_flow_input_type_dropdown; // 输入模式
// 最小切除流量1,最小切除流量2,电流1最小AD转换流量,电流1最大AD转换流量,电流2最小AD转换流量,电流2最大AD转换流量,脉冲系数1,脉冲系数2
lv_obj_t *screen2_flow_textareas[8];
// 编码器端口,编码器系数分子,编码器系数分母,最小深度mm,最大深度mm,采样深度mm,默认深度偏移,最小有效深度,允许换桩深度,行走电机开启电流,行走电机关闭电流,持续时间,持续时间,行走电流通道
lv_obj_t *screen2_depth_textareas[14];
lv_obj_t *screen2_depth_input_type_dropdown; // 0正交 1:正交反向 2:方向脉冲 3:方向脉冲反向
lv_obj_t *screen2_dropdown_list;
cal_4_20ma_t cal_data_struct;
flow_config_t flow_data_struct;
depth_config_t depth_data_struct;
extern lv_obj_t *ui_Screen3;
#if !TEST
extern uint16_t gWordVar[];
#endif
check_t screen2_cal_check_infos[4] = {
{0, CAL1_AD_4MA_ADDR, TYPE_UINT16, 0, 65535, 0},
{1, CAL1_AD_20MA_ADDR, TYPE_UINT16, 0, 65535, 0},
{2, CAL2_AD_4MA_ADDR, TYPE_UINT16, 0, 65535, 0},
{3, CAL2_AD_20MA_ADDR, TYPE_UINT16, 0, 65535, 0},
};
check_t screen2_flow_check_infos[8] = {
{4, FLOW_MIN_FLOW1_ADDR, TYPE_INT16, 0, 32767, -32768},
{5, FLOW_MIN_FLOW2_ADDR, TYPE_INT16, 0, 32767, -32768},
{6, FLOW_AD_CAL1_FLOW_MIN_ADDR, TYPE_INT16, 0, 32767, -32768},
{7, FLOW_AD_CAL1_FLOW_MAX_ADDR, TYPE_INT16, 0, 32767, -32768},
{8, FLOW_AD_CAL2_FLOW_MIN_ADDR, TYPE_INT16, 0, 32767, -32768},
{9, FLOW_AD_CAL2_FLOW_MAX_ADDR, TYPE_INT16, 0, 32767, -32768},
{10, FLOW_PAUSE_COEF1_ADDR, TYPE_UINT16, 0, 65535, 0},
{11, FLOW_PAUSE_COEF2_ADDR, TYPE_UINT16, 0, 65535, 0},
};
check_t screen2_depth_check_infos[14] = {
{12, DEPTH_PORT_ADDR, TYPE_UINT8, 0, 1, 0},//深度端口只有两个
{13, DEPTH_ENC_N_ADDR, TYPE_UINT16, 0, 65535, 0},
{14, DEPTH_ENC_M_ADDR, TYPE_UINT16, 0, 65535, 0},
{15, DEPTH_MIN_DEPTH_ADDR, TYPE_INT16, 0, 32767, -32768},
{16, DEPTH_MAX_DEPTH_ADDR, TYPE_INT16, 0, 32767, -32768},
{17, DEPTH_SAMPLE_DEPTH_ADDR, TYPE_INT16, 0, 32767, -32768},
{18, DEPTH_OFFSET_ADDR, TYPE_INT16, 0, 32767, -32768},
{19, DEPTH_MIN_VALID_ADDR, TYPE_INT16, 0, 32767, -32768},
{20, DEPTH_INC_PILE_ADDR, TYPE_INT16, 0, 32767, -32768},
{21, DEPTH_CUR_ON_ADDR, TYPE_UINT16, 0, 65535, 0},
{22, DEPTH_CUR_OFF_ADDR, TYPE_UINT16, 0, 65535, 0},
{23, DEPTH_MOVE_ON_DUR_ADDR, TYPE_UINT16, 0, 65535, 0},
{24, DEPTH_MOVE_OFF_DUR_ADDR, TYPE_UINT16, 0, 65535, 0},
{25, DEPTH_MOVE_CUR_CH_ADDR, TYPE_UINT16, 0, 65535, 0},
};
/****************************************************************************************************************************/
void screen2_cal_SAVE_btn_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("screen2_cal_SAVE_btn_event clicked");
#if !TEST
/*通过modbus发送保存cal结构体的命令*/
gWordVar[CAL_4_20MA_ADDR] = SAVE_CMD;
zb_ModBusWordWriteHook(CAL_4_20MA_ADDR,1);
#endif
}
}
void screen2_cal_DEFAULT_btn_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("screen2_cal_DEFAULT_btn_event clicked");
lv_textarea_set_text(screen2_cal_textareas[0], "12740");
lv_textarea_set_text(screen2_cal_textareas[1], "63845");
lv_textarea_set_text(screen2_cal_textareas[2], "12760");
lv_textarea_set_text(screen2_cal_textareas[3], "63953");
/*发送默认配置信息*/
#if !TEST
gWordVar[CAL_4_20MA_ADDR] = 0;
gWordVar[CAL1_AD_4MA_ADDR] = 12740;
gWordVar[CAL1_AD_20MA_ADDR] = 63845;
gWordVar[CAL2_AD_4MA_ADDR] = 12760;
gWordVar[CAL2_AD_20MA_ADDR] = 63953;
zb_ModBusWordWriteHook(CAL_4_20MA_ADDR,5);
#endif
}
}
/****************************************************************************************************************************/
void screen2_flow_SAVE_btn_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
// lv_obj_t * obj = lv_event_get_target(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("screen2_flow_SAVE_btn_event clicked");
#if !TEST
/*通过modbus发送保存flow结构体的命令*/
gWordVar[FLOW_CONFIG_ADDR] = SAVE_CMD;
zb_ModBusWordWriteHook(FLOW_CONFIG_ADDR,1);
#endif
}
}
void screen2_flow_DEFAULT_btn_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("screen2_flow_DEFAULT_btn_event clicked");
char default_value[][6] = {"0", "0", "0", "10000", "0", "10000", "6944", "6944"};
for (int i = 0; i < 8; i++){
lv_textarea_set_text(screen2_flow_textareas[i], default_value[i]);
}
lv_dropdown_set_selected(screen2_flow_input_type_dropdown, 0);
#if !TEST
gWordVar[FLOW_CONFIG_ADDR] = 0;
gWordVar[FLOW_INPUT_TYPE_ADDR] = FLOW_INPUT_TYPE_MODE1;
gWordVar[FLOW_MIN_FLOW1_ADDR] = 0;
gWordVar[FLOW_MIN_FLOW2_ADDR] = 0;
gWordVar[FLOW_AD_CAL1_FLOW_MIN_ADDR] = 0;
gWordVar[FLOW_AD_CAL1_FLOW_MAX_ADDR] = 10000;
gWordVar[FLOW_AD_CAL2_FLOW_MIN_ADDR] = 0;
gWordVar[FLOW_AD_CAL2_FLOW_MAX_ADDR] = 10000;
gWordVar[FLOW_PAUSE_COEF1_ADDR] = 6944;
gWordVar[FLOW_PAUSE_COEF2_ADDR] = 6944;
zb_ModBusWordWriteHook(FLOW_CONFIG_ADDR,10);
#endif
}
}
/****************************************************************************************************************************/
void screen2_depth_SAVE_btn_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("screen2_depth_SAVE_btn_event clicked");
#if !TEST
/*通过modbus发送保存depth结构体的命令*/
gWordVar[DEPTH_CONFIG_ADDR] = SAVE_CMD;
zb_ModBusWordWriteHook(DEPTH_CONFIG_ADDR,1);
#endif
}
}
void screen2_depth_DEFAULT_btn_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
LOG_INFO("screen2_depth_DEFAULT_btn_event clicked");
/*将当前栏目的所有文本框都设为默认值*/
char depth_default_data[][6] = {"1", "1000", "640", "-100", "12000", "100", "-100", "1000", "-5000", "500", "100", "150", "150", "2"};
for (int i = 0; i < 14; i++)
{
lv_textarea_set_text(screen2_depth_textareas[i], depth_default_data[i]);
}
lv_dropdown_set_selected(screen2_depth_input_type_dropdown, 0);
/*发送默认配置信息*/
#if !TEST
gWordVar[DEPTH_CONFIG_ADDR] = 0;
gWordVar[DEPTH_INPUT_TYPE_ADDR] = ((gWordVar[DEPTH_INPUT_TYPE_ADDR] & 0x00ff) | DEPTH_INPUT_TYPE_MODE1);
gWordVar[DEPTH_PORT_ADDR] = ((gWordVar[DEPTH_INPUT_TYPE_ADDR] & 0xff00) | 1);
gWordVar[DEPTH_ENC_N_ADDR] = 1000;
gWordVar[DEPTH_ENC_M_ADDR] = 640;
gWordVar[DEPTH_MIN_DEPTH_ADDR] = -100;
gWordVar[DEPTH_MAX_DEPTH_ADDR] = 12000;
gWordVar[DEPTH_SAMPLE_DEPTH_ADDR] = 100;
gWordVar[DEPTH_OFFSET_ADDR] = -100;
gWordVar[DEPTH_MIN_VALID_ADDR] = 1000;
gWordVar[DEPTH_INC_PILE_ADDR] = -5000;
gWordVar[DEPTH_CUR_ON_ADDR] = 500;
gWordVar[DEPTH_CUR_OFF_ADDR] = 100;
gWordVar[DEPTH_MOVE_ON_DUR_ADDR] = 150;
gWordVar[DEPTH_MOVE_OFF_DUR_ADDR] = 150;
gWordVar[DEPTH_MOVE_CUR_CH_ADDR] = 2;
zb_ModBusWordWriteHook(DEPTH_CONFIG_ADDR,15);
#endif
}
}
/****************************************************************************************************************************/
/*选择流量输入模式选择后modbus发送对应值*/
void screen2_flow_input_type_dropdown_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_VALUE_CHANGED){
uint16_t id = lv_dropdown_get_selected(screen2_flow_input_type_dropdown);
LOG_INFO("id = %d",id);
#if !TEST
//4-20ma
if(id == 0){
gWordVar[FLOW_INPUT_TYPE_ADDR] = FLOW_INPUT_TYPE_MODE1;
}
//0-3.6k
else if(id == 1){
gWordVar[FLOW_INPUT_TYPE_ADDR] = FLOW_INPUT_TYPE_MODE2;
}
zb_ModBusWordWriteHook(FLOW_INPUT_TYPE_ADDR,1);
#endif
}
}
void list_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *obj = lv_event_get_target(e);
if (code == LV_EVENT_DELETE)
{
LV_LOG_USER("screen2_dropdown_list is deleted\n");
lv_obj_add_flag(screen2_depth_input_type_dropdown, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_event_cb(obj, list_event);
}
}
void screen2_depth_input_type_dropdown_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *obj = lv_event_get_target(e);
if (code == LV_EVENT_CLICKED)
{
lv_obj_clear_flag(screen2_depth_input_type_dropdown, LV_OBJ_FLAG_CLICKABLE);
screen2_dropdown_list = lv_dropdown_get_list(obj);
lv_obj_clear_flag(screen2_dropdown_list,LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_text_font(screen2_dropdown_list, &ui_font_16, 0);
lv_obj_add_event_cb(screen2_dropdown_list, list_event, LV_EVENT_ALL, NULL);
}
}
void screen2_depth_input_type_dropdown_event2(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_VALUE_CHANGED)
{
uint16_t id = lv_dropdown_get_selected(screen2_depth_input_type_dropdown);
LOG_INFO("id = %d",id);
#if !TEST
if(id == 0){//正交
gWordVar[DEPTH_INPUT_TYPE_ADDR] = ((gWordVar[DEPTH_INPUT_TYPE_ADDR] & 0x00ff) | DEPTH_INPUT_TYPE_MODE1);
}
else if(id == 1){//正交反向
gWordVar[DEPTH_INPUT_TYPE_ADDR] = ((gWordVar[DEPTH_INPUT_TYPE_ADDR] & 0x00ff) | DEPTH_INPUT_TYPE_MODE2);
}
else if(id == 2){//方向脉冲
gWordVar[DEPTH_INPUT_TYPE_ADDR] = ((gWordVar[DEPTH_INPUT_TYPE_ADDR] & 0x00ff) | DEPTH_INPUT_TYPE_MODE3);
}
else if(id == 3){//方向脉冲反向
gWordVar[DEPTH_INPUT_TYPE_ADDR] = ((gWordVar[DEPTH_INPUT_TYPE_ADDR] & 0x00ff) | DEPTH_INPUT_TYPE_MODE4);
}
zb_ModBusWordWriteHook(DEPTH_INPUT_TYPE_ADDR,1);
#endif
}
}
/****************************************************************************************************************************/
/*当点击回车时,获取显示文本框的值,*/
void screen2_btnm_input_data(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *keyboard = lv_event_get_target(e); // 获取键盘指针
check_t *check_info = (check_t *)lv_event_get_user_data(e); // 获取userdata
if (check_info != NULL)
{
//LOG_INFO("id = %d, addr = %d, type = %d, decimal_places = %d, max = %d, min = %d", check_info->id, check_info->addr, check_info->type, check_info->decimal_places, check_info->max_num, check_info->min_num);
}
else
{
LOG_INFO("check_info = NULL");
return;
}
if (code == LV_EVENT_VALUE_CHANGED)
{
/*将键盘的值实时显示在textarea上*/
int id = lv_btnmatrix_get_selected_btn(keyboard); // 获取点击的按钮的序号
const char *txt = lv_btnmatrix_get_btn_text(keyboard, id); // 获取点击的按钮的文本内容
// 当点击回车时
if (strcmp(txt, LV_SYMBOL_OK) == 0)
{
/*获取此时显示文本框的字符串值并转为整型*/
const char *str = lv_textarea_get_text(screen2_btnm_textarea);
//LOG_INFO("str = %s", str);
int num = 0;
if (check_info->type != TYPE_FLOAT)
{
num = atoi(str);
}
/*判断该值是否在定义域内*/
num = (num > check_info->max_num) ? check_info->max_num : ((num < check_info->min_num) ? check_info->min_num : num);
char correct_str[20] = {""};
itoa(num, correct_str, 10);
//LOG_INFO("correct_str = %s", correct_str);
/*将合适的值赋值给触发事件的文本框上*/
if ((check_info->id <= 3) && (check_info->id >= 0))
{
lv_textarea_set_text(screen2_cal_textareas[check_info->id], correct_str);
}
else if ((check_info->id >= 4) && (check_info->id <= 11))
{
lv_textarea_set_text(screen2_flow_textareas[check_info->id - 4], correct_str);
}
else if((check_info->id >= 12) && (check_info->id <= 25)){
lv_textarea_set_text(screen2_depth_textareas[check_info->id - 12], correct_str);
}
/*删除btnm和文本框的控件并将指针置NULL*/
lv_obj_del(screen2_btnm_textarea);
lv_obj_del(screen2_keyboard);
screen2_btnm_textarea = NULL;
screen2_keyboard = NULL;
/*将num赋值给gWrodVar里对应的寄存器地址并传输*/
#if !TEST
if(check_info->id == 12){
gWordVar[check_info->addr] = ((gWordVar[check_info->addr] & 0xff00) | (uint8_t)num);
}
else{
if(check_info->type == TYPE_INT16){
gWordVar[check_info->addr] = (int16_t)num;
}
else if(check_info->type == TYPE_UINT16){
gWordVar[check_info->addr] = (uint16_t)num;
}
}
zb_ModBusWordWriteHook(check_info->addr, 1);
#endif
}
}
}
/*当聚焦textarea时创建btnm和显示文本框关联两者并添加事件函数*/
void screen2_btnm_event(lv_event_t *e)
{
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t *obj = lv_event_get_target(e); // 获取触发事件的文本框的指针
check_t *check_info = lv_event_get_user_data(e);
if (check_info != NULL)
{
//LOG_INFO("id = %d, addr = %d, type = %d, decimal_places = %d, max = %d, min = %d", check_info->id, check_info->addr, check_info->type, check_info->decimal_places, check_info->max_num, check_info->min_num);
}
else
{
LOG_INFO("check_info = NULL");
return;
}
// 当聚焦textarea时
if (event_code == LV_EVENT_FOCUSED)
{
// 创建键盘和textarea
screen2_keyboard = lv_keyboard_create(ui_Screen2);
lv_obj_clear_flag(screen2_keyboard, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_set_size(screen2_keyboard, 480, 220);
lv_obj_align(screen2_keyboard, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_keyboard_set_mode(screen2_keyboard, LV_KEYBOARD_MODE_NUMBER);
screen2_btnm_textarea = lv_textarea_create(ui_Screen2);
lv_obj_set_size(screen2_btnm_textarea, 480, 70);
lv_obj_align_to(screen2_btnm_textarea, screen2_keyboard, LV_ALIGN_OUT_TOP_MID, 0, 0);
lv_obj_set_style_text_font(screen2_btnm_textarea, &lv_font_montserrat_42, 0);
lv_textarea_set_max_length(screen2_btnm_textarea, 5);
// 关联键盘和文本框
lv_keyboard_set_textarea(screen2_keyboard, screen2_btnm_textarea);
// 添加事件函数
lv_obj_add_event_cb(screen2_keyboard, screen2_btnm_input_data, LV_EVENT_VALUE_CHANGED, (void *)check_info); // 把触发事件的控件指针作为userdata传给输入回调函数
// 获取触发事件的文本框的值显示到显示文本框上
const char *textarea_value = lv_textarea_get_text(obj);
lv_textarea_set_text(screen2_btnm_textarea, textarea_value);
// 给文本框添加聚焦状态清除触发obj的聚焦状态
lv_obj_add_state(screen2_btnm_textarea, LV_STATE_FOCUSED);
lv_obj_clear_state(obj, LV_STATE_FOCUSED);
}
}
/****************************************************************************************************************************/
void ui_screen2_init(void)
{
lv_obj_t *screen2_tabview = lv_tabview_create(ui_Screen2, LV_DIR_TOP, 50);
lv_obj_align(screen2_tabview, LV_ALIGN_TOP_MID, 0, 30);
lv_obj_set_style_bg_color(screen2_tabview, lv_color_hex(0x000000), LV_PART_MAIN);
lv_obj_set_style_bg_opa(screen2_tabview, 20, LV_PART_MAIN);
lv_obj_set_style_text_font(screen2_tabview, &ui_font_16, 0);
uint8_t btns_color[2] = {LV_PALETTE_GREEN, LV_PALETTE_BLUE};
char btns_text[2][10] = {"保存", "默认"};
lv_obj_t *screen2_cal_tab = lv_tabview_add_tab(screen2_tabview, "电流");
lv_obj_t *screen2_flow_tab = lv_tabview_add_tab(screen2_tabview, "流量");
lv_obj_t *screen2_depth_tab = lv_tabview_add_tab(screen2_tabview, "深度");
// 在screen2_cal_tab上创建
uint8_t cal_align[4][2] = {{0, 0}, {0, 45}, {0, 90}, {0, 135}};
char cal_text[4][30] = {"1通道AD转换4ma", "1通道AD转换20ma", "2通道AD转换4ma", "2通道AD转换20ma"};
char cal_default_value[4][10] = {"12740", "63845", "12760", "63953"};
uint8_t textarea_size[4][2] = {{100, 40}, {100, 40}, {100, 40}, {100, 40}};
for (uint8_t i = 0; i < 4; i++)
{
lv_obj_t *label = lv_label_create(screen2_cal_tab);
lv_obj_set_size(label, 200, 40);
lv_obj_align(label, LV_ALIGN_TOP_LEFT, cal_align[i][0], cal_align[i][1]);
lv_obj_set_style_text_font(label, &ui_font_16, LV_PART_MAIN);
lv_label_set_text(label, cal_text[i]);
screen2_cal_textareas[i] = lv_textarea_create(screen2_cal_tab);
lv_obj_set_style_text_font(screen2_cal_textareas[i], &lv_font_montserrat_16, LV_PART_MAIN);
lv_obj_set_size(screen2_cal_textareas[i], textarea_size[i][0], textarea_size[i][1]);
lv_obj_align_to(screen2_cal_textareas[i], label, LV_ALIGN_OUT_RIGHT_MID, 0, -8);
lv_obj_clear_flag(screen2_cal_textareas[i], LV_OBJ_FLAG_SCROLLABLE);
lv_textarea_set_text(screen2_cal_textareas[i], cal_default_value[i]);
lv_obj_add_event_cb(screen2_cal_textareas[i], screen2_btnm_event, LV_EVENT_FOCUSED, &screen2_cal_check_infos[i]);
}
lv_obj_t *cal_btns[2] = {NULL, NULL};
int16_t cal_btns_pos[2][2] = {{290, 170}, {50, 170}};
for (uint8_t i = 0; i < 2; i++)
{
cal_btns[i] = lv_btn_create(screen2_cal_tab);
lv_obj_set_size(cal_btns[i], 140, 50);
lv_obj_set_pos(cal_btns[i], cal_btns_pos[i][0], cal_btns_pos[i][1]);
lv_obj_set_style_bg_color(cal_btns[i], lv_palette_main(btns_color[i]), 0);
lv_obj_set_style_opa(cal_btns[i], 150, 0);
lv_obj_t *label = lv_label_create(cal_btns[i]);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0x000000), 0);
lv_obj_set_style_text_font(label, &ui_font_16, 0);
lv_label_set_text(label, btns_text[i]);
}
lv_obj_add_event_cb(cal_btns[0], screen2_cal_SAVE_btn_event, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(cal_btns[1], screen2_cal_DEFAULT_btn_event, LV_EVENT_CLICKED, NULL);
// 在screen2_flow_tab创建的
lv_obj_t *flow_dd_label = lv_label_create(screen2_flow_tab);
lv_obj_set_size(flow_dd_label, 120, 40);
lv_obj_align(flow_dd_label, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_set_style_text_font(flow_dd_label, &ui_font_16, 0);
lv_label_set_text(flow_dd_label, "输入模式");
screen2_flow_input_type_dropdown = lv_dropdown_create(screen2_flow_tab);
lv_obj_set_size(screen2_flow_input_type_dropdown, 130, 40);
lv_obj_align_to(screen2_flow_input_type_dropdown, flow_dd_label, LV_ALIGN_OUT_RIGHT_MID, 0, -10);
lv_obj_set_style_text_font(screen2_flow_input_type_dropdown, &lv_font_montserrat_16, 0);
lv_dropdown_set_options(screen2_flow_input_type_dropdown, "4 ~ 20ma\n0 ~ 3.6k\n");
lv_obj_add_event_cb(screen2_flow_input_type_dropdown,screen2_flow_input_type_dropdown_cb,LV_EVENT_VALUE_CHANGED, NULL);
uint16_t flow_label_align[8][2] = {{0, 45}, {0, 90}, {0, 135}, {0, 180}, {0, 225}, {0, 270}, {0, 315}, {0, 360}};
char flow_label_text[8][35] = {"最小切除流量1", "最小切除流量2", "电流1最小AD转换流量", "电流1最大AD转换流量", "电流2最小AD转换流量", "电流2最大AD转换流量", "脉冲系数1", "脉冲系数2"};
uint8_t flow_textarea_size[8][2] = {{80, 40}, {80, 40}, {100, 40}, {100, 40}, {100, 40}, {100, 40}, {100, 40}, {100, 40}};
char flow_textarea_default_data[8][10] = {"0", "0", "0", "10000", "0", "10000", "6944", "6944"};
for (uint8_t i = 0; i < 8; i++)
{
lv_obj_t *label = lv_label_create(screen2_flow_tab);
lv_obj_set_size(label, 220, 40);
lv_obj_align(label, LV_ALIGN_TOP_LEFT, flow_label_align[i][0], flow_label_align[i][1]);
lv_obj_set_style_text_font(label, &ui_font_16, LV_PART_MAIN);
lv_label_set_text(label, flow_label_text[i]);
screen2_flow_textareas[i] = lv_textarea_create(screen2_flow_tab);
lv_obj_set_style_text_font(screen2_flow_textareas[i], &lv_font_montserrat_16, LV_PART_MAIN);
lv_obj_set_size(screen2_flow_textareas[i], flow_textarea_size[i][0], flow_textarea_size[i][1]);
lv_obj_align_to(screen2_flow_textareas[i], label, LV_ALIGN_OUT_RIGHT_MID, 0, -8);
lv_obj_clear_flag(screen2_flow_textareas[i], LV_OBJ_FLAG_SCROLLABLE);
lv_textarea_set_text(screen2_flow_textareas[i], flow_textarea_default_data[i]);
lv_obj_add_event_cb(screen2_flow_textareas[i], screen2_btnm_event, LV_EVENT_FOCUSED, &screen2_flow_check_infos[i]);
}
lv_obj_t *label = lv_label_create(screen2_flow_tab);
lv_label_set_text(label, "");
lv_obj_set_pos(label, 0, 465);
lv_obj_t *flow_btns[2] = {NULL, NULL};
int16_t flow_btns_pos[2][2] = {{290, 405}, {50, 405}};
for (uint8_t i = 0; i < 2; i++)
{
flow_btns[i] = lv_btn_create(screen2_flow_tab);
lv_obj_set_size(flow_btns[i], 140, 50);
lv_obj_set_pos(flow_btns[i], flow_btns_pos[i][0], flow_btns_pos[i][1]);
lv_obj_set_style_bg_color(flow_btns[i], lv_palette_main(btns_color[i]), 0);
lv_obj_set_style_opa(flow_btns[i], 150, 0);
lv_obj_t *label = lv_label_create(flow_btns[i]);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0x000000), 0);
lv_obj_set_style_text_font(label, &ui_font_16, 0);
lv_label_set_text(label, btns_text[i]);
}
lv_obj_add_event_cb(flow_btns[0], screen2_flow_SAVE_btn_event, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(flow_btns[1], screen2_flow_DEFAULT_btn_event, LV_EVENT_CLICKED, NULL);
// 在screen2_depth_tab创建的
lv_obj_t *depth_dd_label = lv_label_create(screen2_depth_tab);
lv_obj_set_size(depth_dd_label, 120, 40);
lv_obj_align(depth_dd_label, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_set_style_text_font(depth_dd_label, &ui_font_16, 0);
lv_label_set_text(depth_dd_label, "输入模式");
screen2_depth_input_type_dropdown = lv_dropdown_create(screen2_depth_tab);
lv_obj_set_size(screen2_depth_input_type_dropdown, 160, 40);
lv_obj_align_to(screen2_depth_input_type_dropdown, depth_dd_label, LV_ALIGN_OUT_RIGHT_MID, 0, -10);
lv_obj_set_style_text_font(screen2_depth_input_type_dropdown, &ui_font_16, 0);
lv_dropdown_set_options(screen2_depth_input_type_dropdown, "正交\n正交反向\n方向脉冲\n方向脉冲反向\n");
lv_obj_add_event_cb(screen2_depth_input_type_dropdown, screen2_depth_input_type_dropdown_event, LV_EVENT_ALL, NULL);//让下拉列表可以显示汉字
lv_obj_add_event_cb(screen2_depth_input_type_dropdown, screen2_depth_input_type_dropdown_event2, LV_EVENT_VALUE_CHANGED, NULL);//选择后进行参数设置
uint16_t depth_label_align[14][2] = {{0, 45}, {0, 90}, {0, 135}, {0, 180}, {0, 225}, {0, 270}, {0, 315}, {0, 360}, {0, 405}, {0, 450}, {0, 495}, {0, 540}, {0, 595}, {0, 640}};
char depth_label_text[14][30] = {"编码器端口", "编码器系数分子", "编码器系数分母", "最小深度mm", "最大深度mm", "采样深度mm", "默认深度偏移", "最小有效深度", "允许换桩深度", "行走电机开启电流", "行走电机关闭电流", "moveon持续时间", "moveoff持续时间", "行走电流通道"};
char depth_default_data[14][10] = {"1", "1000", "640", "-100", "12000", "100", "-100", "1000", "-5000", "500", "100", "150", "150", "2"};
for (uint8_t i = 0; i < 14; i++)
{
lv_obj_t *label = lv_label_create(screen2_depth_tab);
lv_obj_set_size(label, 200, 40);
lv_obj_align(label, LV_ALIGN_TOP_LEFT, depth_label_align[i][0], depth_label_align[i][1]);
lv_obj_set_style_text_font(label, &ui_font_16, LV_PART_MAIN);
lv_label_set_text(label, depth_label_text[i]);
screen2_depth_textareas[i] = lv_textarea_create(screen2_depth_tab);
lv_obj_set_style_text_font(screen2_depth_textareas[i], &lv_font_montserrat_16, LV_PART_MAIN);
lv_obj_set_size(screen2_depth_textareas[i], 100, 40);
lv_obj_align_to(screen2_depth_textareas[i], label, LV_ALIGN_OUT_RIGHT_MID, 0, -8);
lv_obj_clear_flag(screen2_depth_textareas[i], LV_OBJ_FLAG_SCROLLABLE);
lv_textarea_set_text(screen2_depth_textareas[i], depth_default_data[i]);
lv_obj_add_event_cb(screen2_depth_textareas[i], screen2_btnm_event, LV_EVENT_FOCUSED, &screen2_depth_check_infos[i]);
}
label = lv_label_create(screen2_depth_tab);
lv_label_set_text(label, "");
lv_obj_set_pos(label, 0, 745);
lv_obj_t *depth_btns[2] = {NULL, NULL};
int16_t depth_btns_pos[2][2] = {{290, 685}, {50, 685}};
for (uint8_t i = 0; i < 2; i++)
{
depth_btns[i] = lv_btn_create(screen2_depth_tab);
lv_obj_set_size(depth_btns[i], 140, 50);
lv_obj_set_pos(depth_btns[i], depth_btns_pos[i][0], depth_btns_pos[i][1]);
lv_obj_set_style_bg_color(depth_btns[i], lv_palette_main(btns_color[i]), 0);
lv_obj_set_style_opa(depth_btns[i], 150, 0);
lv_obj_t *label = lv_label_create(depth_btns[i]);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0x000000), 0);
lv_obj_set_style_text_font(label, &ui_font_16, 0);
lv_label_set_text(label, btns_text[i]);
}
lv_obj_add_event_cb(depth_btns[0], screen2_depth_SAVE_btn_event, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(depth_btns[1], screen2_depth_DEFAULT_btn_event, LV_EVENT_CLICKED, NULL);
}

627
EC600U_lvgl/ui/ui_screen3.c Normal file
View File

@ -0,0 +1,627 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "../inc/ui/ui_screen3.h"
#include "../inc/ui/ui_screen3_chart.h"
#include "../inc/ui/fs_function.h"
lv_obj_t * ui_Screen3;//用来切屏
lv_obj_t * screen3_calender;
lv_obj_t * screen3_calender_dsc_label; //用来设置选中日期
lv_obj_t * btn_page_plus; //用来设置是否可选中
lv_obj_t * btn_page_reduce;//用来设置是否可选中
//用来设置数据
lv_obj_t * data_table1;
lv_obj_t * data_table2;
lv_obj_t * data_table3;
lv_obj_t * data_table4;
lv_obj_t * data_table5;
lv_obj_t * data_table6;
lv_obj_t * data_table7;
lv_obj_t * data_table8;
lv_obj_t * screen3_spinbox;
lv_obj_t * screen3_spinbox_dsc_label;
lv_obj_t * screen3_spinbox_label_textarea;
lv_obj_t * btnm;
lv_obj_t * scrren3_spinbox_btn;
lv_obj_t * calendar_dropdown_year;
lv_obj_t * calendar_dropdown_month;
//
lv_obj_t * chart;
lv_obj_t * chart_screen;
lv_chart_series_t * ser1;
lv_chart_series_t * ser2;
lv_chart_series_t * ser3;
lv_chart_series_t * ser4;
lv_obj_t * primary_y_label;
lv_obj_t * secondry_y_label;
lv_obj_t * chart_change_btn_label;
extern lv_obj_t * ui_Screen2;
extern lv_obj_t * ui_Screen4;
extern int current_id;
extern int end_count;
struct Return_Data global_file_data_p;
struct ChartData chartdata;//存储chart数据
uint8_t this_date_have_data = 0; //判断是否需要重新读取文件
uint16_t screen3_spinbox_btnm_teatarea_flag = 0;
uint8_t user_first_see_calendar_or_change_date = 1;
lv_calendar_date_t highlighted_days[31];
uint8_t highlighted_days_cnt = 0;
void ui_Screen3_calendar_set_highlight(void)
{
//通过日期获取路径
char year[10];
char month[10];
char filedir[20];
lv_dropdown_get_selected_str(calendar_dropdown_year, year, sizeof(year));
lv_dropdown_get_selected_str(calendar_dropdown_month, month, sizeof(month));
sprintf(filedir,"SD:/%s/%s",year,month);
//printf("%s\n",filedir);
uint16_t a = atoi(year);
int8_t b = atoi(month);
//printf("%d.%02d\n",a,b);
//lv_calendar_date_t highlighted_days[30];
for(int i = 0; i < 31; i++){
highlighted_days[i].year = a;
highlighted_days[i].month = b;
highlighted_days[i].day = 0;
}
//printf("%d.%02d\n",highlighted_days[0].year,highlighted_days[0].month);
char filenames[30][7];
for (int i = 0; i < 31; i++) {
sprintf(filenames[i], "%02d.txt", i+1);
}
//打开文件读取目录
QDIR * dir = ql_opendir(filedir);
if(dir != NULL){
highlighted_days_cnt = 0;
while(1){
qdirent *dirinfo = ql_readdir(dir);
if(dirinfo == NULL){
break;
}
for( int i = 0; i < 31; i++){
if(strcmp(dirinfo->d_name, filenames[i]) == 0){
highlighted_days[highlighted_days_cnt].day = atoi(filenames[i]);
highlighted_days_cnt++;
break;
}
}
}
lv_calendar_set_highlighted_dates(screen3_calender, highlighted_days, highlighted_days_cnt);//高亮对应的日期
ql_closedir(dir);
}
// else{
// LV_LOG_USER("no date need highlight\n");
// }
}
//点击使spinbox数值+1
void lv_spinbox_increment_event_cb(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
//如果可以点击进入回调函数,代表一定有数据
LV_LOG_USER("increment the data");
lv_spinbox_increment(screen3_spinbox);
//根据增加后的页数设置table数据
uint8_t current_page = (uint8_t)lv_spinbox_get_value(screen3_spinbox);
if(current_page <= 0){
return;
}
uint8_t page_count = global_file_data_p.id_count/8 + 1;
LV_LOG_USER("page:%d page_count:%d\n",current_page,page_count);
setDataToTables(&global_file_data_p,current_page,page_count);
}
}
//点击使spinbox数值-1
void lv_spinbox_decrement_event_cb(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
//LV_LOG_USER("decrement the data");
lv_spinbox_decrement(screen3_spinbox);
//根据增加后的页数设置table数据
uint8_t current_page = (uint8_t)lv_spinbox_get_value(screen3_spinbox);
if(current_page <= 0){
return;
}
uint8_t page_count = global_file_data_p.id_count/8 + 1;
//LV_LOG_USER("page:%d page_count:%d\n",current_page,page_count);
setDataToTables(&global_file_data_p,current_page,page_count);
}
}
//使用keyborad对textarea输入数字按下回车按钮后隐藏textarea和keyboard并将数字设置到spinbox上
void screen3_textarea_keyboard_event_cb(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * target = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
//当能进入这个回调说明spinbox btn 可以点击,说明存在有用的数据
//获取被点击的btn的id
int id = lv_btnmatrix_get_selected_btn(target);
// const char * txt = lv_btnmatrix_get_btn_text(target, id);
// LV_LOG_USER("%s was pressed\n", txt);
uint8_t textarea_page_count = global_file_data_p.id_count/8 + 1;
uint8_t textarea_page = 0;
//回车被点击
if(id == 11){
//隐藏textarea和keyboard
lv_obj_add_flag(screen3_spinbox_label_textarea,LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(btnm,LV_OBJ_FLAG_HIDDEN);
screen3_spinbox_btnm_teatarea_flag = 0;
//获取此时textarea的文本值
const char * textarea_value = lv_textarea_get_text(screen3_spinbox_label_textarea);
//当存在文本时
if (textarea_value[0] != '\0'){
uint8_t value = atoi(textarea_value);
if(value > 0 && value <textarea_page_count){
//代表键入的页数在范围内
textarea_page = value;
}else if(value == 0){
textarea_page = 1;
}else{
textarea_page = textarea_page_count;
}
lv_spinbox_set_value(screen3_spinbox,textarea_page);//将spinbox设置为对应的页数
setDataToTables(&global_file_data_p,textarea_page,textarea_page_count);
}
}
}
}
//点击spinbox右侧显示页数的btn 显示/隐藏手动输入页数界面
void lv_spinbox_textarea_event_cb(lv_event_t * e){
lv_event_code_t event_code = lv_event_get_code(e);
//lv_obj_t * target = lv_event_get_target(e);
if(event_code == LV_EVENT_CLICKED) {
LV_LOG_USER("page label is clicked");
if(screen3_spinbox_btnm_teatarea_flag == 0){
lv_obj_clear_flag(screen3_spinbox_label_textarea,LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(btnm,LV_OBJ_FLAG_HIDDEN);
LV_LOG_USER("can see spinbox_textarea and btnm");
screen3_spinbox_btnm_teatarea_flag = 1;
}else if(screen3_spinbox_btnm_teatarea_flag == 1){
lv_obj_add_flag(screen3_spinbox_label_textarea,LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(btnm,LV_OBJ_FLAG_HIDDEN);
LV_LOG_USER("can not see spinbox_textarea and btnm");
screen3_spinbox_btnm_teatarea_flag = 0;
}
}
}
//当keyboard中键入数值时同步更新到textarea中
void screen3_btnm_event_handler(lv_event_t * e){
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_t * ta = lv_event_get_user_data(e);
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));
if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) lv_textarea_del_char(ta);
else if(strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) lv_event_send(ta, LV_EVENT_READY, NULL);
else lv_textarea_add_text(ta, txt);
}
//点击显示日期的btn可以弹出/隐藏日历界面,当弹出日历时,高亮对应日期
uint16_t calendar_flag = 0;
void screen3_calender_create_cb(lv_event_t *e){
lv_event_code_t event_code = lv_event_get_code(e);
//lv_obj_t * target = lv_event_get_target(e);
if(event_code == LV_EVENT_CLICKED) {
LV_LOG_USER("calender create btn clicked");
if(calendar_flag == 0){
LV_LOG_USER("can see calender");
lv_obj_clear_flag(screen3_calender,LV_OBJ_FLAG_HIDDEN);
//当用户第一次显示日历或者切换日期时
if(user_first_see_calendar_or_change_date){
LV_LOG_USER("user fisrt see calendar or change date\n");
user_first_see_calendar_or_change_date = 0;
ui_Screen3_calendar_set_highlight();
}else{
lv_calendar_set_highlighted_dates(screen3_calender, highlighted_days, highlighted_days_cnt);
}
calendar_flag = 1;
}else if(calendar_flag == 1){
LV_LOG_USER("can not see calender");
lv_obj_add_flag(screen3_calender,LV_OBJ_FLAG_HIDDEN);
calendar_flag = 0;
}
}
}
void screen3_calender_dropdown_cb(lv_event_t *e){
lv_event_code_t code = lv_event_get_code(e);
//lv_obj_t * obj = lv_event_get_target(e); //检测是哪个控件触发的事件
if(code == LV_EVENT_VALUE_CHANGED){
//#if EXIST_RECORD_DATA_FILE
ui_Screen3_calendar_set_highlight();
//#endif
}
}
//选择日历的日期将日历设置为选择的日期并在显示日期的btn上同步更新
void screen3_calender_event_cb(lv_event_t *e){
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * tables[8] = {data_table1,data_table2,data_table3,data_table4,data_table5,data_table6,data_table7,data_table8};
//点击相同日期也会触发
if(code == LV_EVENT_VALUE_CHANGED) {
//隐藏日历
//LV_LOG_USER("calendar date value changed,hide calendar\n");
lv_obj_add_flag(screen3_calender,LV_OBJ_FLAG_HIDDEN);
calendar_flag = 0;
//获取点击的日期
lv_calendar_date_t clicked_date;
lv_calendar_get_pressed_date(screen3_calender, &clicked_date);
lv_label_set_text_fmt(screen3_calender_dsc_label,"%d.%02d.%02d",clicked_date.year,clicked_date.month,clicked_date.day);
//LV_LOG_USER("year:%d,month:%d,day:%d\n",clicked_date.year,clicked_date.month,clicked_date.day);
//遍历高亮日期数组,查询是否存在相同的项,如果存在相同的,则代表点击的日期有效,否则代表无该文件
uint8_t highlight_flag = 0;
for(int i = 0; i < 31; i++){
if( clicked_date.year == highlighted_days[i].year &&
clicked_date.month == highlighted_days[i].month &&
clicked_date.day == highlighted_days[i].day)
{
highlight_flag = 1;
break;
}
}
uint8_t different_date_flag = 1;
char * label_date = lv_label_get_text(screen3_calender_dsc_label);
int year,month,day;
sscanf(label_date,"%d.%d.%d",&year,&month,&day);
//LV_LOG_USER("label_date:%s,year:%d,month:%d,day:%d\n",label_date,year,month,day);
if(year == clicked_date.year && month == clicked_date.month && day == clicked_date.day) different_date_flag = 0;
//当既不是相同日期又存在文件时,需要从新给全局遍历赋值
if(highlight_flag && different_date_flag){
//LV_LOG_USER("different date and file exist\n");
//将结构体变量初始化为0
lv_memset_00(&global_file_data_p,sizeof(struct Return_Data));
//判断文件是否可读
this_date_have_data = findDataFile(clicked_date.year,clicked_date.month,clicked_date.day);
if(this_date_have_data)
{
//LV_LOG_USER("unempty file\n");
//生成文件路径
char file_path[32];
sprintf(file_path,"E:/%d/%02d/%02d.txt",(int)clicked_date.year,(int)clicked_date.month,(int)clicked_date.day);
//LV_LOG_USER("file_path:%s,id num = %d\n",file_path,global_file_data_p.id_count);
//打开文件
// lv_fs_file_t file_in_screen3_calender_event_cb = {
// .drv = NULL,
// .file_d = NULL
// };
QFILE fd = ql_fopen(file_path,"rb");// lv_fs_open(&file_in_screen3_calender_event_cb,file_path,LV_FS_MODE_RD);
//读取文件赋值给变量
readDataFromFile(fd,&global_file_data_p);
ql_fclose(fd);//lv_fs_close(&file_in_screen3_calender_event_cb);
current_id =0;
end_count = 0;
//根据变量处理控件
//计算总页数设置spinbox页数label以及spinbox范围
uint8_t page_count = global_file_data_p.id_count/8 + 1;
lv_label_set_text_fmt(screen3_spinbox_dsc_label,"/ %d",page_count);
lv_spinbox_set_range(screen3_spinbox, 1,page_count);
//LV_LOG_USER("different id num is: %d, page_count = %d\n", global_file_data_p.id_count,page_count);
//根据当前页数和总页数设置tabel的数据
uint8_t current_page = lv_spinbox_get_value(screen3_spinbox);
setDataToTables(&global_file_data_p,current_page,page_count);
//使能点击flag
lv_obj_add_flag(btn_page_plus,LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(btn_page_reduce,LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(scrren3_spinbox_btn,LV_OBJ_FLAG_CLICKABLE);
lv_obj_t * tables[8] = {data_table1,data_table2,data_table3,data_table4,data_table5,data_table6,data_table7,data_table8};
for(int i = 0; i < 8; i++){
lv_obj_add_flag(tables[i],LV_OBJ_FLAG_CLICKABLE);
}
return;
}
//如果文件空
else
{
//LV_LOG_USER("empty file\n");
goto exit;
}
}
else if(!different_date_flag)
{
//代表点击了相同的日期,直接返回
//LV_LOG_USER("click same date\n");
return;
}
else
{
//代表点了不同的日期但是日期对应没有文件
//LV_LOG_USER("click different date but no file\n");
goto exit;
}
}
exit:
//清空table,清除table的可点击flag
for(int i = 0; i < 8; i++){
lv_obj_clear_flag(tables[i],LV_OBJ_FLAG_CLICKABLE);
for(int j = 0; j < 6; j++){
lv_table_set_cell_value_fmt(tables[i],0,j,"");
}
}
//清除spinbox的btn的可点击flag
lv_obj_clear_flag(btn_page_plus,LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(btn_page_reduce,LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(scrren3_spinbox_btn,LV_OBJ_FLAG_CLICKABLE);
//设置spinbox的label
lv_label_set_text(screen3_spinbox_dsc_label,"N/A");
return;
}
//当点击data_table时创建chart界面
void screen3_data_table_chart_create(lv_event_t * e){
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t * target = lv_event_get_target(e);
if(event_code == LV_EVENT_CLICKED){
//LV_LOG_USER("get code and get target\n");
#if 1
/*获取id如果id为空则直接返回不生成chart*/
int id = atoi(lv_table_get_cell_value(target, 0, 0));
if(id == 0){
LV_LOG_USER("id is empty\n");
return;
}
/*根据id获取文件指针位置和偏移量*/
int offset = 0;
for( int i = 0; i < global_file_data_p.id_count; i++){
if(id == global_file_data_p.dataArr[i].id){
offset = global_file_data_p.dataArr[i].offset;
break;
}
}
//LV_LOG_USER("id = %d, offset = %d\n",id,offset);
/*获取该id的chart数据*/
//生成文件路径并打开文件
int year,month,day;
char file_path[18];
sscanf(lv_label_get_text(screen3_calender_dsc_label), "%d.%d.%d", &year,&month,&day);
sprintf(file_path,"E:/%d/%02d/%02d.txt",year,month,day);
//LV_LOG_USER("path:%s\n",file_path);
// lv_fs_file_t file_in_screen3_data_table_chart_create = {
// .drv = NULL,
// .file_d = NULL
// };
// lv_fs_open(&file_in_screen3_data_table_chart_create,file_path,LV_FS_MODE_RD);
QFILE fd = ql_fopen(file_path,"rb");
//读取该id的chart数据
lv_memset_00(&chartdata,sizeof(struct ChartData));
setDataToChart(fd,offset,id,&chartdata);
ql_fclose(fd);//lv_fs_close(&file_in_screen3_data_table_chart_create);
#endif
#if 0
printf("same_id_count = %d\n",chartdata.same_id_count+1);
for(int i = 0; i < chartdata.same_id_count+1; i++){
printf("depth:%.3f\t,V:%.3f,I:%.3f,time:%s\n",
chartdata.depth[i],
chartdata.V[i],
chartdata.I[i],
chartdata.time[i]
);
}
#endif
int num = chartdata.same_id_count+1;
if(num <= 1){
LV_LOG_USER("too few points\n");
return;
}
ui_screen3_chart_init(num);
}
}
/****************************************************************************************************************************************/
void ui_screen3_init(void)
{
/*****************************************************显示日期的label**********************************************************/
lv_obj_t * screen3_calender_btn = lv_btn_create(ui_Screen3);
lv_obj_set_size(screen3_calender_btn, 100, 34);
lv_obj_set_pos(screen3_calender_btn,60,0);
//点击btn显示或隐藏calendar,并高亮有文件的日期
lv_obj_add_event_cb(screen3_calender_btn, screen3_calender_create_cb, LV_EVENT_CLICKED, NULL);
screen3_calender_dsc_label = lv_label_create(screen3_calender_btn);
lv_obj_center(screen3_calender_dsc_label);
/*
// 获取当前时间戳
time_t t = time(NULL);
// 使用localtime函数将时间戳转换为本地时间结构
struct tm *local_time = localtime(&t);
// 从本地时间结构中提取年、月和日
int year = local_time->tm_year + 1900; // 年份需要加上1900
int month = local_time->tm_mon + 1; // 月份从0开始需要加1
int day = local_time->tm_mday;
char calendar[11];
sprintf(calendar,"%d.%d.%d",year,month,day);
*/
lv_label_set_text(screen3_calender_dsc_label,"2024.01.10");
/*****************************************************显示页数的spinbox**********************************************************/
//创建承载spinbox和描述label的btn
scrren3_spinbox_btn = lv_btn_create(ui_Screen3);
lv_obj_set_pos(scrren3_spinbox_btn,330,0);
lv_obj_set_size(scrren3_spinbox_btn,80,30);
lv_obj_set_style_bg_color(scrren3_spinbox_btn,lv_color_hex(0xFFFFFFF),0);
//点击显示输入文本框和键盘
lv_obj_add_event_cb(scrren3_spinbox_btn, lv_spinbox_textarea_event_cb, LV_EVENT_CLICKED, NULL);
//创建spinbox
screen3_spinbox = lv_spinbox_create(scrren3_spinbox_btn);
lv_spinbox_set_digit_format(screen3_spinbox, 2, 2);
lv_obj_set_width(screen3_spinbox, 46);
lv_obj_align(screen3_spinbox,LV_ALIGN_CENTER,-15,0);
lv_spinbox_set_value(screen3_spinbox,1);//设置初始为第一页
lv_obj_set_style_border_opa(screen3_spinbox,0,0);
lv_obj_set_style_outline_opa(screen3_spinbox,0,0);
//创建显示总页数的label
screen3_spinbox_dsc_label = lv_label_create(scrren3_spinbox_btn);
lv_obj_set_size(screen3_spinbox_dsc_label,50,34);
lv_obj_align_to(screen3_spinbox_dsc_label,screen3_spinbox,LV_ALIGN_OUT_RIGHT_MID,0,8);
lv_obj_set_style_text_font(screen3_spinbox_dsc_label,&lv_font_montserrat_16,0);
lv_label_set_text(screen3_spinbox_dsc_label,"/100");
lv_obj_set_style_border_opa(screen3_spinbox_dsc_label,0,0);
lv_obj_set_style_outline_opa(screen3_spinbox_dsc_label,0,0);
/*****************************************************控制翻页的button**********************************************************/
btn_page_plus = lv_btn_create(ui_Screen3);
lv_obj_set_size(btn_page_plus, 30, 30);
lv_obj_set_pos(btn_page_plus,410,0);
lv_obj_set_style_bg_img_src(btn_page_plus, LV_SYMBOL_PLUS, 0);
btn_page_reduce = lv_btn_create(ui_Screen3);
lv_obj_set_size(btn_page_reduce, 30, 30);
lv_obj_set_pos(btn_page_reduce,300,0);
lv_obj_set_style_bg_img_src(btn_page_reduce, LV_SYMBOL_MINUS, 0);
//spinbox页数+1
lv_obj_add_event_cb(btn_page_plus, lv_spinbox_increment_event_cb, LV_EVENT_ALL,NULL);
//spinbox页数-1
lv_obj_add_event_cb(btn_page_reduce, lv_spinbox_decrement_event_cb, LV_EVENT_ALL,NULL);
/*****************************************************记录数据的table**********************************************************/
char text[6][16] = {"桩点号","最大喷浆量","最大深度","平均电流","开始时间","结束时间"};
int align[6] = {-210,-125,-40,35,120,210};
int size[6] = {50,80,70,70,70,70};
for(int i = 0; i < 6; i++){
lv_obj_t * screen3_label = lv_label_create(ui_Screen3);
lv_obj_set_size(screen3_label,size[i],30);
lv_obj_align(screen3_label,LV_ALIGN_TOP_MID, align[i], 45);
lv_obj_clear_flag(screen3_label,LV_OBJ_FLAG_SCROLLABLE);
lv_label_set_text(screen3_label,text[i]);
lv_obj_set_style_text_font(screen3_label, &lv_font_chinese_12_name, 0);
}
data_table1 = lv_table_create(ui_Screen3);
data_table2 = lv_table_create(ui_Screen3);
data_table3 = lv_table_create(ui_Screen3);
data_table4 = lv_table_create(ui_Screen3);
data_table5 = lv_table_create(ui_Screen3);
data_table6 = lv_table_create(ui_Screen3);
data_table7 = lv_table_create(ui_Screen3);
data_table8 = lv_table_create(ui_Screen3);
lv_obj_t*screen3_data_tables[] = {data_table1, data_table2, data_table3, data_table4,data_table5, data_table6, data_table7, data_table8};
int align_data[] = {-224,-192,-160,-128,-96,-64,-32,0};
for(int r=0;r<8;r++){
lv_obj_align(screen3_data_tables[r],LV_ALIGN_BOTTOM_MID, 10, align_data[r]);//设置位置
lv_table_set_row_cnt(screen3_data_tables[r],1);//设置行列数
lv_table_set_col_cnt(screen3_data_tables[r],6);
lv_obj_set_height(screen3_data_tables[r],35);
lv_obj_set_style_border_width(screen3_data_tables[r],1,LV_PART_MAIN);
lv_obj_set_style_pad_top(screen3_data_tables[r],0,LV_PART_ITEMS);
lv_obj_clear_flag(screen3_data_tables[r],LV_OBJ_FLAG_SCROLLABLE);
lv_table_set_col_width(screen3_data_tables[r],0,75);
lv_table_set_col_width(screen3_data_tables[r],1,87);
lv_table_set_col_width(screen3_data_tables[r],2,75);
lv_table_set_col_width(screen3_data_tables[r],3,80);
lv_table_set_col_width(screen3_data_tables[r],4,100);
lv_table_set_col_width(screen3_data_tables[r],5,100);
//当点击table时生成对应的chart
lv_obj_add_event_cb(screen3_data_tables[r], screen3_data_table_chart_create, LV_EVENT_CLICKED,NULL);
}
/*****************************************************calendar**********************************************************/
screen3_calender = lv_calendar_create(ui_Screen3);
lv_obj_set_size(screen3_calender, 480, 287);
lv_obj_align(screen3_calender,LV_ALIGN_BOTTOM_MID,0,0);
lv_obj_add_flag(screen3_calender,LV_OBJ_FLAG_HIDDEN);
//选择日期读取对应日期文件设置table更新label
lv_obj_add_event_cb(screen3_calender, screen3_calender_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_t * calendar_dropdown_background = lv_calendar_header_dropdown_create(screen3_calender);
calendar_dropdown_year = lv_obj_get_child(calendar_dropdown_background,0);
calendar_dropdown_month = lv_obj_get_child(calendar_dropdown_background,1);
//当日历的年月改变时,读取相对应的路径并高亮对应日期
lv_obj_add_event_cb(calendar_dropdown_year, screen3_calender_dropdown_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(calendar_dropdown_month, screen3_calender_dropdown_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*****************************************************textarea+keyboard**********************************************************/
//创建输入文本框并隐藏
screen3_spinbox_label_textarea = lv_textarea_create(ui_Screen3);
lv_textarea_set_one_line(screen3_spinbox_label_textarea, true);
lv_obj_align(screen3_spinbox_label_textarea, LV_ALIGN_TOP_MID, 0, 32);
lv_obj_set_width(screen3_spinbox_label_textarea,480);
lv_obj_add_state(screen3_spinbox_label_textarea, LV_STATE_FOCUSED);
lv_obj_add_flag(screen3_spinbox_label_textarea,LV_OBJ_FLAG_HIDDEN);
//为输入文本框创建输入键盘
static const char * btnm_map[] = {
"1", "2", "3", "\n",
"4", "5", "6", "\n",
"7", "8", "9", "\n",
LV_SYMBOL_BACKSPACE, "0", LV_SYMBOL_NEW_LINE, ""};
btnm = lv_btnmatrix_create(ui_Screen3);
lv_obj_set_size(btnm, 480, 250);
lv_obj_align(btnm, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_clear_flag(btnm, LV_OBJ_FLAG_CLICK_FOCUSABLE); /*To keep the text area focused on button clicks*/
lv_btnmatrix_set_map(btnm, btnm_map);
lv_obj_add_flag(btnm,LV_OBJ_FLAG_HIDDEN);
//回车时设置label隐藏ta和btnm
lv_obj_add_event_cb(btnm, screen3_textarea_keyboard_event_cb, LV_EVENT_ALL, NULL);
//将键盘输入的数据键入到textarea上
lv_obj_add_event_cb(btnm, screen3_btnm_event_handler, LV_EVENT_VALUE_CHANGED, screen3_spinbox_label_textarea);
}

View File

@ -0,0 +1,148 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../inc/ui/ui_screen3_chart.h"
#include "../inc/ui/fs_function.h"
extern struct ChartData chartdata;;
lv_obj_t * chart;
lv_chart_series_t * ser_depth;
lv_chart_series_t * ser_V;
lv_chart_series_t * ser_I;
void chart_btn_event_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
//struct ChartUserData * chart_user_data = (struct ChartUserData *)lv_event_get_user_data(e);
lv_obj_t * chart_screen = (lv_obj_t*)lv_event_get_user_data(e);
if(code == LV_EVENT_CLICKED){
// LV_LOG_USER("delete chart btn clicked,user data p:%p,chart p:%p,depth p:%p,V p:%p\n",
lv_chart_remove_series(chart,ser_depth);
lv_chart_remove_series(chart,ser_V);
lv_chart_remove_series(chart,ser_I);
lv_obj_del(chart_screen);
lv_obj_del(obj);
ser_depth = NULL;
ser_V = NULL;
ser_I = NULL;
chart = NULL;
lv_memset_00(&chartdata,sizeof(struct ChartData));
}
}
//在chart生成时设置x坐标轴的单位
void chart_x_set(lv_event_t * e)
{
lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
if(dsc->part == LV_PART_TICKS && dsc->id == LV_CHART_AXIS_PRIMARY_X) {
lv_snprintf(dsc->text, sizeof(dsc->text), "%s", chartdata.time[dsc->value]);
}
}
//当在chart上点击数据点时将该横坐标对应的纵坐标值分别在各个ser的数据点旁边显示
void chart_see_two_ser_values_after_clicked(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * chart = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
lv_obj_invalidate(chart);
}
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, 20);
}
else if(code == LV_EVENT_DRAW_POST_END) {
int id = lv_chart_get_pressed_point(chart);
if(id == LV_CHART_POINT_NONE) return;
LV_LOG_USER("Selected point %d", id);
lv_chart_series_t * ser = lv_chart_get_series_next(chart, NULL);
while(ser) {
lv_point_t p;
lv_chart_get_point_pos_by_id(chart, ser, id, &p);
lv_coord_t * y_array = lv_chart_get_y_array(chart, ser);
lv_coord_t value = y_array[id];
char buf[16];
lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"$%d", value);
lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
draw_rect_dsc.bg_color = lv_color_black();
draw_rect_dsc.bg_opa = LV_OPA_50;
draw_rect_dsc.radius = 3;
draw_rect_dsc.bg_img_src = buf;
draw_rect_dsc.bg_img_recolor = lv_color_white();
lv_area_t a;
a.x1 = chart->coords.x1 + p.x - 20;
a.x2 = chart->coords.x1 + p.x + 20;
a.y1 = chart->coords.y1 + p.y - 30;
a.y2 = chart->coords.y1 + p.y - 10;
const lv_area_t * clip_area = lv_event_get_clip_area(e);
lv_draw_rect(&a, clip_area, &draw_rect_dsc);
ser = lv_chart_get_series_next(chart, ser);
}
}
else if(code == LV_EVENT_RELEASED) {
lv_obj_invalidate(chart);
}
}
void ui_screen3_chart_init(int num)
{
/*基于当前活动屏幕创建chart所在的屏*/
lv_obj_t * chart_screen = lv_obj_create(lv_scr_act());
lv_obj_center(chart_screen);
lv_obj_set_size(chart_screen,480,320);
lv_obj_clear_flag(chart_screen,LV_OBJ_FLAG_SCROLLABLE);
/*创建关闭btn*/
lv_obj_t *chart_close_btn = lv_btn_create(lv_scr_act());
lv_obj_center(chart_close_btn);
lv_obj_set_size(chart_close_btn,100,40);
lv_obj_align(chart_close_btn,LV_ALIGN_TOP_RIGHT,-40,0);
lv_obj_t *chart_close_btn_label = lv_label_create(chart_close_btn);
lv_label_set_text(chart_close_btn_label,LV_SYMBOL_CLOSE);
lv_obj_center(chart_close_btn_label);
lv_obj_set_style_text_font(chart_close_btn_label,&lv_font_montserrat_24,0);
lv_obj_add_event_cb(chart_close_btn,chart_btn_event_handler,LV_EVENT_ALL,(void*)chart_screen);
chart = lv_chart_create(chart_screen);
lv_obj_set_size(chart, 420, 250);
lv_obj_align(chart,LV_ALIGN_BOTTOM_MID,-5,-5);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE); /*Show lines and points too*/
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 10);//左侧纵坐标范围,深度
lv_chart_set_range(chart, LV_CHART_AXIS_SECONDARY_Y, 0, 1600);//右侧纵坐标范围,累计喷浆量
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 5, 1, 6, 1, true, 50);//给坐标轴添加刻度线
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_SECONDARY_Y, 5, 1, 6, 1, true, 50);
lv_chart_set_zoom_x(chart, 400);//设置x轴缩进
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 5, 1, num, 1, true, 20); //设置横坐标刻度(1个点无法生成line)
lv_chart_set_point_count(chart, num);
ser_depth = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);//深度,深蓝色
ser_V = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_SECONDARY_Y);//累计喷浆,绿色
ser_I = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);//电流 红色
/*根据chart数据生成line*/
for(int i = 0; i < num; i++){
lv_chart_set_next_value(chart,ser_depth,chartdata.depth[i]);
lv_chart_set_next_value(chart,ser_V,chartdata.V[i]);
lv_chart_set_next_value(chart,ser_I,chartdata.I[i]);
}
lv_chart_refresh(chart);
//在绘图时重绘x轴坐标描述
lv_obj_add_event_cb(chart, chart_x_set, LV_EVENT_DRAW_PART_BEGIN,NULL);
//点击数据点时在旁边显示值
lv_obj_add_event_cb(chart, chart_see_two_ser_values_after_clicked, LV_EVENT_ALL, NULL);
}

231
EC600U_lvgl/ui/ui_screen4.c Normal file
View File

@ -0,0 +1,231 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../inc/ui/ui_screen4.h"
#include "osi_api.h"
#define SIZE 300
lv_obj_t * ui_Screen4;
level_instrument_t * level_instrument;
extern lv_obj_t * ui_Screen1;
extern lv_obj_t * ui_Screen3;
/********************************************** 自定义控件创建 *****************************************/
my_panel_t * panel_create(lv_obj_t * obj)
{
/*创建基础红色背景*/
lv_obj_t * red_bg = lv_obj_create(obj);
lv_obj_align(red_bg,LV_ALIGN_CENTER,0,20);
lv_obj_set_size(red_bg,SIZE,SIZE);
lv_obj_set_style_radius(red_bg,200,0);
lv_obj_set_style_bg_color(red_bg,lv_palette_main(LV_PALETTE_RED),0);
lv_obj_set_style_opa(red_bg,150,0);
lv_obj_set_style_border_width(red_bg,0,0);
lv_obj_set_style_outline_width(red_bg,0,0);
/*在红色背景上创建黄色背景*/
lv_obj_t * yellow_bg = lv_obj_create(red_bg);
lv_obj_center(yellow_bg);
lv_obj_set_size(yellow_bg,SIZE/3*2,SIZE/3*2);
lv_obj_set_style_radius(yellow_bg,125,0);
lv_obj_set_style_bg_color(yellow_bg,lv_palette_main(LV_PALETTE_YELLOW),0);
lv_obj_set_style_opa(yellow_bg,150,0);
lv_obj_set_style_border_width(yellow_bg,0,0);
lv_obj_set_style_outline_width(yellow_bg,0,0);
/*在绿色背景上创建绿色背景*/
lv_obj_t * green_bg = lv_obj_create(yellow_bg);
lv_obj_center(green_bg);
lv_obj_set_size(green_bg,SIZE/3,SIZE/3);
lv_obj_set_style_radius(green_bg,125,0);
lv_obj_set_style_bg_color(green_bg,lv_palette_main(LV_PALETTE_GREEN),0);
lv_obj_set_style_opa(green_bg,150,0);
lv_obj_set_style_border_width(green_bg,0,0);
lv_obj_set_style_outline_width(green_bg,0,0);
//创建十字线
lv_obj_t * horizontal_line = lv_obj_create(obj);
lv_obj_set_size(horizontal_line,SIZE+20,2);
lv_obj_set_style_bg_color(horizontal_line,lv_color_hex(0x000000),0);
lv_obj_align(horizontal_line,LV_ALIGN_CENTER,0,20);
lv_obj_set_style_border_width(horizontal_line,0,0);
lv_obj_set_style_outline_width(horizontal_line,0,0);
lv_obj_set_style_shadow_width(horizontal_line,0,0);
lv_obj_t * vertical_line = lv_obj_create(obj);
lv_obj_set_size(vertical_line,2,SIZE+20);
lv_obj_set_style_bg_color(vertical_line,lv_color_hex(0x000000),0);
lv_obj_align(vertical_line,LV_ALIGN_CENTER,0,20);
lv_obj_set_style_border_width(vertical_line,0,0);
lv_obj_set_style_outline_width(vertical_line,0,0);
lv_obj_set_style_shadow_width(vertical_line,0,0);
my_panel_t * my_panel = (my_panel_t *)lv_mem_alloc(sizeof(my_panel_t));
my_panel -> red_bg = red_bg;
my_panel -> yellow_bg = yellow_bg;
my_panel -> green_bg = green_bg;
my_panel -> horizontal_line = horizontal_line;
my_panel -> vertical_line = vertical_line;
// LV_LOG_USER(
// "red_bg = %p\n yellow_bg = %p\n green_bg = %p\n horizontal_line = %p\n vertical_line = %p\n",
// my_panel -> red_bg,
// my_panel -> yellow_bg,
// my_panel -> green_bg,
// my_panel -> horizontal_line,
// my_panel -> vertical_line
// );
return my_panel;
}
lv_obj_t* led_create(lv_obj_t * obj)
{
lv_obj_t * led = lv_led_create(obj);
lv_obj_set_size(led, 10, 10);
lv_obj_align(led,LV_ALIGN_CENTER,0,20);
//LV_LOG_USER("led = %p", led);
return led;
}
level_instrument_t * level_instrument_create(lv_obj_t * obj)
{
level_instrument_t * level_instrument = (level_instrument_t *)lv_mem_alloc(sizeof(level_instrument_t));
level_instrument -> panel = panel_create(obj);
level_instrument -> led = led_create(obj);
level_instrument ->size =270;
level_instrument ->x = 0;
level_instrument ->y = 0;
return level_instrument;
}
/****************************************************自定义控件功能**********************************************************/
//设置控件对应部件大小
void level_instrument_set_size(level_instrument_t * level_instrument, lv_coord_t size, level_instrument_part_t part)
{
if(part == LEVEL_INSTUMENT_PANEL_ALL){
lv_obj_set_size(level_instrument -> panel ->red_bg , size, size);
lv_obj_set_size(level_instrument -> panel ->yellow_bg , size/3*2, size/3*2);
lv_obj_set_size(level_instrument -> panel ->green_bg , size/3, size/3);
lv_obj_set_size(level_instrument -> panel ->horizontal_line , size+10, 2);
lv_obj_set_size(level_instrument -> panel ->vertical_line , 2, size+10);
level_instrument->size = size;//更新大小
}else if(part == LEVEL_INSTUMENT_PANEL_RED){
lv_obj_set_size(level_instrument -> panel ->red_bg , size, size);
}else if(part == LEVEL_INSTUMENT_PANEL_YELLOW){
lv_obj_set_size(level_instrument -> panel ->yellow_bg , size/3*2, size/3*2);
}else if(part == LEVEL_INSTUMENT_PANEL_GREEN){
lv_obj_set_size(level_instrument -> panel ->green_bg , size/3, size/3);
}else if(part == LEVEL_INSTRUMENT_CROSSHAIR){
lv_obj_set_size(level_instrument->panel->horizontal_line,size,2);
lv_obj_set_size(level_instrument->panel->vertical_line,2,size);
}else if(part == LEVEL_INSTRUMENT_LED){
lv_obj_set_size(level_instrument -> led , size, size);
}else{
return;
}
}
//设置光标在面板上的位置(光标以坐标轴方向为准,基于原点偏移)
void level_instrument_set_cursor_pos(level_instrument_t * level_instrument,lv_coord_t x_offset, lv_coord_t y_offset)
{
y_offset = -y_offset;
int size = level_instrument->size;
//LV_LOG_USER("panel size = %d\n",size);
//判断坐标是否超过panel
if (((size * size) >= x_offset * x_offset + y_offset * y_offset) &&
LV_ABS(x_offset) <= size/2 &&
LV_ABS(y_offset) <= size/2){
//光标在内部
level_instrument->x = x_offset;
level_instrument->y = y_offset;
lv_obj_align(level_instrument->led,LV_ALIGN_CENTER,x_offset,y_offset);
//LV_LOG_USER("cursor is in panel\n");
}else{
//光标在外部
//LV_LOG_USER("cursor is out panel\n");
}
}
//设置光标颜色
void level_instrument_set_cursor_color(level_instrument_t * level_instrument,lv_color_t color)
{
lv_led_set_color(level_instrument->led,color);
}
//设置光标亮度
void level_instrument_set_cursor_brightness(level_instrument_t * level_instrument,uint8_t bright)
{
lv_led_set_brightness(level_instrument->led,bright);
}
//设置控件整体的对齐
void level_instrument_set_align(level_instrument_t * level_instrument,lv_align_t align,lv_coord_t x_offset,lv_coord_t y_offset)
{
lv_obj_align(level_instrument->panel->red_bg,align,x_offset,y_offset);
lv_obj_align(level_instrument->panel->green_bg,align,x_offset,y_offset);
lv_obj_align(level_instrument->panel->yellow_bg,align,x_offset,y_offset);
lv_obj_align(level_instrument->panel->horizontal_line,align,x_offset,y_offset);
lv_obj_align(level_instrument->panel->vertical_line,align,x_offset,y_offset);
lv_obj_align(level_instrument->led,align,x_offset+level_instrument->x,y_offset+level_instrument->y);
}
//设置面板透明度
void level_instrument_set_opa(level_instrument_t * level_instrument,lv_opa_t opa)
{
lv_obj_set_style_opa(level_instrument->panel->red_bg,opa,0);
lv_obj_set_style_opa(level_instrument->panel->green_bg,opa,0);
lv_obj_set_style_opa(level_instrument->panel->yellow_bg,opa,0);
lv_obj_set_style_opa(level_instrument->panel->horizontal_line,opa,0);
lv_obj_set_style_opa(level_instrument->panel->vertical_line,opa,0);
}
/********************************************************************************************************/
extern osiThread_t *uart1_thread;
static void btn_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
osiEvent_t event;
event.id = 0x3203;
osiEventSend(uart1_thread, &event);
}
}
static void btn2_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED)
{
osiEvent_t event;
event.id = 0x3299;
osiEventSend(uart1_thread, &event);
}
}
void ui_screen4_init(void)
{
// level_instrument = level_instrument_create(ui_Screen4);
// level_instrument_set_size(level_instrument,270,LEVEL_INSTUMENT_PANEL_ALL);
lv_obj_t * btn = lv_btn_create(ui_Screen4);
lv_obj_align(btn,LV_ALIGN_CENTER,-80,0);
lv_obj_set_size(btn,100,50);
lv_obj_add_event_cb(btn,btn_cb,LV_EVENT_CLICKED,NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label,"flash");
lv_obj_t * btn2 = lv_btn_create(ui_Screen4);
lv_obj_align(btn2,LV_ALIGN_CENTER,80,0);
lv_obj_set_size(btn2,100,50);
lv_obj_add_event_cb(btn2,btn2_cb,LV_EVENT_CLICKED,NULL);
lv_obj_t * label2 = lv_label_create(btn2);
lv_label_set_text(label2,"download");
}

View File

@ -0,0 +1,203 @@
# Copyright (C) 2018 RDA Technologies Limited and/or its affiliates("RDA").
# All rights reserved.
#
# This software is supplied "AS IS" without any warranties.
# RDA assumes no responsibility or liability for the use of the software,
# conveys no license or title under any patent, copyright, or mask work
# right to the product. RDA reserves the right to make changes in the
# software without notification. RDA also make no representation or
# warranty that such application will be suitable for the specified use
# without further testing or modification.
#configure_file(include/lv_gui_config.h.in ${out_inc_dir}/lv_gui_config.h)
set(target lvgl)
#add_app_libraries($<TARGET_FILE:${target}>)
add_library(${target} STATIC)
set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_LVGL)
target_include_directories(${target} PUBLIC ${CMAKE_CURRENT_SRC_DIR} lvgl include)
#target_link_libraries(${target} PRIVATE kernel driver hal ql_api_common)
target_sources(${target} PRIVATE
# core
lvgl/src/core/lv_disp.c
lvgl/src/core/lv_event.c
lvgl/src/core/lv_group.c
lvgl/src/core/lv_indev_scroll.c
lvgl/src/core/lv_indev.c
lvgl/src/core/lv_obj_class.c
lvgl/src/core/lv_obj_draw.c
lvgl/src/core/lv_obj_pos.c
lvgl/src/core/lv_obj_scroll.c
lvgl/src/core/lv_obj_style_gen.c
lvgl/src/core/lv_obj_style.c
lvgl/src/core/lv_obj_tree.c
lvgl/src/core/lv_obj.c
lvgl/src/core/lv_refr.c
lvgl/src/core/lv_theme.c
# draw
# lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.c
# lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.c
# lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.c
# lvgl/src/draw/sdl/lv_draw_sdl_arc.c
# lvgl/src/draw/sdl/lv_draw_sdl_bg.c
# lvgl/src/draw/sdl/lv_draw_sdl_composite.c
# lvgl/src/draw/sdl/lv_draw_sdl_img.c
# lvgl/src/draw/sdl/lv_draw_sdl_label.c
# lvgl/src/draw/sdl/lv_draw_sdl_line.c
# lvgl/src/draw/sdl/lv_draw_sdl_mask.c
# lvgl/src/draw/sdl/lv_draw_sdl_polygon.c
# lvgl/src/draw/sdl/lv_draw_sdl_rect.c
# lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.c
# lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c
# lvgl/src/draw/sdl/lv_draw_sdl_utils.c
# lvgl/src/draw/sdl/lv_draw_sdl.c
# lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c
# lvgl/src/draw/sw/lv_draw_sw.c
# lvgl/src/draw/sw/lv_draw_sw_arc.c
# lvgl/src/draw/sw/lv_draw_sw_blend.c
# lvgl/src/draw/sw/lv_draw_sw_dither.c
# lvgl/src/draw/sw/lv_draw_sw_gradient.c
# lvgl/src/draw/sw/lv_draw_sw_img.c
# lvgl/src/draw/sw/lv_draw_sw_letter.c
# lvgl/src/draw/sw/lv_draw_sw_line.c
# lvgl/src/draw/sw/lv_draw_sw_polygon.c
# lvgl/src/draw/sw/lv_draw_sw_rect.c
lvgl/src/draw/lv_draw_arc.c
lvgl/src/draw/lv_draw_blend.c
lvgl/src/draw/lv_draw_img.c
lvgl/src/draw/lv_draw_label.c
lvgl/src/draw/lv_draw_line.c
lvgl/src/draw/lv_draw_mask.c
lvgl/src/draw/lv_draw_rect.c
lvgl/src/draw/lv_draw_triangle.c
# lvgl/src/draw/lv_draw.c
lvgl/src/draw/lv_img_buf.c
lvgl/src/draw/lv_img_cache.c
lvgl/src/draw/lv_img_decoder.c
# extra
lvgl/src/extra/layouts/flex/lv_flex.c
lvgl/src/extra/layouts/grid/lv_grid.c
lvgl/src/extra/libs/bmp/lv_bmp.c
# lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c
lvgl/src/extra/libs/freetype/lv_freetype.c
lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c
lvgl/src/extra/libs/fsdrv/lv_fs_posix.c
lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c
lvgl/src/extra/libs/fsdrv/lv_fs_win32.c
lvgl/src/extra/libs/gif/gifdec.c
lvgl/src/extra/libs/gif/lv_gif.c
lvgl/src/extra/libs/png/lodepng.c
lvgl/src/extra/libs/png/lv_png.c
lvgl/src/extra/libs/qrcode/lv_qrcode.c
lvgl/src/extra/libs/qrcode/qrcodegen.c
lvgl/src/extra/libs/rlottie/lv_rlottie.c
lvgl/src/extra/libs/sjpg/lv_sjpg.c
lvgl/src/extra/libs/sjpg/tjpgd.c
# lvgl/src/extra/others/gridnav/lv_gridnav.c
# lvgl/src/extra/others/monkey/lv_monkey.c
lvgl/src/extra/others/snapshot/lv_snapshot.c
lvgl/src/extra/themes/basic/lv_theme_basic.c
lvgl/src/extra/themes/default/lv_theme_default.c
lvgl/src/extra/themes/mono/lv_theme_mono.c
lvgl/src/extra/widgets/animimg/lv_animimg.c
lvgl/src/extra/widgets/calendar/lv_calendar_header_arrow.c
lvgl/src/extra/widgets/calendar/lv_calendar_header_dropdown.c
lvgl/src/extra/widgets/calendar/lv_calendar.c
lvgl/src/extra/widgets/chart/lv_chart.c
lvgl/src/extra/widgets/colorwheel/lv_colorwheel.c
lvgl/src/extra/widgets/imgbtn/lv_imgbtn.c
lvgl/src/extra/widgets/keyboard/lv_keyboard.c
lvgl/src/extra/widgets/led/lv_led.c
lvgl/src/extra/widgets/list/lv_list.c
lvgl/src/extra/widgets/meter/lv_meter.c
lvgl/src/extra/widgets/msgbox/lv_msgbox.c
# lvgl/src/extra/widgets/menu/lv_menu.c
lvgl/src/extra/widgets/span/lv_span.c
lvgl/src/extra/widgets/spinbox/lv_spinbox.c
lvgl/src/extra/widgets/spinner/lv_spinner.c
lvgl/src/extra/widgets/tabview/lv_tabview.c
lvgl/src/extra/widgets/tileview/lv_tileview.c
lvgl/src/extra/widgets/win/lv_win.c
lvgl/src/extra/lv_extra.c
# font
lvgl/src/font/lv_font.c
lvgl/src/font/lv_font_fmt_txt.c
lvgl/src/font/lv_font_loader.c
# lvgl/src/font/lv_font_montserrat_8.c
# lvgl/src/font/lv_font_montserrat_10.c
# lvgl/src/font/lv_font_montserrat_12_subpx.c
lvgl/src/font/lv_font_montserrat_14.c
# lvgl/src/font/lv_font_montserrat_18.c
# lvgl/src/font/lv_font_montserrat_22.c
lvgl/src/font/lv_font_montserrat_24.c
# lvgl/src/font/lv_font_montserrat_26.c
# lvgl/src/font/lv_font_montserrat_28_compressed.c
# lvgl/src/font/lv_font_montserrat_30.c
# lvgl/src/font/lv_font_montserrat_32.c
# lvgl/src/font/lv_font_montserrat_34.c
# lvgl/src/font/lv_font_montserrat_36.c
# lvgl/src/font/lv_font_montserrat_38.c
# lvgl/src/font/lv_font_montserrat_40.c
# lvgl/src/font/lv_font_montserrat_44.c
# lvgl/src/font/lv_font_montserrat_46.c
# lvgl/src/font/lv_font_montserrat_48.c
lvgl/src/font/ui_screen1_font_16.c
lvgl/src/font/system_status_icon_24.c
lvgl/src/font/lv_font_montserrat_12.c
lvgl/src/font/lv_font_montserrat_16.c
lvgl/src/font/lv_font_montserrat_20.c
lvgl/src/font/lv_font_montserrat_42.c
lvgl/src/font/ui_screen2_font_16.c
lvgl/src/font/lv_font_chinese_12_name.c
lvgl/src/font/ui_font_12.c
lvgl/src/font/ui_font_16.c
# gpu
# hal
lvgl/src/hal/lv_hal_disp.c
lvgl/src/hal/lv_hal_indev.c
lvgl/src/hal/lv_hal_tick.c
# misc
lvgl/src/misc/lv_anim_timeline.c
lvgl/src/misc/lv_anim.c
lvgl/src/misc/lv_area.c
lvgl/src/misc/lv_async.c
lvgl/src/misc/lv_bidi.c
lvgl/src/misc/lv_color.c
lvgl/src/misc/lv_fs.c
lvgl/src/misc/lv_gc.c
lvgl/src/misc/lv_ll.c
lvgl/src/misc/lv_log.c
# lvgl/src/misc/lv_lru.c
lvgl/src/misc/lv_math.c
lvgl/src/misc/lv_mem.c
lvgl/src/misc/lv_printf.c
lvgl/src/misc/lv_style_gen.c
lvgl/src/misc/lv_style.c
lvgl/src/misc/lv_templ.c
lvgl/src/misc/lv_timer.c
lvgl/src/misc/lv_tlsf.c
lvgl/src/misc/lv_txt_ap.c
lvgl/src/misc/lv_txt.c
lvgl/src/misc/lv_utils.c
# widgets
lvgl/src/widgets/lv_arc.c
lvgl/src/widgets/lv_bar.c
lvgl/src/widgets/lv_btn.c
lvgl/src/widgets/lv_btnmatrix.c
lvgl/src/widgets/lv_canvas.c
lvgl/src/widgets/lv_checkbox.c
lvgl/src/widgets/lv_dropdown.c
lvgl/src/widgets/lv_img.c
lvgl/src/widgets/lv_label.c
lvgl/src/widgets/lv_line.c
lvgl/src/widgets/lv_objx_templ.c
lvgl/src/widgets/lv_roller.c
lvgl/src/widgets/lv_slider.c
lvgl/src/widgets/lv_switch.c
lvgl/src/widgets/lv_table.c
lvgl/src/widgets/lv_textarea.c
)
relative_glob(srcs include/*.h src/*.c inc/*.h)
beautify_c_code(${target} ${srcs})

702
EC600U_lvgl_lib/lv_conf.h Normal file
View File

@ -0,0 +1,702 @@
/**
* @file lv_conf.h
* Configuration file for v8.0.0
*/
/*
* Copy this file as `lv_conf.h`
* 1. simply next to the `lvgl` folder
* 2. or any other places and
* - define `LV_CONF_INCLUDE_SIMPLE`
* - add the path as include path
*/
/* clang-format off */
#if 1 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
/*====================
COLOR SETTINGS
*====================*/
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
/*Enable more complex drawing routines to manage screens transparency.
*Can be used if the UI is above another layer, e.g. an OSD menu or video player.
*Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
#define LV_COLOR_SCREEN_TRANSP 0
/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
/*=========================
MEMORY SETTINGS
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (1024U * 64) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#define LV_MEM_ADR 0 /*0: unused*/
/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
#if LV_MEM_ADR == 0
//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
#endif
#else /*LV_MEM_CUSTOM*/
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_FREE free
#define LV_MEM_CUSTOM_REALLOC realloc
#endif /*LV_MEM_CUSTOM*/
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
*You will see an error log message if there wasn't enough buffers. */
#define LV_MEM_BUF_MAX_NUM 16
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*====================
HAL SETTINGS
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
#define LV_TICK_CUSTOM 0
#if LV_TICK_CUSTOM
// #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
// #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
#define LV_TICK_CUSTOM_INCLUDE "ql_api_osi.h"
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (ql_rtos_up_time_ms())
#endif /*LV_TICK_CUSTOM*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
*(Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI_DEF 130 /*[px/inch]*/
/*=======================
* FEATURE CONFIGURATION
*=======================*/
/*-------------
* Drawing
*-----------*/
/*Enable complex draw engine.
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
#define LV_DRAW_COMPLEX 1
#if LV_DRAW_COMPLEX != 0
/*Allow buffering some shadow calculation.
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_SHADOW_CACHE_SIZE 0
/* Set number of maximally cached circle data.
* The circumference of 1/4 circle are saved for anti-aliasing
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
* 0: to disable caching */
#define LV_CIRCLE_CACHE_SIZE 4
#endif /*LV_DRAW_COMPLEX*/
/*Default image cache size. Image caching keeps the images opened.
*If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
*With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
*However the opened images might consume additional RAM.
*0: to disable caching*/
#define LV_IMG_CACHE_DEF_SIZE 0
/*Number of stops allowed per gradient. Increase this to allow more stops.
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
#define LV_GRADIENT_MAX_STOPS 2
/*Default gradient buffer size.
*When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
*LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes.
*If the cache is too small the map will be allocated only while it's required for the drawing.
*0 mean no caching.*/
#define LV_GRAD_CACHE_DEF_SIZE 0
/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)
*LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
#define LV_DITHER_GRADIENT 0
#if LV_DITHER_GRADIENT
/*Add support for error diffusion dithering.
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
*The increase in memory consumption is (24 bits * object's width)*/
#define LV_DITHER_ERROR_DIFFUSION 0
#endif
/*Maximum buffer size to allocate for rotation.
*Only used if software rotation is enabled in the display driver.*/
#define LV_DISP_ROT_MAX_BUF (10*1024)
/*-------------
* GPU
*-----------*/
/*Use STM32's DMA2D (aka Chrom Art) GPU*/
#define LV_USE_GPU_STM32_DMA2D 0
#if LV_USE_GPU_STM32_DMA2D
/*Must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
#define LV_GPU_DMA2D_CMSIS_INCLUDE
#endif
/*Use NXP's PXP GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_PXP 0
#if LV_USE_GPU_NXP_PXP
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
*/
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
#endif
/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_VG_LITE 0
/*Use SDL renderer API*/
#define LV_USE_GPU_SDL 0
#if LV_USE_GPU_SDL
#define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
/*Texture cache size, 8MB by default*/
#define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8)
/*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/
#define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
#endif
/*-------------
* Logging
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 0
#if LV_USE_LOG
/*How important log should be added:
*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
*LV_LOG_LEVEL_INFO Log important events
*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
#define LV_LOG_PRINTF 0
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
#define LV_LOG_TRACE_MEM 1
#define LV_LOG_TRACE_TIMER 1
#define LV_LOG_TRACE_INDEV 1
#define LV_LOG_TRACE_DISP_REFR 1
#define LV_LOG_TRACE_EVENT 1
#define LV_LOG_TRACE_OBJ_CREATE 1
#define LV_LOG_TRACE_LAYOUT 1
#define LV_LOG_TRACE_ANIM 1
#endif /*LV_USE_LOG*/
/*-------------
* Asserts
*-----------*/
/*Enable asserts if an operation is failed or an invalid data is found.
*If LV_USE_LOG is enabled an error message will be printed on failure*/
#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/
#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/
#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/
/*Add a custom handler when assert happens e.g. to restart the MCU*/
#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
/*-------------
* Others
*-----------*/
/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
/*1: Show the used memory and the memory fragmentation
* Requires LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR 0
#if LV_USE_MEM_MONITOR
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif
/*1: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0
/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM
#define LV_SPRINTF_INCLUDE <stdio.h>
#define lv_snprintf snprintf
#define lv_vsnprintf vsnprintf
#else /*LV_SPRINTF_CUSTOM*/
#define LV_SPRINTF_USE_FLOAT 0
#endif /*LV_SPRINTF_CUSTOM*/
#define LV_USE_USER_DATA 1
/*Garbage Collector settings
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
/*=====================
* COMPILER SETTINGS
*====================*/
/*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0
/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC
/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER
/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY
/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
* E.g. __attribute__((aligned(4)))*/
#define LV_ATTRIBUTE_MEM_ALIGN
/*Attribute to mark large constant arrays for example font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST
/*Compiler prefix for a big array declaration in RAM*/
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
/*Place performance critical functions into a faster memory (e.g RAM)*/
#define LV_ATTRIBUTE_FAST_MEM
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
*should also appear on LVGL binding API such as Micropython.*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
/*==================
* FONT USAGE
*===================*/
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 1
#define LV_FONT_MONTSERRAT_22 0
#define LV_FONT_MONTSERRAT_24 1
#define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 0
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 0
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 1
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
/*Demonstrate special features*/
#define LV_FONT_MONTSERRAT_12_SUBPX 0
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
/*Pixel perfect monospace fonts*/
#define LV_FONT_UNSCII_8 0
#define LV_FONT_UNSCII_16 0
/*Optionally declare custom fonts here.
*You can use these fonts as default font too and they will be available globally.
*E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
#define LV_FONT_CUSTOM_DECLARE
/*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_montserrat_14
/*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp.
*Compiler error will be triggered if a font needs it.*/
#define LV_FONT_FMT_TXT_LARGE 0
/*Enables/disables support for compressed fonts.*/
#define LV_USE_FONT_COMPRESSED 0
/*Enable subpixel rendering*/
#define LV_USE_FONT_SUBPX 0
#if LV_USE_FONT_SUBPX
/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
#endif
/*=================
* TEXT SETTINGS
*=================*/
/**
* Select a character encoding for strings.
* Your IDE or editor should have the same character encoding
* - LV_TXT_ENC_UTF8
* - LV_TXT_ENC_ASCII
*/
#define LV_TXT_ENC LV_TXT_ENC_UTF8
/*Can break (wrap) texts on these chars*/
#define LV_TXT_BREAK_CHARS " ,.;:-_"
/*If a word is at least this long, will break wherever "prettiest"
*To disable, set to a value <= 0*/
#define LV_TXT_LINE_BREAK_LONG_LEN 0
/*Minimum number of characters in a long word to put on a line before a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
/*Minimum number of characters in a long word to put on a line after a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
/*The control character to use for signalling text recoloring.*/
#define LV_TXT_COLOR_CMD "#"
/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
*The direction will be processed according to the Unicode Bidirectional Algorithm:
*https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
#define LV_USE_BIDI 0
#if LV_USE_BIDI
/*Set the default direction. Supported values:
*`LV_BASE_DIR_LTR` Left-to-Right
*`LV_BASE_DIR_RTL` Right-to-Left
*`LV_BASE_DIR_AUTO` detect texts base direction*/
#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
#endif
/*Enable Arabic/Persian processing
*In these languages characters should be replaced with an other form based on their position in the text*/
#define LV_USE_ARABIC_PERSIAN_CHARS 0
/*==================
* WIDGET USAGE
*================*/
/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
#define LV_USE_ARC 1
#define LV_USE_ANIMIMG 1
#define LV_USE_BAR 1
#define LV_USE_BTN 1
#define LV_USE_BTNMATRIX 1
#define LV_USE_CANVAS 1
#define LV_USE_CHECKBOX 1
#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/
#define LV_USE_IMG 1 /*Requires: lv_label*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL
#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
#endif
#define LV_USE_LINE 1
#define LV_USE_ROLLER 1 /*Requires: lv_label*/
#if LV_USE_ROLLER
#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
#define LV_USE_SWITCH 1
#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
#if LV_USE_TEXTAREA != 0
#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#endif
#define LV_USE_TABLE 1
/*==================
* EXTRA COMPONENTS
*==================*/
/*-----------
* Widgets
*----------*/
#define LV_USE_CALENDAR 1
#if LV_USE_CALENDAR
#define LV_CALENDAR_WEEK_STARTS_MONDAY 0
#if LV_CALENDAR_WEEK_STARTS_MONDAY
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
#else
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
#endif
#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
#define LV_USE_CALENDAR_HEADER_ARROW 1
#define LV_USE_CALENDAR_HEADER_DROPDOWN 1
#endif /*LV_USE_CALENDAR*/
#define LV_USE_CHART 1
#define LV_USE_COLORWHEEL 1
#define LV_USE_IMGBTN 1
#define LV_USE_KEYBOARD 1
#define LV_USE_LED 1
#define LV_USE_LIST 1
#define LV_USE_MENU 1
#define LV_USE_METER 1
#define LV_USE_MSGBOX 1
#define LV_USE_SPINBOX 1
#define LV_USE_SPINNER 1
#define LV_USE_TABVIEW 1
#define LV_USE_TILEVIEW 1
#define LV_USE_WIN 1
#define LV_USE_SPAN 1
#if LV_USE_SPAN
/*A line text can contain maximum num of span descriptor */
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/*-----------
* Themes
*----------*/
/*A simple, impressive and very complete theme*/
#define LV_USE_THEME_DEFAULT 1
#if LV_USE_THEME_DEFAULT
/*0: Light mode; 1: Dark mode*/
#define LV_THEME_DEFAULT_DARK 0
/*1: Enable grow on press*/
#define LV_THEME_DEFAULT_GROW 1
/*Default transition time in [ms]*/
#define LV_THEME_DEFAULT_TRANSITION_TIME 80
#endif /*LV_USE_THEME_DEFAULT*/
/*A very simple theme that is a good starting point for a custom theme*/
#define LV_USE_THEME_BASIC 1
/*A theme designed for monochrome displays*/
#define LV_USE_THEME_MONO 1
/*-----------
* Layouts
*----------*/
/*A layout similar to Flexbox in CSS.*/
#define LV_USE_FLEX 1
/*A layout similar to Grid in CSS.*/
#define LV_USE_GRID 1
/*---------------------
* 3rd party libraries
*--------------------*/
/*File system interfaces for common APIs */
/*API for fopen, fread, etc*/
#define LV_USE_FS_STDIO 0
#if LV_USE_FS_STDIO
#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for open, read, etc*/
#define LV_USE_FS_POSIX 0
#if LV_USE_FS_POSIX
#define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for CreateFile, ReadFile, etc*/
#define LV_USE_FS_WIN32 0
#if LV_USE_FS_WIN32
#define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
#define LV_USE_FS_FATFS 0
#if LV_USE_FS_FATFS
#define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*PNG decoder library*/
#define LV_USE_PNG 0
/*BMP decoder library*/
#define LV_USE_BMP 0
/* JPG + split JPG decoder library.
* Split JPG is a custom format optimized for embedded systems. */
#define LV_USE_SJPG 0
/*GIF decoder library*/
#define LV_USE_GIF 0
/*QR code library*/
#define LV_USE_QRCODE 0
/*FreeType library*/
#define LV_USE_FREETYPE 0
#if LV_USE_FREETYPE
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
#define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#if LV_FREETYPE_CACHE_SIZE >= 0
/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
/* if font size >= 256, must be configured as image cache */
#define LV_FREETYPE_SBIT_CACHE 0
/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
/* (0:use system defaults) */
#define LV_FREETYPE_CACHE_FT_FACES 0
#define LV_FREETYPE_CACHE_FT_SIZES 0
#endif
#endif
/*Rlottie library*/
#define LV_USE_RLOTTIE 0
/*FFmpeg library for image decoding and playing videos
*Supports all major image formats so do not enable other image decoder with it*/
#define LV_USE_FFMPEG 0
#if LV_USE_FFMPEG
/*Dump input information to stderr*/
#define LV_FFMPEG_AV_DUMP_FORMAT 0
#endif
/*-----------
* Others
*----------*/
/*1: Enable API to take snapshot for object*/
#define LV_USE_SNAPSHOT 0
/*1: Enable Monkey test*/
#define LV_USE_MONKEY 0
/*1: Enable grid navigation*/
#define LV_USE_GRIDNAV 0
/*==================
* EXAMPLES
*==================*/
/*Enable the examples to be built with the library*/
#define LV_BUILD_EXAMPLES 1
/*===================
* DEMO USAGE
====================*/
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
#define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 0
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
/*Music player demo*/
#define LV_USE_DEMO_MUSIC 0
#if LV_USE_DEMO_MUSIC
# define LV_DEMO_MUSIC_SQUARE 0
# define LV_DEMO_MUSIC_LANDSCAPE 0
# define LV_DEMO_MUSIC_ROUND 0
# define LV_DEMO_MUSIC_LARGE 0
# define LV_DEMO_MUSIC_AUTO_PLAY 0
#endif
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/
#endif /*End of "Content enable"*/

143
EC600U_lvgl_lib/lvgl/lvgl.h Normal file
View File

@ -0,0 +1,143 @@
/**
* @file lvgl.h
* Include all LVGL related headers
*/
#ifndef LVGL_H
#define LVGL_H
#ifdef __cplusplus
extern "C" {
#endif
/***************************
* CURRENT VERSION OF LVGL
***************************/
#define LVGL_VERSION_MAJOR 8
#define LVGL_VERSION_MINOR 2
#define LVGL_VERSION_PATCH 0
#define LVGL_VERSION_INFO ""
/*********************
* INCLUDES
*********************/
#include "src/misc/lv_log.h"
#include "src/misc/lv_timer.h"
#include "src/misc/lv_math.h"
#include "src/misc/lv_mem.h"
#include "src/misc/lv_async.h"
#include "src/misc/lv_anim_timeline.h"
#include "src/misc/lv_printf.h"
#include "src/hal/lv_hal.h"
#include "src/core/lv_obj.h"
#include "src/core/lv_group.h"
#include "src/core/lv_indev.h"
#include "src/core/lv_refr.h"
#include "src/core/lv_disp.h"
#include "src/core/lv_theme.h"
#include "src/font/lv_font.h"
#include "src/font/lv_font_loader.h"
#include "src/font/lv_font_fmt_txt.h"
#include "src/widgets/lv_arc.h"
#include "src/widgets/lv_btn.h"
#include "src/widgets/lv_img.h"
#include "src/widgets/lv_label.h"
#include "src/widgets/lv_line.h"
#include "src/widgets/lv_table.h"
#include "src/widgets/lv_checkbox.h"
#include "src/widgets/lv_bar.h"
#include "src/widgets/lv_slider.h"
#include "src/widgets/lv_btnmatrix.h"
#include "src/widgets/lv_dropdown.h"
#include "src/widgets/lv_roller.h"
#include "src/widgets/lv_textarea.h"
#include "src/widgets/lv_canvas.h"
#include "src/widgets/lv_switch.h"
#include "src/draw/lv_draw.h"
#include "src/lv_api_map.h"
/*-----------------
* EXTRAS
*----------------*/
#include "src/extra/lv_extra.h"
#include "src/extra/widgets/lv_widgets.h"
#include "src/extra/layouts/lv_layouts.h"
#include "src/extra/themes/lv_themes.h"
#include "src/extra/others/lv_others.h"
#include "src/extra/libs/lv_libs.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/** Gives 1 if the x.y.z version is supported in the current version
* Usage:
*
* - Require v6
* #if LV_VERSION_CHECK(6,0,0)
* new_func_in_v6();
* #endif
*
*
* - Require at least v5.3
* #if LV_VERSION_CHECK(5,3,0)
* new_feature_from_v5_3();
* #endif
*
*
* - Require v5.3.2 bugfixes
* #if LV_VERSION_CHECK(5,3,2)
* bugfix_in_v5_3_2();
* #endif
*
*/
#define LV_VERSION_CHECK(x,y,z) (x == LVGL_VERSION_MAJOR && (y < LVGL_VERSION_MINOR || (y == LVGL_VERSION_MINOR && z <= LVGL_VERSION_PATCH)))
/**
* Wrapper functions for VERSION macros
*/
static inline int lv_version_major(void)
{
return LVGL_VERSION_MAJOR;
}
static inline int lv_version_minor(void)
{
return LVGL_VERSION_MINOR;
}
static inline int lv_version_patch(void)
{
return LVGL_VERSION_PATCH;
}
static inline const char *lv_version_info(void)
{
return LVGL_VERSION_INFO;
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LVGL_H*/

View File

@ -0,0 +1,20 @@
CSRCS += lv_disp.c
CSRCS += lv_group.c
CSRCS += lv_indev.c
CSRCS += lv_indev_scroll.c
CSRCS += lv_obj.c
CSRCS += lv_obj_class.c
CSRCS += lv_obj_draw.c
CSRCS += lv_obj_pos.c
CSRCS += lv_obj_scroll.c
CSRCS += lv_obj_style.c
CSRCS += lv_obj_style_gen.c
CSRCS += lv_obj_tree.c
CSRCS += lv_event.c
CSRCS += lv_refr.c
CSRCS += lv_theme.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core"

View File

@ -0,0 +1,440 @@
/**
* @file lv_disp.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_disp.h"
#include "../misc/lv_math.h"
#include "../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scr_load_anim_start(lv_anim_t * a);
static void opa_scale_anim(void * obj, int32_t v);
static void set_x_anim(void * obj, int32_t v);
static void set_y_anim(void * obj, int32_t v);
static void scr_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Return with a pointer to the active screen
* @param disp pointer to display which active screen should be get. (NULL to use the default
* screen)
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered to get its active screen");
return NULL;
}
return disp->act_scr;
}
/**
* Return with a pointer to the previous screen. Only used during screen transitions.
* @param disp pointer to display which previous screen should be get. (NULL to use the default
* screen)
* @return pointer to the previous screen object or NULL if not used now
*/
lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered to get its previous screen");
return NULL;
}
return disp->prev_scr;
}
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_load_scr(lv_obj_t * scr)
{
lv_disp_t * d = lv_obj_get_disp(scr);
if(!d) return; /*Shouldn't happen, just to be sure*/
if(d->act_scr) lv_event_send(d->act_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
d->act_scr = scr;
if(d->act_scr) lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOADED, NULL);
lv_obj_invalidate(scr);
}
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
return NULL;
}
return disp->top_layer;
}
/**
* Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
* layer)
* @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
* @return pointer to the sys layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer");
return NULL;
}
return disp->sys_layer;
}
/**
* Set the theme of a display
* @param disp pointer to a display
*/
void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th)
{
if(disp == NULL) disp = lv_disp_get_default();
disp->theme = th;
if(disp->screen_cnt == 3 &&
lv_obj_get_child_cnt(disp->screens[0]) == 0 &&
lv_obj_get_child_cnt(disp->screens[1]) == 0 &&
lv_obj_get_child_cnt(disp->screens[2]) == 0) {
lv_theme_apply(disp->screens[0]);
}
}
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
lv_theme_t * lv_disp_get_theme(lv_disp_t * disp)
{
if(disp == NULL) disp = lv_disp_get_default();
return disp->theme;
}
/**
* Set the background color of a display
* @param disp pointer to a display
* @param color color of the background
*/
void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_color = color;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Set the background image of a display
* @param disp pointer to a display
* @param img_src path to file or pointer to an `lv_img_dsc_t` variable
*/
void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_img = img_src;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Set opacity of the background
* @param disp pointer to a display
* @param opa opacity (0..255)
*/
void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("no display registered");
return;
}
disp->bg_opa = opa;
lv_area_t a;
lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
_lv_inv_area(disp, &a);
}
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
* @param time time of the animation
* @param delay delay before the transition
* @param auto_del true: automatically delete the old screen
*/
void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del)
{
lv_disp_t * d = lv_obj_get_disp(new_scr);
lv_obj_t * act_scr = lv_scr_act();
if(d->scr_to_load && act_scr != d->scr_to_load) {
lv_disp_load_scr(d->scr_to_load);
lv_anim_del(d->scr_to_load, NULL);
lv_obj_set_pos(d->scr_to_load, 0, 0);
lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0);
if(d->del_prev) {
lv_obj_del(act_scr);
}
act_scr = d->scr_to_load;
}
d->scr_to_load = new_scr;
if(d->prev_scr && d->del_prev) {
lv_obj_del(d->prev_scr);
d->prev_scr = NULL;
}
d->del_prev = auto_del;
/*Be sure there is no other animation on the screens*/
lv_anim_del(new_scr, NULL);
lv_anim_del(lv_scr_act(), NULL);
/*Be sure both screens are in a normal position*/
lv_obj_set_pos(new_scr, 0, 0);
lv_obj_set_pos(lv_scr_act(), 0, 0);
lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0);
lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0);
lv_anim_t a_new;
lv_anim_init(&a_new);
lv_anim_set_var(&a_new, new_scr);
lv_anim_set_start_cb(&a_new, scr_load_anim_start);
lv_anim_set_ready_cb(&a_new, scr_anim_ready);
lv_anim_set_time(&a_new, time);
lv_anim_set_delay(&a_new, delay);
lv_anim_t a_old;
lv_anim_init(&a_old);
lv_anim_set_var(&a_old, d->act_scr);
lv_anim_set_time(&a_old, time);
lv_anim_set_delay(&a_old, delay);
switch(anim_type) {
case LV_SCR_LOAD_ANIM_NONE:
/*Create a dummy animation to apply the delay*/
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, 0, 0);
break;
case LV_SCR_LOAD_ANIM_OVER_LEFT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_RIGHT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_TOP:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_OVER_BOTTOM:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
break;
case LV_SCR_LOAD_ANIM_MOVE_LEFT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
lv_anim_set_exec_cb(&a_new, set_x_anim);
lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_x_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_TOP:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_MOVE_BOTTOM:
lv_anim_set_exec_cb(&a_new, set_y_anim);
lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
lv_anim_set_exec_cb(&a_old, set_y_anim);
lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
break;
case LV_SCR_LOAD_ANIM_FADE_ON:
lv_anim_set_exec_cb(&a_new, opa_scale_anim);
lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER);
break;
}
lv_anim_start(&a_new);
lv_anim_start(&a_old);
}
/**
* Get elapsed time since last user activity on a display (e.g. click)
* @param disp pointer to a display (NULL to get the overall smallest inactivity)
* @return elapsed ticks (milliseconds) since the last activity
*/
uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp)
{
if(disp) return lv_tick_elaps(disp->last_activity_time);
lv_disp_t * d;
uint32_t t = UINT32_MAX;
d = lv_disp_get_next(NULL);
while(d) {
uint32_t elaps = lv_tick_elaps(d->last_activity_time);
t = LV_MIN(t, elaps);
d = lv_disp_get_next(d);
}
return t;
}
/**
* Manually trigger an activity on a display
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_trig_activity(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_trig_activity: no display registered");
return;
}
disp->last_activity_time = lv_tick_get();
}
/**
* Clean any CPU cache that is related to the display.
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_clean_dcache(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_clean_dcache: no display registered");
return;
}
if(disp->driver->clean_dcache_cb)
disp->driver->clean_dcache_cb(disp->driver);
}
/**
* Get a pointer to the screen refresher timer to
* modify its parameters with `lv_timer_...` functions.
* @param disp pointer to a display
* @return pointer to the display refresher timer. (NULL on error)
*/
lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_disp_get_refr_timer: no display registered");
return NULL;
}
return disp->refr_timer;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scr_load_anim_start(lv_anim_t * a)
{
lv_disp_t * d = lv_obj_get_disp(a->var);
d->prev_scr = lv_scr_act();
lv_disp_load_scr(a->var);
}
static void opa_scale_anim(void * obj, int32_t v)
{
lv_obj_set_style_opa(obj, v, 0);
}
static void set_x_anim(void * obj, int32_t v)
{
lv_obj_set_x(obj, v);
}
static void set_y_anim(void * obj, int32_t v)
{
lv_obj_set_y(obj, v);
}
static void scr_anim_ready(lv_anim_t * a)
{
lv_disp_t * d = lv_obj_get_disp(a->var);
if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr);
d->prev_scr = NULL;
d->scr_to_load = NULL;
lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
}

View File

@ -0,0 +1,244 @@
/**
* @file lv_disp.h
*
*/
#ifndef LV_DISP_H
#define LV_DISP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../hal/lv_hal.h"
#include "lv_obj.h"
#include "lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_SCR_LOAD_ANIM_NONE,
LV_SCR_LOAD_ANIM_OVER_LEFT,
LV_SCR_LOAD_ANIM_OVER_RIGHT,
LV_SCR_LOAD_ANIM_OVER_TOP,
LV_SCR_LOAD_ANIM_OVER_BOTTOM,
LV_SCR_LOAD_ANIM_MOVE_LEFT,
LV_SCR_LOAD_ANIM_MOVE_RIGHT,
LV_SCR_LOAD_ANIM_MOVE_TOP,
LV_SCR_LOAD_ANIM_MOVE_BOTTOM,
LV_SCR_LOAD_ANIM_FADE_ON,
} lv_scr_load_anim_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Return with a pointer to the active screen
* @param disp pointer to display which active screen should be get. (NULL to use the default
* screen)
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp);
/**
* Return with a pointer to the previous screen. Only used during screen transitions.
* @param disp pointer to display which previous screen should be get. (NULL to use the default
* screen)
* @return pointer to the previous screen object or NULL if not used now
*/
lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp);
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_load_scr(lv_obj_t * scr);
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp);
/**
* Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
* layer)
* @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
* @return pointer to the sys layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp);
/**
* Set the theme of a display
* @param disp pointer to a display
*/
void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th);
/**
* Get the theme of a display
* @param disp pointer to a display
* @return the display's theme (can be NULL)
*/
lv_theme_t * lv_disp_get_theme(lv_disp_t * disp);
/**
* Set the background color of a display
* @param disp pointer to a display
* @param color color of the background
*/
void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color);
/**
* Set the background image of a display
* @param disp pointer to a display
* @param img_src path to file or pointer to an `lv_img_dsc_t` variable
*/
void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src);
/**
* Set opacity of the background
* @param disp pointer to a display
* @param opa opacity (0..255)
*/
void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa);
/**
* Switch screen with animation
* @param scr pointer to the new screen to load
* @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
* @param time time of the animation
* @param delay delay before the transition
* @param auto_del true: automatically delete the old screen
*/
void lv_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del);
/**
* Get elapsed time since last user activity on a display (e.g. click)
* @param disp pointer to a display (NULL to get the overall smallest inactivity)
* @return elapsed ticks (milliseconds) since the last activity
*/
uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp);
/**
* Manually trigger an activity on a display
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_trig_activity(lv_disp_t * disp);
/**
* Clean any CPU cache that is related to the display.
* @param disp pointer to a display (NULL to use the default display)
*/
void lv_disp_clean_dcache(lv_disp_t * disp);
/**
* Get a pointer to the screen refresher timer to
* modify its parameters with `lv_timer_...` functions.
* @param disp pointer to a display
* @return pointer to the display refresher timer. (NULL on error)
*/
lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp);
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
/**
* Get the active screen of the default display
* @return pointer to the active screen
*/
static inline lv_obj_t * lv_scr_act(void)
{
return lv_disp_get_scr_act(lv_disp_get_default());
}
/**
* Get the top layer of the default display
* @return pointer to the top layer
*/
static inline lv_obj_t * lv_layer_top(void)
{
return lv_disp_get_layer_top(lv_disp_get_default());
}
/**
* Get the active screen of the default display
* @return pointer to the sys layer
*/
static inline lv_obj_t * lv_layer_sys(void)
{
return lv_disp_get_layer_sys(lv_disp_get_default());
}
static inline void lv_scr_load(lv_obj_t * scr)
{
lv_disp_load_scr(scr);
}
/**********************
* MACROS
**********************/
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
#ifndef LV_HOR_RES
/**
* The horizontal resolution of the currently active display.
*/
#define LV_HOR_RES lv_disp_get_hor_res(lv_disp_get_default())
#endif
#ifndef LV_VER_RES
/**
* The vertical resolution of the currently active display.
*/
#define LV_VER_RES lv_disp_get_ver_res(lv_disp_get_default())
#endif
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the default display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_dpx(lv_coord_t n)
{
return LV_DPX(n);
}
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the given display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param obj a display whose dpi should be considered
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_disp_dpx(const lv_disp_t * disp, lv_coord_t n)
{
return _LV_DPX_CALC(lv_disp_get_dpi(disp), n);
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DISP_H*/

View File

@ -0,0 +1,480 @@
/**
* @file lv_event.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_event_dsc_t {
lv_event_cb_t cb;
void * user_data;
lv_event_code_t filter : 8;
} lv_event_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id);
static lv_res_t event_send_core(lv_event_t * e);
static bool event_is_bubbled(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
static lv_event_t * event_head;
/**********************
* MACROS
**********************/
#if LV_LOG_TRACE_EVENT
#define EVENT_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define EVENT_TRACE(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param)
{
if(obj == NULL) return LV_RES_OK;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_event_t e;
e.target = obj;
e.current_target = obj;
e.code = event_code;
e.user_data = NULL;
e.param = param;
e.deleted = 0;
/*Build a simple linked list from the objects used in the events
*It's important to know if an this object was deleted by a nested event
*called from this `event_cb`.*/
e.prev = event_head;
event_head = &e;
/*Send the event*/
lv_res_t res = event_send_core(&e);
/*Remove this element from the list*/
event_head = e.prev;
return res;
}
lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e)
{
const lv_obj_class_t * base;
if(class_p == NULL) base = e->current_target->class_p;
else base = class_p->base_class;
/*Find a base in which Call the ancestor's event handler_cb is set*/
while(base && base->event_cb == NULL) base = base->base_class;
if(base == NULL) return LV_RES_OK;
if(base->event_cb == NULL) return LV_RES_OK;
/*Call the actual event callback*/
e->user_data = NULL;
base->event_cb(base, e);
lv_res_t res = LV_RES_OK;
/*Stop if the object is deleted*/
if(e->deleted) res = LV_RES_INV;
return res;
}
lv_obj_t * lv_event_get_target(lv_event_t * e)
{
return e->target;
}
lv_obj_t * lv_event_get_current_target(lv_event_t * e)
{
return e->current_target;
}
lv_event_code_t lv_event_get_code(lv_event_t * e)
{
return e->code;
}
void * lv_event_get_param(lv_event_t * e)
{
return e->param;
}
void * lv_event_get_user_data(lv_event_t * e)
{
return e->user_data;
}
uint32_t lv_event_register_id(void)
{
static uint32_t last_id = _LV_EVENT_LAST;
last_id ++;
return last_id;
}
void _lv_event_mark_deleted(lv_obj_t * obj)
{
lv_event_t * e = event_head;
while(e) {
if(e->current_target == obj || e->target == obj) e->deleted = 1;
e = e->prev;
}
}
struct _lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter,
void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->event_dsc_cnt++;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb;
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].filter = filter;
obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data;
return &obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1];
}
bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
bool lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, const void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb) &&
obj->spec_attr->event_dsc[i].user_data == user_data) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
bool lv_obj_remove_event_dsc(lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return false;
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if(&obj->spec_attr->event_dsc[i] == event_dsc) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
}
obj->spec_attr->event_dsc_cnt--;
obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
return true;
}
}
/*No event handler found*/
return false;
}
lv_indev_t * lv_event_get_indev(lv_event_t * e)
{
if(e->code == LV_EVENT_PRESSED ||
e->code == LV_EVENT_PRESSING ||
e->code == LV_EVENT_PRESS_LOST ||
e->code == LV_EVENT_SHORT_CLICKED ||
e->code == LV_EVENT_LONG_PRESSED ||
e->code == LV_EVENT_LONG_PRESSED_REPEAT ||
e->code == LV_EVENT_CLICKED ||
e->code == LV_EVENT_RELEASED ||
e->code == LV_EVENT_SCROLL_BEGIN ||
e->code == LV_EVENT_SCROLL_END ||
e->code == LV_EVENT_SCROLL ||
e->code == LV_EVENT_GESTURE ||
e->code == LV_EVENT_KEY ||
e->code == LV_EVENT_FOCUSED ||
e->code == LV_EVENT_DEFOCUSED ||
e->code == LV_EVENT_LEAVE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_PART_BEGIN ||
e->code == LV_EVENT_DRAW_PART_END) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_clip_area(lv_event_t * e)
{
if(e->code == LV_EVENT_DRAW_MAIN ||
e->code == LV_EVENT_DRAW_MAIN_BEGIN ||
e->code == LV_EVENT_DRAW_MAIN_END ||
e->code == LV_EVENT_DRAW_POST ||
e->code == LV_EVENT_DRAW_POST_BEGIN ||
e->code == LV_EVENT_DRAW_POST_END) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
const lv_area_t * lv_event_get_old_size(lv_event_t * e)
{
if(e->code == LV_EVENT_SIZE_CHANGED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
uint32_t lv_event_get_key(lv_event_t * e)
{
if(e->code == LV_EVENT_KEY) {
uint32_t * k = lv_event_get_param(e);
if(k) return *k;
else return 0;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e)
{
if(e->code == LV_EVENT_SCROLL_BEGIN) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size)
{
if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * cur_size = lv_event_get_param(e);
*cur_size = LV_MAX(*cur_size, size);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
lv_point_t * lv_event_get_self_size_info(lv_event_t * e)
{
if(e->code == LV_EVENT_GET_SELF_SIZE) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e)
{
if(e->code == LV_EVENT_HIT_TEST) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return 0;
}
}
const lv_area_t * lv_event_get_cover_area(lv_event_t * e)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
return p->area;
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res)
{
if(e->code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * p = lv_event_get_param(e);
if(res > p->res) p->res = res; /*Save only "stronger" results*/
}
else {
LV_LOG_WARN("Not interpreted with this event code");
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!obj->spec_attr) return NULL;
if(id >= obj->spec_attr->event_dsc_cnt) return NULL;
return &obj->spec_attr->event_dsc[id];
}
static lv_res_t event_send_core(lv_event_t * e)
{
EVENT_TRACE("Sending event %d to %p with %p param", e->code, (void *)e->current_target, e->param);
/*Call the input device's feedback callback if set*/
lv_indev_t * indev_act = lv_indev_get_act();
if(indev_act) {
if(indev_act->driver->feedback_cb) indev_act->driver->feedback_cb(indev_act->driver, e->code);
}
lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(e->current_target, 0);
lv_res_t res = LV_RES_OK;
res = lv_obj_event_base(NULL, e);
uint32_t i = 0;
while(event_dsc && res == LV_RES_OK) {
if(event_dsc->cb && (event_dsc->filter == LV_EVENT_ALL || event_dsc->filter == e->code)) {
e->user_data = event_dsc->user_data;
event_dsc->cb(e);
/*Stop if the object is deleted*/
if(e->deleted) return LV_RES_INV;
}
i++;
event_dsc = lv_obj_get_event_dsc(e->current_target, i);
}
if(res == LV_RES_OK && e->current_target->parent && event_is_bubbled(e))
{
e->current_target = e->current_target->parent;
res = event_send_core(e);
if(res != LV_RES_OK) return LV_RES_INV;
}
return res;
}
static bool event_is_bubbled(lv_event_t * e)
{
/*Event codes that always bubble*/
switch(e->code) {
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
return true;
default:
break;
}
/*Check other codes only if bubbling is enabled*/
if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) == false) return false;
switch(e->code) {
case LV_EVENT_HIT_TEST:
case LV_EVENT_COVER_CHECK:
case LV_EVENT_REFR_EXT_DRAW_SIZE:
case LV_EVENT_DRAW_MAIN_BEGIN:
case LV_EVENT_DRAW_MAIN:
case LV_EVENT_DRAW_MAIN_END:
case LV_EVENT_DRAW_POST_BEGIN:
case LV_EVENT_DRAW_POST:
case LV_EVENT_DRAW_POST_END:
case LV_EVENT_DRAW_PART_BEGIN:
case LV_EVENT_DRAW_PART_END:
case LV_EVENT_REFRESH:
case LV_EVENT_DELETE:
case LV_EVENT_CHILD_CREATED:
case LV_EVENT_CHILD_DELETED:
case LV_EVENT_CHILD_CHANGED:
case LV_EVENT_SIZE_CHANGED:
case LV_EVENT_STYLE_CHANGED:
case LV_EVENT_GET_SELF_SIZE:
return false;
default:
return true;
}
}

View File

@ -0,0 +1,335 @@
/**
* @file lv_templ.h
*
*/
#ifndef LV_EVENT_H
#define LV_EVENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_event_dsc_t;
/**
* Type of event being sent to the object.
*/
typedef enum {
LV_EVENT_ALL = 0,
/** Input device events*/
LV_EVENT_PRESSED, /**< The object has been pressed*/
LV_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/
LV_EVENT_PRESS_LOST, /**< The object is still being pressed but slid cursor/finger off of the object */
LV_EVENT_SHORT_CLICKED, /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `long_press_time`. Not called if scrolled.*/
LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.*/
LV_EVENT_CLICKED, /**< Called on release if not scrolled (regardless to long press)*/
LV_EVENT_RELEASED, /**< Called in every cases when the object has been released*/
LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins*/
LV_EVENT_SCROLL_END, /**< Scrolling ends*/
LV_EVENT_SCROLL, /**< Scrolling*/
LV_EVENT_GESTURE, /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */
LV_EVENT_KEY, /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/
LV_EVENT_FOCUSED, /**< The object is focused*/
LV_EVENT_DEFOCUSED, /**< The object is defocused*/
LV_EVENT_LEAVE, /**< The object is defocused but still selected*/
LV_EVENT_HIT_TEST, /**< Perform advanced hit-testing*/
/** Drawing events*/
LV_EVENT_COVER_CHECK, /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/
LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get the required extra draw area around the object (e.g. for shadow). The event parameter is `lv_coord_t *` to store the size.*/
LV_EVENT_DRAW_MAIN_BEGIN, /**< Starting the main drawing phase*/
LV_EVENT_DRAW_MAIN, /**< Perform the main drawing*/
LV_EVENT_DRAW_MAIN_END, /**< Finishing the main drawing phase*/
LV_EVENT_DRAW_POST_BEGIN, /**< Starting the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_POST, /**< Perform the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_POST_END, /**< Finishing the post draw phase (when all children are drawn)*/
LV_EVENT_DRAW_PART_BEGIN, /**< Starting to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
LV_EVENT_DRAW_PART_END, /**< Finishing to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
/** Special events*/
LV_EVENT_VALUE_CHANGED, /**< The object's value has changed (i.e. slider moved)*/
LV_EVENT_INSERT, /**< A text is inserted to the object. The event data is `char *` being inserted.*/
LV_EVENT_REFRESH, /**< Notify the object to refresh something on it (for the user)*/
LV_EVENT_READY, /**< A process has finished*/
LV_EVENT_CANCEL, /**< A process has been cancelled */
/** Other events*/
LV_EVENT_DELETE, /**< Object is being deleted*/
LV_EVENT_CHILD_CHANGED, /**< Child was removed, added, or its size, position were changed */
LV_EVENT_CHILD_CREATED, /**< Child was created, always bubbles up to all parents*/
LV_EVENT_CHILD_DELETED, /**< Child was deleted, always bubbles up to all parents*/
LV_EVENT_SCREEN_LOADED, /**< A screen was loaded*/
LV_EVENT_SCREEN_UNLOADED, /**< A screen was unloaded*/
LV_EVENT_SIZE_CHANGED, /**< Object coordinates/size have changed*/
LV_EVENT_STYLE_CHANGED, /**< Object's style has changed*/
LV_EVENT_LAYOUT_CHANGED, /**< The children position has changed due to a layout recalculation*/
LV_EVENT_GET_SELF_SIZE, /**< Get the internal size of a widget*/
_LV_EVENT_LAST /** Number of default events*/
} lv_event_code_t;
typedef struct _lv_event_t {
struct _lv_obj_t * target;
struct _lv_obj_t * current_target;
lv_event_code_t code;
void * user_data;
void * param;
struct _lv_event_t * prev;
uint8_t deleted : 1;
} lv_event_t;
/**
* @brief Event callback.
* Events are used to notify the user of some action being taken on the object.
* For details, see ::lv_event_t.
*/
typedef void (*lv_event_cb_t)(lv_event_t * e);
/**
* Used as the event parameter of ::LV_EVENT_HIT_TEST to check if an `point` can click the object or not.
* `res` should be set like this:
* - If already set to `false` an other event wants that point non clickable. If you want to respect it leave it as `false` or set `true` to overwrite it.
* - If already set `true` and `point` shouldn't be clickable set to `false`
* - If already set to `true` you agree that `point` can click the object leave it as `true`
*/
typedef struct {
const lv_point_t * point; /**< A point relative to screen to check if it can click the object or not*/
bool res; /**< true: `point` can click the object; false: it cannot*/
} lv_hit_test_info_t;
/**
* Used as the event parameter of ::LV_EVENT_COVER_CHECK to check if an area is covered by the object or not.
* In the event use `const lv_area_t * area = lv_event_get_cover_area(e)` to get the area to check
* and `lv_event_set_cover_res(e, res)` to set the result.
*/
typedef struct {
lv_cover_res_t res;
const lv_area_t * area;
} lv_cover_check_info_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Send an event to the object
* @param obj pointer to an object
* @param event_code the type of the event from `lv_event_t`
* @param param arbitrary data depending on the widget type and the event. (Usually `NULL`)
* @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event_code
*/
lv_res_t lv_event_send(struct _lv_obj_t * obj, lv_event_code_t event_code, void * param);
/**
* Used by the widgets internally to call the ancestor widget types's event handler
* @param class_p pointer to the class of the widget (NOT the ancestor class)
* @param e pointer to the event descriptor
* @return LV_RES_OK: the taget object was not deleted in the event; LV_RES_INV: it was deleted in the event_code
*/
lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e);
/**
* Get the object originally targeted by the event. It's the same even if the event is bubbled.
* @param e pointer to the event descriptor
* @return the target of the event_code
*/
struct _lv_obj_t * lv_event_get_target(lv_event_t * e);
/**
* Get the current target of the event. It's the object which event handler being called.
* If the event is not bubbled it's the same as "normal" target.
* @param e pointer to the event descriptor
* @return pointer to the current target of the event_code
*/
struct _lv_obj_t * lv_event_get_current_target(lv_event_t * e);
/**
* Get the event code of an event
* @param e pointer to the event descriptor
* @return the event code. (E.g. `LV_EVENT_CLICKED`, `LV_EVENT_FOCUSED`, etc)
*/
lv_event_code_t lv_event_get_code(lv_event_t * e);
/**
* Get the parameter passed when the event was sent
* @param e pointer to the event descriptor
* @return pointer to the parameter
*/
void * lv_event_get_param(lv_event_t * e);
/**
* Get the user_data passed when the event was registered on the object
* @param e pointer to the event descriptor
* @return pointer to the user_data
*/
void * lv_event_get_user_data(lv_event_t * e);
/**
* Register a new, custom event ID.
* It can be used the same way as e.g. `LV_EVENT_CLICKED` to send custom events
* @return the new event id
* @example
* uint32_t LV_EVENT_MINE = 0;
* ...
* e = lv_event_register_id();
* ...
* lv_event_send(obj, LV_EVENT_MINE, &some_data);
*/
uint32_t lv_event_register_id(void);
/**
* Nested events can be called and one of them might belong to an object that is being deleted.
* Mark this object's `event_temp_data` deleted to know that its `lv_event_send` should return `LV_RES_INV`
* @param obj pointer to an object to mark as deleted
*/
void _lv_event_mark_deleted(struct _lv_obj_t * obj);
/**
* Add an event handler function for an object.
* Used by the user to react on event which happens with the object.
* An object can have multiple event handler. They will be called in the same order as they were added.
* @param obj pointer to an object
* @param filter and event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be sued the receive all the events.
* @param event_cb the new event function
* @param user_data custom data data will be available in `event_cb`
* @return a pointer the event descriptor. Can be used in ::lv_obj_remove_event_dsc
*/
struct _lv_event_dsc_t * lv_obj_add_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter,
void * user_data);
/**
* Remove an event handler function for an object.
* @param obj pointer to an object
* @param event_cb the event function to remove, or `NULL` to remove the the firstly added event callback
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
/**
* Remove an event handler function with a specific user_data from an object.
* @param obj pointer to an object
* @param event_cb the event function to remove, or `NULL` only `user_data` matters.
* @param event_user_data the user_data specified in ::lv_obj_add_event_cb
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_cb_with_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb,
const void * event_user_data);
/**
* DEPRACTED because doesn't work if multiple event handlers are added to an object.
* Remove an event handler function for an object.
* @param obj pointer to an object
* @param event_dsc pointer to an event descriptor to remove (returned by ::lv_obj_add_event_cb)
* @return true if any event handlers were removed
*/
bool lv_obj_remove_event_dsc(struct _lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc);
/**
* Get the input device passed as parameter to indev related events.
* @param e pointer to an event
* @return the indev that triggered the event or NULL if called on a not indev related event
*/
lv_indev_t * lv_event_get_indev(lv_event_t * e);
/**
* Get the part draw descriptor passed as parameter to `LV_EVENT_DRAW_PART_BEGIN/END`.
* @param e pointer to an event
* @return the part draw descriptor to hook the drawing or NULL if called on an unrelated event
*/
lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e);
/**
* Get the clip area passed as parameter to draw events events.
* Namely: `LV_EVENT_DRAW_MAIN/POST`, `LV_EVENT_DRAW_MAIN/POST_BEGIN`, `LV_EVENT_DRAW_MAIN/POST_END`
* @param e pointer to an event
* @return the clip area to use during drawing or NULL if called on an unrelated event
*/
const lv_area_t * lv_event_get_clip_area(lv_event_t * e);
/**
* Get the old area of the object before its size was changed. Can be used in `LV_EVENT_SIZE_CHANGED`
* @param e pointer to an event
* @return the old absolute area of the object or NULL if called on an unrelated event
*/
const lv_area_t * lv_event_get_old_size(lv_event_t * e);
/**
* Get the key passed as parameter to an event. Can be used in `LV_EVENT_KEY`
* @param e pointer to an event
* @return the triggering key or NULL if called on an unrelated event
*/
uint32_t lv_event_get_key(lv_event_t * e);
/**
* Get the animation descriptor of a scrolling. Can be used in `LV_EVENT_SCROLL_BEGIN`
* @param e pointer to an event
* @return the animation that will scroll the object. (can be modified as required)
*/
lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e);
/**
* Set the new extra draw size. Can be used in `LV_EVENT_REFR_EXT_DRAW_SIZE`
* @param e pointer to an event
* @param size The new extra draw size
*/
void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size);
/**
* Get a pointer to an `lv_point_t` variable in which the self size should be saved (width in `point->x` and height `point->y`).
* Can be used in `LV_EVENT_GET_SELF_SIZE`
* @param e pointer to an event
* @return pointer to `lv_point_t` or NULL if called on an unrelated event
*/
lv_point_t * lv_event_get_self_size_info(lv_event_t * e);
/**
* Get a pointer to an `lv_hit_test_info_t` variable in which the hit test result should be saved. Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return pointer to `lv_hit_test_info_t` or NULL if called on an unrelated event
*/
lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e);
/**
* Get a pointer to an area which should be examined whether the object fully covers it or not.
* Can be used in `LV_EVENT_HIT_TEST`
* @param e pointer to an event
* @return an area with absolute coordinates to check
*/
const lv_area_t * lv_event_get_cover_area(lv_event_t * e);
/**
* Set the result of cover checking. Can be used in `LV_EVENT_COVER_CHECK`
* @param e pointer to an event
* @param res an element of ::lv_cover_check_info_t
*/
void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EVENT_H*/

View File

@ -0,0 +1,459 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_group.h"
#include "../misc/lv_gc.h"
#include "../core/lv_obj.h"
#include "../core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *));
static void lv_group_refocus(lv_group_t * g);
static lv_indev_t * get_indev(const lv_group_t * g);
/**********************
* STATIC VARIABLES
**********************/
static lv_group_t * default_group;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_group_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_group_ll), sizeof(lv_group_t));
}
lv_group_t * lv_group_create(void)
{
lv_group_t * group = _lv_ll_ins_head(&LV_GC_ROOT(_lv_group_ll));
LV_ASSERT_MALLOC(group);
if(group == NULL) return NULL;
_lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
group->obj_focus = NULL;
group->frozen = 0;
group->focus_cb = NULL;
group->editing = 0;
group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
group->wrap = 1;
#if LV_USE_USER_DATA
group->user_data = NULL;
#endif
return group;
}
void lv_group_del(lv_group_t * group)
{
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
_LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
}
_lv_ll_clear(&(group->obj_ll));
_lv_ll_remove(&LV_GC_ROOT(_lv_group_ll), group);
lv_mem_free(group);
}
void lv_group_set_default(lv_group_t * group)
{
default_group = group;
}
lv_group_t * lv_group_get_default(void)
{
return default_group;
}
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
if(group == NULL) return;
LV_LOG_TRACE("begin");
/*Do not add the object twice*/
lv_obj_t ** obj_i;
_LV_LL_READ(&group->obj_ll, obj_i) {
if((*obj_i) == obj) {
LV_LOG_INFO("the object is already added to this group");
return;
}
}
/*If the object is already in a group and focused then refocus it*/
lv_group_t * group_cur = lv_obj_get_group(obj);
if(group_cur) {
if(obj->spec_attr->group_p && *(obj->spec_attr->group_p->obj_focus) == obj) {
lv_group_refocus(group_cur);
LV_LOG_INFO("changing object's group");
}
}
if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
obj->spec_attr->group_p = group;
lv_obj_t ** next = _lv_ll_ins_tail(&group->obj_ll);
LV_ASSERT_MALLOC(next);
if(next == NULL) return;
*next = obj;
/*If the head and the tail is equal then there is only one object in the linked list.
*In this case automatically activate it*/
if(_lv_ll_get_head(&group->obj_ll) == next) {
lv_group_refocus(group);
}
LV_LOG_TRACE("finished");
}
void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2)
{
lv_group_t * g1 = lv_obj_get_group(obj1);
lv_group_t * g2 = lv_obj_get_group(obj2);
if(g1 != g2) return;
if(g1 == NULL) return;
/*Do not add the object twice*/
lv_obj_t ** obj_i;
_LV_LL_READ(&g1->obj_ll, obj_i) {
if((*obj_i) == obj1)(*obj_i) = obj2;
else if((*obj_i) == obj2)(*obj_i) = obj1;
}
if(*g1->obj_focus == obj1) lv_group_focus_obj(obj2);
else if(*g1->obj_focus == obj2) lv_group_focus_obj(obj1);
}
void lv_group_remove_obj(lv_obj_t * obj)
{
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
LV_LOG_TRACE("begin");
/*Focus on the next object*/
if(g->obj_focus && *g->obj_focus == obj) {
if(g->frozen) g->frozen = 0;
/*If this is the only object in the group then focus to nothing.*/
if(_lv_ll_get_head(&g->obj_ll) == g->obj_focus && _lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
}
/*If there more objects in the group then focus to the next/prev object*/
else {
lv_group_refocus(g);
}
}
/*If the focuses object is still the same then it was the only object in the group but it will
*be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
*zero objects*/
if(g->obj_focus && *g->obj_focus == obj) {
g->obj_focus = NULL;
}
/*Search the object and remove it from its group*/
lv_obj_t ** i;
_LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
_lv_ll_remove(&g->obj_ll, i);
lv_mem_free(i);
if(obj->spec_attr) obj->spec_attr->group_p = NULL;
break;
}
}
LV_LOG_TRACE("finished");
}
void lv_group_remove_all_objs(lv_group_t * group)
{
/*Defocus the currently focused object*/
if(group->obj_focus != NULL) {
lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
lv_obj_invalidate(*group->obj_focus);
group->obj_focus = NULL;
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
_LV_LL_READ(&group->obj_ll, obj) {
if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
}
_lv_ll_clear(&(group->obj_ll));
}
void lv_group_focus_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_group_t * g = lv_obj_get_group(obj);
if(g == NULL) return;
if(g->frozen != 0) return;
/*On defocus edit mode must be leaved*/
lv_group_set_editing(g, false);
lv_obj_t ** i;
_LV_LL_READ(&g->obj_ll, i) {
if(*i == obj) {
if(g->obj_focus != NULL && obj != *g->obj_focus) { /*Do not defocus if the same object needs to be focused again*/
lv_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
if(g->obj_focus != NULL) {
if(g->focus_cb) g->focus_cb(g);
lv_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
break;
}
}
}
void lv_group_focus_next(lv_group_t * group)
{
focus_next_core(group, _lv_ll_get_head, _lv_ll_get_next);
}
void lv_group_focus_prev(lv_group_t * group)
{
focus_next_core(group, _lv_ll_get_tail, _lv_ll_get_prev);
}
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RES_OK;
return lv_event_send(act, LV_EVENT_KEY, &c);
}
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
group->focus_cb = focus_cb;
}
void lv_group_set_editing(lv_group_t * group, bool edit)
{
if(group == NULL) return;
uint8_t en_val = edit ? 1 : 0;
if(en_val == group->editing) return; /*Do not set the same mode again*/
group->editing = en_val;
lv_obj_t * focused = lv_group_get_focused(group);
if(focused) {
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(focused);
}
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
{
group->refocus_policy = policy & 0x01;
}
void lv_group_set_wrap(lv_group_t * group, bool en)
{
group->wrap = en ? 1 : 0;
}
lv_obj_t * lv_group_get_focused(const lv_group_t * group)
{
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
{
if(!group) return NULL;
return group->focus_cb;
}
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing ? true : false;
}
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap ? true : false;
}
uint32_t lv_group_get_obj_count(lv_group_t * group)
{
return _lv_ll_get_len(&group->obj_ll);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_group_refocus(lv_group_t * g)
{
/*Refocus must temporarily allow wrapping to work correctly*/
uint8_t temp_wrap = g->wrap;
g->wrap = 1;
if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
lv_group_focus_next(g);
else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
lv_group_focus_prev(g);
/*Restore wrap property*/
g->wrap = temp_wrap;
}
static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
void * (*move)(const lv_ll_t *, const void *))
{
if(group->frozen) return;
lv_obj_t ** obj_next = group->obj_focus;
lv_obj_t ** obj_sentinel = NULL;
bool can_move = true;
bool can_begin = true;
for(;;) {
if(obj_next == NULL) {
if(group->wrap || obj_sentinel == NULL) {
if(!can_begin) return;
obj_next = begin(&group->obj_ll);
can_move = false;
can_begin = false;
}
else {
/*Currently focused object is the last/first in the group, keep it that way*/
return;
}
}
if(obj_sentinel == NULL) {
obj_sentinel = obj_next;
if(obj_sentinel == NULL) return; /*Group is empty*/
}
if(can_move) {
obj_next = move(&group->obj_ll, obj_next);
/*Give up if we walked the entire list and haven't found another visible object*/
if(obj_next == obj_sentinel) return;
}
can_move = true;
if(obj_next == NULL) continue;
if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
/*Hidden objects don't receive focus.
*If any parent is hidden, the object is also hidden)*/
lv_obj_t * parent = *obj_next;
while(parent) {
if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
parent = lv_obj_get_parent(parent);
}
if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
/*If we got her a good candidate is found*/
break;
}
if(obj_next == group->obj_focus) return; /*There's only one visible object and it's already focused*/
if(group->obj_focus) {
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
}
group->obj_focus = obj_next;
lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
}
/**
* Find an indev preferably with KEYPAD or ENCOEDR type that uses the given group.
* In other words, find an indev, that is related to the given group.
* In the worst case simply return the latest indev
* @param g a group the find in the indevs
* @return the suggested indev
*/
static lv_indev_t * get_indev(const lv_group_t * g)
{
lv_indev_t * indev_encoder = NULL;
lv_indev_t * indev_group = NULL;
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev->group == g) {
/*Prefer KEYPAD*/
if(indev_type == LV_INDEV_TYPE_KEYPAD) return indev;
if(indev_type == LV_INDEV_TYPE_ENCODER) indev_encoder = indev;
indev_group = indev;
}
indev = lv_indev_get_next(indev);
}
if(indev_encoder) return indev_encoder;
if(indev_group) return indev_group;
/*In lack of a better option use the first input device. (It can be NULL if there is no input device)*/
return lv_indev_get_next(NULL);
}

View File

@ -0,0 +1,246 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include <stdbool.h>
#include "../misc/lv_ll.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/*Predefined keys to control the focused object via lv_group_send(group, c)*/
enum {
LV_KEY_UP = 17, /*0x11*/
LV_KEY_DOWN = 18, /*0x12*/
LV_KEY_RIGHT = 19, /*0x13*/
LV_KEY_LEFT = 20, /*0x14*/
LV_KEY_ESC = 27, /*0x1B*/
LV_KEY_DEL = 127, /*0x7F*/
LV_KEY_BACKSPACE = 8, /*0x08*/
LV_KEY_ENTER = 10, /*0x0A, '\n'*/
LV_KEY_NEXT = 9, /*0x09, '\t'*/
LV_KEY_PREV = 11, /*0x0B, '*/
LV_KEY_HOME = 2, /*0x02, STX*/
LV_KEY_END = 3, /*0x03, ETX*/
};
typedef uint8_t lv_key_t;
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_group_t;
typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
/**
* Groups can be used to logically hold objects so that they can be individually focused.
* They are NOT for laying out objects on a screen (try layouts for that).
*/
typedef struct _lv_group_t {
lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/
struct _lv_obj_t ** obj_focus; /**< The object in focus*/
lv_group_focus_cb_t focus_cb; /**< A function to call when a new object is focused (optional)*/
#if LV_USE_USER_DATA
void * user_data;
#endif
uint8_t frozen : 1; /**< 1: can't focus to new object*/
uint8_t editing : 1; /**< 1: Edit mode, 0: Navigate mode*/
uint8_t refocus_policy : 1; /**< 1: Focus prev if focused on deletion. 0: Focus next if focused on
deletion.*/
uint8_t wrap : 1; /**< 1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end
of list.*/
} lv_group_t;
typedef enum {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
} lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the group module
* @remarks Internal function, do not call directly.
*/
void _lv_group_init(void);
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void);
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group);
/**
* Set a default group. New object are added to this group if it's enabled in their class with `add_to_def_group = true`
* @param group pointer to a group (can be `NULL`)
*/
void lv_group_set_default(lv_group_t * group);
/**
* Get the default group
* @return pointer to the default group
*/
lv_group_t * lv_group_get_default(void);
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, struct _lv_obj_t * obj);
/**
* Swap 2 object in a group. The object must be in the same group
* @param obj1 pointer to an object
* @param obj2 pointer to an other object
*/
void lv_group_swap_obj(struct _lv_obj_t * obj1, struct _lv_obj_t * obj2);
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(struct _lv_obj_t * obj);
/**
* Remove all objects from a group
* @param group pointer to a group
*/
void lv_group_remove_all_objs(lv_group_t * group);
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(struct _lv_obj_t * obj);
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en);
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c);
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
/**
* Set whether the next or previous item in a group is focused if the currently focused obj is
* deleted.
* @param group pointer to a group
* @param policy new refocus policy enum
*/
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit);
/**
* Set whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
void lv_group_set_wrap(lv_group_t * group, bool en);
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
struct _lv_obj_t * lv_group_get_focused(const lv_group_t * group);
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group);
/**
* Get whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group);
/**
* Get the number of object in the group
* @param group pointer to a group
* @return number of objects in the group
*/
uint32_t lv_group_get_obj_count(lv_group_t * group);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GROUP_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
/**
* @file lv_indev.h
*
*/
#ifndef LV_INDEV_H
#define LV_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../hal/lv_hal_indev.h"
#include "lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Called periodically to read the input devices
* @param param pointer to and input device to read
*/
void lv_indev_read_timer_cb(lv_timer_t * timer);
void lv_indev_enable(lv_indev_t * indev, bool en);
/**
* Get the currently processed input device. Can be used in action functions too.
* @return pointer to the currently processed input device or NULL if no input device processing
* right now
*/
lv_indev_t * lv_indev_get_act(void);
/**
* Get the type of an input device
* @param indev pointer to an input device
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
*/
lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
/**
* Reset one or all input devices
* @param indev pointer to an input device to reset or NULL to reset all of them
* @param obj pointer to an object which triggers the reset.
*/
void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj);
/**
* Reset the long press state of an input device
* @param indev pointer to an input device
*/
void lv_indev_reset_long_press(lv_indev_t * indev);
/**
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
* @param indev pointer to an input device
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj);
/**
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group);
/**
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
* These points will be assigned to the buttons to press a specific point on the screen
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[]);
/**
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
/**
* Get the current gesture direct
* @param indev pointer to an input device
* @return current gesture direct
*/
lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev);
/**
* Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @return the last pressed key (0 on error)
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev);
/**
* Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return LV_DIR_NONE: no scrolling now
* LV_DIR_HOR/VER
*/
lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev);
/**
* Get the currently scrolled object (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return pointer to the currently scrolled object or NULL if no scrolling by this indev
*/
lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev);
/**
* Get the movement vector of an input device (for LV_INDEV_TYPE_POINTER and
* LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the types.pointer.vector
*/
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
/**
* Do nothing until the next release
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev);
/**
* Gets a pointer to the currently active object in the currently processed input device.
* @return pointer to currently active object or NULL if no active object
*/
lv_obj_t * lv_indev_get_obj_act(void);
/**
* Get a pointer to the indev read timer to
* modify its parameters with `lv_timer_...` functions.
* @param indev pointer to an input device
* @return pointer to the indev read refresher timer. (NULL on error)
*/
lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev);
/**
* Search the most top, clickable object by a point
* @param obj pointer to a start object, typically the screen
* @param point pointer to a point for searching the most top child
* @return pointer to the found object or NULL if there was no suitable object
*/
lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_INDEV_H*/

View File

@ -0,0 +1,647 @@
/**
* @file lv_indev_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_indev.h"
#include "lv_indev_scroll.h"
/*********************
* DEFINES
*********************/
#define ELASTIC_SLOWNESS_FACTOR 4 /*Scrolling on elastic parts are slower by this factor*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc);
static void init_scroll_limits(_lv_indev_proc_t * proc);
static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y);
static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc);
static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc);
static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
lv_dir_t dir);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_indev_scroll_handler(_lv_indev_proc_t * proc)
{
lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
/*If there is no scroll object yet try to find one*/
if(scroll_obj == NULL) {
proc->types.pointer.scroll_sum.x += proc->types.pointer.vect.x;
proc->types.pointer.scroll_sum.y += proc->types.pointer.vect.y;
scroll_obj = find_scroll_obj(proc);
if(scroll_obj == NULL) return;
init_scroll_limits(proc);
lv_event_send(scroll_obj, LV_EVENT_SCROLL_BEGIN, NULL);
if(proc->reset_query) return;
}
/*Set new position or scroll if the vector is not zero*/
if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
lv_coord_t diff_x = 0;
lv_coord_t diff_y = 0;
if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
}
else {
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
}
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
if((scroll_dir & LV_DIR_LEFT) == 0 && diff_x > 0) diff_x = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && diff_x < 0) diff_x = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && diff_y > 0) diff_y = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
/*Respect the scroll limit area*/
scroll_limit_diff(proc, &diff_x, &diff_y);
lv_obj_scroll_by(scroll_obj, diff_x, diff_y, LV_ANIM_OFF);
proc->types.pointer.scroll_sum.x += diff_x;
proc->types.pointer.scroll_sum.y += diff_y;
}
}
void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)
{
lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
if(scroll_obj == NULL) return;
if(proc->types.pointer.scroll_dir == LV_DIR_NONE) return;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
proc->types.pointer.scroll_throw_vect.y = 0;
proc->types.pointer.scroll_throw_vect.x = 0;
}
lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
if(proc->types.pointer.scroll_dir == LV_DIR_VER) {
proc->types.pointer.scroll_throw_vect.x = 0;
/*If no snapping "throw"*/
if(align_y == LV_SCROLL_SNAP_NONE) {
proc->types.pointer.scroll_throw_vect.y =
proc->types.pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
proc->types.pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.y, st, sb,
LV_DIR_VER);
lv_obj_scroll_by(scroll_obj, 0, proc->types.pointer.scroll_throw_vect.y, LV_ANIM_OFF);
}
/*With snapping find the nearest snap point and scroll there*/
else {
lv_coord_t diff_y = scroll_throw_predict_y(proc);
proc->types.pointer.scroll_throw_vect.y = 0;
scroll_limit_diff(proc, NULL, &diff_y);
lv_coord_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
}
}
else if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
proc->types.pointer.scroll_throw_vect.y = 0;
/*If no snapping "throw"*/
if(align_x == LV_SCROLL_SNAP_NONE) {
proc->types.pointer.scroll_throw_vect.x =
proc->types.pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
proc->types.pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.x, sl, sr,
LV_DIR_HOR);
lv_obj_scroll_by(scroll_obj, proc->types.pointer.scroll_throw_vect.x, 0, LV_ANIM_OFF);
}
/*With snapping find the nearest snap point and scroll there*/
else {
lv_coord_t diff_x = scroll_throw_predict_x(proc);
proc->types.pointer.scroll_throw_vect.x = 0;
scroll_limit_diff(proc, &diff_x, NULL);
lv_coord_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
}
}
/*Check if the scroll has finished*/
if(proc->types.pointer.scroll_throw_vect.x == 0 && proc->types.pointer.scroll_throw_vect.y == 0) {
/*Revert if scrolled in*/
/*If vertically scrollable and not controlled by snap*/
if(align_y == LV_SCROLL_SNAP_NONE) {
lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
if(st > 0 || sb > 0) {
if(st < 0) {
lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
}
else if(sb < 0) {
lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
}
}
}
/*If horizontally scrollable and not controlled by snap*/
if(align_x == LV_SCROLL_SNAP_NONE) {
lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
if(sl > 0 || sr > 0) {
if(sl < 0) {
lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
}
else if(sr < 0) {
lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
}
}
}
lv_event_send(scroll_obj, LV_EVENT_SCROLL_END, indev_act);
if(proc->reset_query) return;
proc->types.pointer.scroll_dir = LV_DIR_NONE;
proc->types.pointer.scroll_obj = NULL;
}
}
/**
* Predict where would a scroll throw end
* @param indev pointer to an input device
* @param dir `LV_DIR_VER` or `LV_DIR_HOR`
* @return the difference compared to the current position when the throw would be finished
*/
lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
{
if(indev == NULL) return 0;
lv_coord_t v;
switch(dir) {
case LV_DIR_VER:
v = indev->proc.types.pointer.scroll_throw_vect_ori.y;
break;
case LV_DIR_HOR:
v = indev->proc.types.pointer.scroll_throw_vect_ori.x;
break;
default:
return 0;
}
lv_coord_t scroll_throw = indev->driver->scroll_throw;
lv_coord_t sum = 0;
while(v) {
sum += v;
v = v * (100 - scroll_throw) / 100;
}
return sum;
}
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
{
p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc)
{
lv_obj_t * obj_candidate = NULL;
lv_dir_t dir_candidate = LV_DIR_NONE;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_limit = indev_act->driver->scroll_limit;
/*Go until find a scrollable object in the current direction
*More precisely:
* 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
* 2. Scrollable means it has some content out of its area
* 3. If an object can be scrolled into the current direction then use it ("real match"")
* 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
* 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
lv_obj_t * obj_act = proc->types.pointer.act_obj;
while(obj_act) {
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
/*If this object don't want to chain the scroll ot the parent stop searching*/
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN) == false) break;
obj_act = lv_obj_get_parent(obj_act);
continue;
}
/*Decide if it's a horizontal or vertical scroll*/
bool hor_en = false;
bool ver_en = false;
if(LV_ABS(proc->types.pointer.scroll_sum.x) > LV_ABS(proc->types.pointer.scroll_sum.y)) {
hor_en = true;
}
else {
ver_en = true;
}
/*Consider both up-down or left/right scrollable according to the current direction*/
bool up_en = ver_en;
bool down_en = ver_en;
bool left_en = hor_en;
bool right_en = hor_en;
/*The object might have disabled some directions.*/
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
/*The object is scrollable to a direction if its content overflow in that direction.*/
lv_coord_t st = lv_obj_get_scroll_top(obj_act);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj_act);
lv_coord_t sl = lv_obj_get_scroll_left(obj_act);
lv_coord_t sr = lv_obj_get_scroll_right(obj_act);
/*If this object is scrollable into the current scroll direction then save it as a candidate.
*It's important only to be scrollable on the current axis (hor/ver) because if the scroll
*is propagated to this object it can show at least elastic scroll effect.
*But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
if((st > 0 || sb > 0) &&
((up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
(down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit))) {
obj_candidate = obj_act;
dir_candidate = LV_DIR_VER;
}
if((sl > 0 || sr > 0) &&
((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
(right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit))) {
obj_candidate = obj_act;
dir_candidate = LV_DIR_HOR;
}
if(st <= 0) up_en = false;
if(sb <= 0) down_en = false;
if(sl <= 0) left_en = false;
if(sr <= 0) right_en = false;
/*If the object really can be scrolled into the current direction the use it.*/
if((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
(right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit) ||
(up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
(down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit)) {
proc->types.pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
break;
}
/*If this object don't want to chain the scroll ot the parent stop searching*/
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN) == false) break;
/*Try the parent*/
obj_act = lv_obj_get_parent(obj_act);
}
/*Use the last candidate*/
if(obj_candidate) {
proc->types.pointer.scroll_dir = dir_candidate;
proc->types.pointer.scroll_obj = obj_candidate;
proc->types.pointer.scroll_sum.x = 0;
proc->types.pointer.scroll_sum.y = 0;
}
return obj_candidate;
}
static void init_scroll_limits(_lv_indev_proc_t * proc)
{
lv_obj_t * obj = proc->types.pointer.scroll_obj;
/*If there no STOP allow scrolling anywhere*/
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
lv_area_set(&proc->types.pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
}
/*With STOP limit the scrolling to the perv and next snap point*/
else {
switch(lv_obj_get_scroll_snap_y(obj)) {
case LV_SCROLL_SNAP_START:
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
break;
case LV_SCROLL_SNAP_END:
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
break;
case LV_SCROLL_SNAP_CENTER: {
lv_coord_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
break;
}
default:
proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
break;
}
switch(lv_obj_get_scroll_snap_x(obj)) {
case LV_SCROLL_SNAP_START:
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
break;
case LV_SCROLL_SNAP_END:
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
break;
case LV_SCROLL_SNAP_CENTER: {
lv_coord_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
break;
}
default:
proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
break;
}
}
/*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
if(proc->types.pointer.scroll_area.x1 == 0) proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
if(proc->types.pointer.scroll_area.x2 == 0) proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
if(proc->types.pointer.scroll_area.y1 == 0) proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
if(proc->types.pointer.scroll_area.y2 == 0) proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
}
/**
* Search for snap point in the `min` - `max` range.
* @param obj the object on which snap point should be found
* @param min ignore snap points smaller than this. (Absolute coordinate)
* @param max ignore snap points greater than this. (Absolute coordinate)
* @param ofs offset to snap points. Useful the get a snap point in an imagined case
* what if children are already moved by this value
* @return the distance of the snap point.
*/
static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
{
lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
if(align == LV_SCROLL_SNAP_NONE) return 0;
lv_coord_t dist = LV_COORD_MAX;
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
lv_coord_t x_child = 0;
lv_coord_t x_parent = 0;
switch(align) {
case LV_SCROLL_SNAP_START:
x_child = child->coords.x1;
x_parent = obj->coords.x1 + pad_left;
break;
case LV_SCROLL_SNAP_END:
x_child = child->coords.x2;
x_parent = obj->coords.x2 - pad_right;
break;
case LV_SCROLL_SNAP_CENTER:
x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
break;
default:
continue;
}
x_child += ofs;
if(x_child >= min && x_child <= max) {
lv_coord_t x = x_child - x_parent;
if(LV_ABS(x) < LV_ABS(dist)) dist = x;
}
}
}
return dist == LV_COORD_MAX ? 0 : -dist;
}
/**
* Search for snap point in the `min` - `max` range.
* @param obj the object on which snap point should be found
* @param min ignore snap points smaller than this. (Absolute coordinate)
* @param max ignore snap points greater than this. (Absolute coordinate)
* @param ofs offset to snap points. Useful to get a snap point in an imagined case
* what if children are already moved by this value
* @return the distance of the snap point.
*/
static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
{
lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
if(align == LV_SCROLL_SNAP_NONE) return 0;
lv_coord_t dist = LV_COORD_MAX;
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
lv_coord_t y_child = 0;
lv_coord_t y_parent = 0;
switch(align) {
case LV_SCROLL_SNAP_START:
y_child = child->coords.y1;
y_parent = obj->coords.y1 + pad_top;
break;
case LV_SCROLL_SNAP_END:
y_child = child->coords.y2;
y_parent = obj->coords.y2 - pad_bottom;
break;
case LV_SCROLL_SNAP_CENTER:
y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
break;
default:
continue;
}
y_child += ofs;
if(y_child >= min && y_child <= max) {
lv_coord_t y = y_child - y_parent;
if(LV_ABS(y) < LV_ABS(dist)) dist = y;
}
}
}
return dist == LV_COORD_MAX ? 0 : -dist;
}
static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y)
{
if(diff_y) {
if(proc->types.pointer.scroll_sum.y + *diff_y < proc->types.pointer.scroll_area.y1) {
*diff_y = proc->types.pointer.scroll_area.y1 - proc->types.pointer.scroll_sum.y;
}
if(proc->types.pointer.scroll_sum.y + *diff_y > proc->types.pointer.scroll_area.y2) {
*diff_y = proc->types.pointer.scroll_area.y2 - proc->types.pointer.scroll_sum.y;
}
}
if(diff_x) {
if(proc->types.pointer.scroll_sum.x + *diff_x < proc->types.pointer.scroll_area.x1) {
*diff_x = proc->types.pointer.scroll_area.x1 - proc->types.pointer.scroll_sum.x;
}
if(proc->types.pointer.scroll_sum.x + *diff_x > proc->types.pointer.scroll_area.x2) {
*diff_x = proc->types.pointer.scroll_area.x2 - proc->types.pointer.scroll_sum.x;
}
}
}
static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc)
{
lv_coord_t y = proc->types.pointer.scroll_throw_vect.y;
lv_coord_t move = 0;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
while(y) {
move += y;
y = y * (100 - scroll_throw) / 100;
}
return move;
}
static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc)
{
lv_coord_t x = proc->types.pointer.scroll_throw_vect.x;
lv_coord_t move = 0;
lv_indev_t * indev_act = lv_indev_get_act();
lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
while(x) {
move += x;
x = x * (100 - scroll_throw) / 100;
}
return move;
}
static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
lv_dir_t dir)
{
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
/*If there is snapping in the current direction don't use the elastic factor because
*it's natural that the first and last items are scrolled (snapped) in.*/
lv_scroll_snap_t snap;
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
lv_obj_t * act_obj = lv_indev_get_obj_act();
lv_coord_t snap_point = 0;
lv_coord_t act_obj_point = 0;
if(dir == LV_DIR_HOR) {
lv_coord_t pad_left = lv_obj_get_style_pad_left(scroll_obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(scroll_obj, LV_PART_MAIN);
switch(snap) {
case LV_SCROLL_SNAP_CENTER:
snap_point = pad_left + (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2 + scroll_obj->coords.x1;
act_obj_point = lv_area_get_width(&act_obj->coords) / 2 + act_obj->coords.x1;
break;
case LV_SCROLL_SNAP_START:
snap_point = scroll_obj->coords.x1 + pad_left;
act_obj_point = act_obj->coords.x1;
break;
case LV_SCROLL_SNAP_END:
snap_point = scroll_obj->coords.x2 - pad_right;
act_obj_point = act_obj->coords.x2;
break;
}
}
else {
lv_coord_t pad_top = lv_obj_get_style_pad_top(scroll_obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, LV_PART_MAIN);
switch(snap) {
case LV_SCROLL_SNAP_CENTER:
snap_point = pad_top + (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2 + scroll_obj->coords.y1;
act_obj_point = lv_area_get_height(&act_obj->coords) / 2 + act_obj->coords.y1;
break;
case LV_SCROLL_SNAP_START:
snap_point = scroll_obj->coords.y1 + pad_top;
act_obj_point = act_obj->coords.y1;
break;
case LV_SCROLL_SNAP_END:
snap_point = scroll_obj->coords.y2 - pad_bottom;
act_obj_point = act_obj->coords.y2;
break;
}
}
if(scroll_end < 0) {
if(snap != LV_SCROLL_SNAP_NONE && act_obj_point > snap_point) return diff;
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
else if(scroll_start < 0) {
if(snap != LV_SCROLL_SNAP_NONE && act_obj_point < snap_point) return diff;
/*Rounding*/
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
return diff / ELASTIC_SLOWNESS_FACTOR;
}
}
else {
/*Scroll back to the boundary if required*/
if(scroll_end + diff < 0) diff = - scroll_end;
if(scroll_start - diff < 0) diff = scroll_start;
}
return diff;
}

View File

@ -0,0 +1,65 @@
/**
* @file lv_indev_scroll.h
*
*/
#ifndef LV_INDEV_SCROLL_H
#define LV_INDEV_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Handle scrolling. Called by LVGL during input device processing
* @param proc pointer to an input device's proc field
*/
void _lv_indev_scroll_handler(_lv_indev_proc_t * proc);
/**
* Handle throwing after scrolling. Called by LVGL during input device processing
* @param proc pointer to an input device's proc field
*/
void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc);
/**
* Predict where would a scroll throw end
* @param indev pointer to an input device
* @param dir ` LV_DIR_VER` or `LV_DIR_HOR`
* @return the difference compared to the current position when the throw would be finished
*/
lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir);
/**
* Get the distance of the nearest snap point
* @param obj the object on which snap points should be found
* @param p save the distance of the found snap point there
*/
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_INDEV_SCROLL_H*/

View File

@ -0,0 +1,944 @@
/**
* @file lv_obj.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_indev.h"
#include "lv_refr.h"
#include "lv_group.h"
#include "lv_disp.h"
#include "lv_theme.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_async.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_gc.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../hal/lv_hal.h"
#include "../extra/lv_extra.h"
#include <stdint.h>
#include <string.h>
#if LV_USE_GPU_STM32_DMA2D
#include "../gpu/lv_gpu_stm32_dma2d.h"
#endif
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
#include "../gpu/lv_gpu_nxp_pxp.h"
#include "../gpu/lv_gpu_nxp_pxp_osa.h"
#endif
#if LV_USE_GPU_SDL
#include "../gpu/lv_gpu_sdl.h"
#endif
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
#define LV_OBJ_DEF_WIDTH (LV_DPX(100))
#define LV_OBJ_DEF_HEIGHT (LV_DPX(50))
#define GRID_DEBUG 0 /*Draw rectangles on grid cells*/
#define STYLE_TRANSITION_MAX 32
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_obj_draw(lv_event_t * e);
static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_scrollbar(lv_obj_t * obj, const lv_area_t * clip_area);
static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state);
/**********************
* STATIC VARIABLES
**********************/
static bool lv_initialized = false;
const lv_obj_class_t lv_obj_class = {
.constructor_cb = lv_obj_constructor,
.destructor_cb = lv_obj_destructor,
.event_cb = lv_obj_event,
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
.editable = LV_OBJ_CLASS_EDITABLE_FALSE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
.instance_size = (sizeof(lv_obj_t)),
.base_class = NULL,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_is_initialized(void)
{
return lv_initialized;
}
void lv_init(void)
{
/*Do nothing if already initialized*/
if(lv_initialized) {
LV_LOG_WARN("lv_init: already inited");
return;
}
LV_LOG_INFO("begin");
/*Initialize the misc modules*/
lv_mem_init();
_lv_timer_core_init();
_lv_fs_init();
_lv_anim_core_init();
_lv_group_init();
#if LV_USE_GPU_STM32_DMA2D
/*Initialize DMA2D GPU*/
lv_gpu_stm32_dma2d_init();
#endif
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) {
LV_LOG_ERROR("PXP init error. STOP.\n");
for(; ;) ;
}
#endif
#if LV_USE_GPU_SDL
lv_gpu_sdl_init();
#endif
_lv_obj_style_init();
_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
_lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
/*Initialize the screen refresh system*/
_lv_refr_init();
_lv_img_decoder_init();
#if LV_IMG_CACHE_DEF_SIZE
lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
#endif
// /*Test if the IDE has UTF-8 encoding*/
// char * txt = "Á";
// uint8_t * txt_u8 = (uint8_t *)txt;
// if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
// LV_LOG_WARN("The strings has no UTF-8 encoding. Non-ASCII characters won't be displayed.");
// }
// uint32_t endianess_test = 0x11223344;
// uint8_t * endianess_test_p = (uint8_t *) &endianess_test;
// bool big_endian = endianess_test_p[0] == 0x11 ? true : false;
// if(big_endian) {
// LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1,
// "It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h");
// }
// else {
// LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0,
// "It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h");
// }
// #if LV_USE_ASSERT_MEM_INTEGRITY
// LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower");
// #endif
// #if LV_USE_ASSERT_OBJ
// LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower");
// #endif
// #if LV_USE_ASSERT_STYLE
// LV_LOG_WARN("Style sanity checks are enabled that uses more RAM");
// #endif
// #if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE
// LV_LOG_WARN("Log level is set the Trace which makes LVGL much slower");
// #endif
lv_extra_init();
lv_initialized = true;
LV_LOG_TRACE("finished");
}
#if LV_ENABLE_GC || !LV_MEM_CUSTOM || LV_USE_GPU_SDL
void lv_deinit(void)
{
#if LV_USE_GPU_SDL
lv_gpu_sdl_deinit();
#endif
_lv_gc_clear_roots();
lv_disp_set_default(NULL);
lv_mem_deinit();
lv_initialized = false;
LV_LOG_INFO("lv_deinit done");
#if LV_USE_LOG
lv_log_register_print_cb(NULL);
#endif
}
#endif
lv_obj_t * lv_obj_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
*====================*/
/*-----------------
* Attribute set
*----------------*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
bool was_on_layout = lv_obj_is_layout_positioned(obj);
if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
obj->flags |= f;
if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj);
}
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
if(f & LV_OBJ_FLAG_SCROLLABLE) {
lv_area_t hor_area, ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
lv_obj_invalidate_area(obj, &hor_area);
lv_obj_invalidate_area(obj, &ver_area);
}
}
void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
bool was_on_layout = lv_obj_is_layout_positioned(obj);
if(f & LV_OBJ_FLAG_SCROLLABLE) {
lv_area_t hor_area, ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
lv_obj_invalidate_area(obj, &hor_area);
lv_obj_invalidate_area(obj, &ver_area);
}
obj->flags &= (~f);
if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj);
if(lv_obj_is_layout_positioned(obj)) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
}
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
}
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t new_state = obj->state | state;
if(obj->state != new_state) {
lv_obj_set_state(obj, new_state);
}
}
void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t new_state = obj->state & (~state);
if(obj->state != new_state) {
lv_obj_set_state(obj, new_state);
}
}
/*=======================
* Getter functions
*======================*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return (obj->flags & f) == f ? true : false;
}
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return (obj->flags & f) ? true : false;
}
lv_state_t lv_obj_get_state(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->state;
}
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->state & state ? true : false;
}
void * lv_obj_get_group(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr) return obj->spec_attr->group_p;
else return NULL;
}
/*-------------------
* OTHER FUNCTIONS
*------------------*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) {
static uint32_t x = 0;
x++;
obj->spec_attr = lv_mem_alloc(sizeof(_lv_obj_spec_attr_t));
LV_ASSERT_MALLOC(obj->spec_attr);
if(obj->spec_attr == NULL) return;
lv_memset_00(obj->spec_attr, sizeof(_lv_obj_spec_attr_t));
obj->spec_attr->scroll_dir = LV_DIR_ALL;
obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
}
}
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
if(obj == NULL) return false;
return obj->class_p == class_p ? true : false;
}
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
{
const lv_obj_class_t * obj_class = obj->class_p;
while(obj_class) {
if(obj_class == class_p) return true;
obj_class = obj_class->base_class;
}
return false;
}
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
{
return obj->class_p;
}
bool lv_obj_is_valid(const lv_obj_t * obj)
{
lv_disp_t * disp = lv_disp_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) return true;
bool found = obj_valid_child(disp->screens[i], obj);
if(found) return true;
}
disp = lv_disp_get_next(disp);
}
return false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_obj_t * parent = obj->parent;
if(parent) {
lv_coord_t sl = lv_obj_get_scroll_left(parent);
lv_coord_t st = lv_obj_get_scroll_top(parent);
obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
obj->coords.y2 = obj->coords.y1 - 1;
obj->coords.x1 = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
obj->coords.x2 = obj->coords.x1 - 1;
}
/*Set attributes*/
obj->flags = LV_OBJ_FLAG_CLICKABLE;
obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
_lv_event_mark_deleted(obj);
/*Remove all style*/
lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
lv_obj_remove_style_all(obj);
lv_obj_enable_style_refresh(true);
/*Remove the animations from this object*/
lv_anim_del(obj, NULL);
/*Delete from the group*/
lv_group_t * group = lv_obj_get_group(obj);
if(group) lv_group_remove_obj(obj);
if(obj->spec_attr) {
if(obj->spec_attr->children) {
lv_mem_free(obj->spec_attr->children);
obj->spec_attr->children = NULL;
}
if(obj->spec_attr->event_dsc) {
lv_mem_free(obj->spec_attr->event_dsc);
obj->spec_attr->event_dsc = NULL;
}
lv_mem_free(obj->spec_attr);
obj->spec_attr = NULL;
}
}
static void lv_obj_draw(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res == LV_COVER_RES_MASKED) return;
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
info->res = LV_COVER_RES_MASKED;
return;
}
/*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
if(_lv_area_is_in(info->area, &coords, r) == false) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_blend_mode(obj, LV_PART_MAIN) != LV_BLEND_MODE_NORMAL) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
#endif
if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
info->res = LV_COVER_RES_COVER;
}
else if(code == LV_EVENT_DRAW_MAIN) {
const lv_area_t * clip_area = lv_event_get_param(e);
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
/*If the border is drawn later disable loading its properties*/
if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
draw_dsc.border_post = 1;
}
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
lv_obj_draw_part_dsc_t part_dsc;
lv_obj_draw_dsc_init(&part_dsc, clip_area);
part_dsc.class_p = MY_CLASS;
part_dsc.type = LV_OBJ_DRAW_PART_RECTANGLE;
part_dsc.rect_dsc = &draw_dsc;
part_dsc.draw_area = &coords;
part_dsc.part = LV_PART_MAIN;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
lv_draw_rect(&coords, clip_area, &draw_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
/*If the radius is 0 the parent's coordinates will clip anyway*/
lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
if(r != 0) {
lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
lv_draw_mask_radius_init(mp, &obj->coords, r, false);
/*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
lv_draw_mask_add(mp, obj + 8);
}
}
#endif
}
else if(code == LV_EVENT_DRAW_POST) {
const lv_area_t * clip_area = lv_event_get_param(e);
draw_scrollbar(obj, clip_area);
#if LV_DRAW_COMPLEX
if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
if(param) {
lv_draw_mask_free_param(param);
lv_mem_buf_release(param);
}
}
#endif
/*If the border is drawn later disable loading other properties*/
if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
draw_dsc.bg_opa = LV_OPA_TRANSP;
draw_dsc.outline_opa = LV_OPA_TRANSP;
draw_dsc.shadow_opa = LV_OPA_TRANSP;
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= w;
coords.x2 += w;
coords.y1 -= h;
coords.y2 += h;
lv_obj_draw_part_dsc_t part_dsc;
lv_obj_draw_dsc_init(&part_dsc, clip_area);
part_dsc.class_p = MY_CLASS;
part_dsc.type = LV_OBJ_DRAW_PART_BORDER_POST;
part_dsc.rect_dsc = &draw_dsc;
part_dsc.draw_area = &coords;
part_dsc.part = LV_PART_MAIN;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
lv_draw_rect(&coords, clip_area, &draw_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
}
}
static void draw_scrollbar(lv_obj_t * obj, const lv_area_t * clip_area)
{
lv_area_t hor_area;
lv_area_t ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
lv_draw_rect_dsc_t draw_dsc;
lv_res_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
if(sb_res != LV_RES_OK) return;
lv_obj_draw_part_dsc_t part_dsc;
lv_obj_draw_dsc_init(&part_dsc, clip_area);
part_dsc.class_p = MY_CLASS;
part_dsc.type = LV_OBJ_DRAW_PART_SCROLLBAR;
part_dsc.rect_dsc = &draw_dsc;
part_dsc.part = LV_PART_SCROLLBAR;
if(lv_area_get_size(&hor_area) > 0) {
part_dsc.draw_area = &hor_area;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
lv_draw_rect(&hor_area, clip_area, &draw_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
if(lv_area_get_size(&ver_area) > 0) {
part_dsc.draw_area = &ver_area;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
lv_draw_rect(&ver_area, clip_area, &draw_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
}
}
/**
* Initialize the draw descriptor for the scrollbar
* @param obj pointer to an object
* @param dsc the draw descriptor to initialize
* @return LV_RES_OK: the scrollbar is visible; LV_RES_INV: the scrollbar is not visible
*/
static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
{
lv_draw_rect_dsc_init(dsc);
dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
if(dsc->bg_opa > LV_OPA_MIN) {
dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
}
dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
if(dsc->border_opa > LV_OPA_MIN) {
dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
if(dsc->border_width > 0) {
dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
}
else {
dsc->border_opa = LV_OPA_TRANSP;
}
}
#if LV_DRAW_COMPLEX
dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
if(dsc->shadow_opa > LV_OPA_MIN) {
dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
if(dsc->shadow_width > 0) {
dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
}
else {
dsc->shadow_opa = LV_OPA_TRANSP;
}
}
lv_opa_t opa = lv_obj_get_style_opa(obj, LV_PART_SCROLLBAR);
if(opa < LV_OPA_MAX) {
dsc->bg_opa = (dsc->bg_opa * opa) >> 8;
dsc->border_opa = (dsc->bg_opa * opa) >> 8;
dsc->shadow_opa = (dsc->bg_opa * opa) >> 8;
}
if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
return LV_RES_OK;
}
else {
return LV_RES_INV;
}
#else
if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP) return LV_RES_OK;
else return LV_RES_INV;
#endif
}
static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED) {
lv_obj_add_state(obj, LV_STATE_PRESSED);
}
else if(code == LV_EVENT_RELEASED) {
lv_obj_clear_state(obj, LV_STATE_PRESSED);
void * param = lv_event_get_param(e);
/*Go the checked state if enabled*/
if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
else lv_obj_clear_state(obj, LV_STATE_CHECKED);
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_PRESS_LOST) {
lv_obj_clear_state(obj, LV_STATE_PRESSED);
}
else if(code == LV_EVENT_STYLE_CHANGED) {
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(uint32_t i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_mark_layout_as_dirty(child);
}
}
else if(code == LV_EVENT_KEY) {
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_obj_add_state(obj, LV_STATE_CHECKED);
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_obj_clear_state(obj, LV_STATE_CHECKED);
}
/*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
if(c != LV_KEY_ENTER) {
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) && !lv_obj_is_editable(obj)) {
/*scroll by keypad or encoder*/
lv_anim_enable_t anim_enable = LV_ANIM_OFF;
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_t sr = lv_obj_get_scroll_right(obj);
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_DOWN) {
/*use scroll_to_x/y functions to enforce scroll limits*/
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
}
else if(c == LV_KEY_UP) {
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
}
else if(c == LV_KEY_RIGHT) {
/*If the object can't be scrolled horizontally then scroll it vertically*/
if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
else
lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
}
else if(c == LV_KEY_LEFT) {
/*If the object can't be scrolled horizontally then scroll it vertically*/
if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
else
lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
}
}
}
else if(code == LV_EVENT_FOCUSED) {
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
}
bool editing = false;
editing = lv_group_get_editing(lv_obj_get_group(obj));
lv_state_t state = LV_STATE_FOCUSED;
/* Use the indev for then indev handler.
* But if the obj was focused manually it returns NULL so try to
* use the indev from the event*/
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) indev = lv_event_get_indev(e);
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
if(editing) {
state |= LV_STATE_EDITED;
lv_obj_add_state(obj, state);
}
else {
lv_obj_add_state(obj, state);
lv_obj_clear_state(obj, LV_STATE_EDITED);
}
}
else if(code == LV_EVENT_SCROLL_BEGIN) {
lv_obj_add_state(obj, LV_STATE_SCROLLED);
}
else if(code == LV_EVENT_SCROLL_END) {
lv_obj_clear_state(obj, LV_STATE_SCROLLED);
if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
lv_area_t hor_area, ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
lv_obj_invalidate_area(obj, &hor_area);
lv_obj_invalidate_area(obj, &ver_area);
}
}
else if(code == LV_EVENT_DEFOCUSED) {
lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout || align) {
lv_obj_mark_layout_as_dirty(obj);
}
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_mark_layout_as_dirty(child);
}
}
else if(code == LV_EVENT_CHILD_CHANGED) {
lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
lv_obj_mark_layout_as_dirty(obj);
}
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
*s = LV_MAX(*s, d);
}
else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
lv_obj_draw(e);
}
}
/**
* Set the state (fully overwrite) of an object.
* If specified in the styles, transition animations will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the new state
*/
static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
{
if(obj->state == new_state) return;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t prev_state = obj->state;
obj->state = new_state;
_lv_style_state_cmp_t cmp_res = _lv_obj_style_state_compare(obj, prev_state, new_state);
/*If there is no difference in styles there is nothing else to do*/
if(cmp_res == _LV_STYLE_STATE_CMP_SAME) return;
_lv_obj_style_transition_dsc_t * ts = lv_mem_buf_get(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
lv_memset_00(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
uint32_t tsi = 0;
uint32_t i;
for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
if(obj_style->is_trans) continue;
lv_style_value_t v;
if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) == false) continue;
const lv_style_transition_dsc_t * tr = v.ptr;
/*Add the props to the set if not added yet or added but with smaller weight*/
uint32_t j;
for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
uint32_t t;
for(t = 0; t < tsi; t++) {
lv_style_selector_t selector = ts[t].selector;
lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
}
/*If not found add it*/
if(t == tsi) {
ts[tsi].time = tr->time;
ts[tsi].delay = tr->delay;
ts[tsi].path_cb = tr->path_xcb;
ts[tsi].prop = tr->props[j];
#if LV_USE_USER_DATA
ts[tsi].user_data = tr->user_data;
#endif
ts[tsi].selector = obj_style->selector;
tsi++;
}
}
}
for(i = 0; i < tsi; i++) {
lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
_lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
}
lv_mem_buf_release(ts);
if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_REDRAW) {
lv_obj_invalidate(obj);
}
else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
}
else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
lv_obj_invalidate(obj);
lv_obj_refresh_ext_draw_size(obj);
}
}
static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
{
/*Check all children of `parent`*/
uint32_t child_cnt = 0;
if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = parent->spec_attr->children[i];
if(child == obj_to_find) {
return true;
}
/*Check the children*/
bool found = obj_valid_child(child, obj_to_find);
if(found) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,400 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stddef.h>
#include <stdbool.h>
#include "../misc/lv_style.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_assert.h"
#include "../hal/lv_hal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
/**
* Possible states of a widget.
* OR-ed values are possible
*/
enum {
LV_STATE_DEFAULT = 0x0000,
LV_STATE_CHECKED = 0x0001,
LV_STATE_FOCUSED = 0x0002,
LV_STATE_FOCUS_KEY = 0x0004,
LV_STATE_EDITED = 0x0008,
LV_STATE_HOVERED = 0x0010,
LV_STATE_PRESSED = 0x0020,
LV_STATE_SCROLLED = 0x0040,
LV_STATE_DISABLED = 0x0080,
LV_STATE_USER_1 = 0x1000,
LV_STATE_USER_2 = 0x2000,
LV_STATE_USER_3 = 0x4000,
LV_STATE_USER_4 = 0x8000,
LV_STATE_ANY = 0xFFFF, /**< Special value can be used in some functions to target all states*/
};
typedef uint16_t lv_state_t;
/**
* The possible parts of widgets.
* The parts can be considered as the internal building block of the widgets.
* E.g. slider = background + indicator + knob
* Note every part is used by every widget
*/
enum {
LV_PART_MAIN = 0x000000, /**< A background like rectangle*/
LV_PART_SCROLLBAR = 0x010000, /**< The scrollbar(s)*/
LV_PART_INDICATOR = 0x020000, /**< Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox*/
LV_PART_KNOB = 0x030000, /**< Like handle to grab to adjust the value*/
LV_PART_SELECTED = 0x040000, /**< Indicate the currently selected option or section*/
LV_PART_ITEMS = 0x050000, /**< Used if the widget has multiple similar elements (e.g. table cells)*/
LV_PART_TICKS = 0x060000, /**< Ticks on scale e.g. for a chart or meter*/
LV_PART_CURSOR = 0x070000, /**< Mark a specific place e.g. for text area's cursor or on a chart*/
LV_PART_CUSTOM_FIRST = 0x080000, /**< Extension point for custom widgets*/
LV_PART_ANY = 0x0F0000, /**< Special value can be used in some functions to target all parts*/
};
typedef uint32_t lv_part_t;
/**
* On/Off features controlling the object's behavior.
* OR-ed values are possible
*/
enum {
LV_OBJ_FLAG_HIDDEN = (1L << 0), /**< Make the object hidden. (Like it wasn't there at all)*/
LV_OBJ_FLAG_CLICKABLE = (1L << 1), /**< Make the object clickable by the input devices*/
LV_OBJ_FLAG_CLICK_FOCUSABLE = (1L << 2), /**< Add focused state to the object when clicked*/
LV_OBJ_FLAG_CHECKABLE = (1L << 3), /**< Toggle checked state when the object is clicked*/
LV_OBJ_FLAG_SCROLLABLE = (1L << 4), /**< Make the object scrollable*/
LV_OBJ_FLAG_SCROLL_ELASTIC = (1L << 5), /**< Allow scrolling inside but with slower speed*/
LV_OBJ_FLAG_SCROLL_MOMENTUM = (1L << 6), /**< Make the object scroll further when "thrown"*/
LV_OBJ_FLAG_SCROLL_ONE = (1L << 7), /**< Allow scrolling only one snappable children*/
LV_OBJ_FLAG_SCROLL_CHAIN = (1L << 8), /**< Allow propagating the scroll to a parent*/
LV_OBJ_FLAG_SCROLL_ON_FOCUS = (1L << 9), /**< Automatically scroll object to make it visible when focused*/
LV_OBJ_FLAG_SNAPPABLE = (1L << 10), /**< If scroll snap is enabled on the parent it can snap to this object*/
LV_OBJ_FLAG_PRESS_LOCK = (1L << 11), /**< Keep the object pressed even if the press slid from the object*/
LV_OBJ_FLAG_EVENT_BUBBLE = (1L << 12), /**< Propagate the events to the parent too*/
LV_OBJ_FLAG_GESTURE_BUBBLE = (1L << 13), /**< Propagate the gestures to the parent*/
LV_OBJ_FLAG_ADV_HITTEST = (1L << 14), /**< Allow performing more accurate hit (click) test. E.g. consider rounded corners.*/
LV_OBJ_FLAG_IGNORE_LAYOUT = (1L << 15), /**< Make the object position-able by the layouts*/
LV_OBJ_FLAG_FLOATING = (1L << 16), /**< Do not scroll the object when the parent scrolls and ignore layout*/
LV_OBJ_FLAG_LAYOUT_1 = (1L << 23), /**< Custom flag, free to use by layouts*/
LV_OBJ_FLAG_LAYOUT_2 = (1L << 24), /**< Custom flag, free to use by layouts*/
LV_OBJ_FLAG_WIDGET_1 = (1L << 25), /**< Custom flag, free to use by widget*/
LV_OBJ_FLAG_WIDGET_2 = (1L << 26), /**< Custom flag, free to use by widget*/
LV_OBJ_FLAG_USER_1 = (1L << 27), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_2 = (1L << 28), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_3 = (1L << 29), /**< Custom flag, free to use by user*/
LV_OBJ_FLAG_USER_4 = (1L << 30), /**< Custom flag, free to use by user*/
};
typedef uint32_t lv_obj_flag_t;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_obj_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_OBJ_DRAW_PART_RECTANGLE, /**< The main rectangle*/
LV_OBJ_DRAW_PART_BORDER_POST,/**< The border if style_border_post = true*/
LV_OBJ_DRAW_PART_SCROLLBAR, /**< The scrollbar*/
} lv_obj_draw_part_type_t;
#include "lv_obj_tree.h"
#include "lv_obj_pos.h"
#include "lv_obj_scroll.h"
#include "lv_obj_style.h"
#include "lv_obj_draw.h"
#include "lv_obj_class.h"
#include "lv_event.h"
#include "lv_group.h"
/**
* Make the base object's class publicly available.
*/
extern const lv_obj_class_t lv_obj_class;
/**
* Special, rarely used attributes.
* They are allocated automatically if any elements is set.
*/
typedef struct {
struct _lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
uint32_t child_cnt; /**< Number of children*/
lv_group_t * group_p;
struct _lv_event_dsc_t * event_dsc; /**< Dynamically allocated event callback and user data array*/
lv_point_t scroll; /**< The current X/Y scroll offset*/
lv_coord_t ext_click_pad; /**< Extra click padding in all direction*/
lv_coord_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
lv_scrollbar_mode_t scrollbar_mode : 2; /**< How to display scrollbars*/
lv_scroll_snap_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally*/
lv_scroll_snap_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/
lv_dir_t scroll_dir : 4; /**< The allowed scroll direction(s)*/
uint8_t event_dsc_cnt; /**< Number of event callbacks stored in `event_dsc` array*/
} _lv_obj_spec_attr_t;
typedef struct _lv_obj_t {
const lv_obj_class_t * class_p;
struct _lv_obj_t * parent;
_lv_obj_spec_attr_t * spec_attr;
_lv_obj_style_t * styles;
#if LV_USE_USER_DATA
void * user_data;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
lv_state_t state;
uint16_t layout_inv : 1;
uint16_t scr_layout_inv : 1;
uint16_t skip_trans : 1;
uint16_t style_cnt : 6;
uint16_t h_layout : 1;
uint16_t w_layout : 1;
} lv_obj_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize LVGL library.
* Should be called before any other LVGL related function.
*/
void lv_init(void);
#if LV_ENABLE_GC || !LV_MEM_CUSTOM || LV_USE_GPU_SDL
/**
* Deinit the 'lv' library
* Currently only implemented when not using custom allocators, or GC is enabled.
*/
void lv_deinit(void);
#endif
/**
* Returns whether the 'lv' library is currently initialized
*/
bool lv_is_initialized(void);
/**
* Create a base object (a rectangle)
* @param parent pointer to a parent object. If NULL then a screen will be created.
* @return pointer to the new object
*/
lv_obj_t * lv_obj_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set one or more flags
* @param obj pointer to an object
* @param f R-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Clear one or more flags
* @param obj pointer to an object
* @param f OR-ed values from `lv_obj_flag_t` to set.
*/
void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f);
/**
* Add one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_add_state(lv_obj_t * obj, lv_state_t state);
/**
* Remove one or more states to the object. The other state bits will remain unchanged.
* If specified in the styles, transition animation will be started from the previous state to the current.
* @param obj pointer to an object
* @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
*/
void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state);
/**
* Set the user_data field of the object
* @param obj pointer to an object
* @param user_data pointer to the new user_data.
*/
#if LV_USE_USER_DATA
static inline void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
{
obj->user_data = user_data;
}
#endif
/*=======================
* Getter functions
*======================*/
/**
* Check if a given flag or all the given flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: all flags are set; false: not all flags are set
*/
bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Check if a given flag or any of the flags are set on an object.
* @param obj pointer to an object
* @param f the flag(s) to check (OR-ed values can be used)
* @return true: at lest one flag flag is set; false: none of the flags are set
*/
bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f);
/**
* Get the state of an object
* @param obj pointer to an object
* @return the state (OR-ed values from `lv_state_t`)
*/
lv_state_t lv_obj_get_state(const lv_obj_t * obj);
/**
* Check if the object is in a given state or not.
* @param obj pointer to an object
* @param state a state or combination of states to check
* @return true: `obj` is in `state`; false: `obj` is not in `state`
*/
bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state);
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
void * lv_obj_get_group(const lv_obj_t * obj);
/**
* Get the user_data field of the object
* @param obj pointer to an object
* @return the pointer to the user_data of the object
*/
#if LV_USE_USER_DATA
static inline void * lv_obj_get_user_data(lv_obj_t * obj)
{
return obj->user_data;
}
#endif
/*=======================
* Other functions
*======================*/
/**
* Allocate special data for an object if not allocated yet.
* @param obj pointer to an object
*/
void lv_obj_allocate_spec_attr(lv_obj_t * obj);
/**
* Check the type of obj.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `class_p` is the `obj` class.
*/
bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Check if any object has a given class (type).
* It checks the ancestor classes too.
* @param obj pointer to an object
* @param class_p a class to check (e.g. `lv_slider_class`)
* @return true: `obj` has the given class
*/
bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p);
/**
* Get the class (type) of the object
* @param obj pointer to an object
* @return the class (type) of the object
*/
const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj);
/**
* Check if any object is still "alive".
* @param obj pointer to an object
* @return true: valid
*/
bool lv_obj_is_valid(const lv_obj_t * obj);
/**
* Scale the given number of pixels (a distance or size) relative to a 160 DPI display
* considering the DPI of the `obj`'s display.
* It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
* DPI of the display.
* @param obj an object whose display's dpi should be considered
* @param n the number of pixels to scale
* @return `n x current_dpi/160`
*/
static inline lv_coord_t lv_obj_dpx(const lv_obj_t * obj, lv_coord_t n)
{
return _LV_DPX_CALC(lv_disp_get_dpi(lv_obj_get_disp(obj)), n);
}
/**********************
* MACROS
**********************/
#if LV_USE_ASSERT_OBJ
# define LV_ASSERT_OBJ(obj_p, obj_class) \
LV_ASSERT_MSG(obj_p != NULL, "The object is NULL"); \
LV_ASSERT_MSG(lv_obj_has_class(obj_p, obj_class) == true, "Incompatible object type."); \
LV_ASSERT_MSG(lv_obj_is_valid(obj_p) == true, "The object is invalid, deleted or corrupted?");
# else
# define LV_ASSERT_OBJ(obj_p, obj_class) do{}while(0)
#endif
#if LV_USE_LOG && LV_LOG_TRACE_OBJ_CREATE
# define LV_TRACE_OBJ_CREATE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
# define LV_TRACE_OBJ_CREATE(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_H*/

View File

@ -0,0 +1,207 @@
/**
* @file lv_obj_class.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_theme.h"
#include "ql_log.h"
#define LOG_INFO(msg, ...) QL_LOG(QL_LOG_LEVEL_INFO, "lvobjclass", msg, ##__VA_ARGS__)
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_construct(lv_obj_t * obj);
static uint32_t get_instance_size(const lv_obj_class_t * class_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
{
LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent);
uint32_t s = get_instance_size(class_p);
lv_obj_t * obj = lv_mem_alloc(s);
if(obj == NULL) return NULL;
lv_memset_00(obj, s);
obj->class_p = class_p;
obj->parent = parent;
/*Create a screen*/
if(parent == NULL) {
LV_TRACE_OBJ_CREATE("creating a screen");
LOG_INFO("creating a screen");
lv_disp_t * disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("No display created to so far. No place to assign the new screen");
LOG_INFO("No display created to so far. No place to assign the new screen");
return NULL;
}
if(disp->screens == NULL) {
LOG_INFO("disp->screens == NULL");
disp->screens = lv_mem_alloc(sizeof(lv_obj_t *));
disp->screens[0] = obj;
disp->screen_cnt = 1;
}
else {
LOG_INFO("disp->screens != NULL");
disp->screen_cnt++;
disp->screens = lv_mem_realloc(disp->screens, sizeof(lv_obj_t *) * disp->screen_cnt);
disp->screens[disp->screen_cnt - 1] = obj;
}
/*Set coordinates to full screen size*/
obj->coords.x1 = 0;
obj->coords.y1 = 0;
obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1;
obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1;
LOG_INFO("x2 = %d, y2 = %d",obj->coords.x2,obj->coords.y2);
}
/*Create a normal object*/
else {
LV_TRACE_OBJ_CREATE("creating normal object");
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent->spec_attr == NULL) {
lv_obj_allocate_spec_attr(parent);
}
if(parent->spec_attr->children == NULL) {
parent->spec_attr->children = lv_mem_alloc(sizeof(lv_obj_t *));
parent->spec_attr->children[0] = obj;
parent->spec_attr->child_cnt = 1;
}
else {
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children,
sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
}
}
return obj;
}
void lv_obj_class_init_obj(lv_obj_t * obj)
{
lv_obj_mark_layout_as_dirty(obj);
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj);
lv_obj_construct(obj);
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
lv_obj_refresh_self_size(obj);
lv_group_t * def_group = lv_group_get_default();
if(def_group && lv_obj_is_group_def(obj)) {
lv_group_add_obj(def_group, obj);
}
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
/*Call the ancestor's event handler to the parent to notify it about the new child.
*Also triggers layout update*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj);
/*Invalidate the area if not screen created*/
lv_obj_invalidate(obj);
}
}
void _lv_obj_destruct(lv_obj_t * obj)
{
if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj);
if(obj->class_p->base_class) {
/*Don't let the descendant methods run during destructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Call the base class's destructor too*/
_lv_obj_destruct(obj);
}
}
bool lv_obj_is_editable(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which editable is set*/
while(class_p && class_p->editable == LV_OBJ_CLASS_EDITABLE_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->editable == LV_OBJ_CLASS_EDITABLE_TRUE ? true : false;
}
bool lv_obj_is_group_def(lv_obj_t * obj)
{
const lv_obj_class_t * class_p = obj->class_p;
/*Find a base in which group_def is set*/
while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;
if(class_p == NULL) return false;
return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE ? true : false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_construct(lv_obj_t * obj)
{
const lv_obj_class_t * original_class_p = obj->class_p;
if(obj->class_p->base_class) {
/*Don't let the descendant methods run during constructing the ancestor type*/
obj->class_p = obj->class_p->base_class;
/*Construct the base first*/
lv_obj_construct(obj);
}
/*Restore the original class*/
obj->class_p = original_class_p;
if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(obj->class_p, obj);
}
static uint32_t get_instance_size(const lv_obj_class_t * class_p)
{
/*Find a base in which instance size is set*/
const lv_obj_class_t * base = class_p;
while(base && base->instance_size == 0) base = base->base_class;
if(base == NULL) return 0; /*Never happens: set at least in `lv_obj` class*/
return base->instance_size;
}

View File

@ -0,0 +1,94 @@
/**
* @file lv_obj_class.h
*
*/
#ifndef LV_OBJ_CLASS_H
#define LV_OBJ_CLASS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
struct _lv_event_t;
typedef enum {
LV_OBJ_CLASS_EDITABLE_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_EDITABLE_TRUE,
LV_OBJ_CLASS_EDITABLE_FALSE,
} lv_obj_class_editable_t;
typedef enum {
LV_OBJ_CLASS_GROUP_DEF_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
LV_OBJ_CLASS_GROUP_DEF_TRUE,
LV_OBJ_CLASS_GROUP_DEF_FALSE,
} lv_obj_class_group_def_t;
typedef void (*lv_obj_class_event_cb_t)(struct _lv_obj_class_t * class_p, struct _lv_event_t * e);
/**
* Describe the common methods of every object.
* Similar to a C++ class.
*/
typedef struct _lv_obj_class_t {
const struct _lv_obj_class_t * base_class;
void (*constructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
void (*destructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
#if LV_USE_USER_DATA
void * user_data;
#endif
void (*event_cb)(const struct _lv_obj_class_t * class_p,
struct _lv_event_t * e); /**< Widget type specific event function*/
lv_coord_t width_def;
lv_coord_t height_def;
uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
uint32_t instance_size : 16;
} lv_obj_class_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an object form a class descriptor
* @param class_p pointer to a class
* @param parent pointer to an object where the new object should be created
* @return pointer to the created object
*/
struct _lv_obj_t * lv_obj_class_create_obj(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * parent);
void lv_obj_class_init_obj(struct _lv_obj_t * obj);
void _lv_obj_destruct(struct _lv_obj_t * obj);
bool lv_obj_is_editable(struct _lv_obj_t * obj);
bool lv_obj_is_group_def(struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_CLASS_H*/

View File

@ -0,0 +1,369 @@
/**
* @file lv_obj_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_draw.h"
#include "lv_obj.h"
#include "lv_disp.h"
#include "lv_indev.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
draw_dsc->radius = lv_obj_get_style_radius(obj, part);
lv_opa_t main_opa = part != LV_PART_MAIN ? lv_obj_get_style_opa(obj, part) : LV_OPA_COVER;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa <= LV_OPA_MIN || main_opa <= LV_OPA_MIN) {
draw_dsc->bg_opa = LV_OPA_TRANSP;
draw_dsc->border_opa = LV_OPA_TRANSP;
draw_dsc->shadow_opa = LV_OPA_TRANSP;
draw_dsc->outline_opa = LV_OPA_TRANSP;
return;
}
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
if(draw_dsc->bg_opa > LV_OPA_MIN) {
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
draw_dsc->bg_grad_dir = lv_obj_get_style_bg_grad_dir(obj, part);
if(draw_dsc->bg_grad_dir != LV_GRAD_DIR_NONE) {
draw_dsc->bg_grad_color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
draw_dsc->bg_main_color_stop = lv_obj_get_style_bg_main_stop(obj, part);
draw_dsc->bg_grad_color_stop = lv_obj_get_style_bg_grad_stop(obj, part);
}
}
}
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
if(draw_dsc->border_opa > LV_OPA_MIN) {
draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
}
}
}
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(draw_dsc->outline_opa > LV_OPA_MIN) {
draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
draw_dsc->outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
}
}
}
if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
if(draw_dsc->bg_img_src) {
draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
draw_dsc->bg_img_symbol_font = lv_obj_get_style_text_font(obj, part);
draw_dsc->bg_img_recolor = lv_obj_get_style_text_color_filtered(obj, part);
}
else {
draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor_filtered(obj, part);
draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
}
}
}
}
if(draw_dsc->shadow_opa) {
draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
if(draw_dsc->shadow_width) {
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
if(draw_dsc->shadow_opa > LV_OPA_MIN) {
draw_dsc->shadow_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
draw_dsc->shadow_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
draw_dsc->shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
}
}
}
}
if(main_opa < LV_OPA_MAX) {
opa = (uint16_t)((uint16_t) main_opa * opa) >> 8;
}
if(opa < LV_OPA_MAX) {
draw_dsc->bg_opa = (uint16_t)((uint16_t)draw_dsc->bg_opa * opa) >> 8;
draw_dsc->border_opa = (uint16_t)((uint16_t)draw_dsc->border_opa * opa) >> 8;
draw_dsc->shadow_opa = (uint16_t)((uint16_t)draw_dsc->shadow_opa * opa) >> 8;
draw_dsc->outline_opa = (uint16_t)((uint16_t)draw_dsc->outline_opa * opa) >> 8;
}
#else /*LV_DRAW_COMPLEX*/
if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
if(draw_dsc->bg_opa > LV_OPA_MIN) {
draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
}
}
draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
if(draw_dsc->border_width) {
if(draw_dsc->border_opa != LV_OPA_TRANSP) {
draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
if(draw_dsc->border_opa > LV_OPA_MIN) {
draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
}
}
}
draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
if(draw_dsc->outline_width) {
if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(draw_dsc->outline_opa > LV_OPA_MIN) {
draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
draw_dsc->outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
}
}
}
if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
if(draw_dsc->bg_img_src) {
draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
draw_dsc->bg_img_symbol_font = lv_obj_get_style_text_font(obj, part);
draw_dsc->bg_img_recolor = lv_obj_get_style_text_color_filtered(obj, part);
}
else {
draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor_filtered(obj, part);
draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
}
}
}
}
#endif
}
void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc)
{
draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_text_color_filtered(obj, part);
draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part);
draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part);
draw_dsc->decor = lv_obj_get_style_text_decor(obj, part);
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
draw_dsc->font = lv_obj_get_style_text_font(obj, part);
#if LV_USE_BIDI
draw_dsc->bidi_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
#endif
draw_dsc->align = lv_obj_get_style_text_align(obj, part);
}
void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc)
{
draw_dsc->opa = lv_obj_get_style_img_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa_scale = lv_obj_get_style_opa(obj, part);
if(opa_scale < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->angle = 0;
draw_dsc->zoom = LV_IMG_ZOOM_NONE;
draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
draw_dsc->recolor_opa = lv_obj_get_style_img_recolor_opa(obj, part);
if(draw_dsc->recolor_opa > 0) {
draw_dsc->recolor = lv_obj_get_style_img_recolor_filtered(obj, part);
}
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc)
{
draw_dsc->width = lv_obj_get_style_line_width(obj, part);
if(draw_dsc->width == 0) return;
draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_line_color_filtered(obj, part);
draw_dsc->dash_width = lv_obj_get_style_line_dash_width(obj, part);
if(draw_dsc->dash_width) {
draw_dsc->dash_gap = lv_obj_get_style_line_dash_gap(obj, part);
}
draw_dsc->round_start = lv_obj_get_style_line_rounded(obj, part);
draw_dsc->round_end = draw_dsc->round_start;
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc)
{
draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
if(draw_dsc->width == 0) return;
draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
if(draw_dsc->opa <= LV_OPA_MIN) return;
lv_opa_t opa = lv_obj_get_style_opa(obj, part);
if(opa < LV_OPA_MAX) {
draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8;
}
if(draw_dsc->opa <= LV_OPA_MIN) return;
draw_dsc->color = lv_obj_get_style_arc_color_filtered(obj, part);
draw_dsc->img_src = lv_obj_get_style_arc_img_src(obj, part);
draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
#if LV_DRAW_COMPLEX
draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
#endif
}
lv_coord_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, uint32_t part)
{
lv_coord_t s = 0;
lv_coord_t sh_width = lv_obj_get_style_shadow_width(obj, part);
if(sh_width) {
lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
if(sh_opa > LV_OPA_MIN) {
sh_width = sh_width / 2 + 1; /*The blur adds only half width*/
sh_width += lv_obj_get_style_shadow_spread(obj, part);
lv_coord_t sh_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
lv_coord_t sh_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
s = LV_MAX(s, sh_width);
}
}
lv_coord_t outline_width = lv_obj_get_style_outline_width(obj, part);
if(outline_width) {
lv_opa_t outline_opa = lv_obj_get_style_outline_opa(obj, part);
if(outline_opa > LV_OPA_MIN) {
lv_coord_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
s = LV_MAX(s, outline_pad + outline_width);
}
}
lv_coord_t w = lv_obj_get_style_transform_width(obj, part);
lv_coord_t h = lv_obj_get_style_transform_height(obj, part);
lv_coord_t wh = LV_MAX(w, h);
if(wh > 0) s += wh;
return s;
}
void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, const lv_area_t * clip_area)
{
lv_memset_00(dsc, sizeof(lv_obj_draw_part_dsc_t));
dsc->clip_area = clip_area;
}
bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const lv_obj_class_t * class_p, uint32_t type)
{
if(dsc->class_p == class_p && dsc->type == type) return true;
else return false;
}
void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t s_old = _lv_obj_get_ext_draw_size(obj);
lv_coord_t s_new = 0;
lv_event_send(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
if(s_new != s_old) lv_obj_invalidate(obj);
/*Store the result if the special attrs already allocated*/
if(obj->spec_attr) {
obj->spec_attr->ext_draw_size = s_new;
}
/*Allocate spec. attrs. only if the result is not zero.
*Zero is the default value if the spec. attr. are not defined.*/
else if(s_new != 0) {
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->ext_draw_size = s_new;
}
if(s_new != s_old) lv_obj_invalidate(obj);
}
lv_coord_t _lv_obj_get_ext_draw_size(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->ext_draw_size;
else return 0;
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,163 @@
/**
* @file lv_obj_draw.h
*
*/
#ifndef LV_OBJ_DRAW_H
#define LV_OBJ_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../draw/lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
/** Cover check results.*/
typedef enum {
LV_COVER_RES_COVER = 0,
LV_COVER_RES_NOT_COVER = 1,
LV_COVER_RES_MASKED = 2,
} lv_cover_res_t;
typedef struct {
const lv_area_t * clip_area; /**< The current clip area, required if you need to draw something in the event*/
const struct _lv_obj_class_t * class_p; /**< The class that sent the event */
uint32_t type; /**< The type if part being draw. Element of `lv_<name>_draw_part_type_t` */
lv_area_t * draw_area; /**< The area of the part being drawn*/
lv_draw_rect_dsc_t *
rect_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for rectangle-like parts*/
lv_draw_label_dsc_t *
label_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for text-like parts*/
lv_draw_line_dsc_t *
line_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for line-like parts*/
lv_draw_img_dsc_t *
img_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for image-like parts*/
lv_draw_arc_dsc_t *
arc_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for arc-like parts*/
const lv_point_t *
p1; /**< A point calculated during drawing. E.g. a point of chart or the center of an arc.*/
const lv_point_t * p2; /**< A point calculated during drawing. E.g. a point of chart.*/
char * text; /**< A text calculated during drawing. Can be modified. E.g. tick labels on a chart axis.*/
uint32_t text_length; /**< Size of the text buffer containing null-terminated text string calculated during drawing.*/
uint32_t part; /**< The current part for which the event is sent*/
uint32_t id; /**< The index of the part. E.g. a button's index on button matrix or table cell index.*/
lv_coord_t radius; /**< E.g. the radius of an arc (not the corner radius).*/
int32_t value; /**< A value calculated during drawing. E.g. Chart's tick line value.*/
const void * sub_part_ptr; /**< A pointer the identifies something in the part. E.g. chart series. */
} lv_obj_draw_part_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a rectangle draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized.
* Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`.
* @note Only the relevant fields will be set.
* E.g. if `border width == 0` the other border properties won't be evaluated.
*/
void lv_obj_init_draw_rect_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc);
/**
* Initialize a label draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized.
* Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_label_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc);
/**
* Initialize an image draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_img_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc);
/**
* Initialize a line draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_line_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc);
/**
* Initialize an arc draw descriptor from an object's styles in its current state
* @param obj pointer to an object
* @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
* @param draw_dsc the descriptor to initialize.
* Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`.
*/
void lv_obj_init_draw_arc_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc);
/**
* Get the required extra size (around the object's part) to draw shadow, outline, value etc.
* @param obj pointer to an object
* @param part part of the object
* @return the extra size required around the object
*/
lv_coord_t lv_obj_calculate_ext_draw_size(struct _lv_obj_t * obj, uint32_t part);
/**
* Initialize a draw descriptor used in events.
* @param dsc pointer to a descriptor. Later it should be passed as parameter to an `LV_EEVNT_DRAW_PART_BEGIN/END` event.
* @param clip_area the current clip area of the drawing
*/
void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, const lv_area_t * clip_area);
/**
* Check the type obj a part draw descriptor
* @param dsc the descriptor (normally the event parameter)
* @param class_p pointer to class to which `type` is related
* @param type element of `lv_<name>_draw_part_type_t`
* @return true if ::dsc is related to ::class_p and ::type
*/
bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const struct _lv_obj_class_t * class_p, uint32_t type);
/**
* Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size.
* The result will be saved in `obj`.
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_draw_size(struct _lv_obj_t * obj);
/**
* Get the extended draw area of an object.
* @param obj pointer to an object
* @return the size extended draw area around the real coordinates
*/
lv_coord_t _lv_obj_get_ext_draw_size(const struct _lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_DRAW_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,405 @@
/**
* @file lv_obj_pos.h
*
*/
#ifndef LV_OBJ_POS_H
#define LV_OBJ_POS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
typedef void (*lv_layout_update_cb_t)(struct _lv_obj_t *, void * user_data);
typedef struct {
lv_layout_update_cb_t cb;
void * user_data;
} lv_layout_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Set the relative the position of an object (relative to the parent's top left corner)
* @param obj pointer to an object
* @param x new distance from the left side of the parent plus the parent's left padding
* @param y new distance from the top side of the parent plus the parent's right padding
*/
void lv_obj_set_pos(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
/**
* Set the x coordinate of a object
* @param obj pointer to an object
* @param x new distance from the left side from the parent plus the parent's left padding
*/
void lv_obj_set_x(struct _lv_obj_t * obj, lv_coord_t x);
/**
* Set the y coordinate of a object
* @param obj pointer to an object
* @param y new distance from the top of the parent plus the parent's top padding
*/
void lv_obj_set_y(struct _lv_obj_t * obj, lv_coord_t y);
/**
* Set the size of an object.
* @param obj pointer to an object
* @param w the new width
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* LV_SIZE_PCT(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_size(struct _lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
/**
* Recalculate the size of the object
* @param obj pointer to an object
* @return true: the size has been changed
*/
bool lv_obj_refr_size(struct _lv_obj_t * obj);
/**
* Set the width of an object
* @param obj pointer to an object
* @param w the new width
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_width(struct _lv_obj_t * obj, lv_coord_t w);
/**
* Set the height of an object
* @param obj pointer to an object
* @param h the new height
* @note possible values are:
* pixel simple set the size accordingly
* LV_SIZE_CONTENT set the size to involve all children in the given direction
* lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
* x should be in [0..1000]% range
*/
void lv_obj_set_height(struct _lv_obj_t * obj, lv_coord_t h);
/**
* Set the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @param w the width without paddings in pixels
*/
void lv_obj_set_content_width(struct _lv_obj_t * obj, lv_coord_t w);
/**
* Set the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @param h the height without paddings in pixels
*/
void lv_obj_set_content_height(struct _lv_obj_t * obj, lv_coord_t h);
/**
* Set a layout for an object
* @param obj pointer to an object
* @param layout pointer to a layout descriptor to set
*/
void lv_obj_set_layout(struct _lv_obj_t * obj, uint32_t layout);
/**
* Test whether the and object is positioned by a layout or not
* @param obj pointer to an object to test
* @return true: positioned by a layout; false: not positioned by a layout
*/
bool lv_obj_is_layout_positioned(const struct _lv_obj_t * obj);
/**
* Mark the object for layout update.
* @param obj pointer to an object whose children needs to be updated
*/
void lv_obj_mark_layout_as_dirty(struct _lv_obj_t * obj);
/**
* Update the layout of an object.
* @param obj pointer to an object whose children needs to be updated
*/
void lv_obj_update_layout(const struct _lv_obj_t * obj);
/**
* Regsiter a new layout
* @param cb the layout update callback
* @param user_data custom data that will be passed to `cb`
* @return the ID of the new layout
*/
uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data);
/**
* Change the alignment of an object.
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
*/
void lv_obj_set_align(struct _lv_obj_t * obj, lv_align_t align);
/**
* Change the alignment of an object and set new coordinates.
* Equivalent to:
* lv_obj_set_align(obj, align);
* lv_obj_set_pos(obj, x_ofs, y_ofs);
* @param obj pointer to an object to align
* @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
*/
void lv_obj_align(struct _lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an other object (if NULL `obj`s parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_ofs x coordinate offset after alignment
* @param y_ofs y coordinate offset after alignment
* @note if the position or size of `base` changes `obj` needs to be aligned manually again
*/
void lv_obj_align_to(struct _lv_obj_t * obj, const struct _lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs,
lv_coord_t y_ofs);
/**
* Align an object to the center on its parent.
* @param obj pointer to an object to align
* @note if the parent size changes `obj` needs to be aligned manually again
*/
static inline void lv_obj_center(struct _lv_obj_t * obj)
{
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
}
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
* @param coords pointer to an area to store the coordinates
*/
void lv_obj_get_coords(const struct _lv_obj_t * obj, lv_area_t * coords);
/**
* Get the x coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the left side of its parent plus the parent's left padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the left padding of the parent, and not on the left edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_x(const struct _lv_obj_t * obj);
/**
* Get the x2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the right side of its parent plus the parent's right padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the right padding of the parent, and not on the right edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_x2(const struct _lv_obj_t * obj);
/**
* Get the y coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the top side of its parent plus the parent's top padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the top padding of the parent, and not on the top edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_y(const struct _lv_obj_t * obj);
/**
* Get the y2 coordinate of object.
* @param obj pointer to an object
* @return distance of `obj` from the bottom side of its parent plus the parent's bottom padding
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @note Zero return value means the object is on the bottom padding of the parent, and not on the bottom edge.
* @note Scrolling of the parent doesn't change the returned value.
* @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
*/
lv_coord_t lv_obj_get_y2(const struct _lv_obj_t * obj);
/**
* Get the width of an object
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the width in pixels
*/
lv_coord_t lv_obj_get_width(const struct _lv_obj_t * obj);
/**
* Get the height of an object
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the height in pixels
*/
lv_coord_t lv_obj_get_height(const struct _lv_obj_t * obj);
/**
* Get the width reduced by the left and right padding and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the width which still fits into its parent without causing overflow (making the parent scrollable)
*/
lv_coord_t lv_obj_get_content_width(const struct _lv_obj_t * obj);
/**
* Get the height reduced by the top and bottom padding and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @return the height which still fits into the parent without causing overflow (making the parent scrollable)
*/
lv_coord_t lv_obj_get_content_height(const struct _lv_obj_t * obj);
/**
* Get the area reduced by the paddings and the border width.
* @param obj pointer to an object
* @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
* call `lv_obj_update_layout(obj)`.
* @param area the area which still fits into the parent without causing overflow (making the parent scrollable)
*/
void lv_obj_get_content_coords(const struct _lv_obj_t * obj, lv_area_t * area);
/**
* Get the width occupied by the "parts" of the widget. E.g. the width of all columns of a table.
* @param obj pointer to an objects
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
lv_coord_t lv_obj_get_self_width(const struct _lv_obj_t * obj);
/**
* Get the height occupied by the "parts" of the widget. E.g. the height of all rows of a table.
* @param obj pointer to an objects
* @return the width of the virtually drawn content
* @note This size independent from the real size of the widget.
* It just tells how large the internal ("virtual") content is.
*/
lv_coord_t lv_obj_get_self_height(const struct _lv_obj_t * obj);
/**
* Handle if the size of the internal ("virtual") content of an object has changed.
* @param obj pointer to an object
* @return false: nothing happened; true: refresh happened
*/
bool lv_obj_refresh_self_size(struct _lv_obj_t * obj);
void lv_obj_refr_pos(struct _lv_obj_t * obj);
void lv_obj_move_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
void lv_obj_move_children_by(struct _lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating);
/**
* Mark an area of an object as invalid.
* The area will be truncated to the object's area and marked for redraw.
* @param obj pointer to an object
* @param area the area to redraw
*/
void lv_obj_invalidate_area(const struct _lv_obj_t * obj, const lv_area_t * area);
/**
* Mark the object as invalid to redrawn its area
* @param obj pointer to an object
*/
void lv_obj_invalidate(const struct _lv_obj_t * obj);
/**
* Tell whether an area of an object is visible (even partially) now or not
* @param obj pointer to an object
* @param area the are to check. The visible part of the area will be written back here.
* @return true visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_area_is_visible(const struct _lv_obj_t * obj, lv_area_t * area);
/**
* Tell whether an object is visible (even partially) now or not
* @param obj pointer to an object
* @return true: visible; false not visible (hidden, out of parent, on other screen, etc)
*/
bool lv_obj_is_visible(const struct _lv_obj_t * obj);
/**
* Set the size of an extended clickable area
* @param obj pointer to an object
* @param size extended clickable area in all 4 directions [px]
*/
void lv_obj_set_ext_click_area(struct _lv_obj_t * obj, lv_coord_t size);
/**
* Get the an area where to object can be clicked.
* It's the object's normal area plus the extended click area.
* @param obj pointer to an object
* @param area store the result area here
*/
void lv_obj_get_click_area(const struct _lv_obj_t * obj, lv_area_t * area);
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point (absolute coordinate)
* @return true: if the object is considered under the point
*/
bool lv_obj_hit_test(struct _lv_obj_t * obj, const lv_point_t * point);
/**
* Clamp a width between min and max width. If the min/max width is in percentage value use the ref_width
* @param width width to clamp
* @param min_width the minimal width
* @param max_width the maximal width
* @param ref_width the reference width used when min/max width is in percentage
* @return the clamped width
*/
lv_coord_t lv_clamp_width(lv_coord_t width, lv_coord_t min_width, lv_coord_t max_width, lv_coord_t ref_width);
/**
* Clamp a height between min and max height. If the min/max height is in percentage value use the ref_height
* @param height height to clamp
* @param min_height the minimal height
* @param max_height the maximal height
* @param ref_height the reference height used when min/max height is in percentage
* @return the clamped height
*/
lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t max_height, lv_coord_t ref_height);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_POS_H*/

View File

@ -0,0 +1,758 @@
/**
* @file lv_obj_scroll.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj_scroll.h"
#include "lv_obj.h"
#include "lv_indev.h"
#include "lv_disp.h"
#include "lv_indev_scroll.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
#define SCROLL_ANIM_TIME_MIN 200 /*ms*/
#define SCROLL_ANIM_TIME_MAX 400 /*ms*/
#define SCROLLBAR_MIN_SIZE (LV_DPX(10))
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
static void scroll_x_anim(void * obj, int32_t v);
static void scroll_y_anim(void * obj, int32_t v);
static void scroll_anim_ready_cb(lv_anim_t * a);
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
lv_anim_enable_t anim_en);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
if(obj->spec_attr->scrollbar_mode == mode) return;
obj->spec_attr->scrollbar_mode = mode;
lv_obj_invalidate(obj);
}
void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
{
lv_obj_allocate_spec_attr(obj);
if(dir != obj->spec_attr->scroll_dir) {
obj->spec_attr->scroll_dir = dir;
}
}
void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_x = align;
}
void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
{
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll_snap_y = align;
}
/*=====================
* Getter functions
*====================*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scrollbar_mode;
else return LV_SCROLLBAR_MODE_AUTO;
}
lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scroll_dir;
else return LV_DIR_ALL;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scroll_snap_x;
else return LV_SCROLL_SNAP_NONE;
}
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->scroll_snap_y;
else return LV_SCROLL_SNAP_NONE;
}
lv_coord_t lv_obj_get_scroll_x(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
lv_coord_t lv_obj_get_scroll_y(const lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
lv_coord_t lv_obj_get_scroll_top(lv_obj_t * obj)
{
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.y;
}
lv_coord_t lv_obj_get_scroll_bottom(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
child_res = LV_MAX(child_res, child->coords.y2);
}
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.y2 - pad_bottom - border_width);
}
lv_coord_t self_h = lv_obj_get_self_height(obj);
self_h = self_h - (lv_obj_get_height(obj) - pad_top - pad_bottom - 2 * border_width);
self_h -= lv_obj_get_scroll_y(obj);
return LV_MAX(child_res, self_h);
}
lv_coord_t lv_obj_get_scroll_left(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Normally can't scroll the object out on the left.
*So simply use the current scroll position as "left size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return -obj->spec_attr->scroll.x;
}
/*With RTL base direction scrolling the left is normal so find the left most coordinate*/
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t child_res = 0;
uint32_t i;
lv_coord_t x1 = LV_COORD_MAX;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
x1 = LV_MIN(x1, child->coords.x1);
}
if(x1 != LV_COORD_MAX) {
child_res = x1;
child_res = (obj->coords.x1 + pad_left + border_width) - child_res;
}
else {
child_res = LV_COORD_MIN;
}
lv_coord_t self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
self_w += lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
lv_coord_t lv_obj_get_scroll_right(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*With RTL base dir can't scroll to the object out on the right.
*So simply use the current scroll position as "right size"*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->scroll.x;
}
/*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
child_res = LV_MAX(child_res, child->coords.x2);
}
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
if(child_res != LV_COORD_MIN) {
child_res -= (obj->coords.x2 - pad_right - border_width);
}
lv_coord_t self_w;
self_w = lv_obj_get_self_width(obj);
self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
self_w -= lv_obj_get_scroll_x(obj);
return LV_MAX(child_res, self_w);
}
void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end)
{
lv_anim_t * a;
a = lv_anim_get(obj, scroll_x_anim);
end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
a = lv_anim_get(obj, scroll_y_anim);
end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
}
/*=====================
* Other functions
*====================*/
void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
{
if(x == 0 && y == 0) return;
if(anim_en == LV_ANIM_ON) {
lv_disp_t * d = lv_obj_get_disp(obj);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_ready_cb(&a, scroll_anim_ready_cb);
if(x) {
uint32_t t = lv_anim_speed_to_time((lv_disp_get_hor_res(d) * 2) >> 2, 0, x);
if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
lv_anim_set_time(&a, t);
lv_coord_t sx = lv_obj_get_scroll_x(obj);
lv_anim_set_values(&a, -sx, -sx + x);
lv_anim_set_exec_cb(&a, scroll_x_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RES_OK) return;
lv_anim_start(&a);
}
if(y) {
uint32_t t = lv_anim_speed_to_time((lv_disp_get_ver_res(d) * 2) >> 2, 0, y);
if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
lv_anim_set_time(&a, t);
lv_coord_t sy = lv_obj_get_scroll_y(obj);
lv_anim_set_values(&a, -sy, -sy + y);
lv_anim_set_exec_cb(&a, scroll_y_anim);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
if(res != LV_RES_OK) return;
lv_anim_start(&a);
}
}
else {
/*Remove pending animations*/
bool y_del = lv_anim_del(obj, scroll_y_anim);
bool x_del = lv_anim_del(obj, scroll_x_anim);
scroll_by_raw(obj, x, y);
if(y_del || x_del) {
lv_res_t res;
res = lv_event_send(obj, LV_EVENT_SCROLL_END, NULL);
if(res != LV_RES_OK) return;
}
}
}
void lv_obj_scroll_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
{
lv_obj_scroll_to_x(obj, x, anim_en);
lv_obj_scroll_to_y(obj, y, anim_en);
}
void lv_obj_scroll_to_x(lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en)
{
lv_anim_del(obj, scroll_x_anim);
/*Don't let scroll more then naturally possible by the size of the content*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
if(x < 0) x = 0;
if(x > 0) {
lv_coord_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x > scroll_max) x = scroll_max;
}
}
else {
if(x > 0) x = 0;
if(x < 0) {
lv_coord_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
if(scroll_max < 0) scroll_max = 0;
if(x < -scroll_max) x = -scroll_max;
}
}
lv_coord_t scroll_x = lv_obj_get_scroll_x(obj);
lv_coord_t diff = -x + scroll_x;
lv_obj_scroll_by(obj, diff, 0, anim_en);
}
void lv_obj_scroll_to_y(lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en)
{
lv_anim_del(obj, scroll_y_anim);
/*Don't let scroll more then naturally possible by the size of the content*/
if(y < 0) y = 0;
if(y > 0) {
lv_coord_t scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
if(scroll_max < 0) scroll_max = 0;
if(y > scroll_max) y = scroll_max;
}
lv_coord_t scroll_y = lv_obj_get_scroll_y(obj);
lv_coord_t diff = -y + scroll_y;
lv_obj_scroll_by(obj, 0, diff, anim_en);
}
void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
scroll_area_into_view(&obj->coords, obj, &p, anim_en);
}
void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the screens layout is correct*/
lv_obj_update_layout(obj);
lv_point_t p = {0, 0};
lv_obj_t * child = obj;
lv_obj_t * parent = lv_obj_get_parent(child);
while(parent) {
scroll_area_into_view(&obj->coords, child, &p, anim_en);
child = parent;
parent = lv_obj_get_parent(parent);
}
}
bool lv_obj_is_scrolling(const lv_obj_t * obj)
{
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) return true;
indev = lv_indev_get_next(indev);
}
return false;
}
void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
lv_obj_update_layout(obj);
lv_point_t p;
lv_indev_scroll_get_snap_dist(obj, &p);
lv_obj_scroll_by(obj, p.x, p.y, anim_en);
}
void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
{
lv_area_set(hor_area, 0, 0, -1, -1);
lv_area_set(ver_area, 0, 0, -1, -1);
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
lv_dir_t sm = lv_obj_get_scrollbar_mode(obj);
if(sm == LV_SCROLLBAR_MODE_OFF) return;
/*If there is no indev scrolling this object but `mode==active` return*/
lv_indev_t * indev = lv_indev_get_next(NULL);
if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
while(indev) {
if(lv_indev_get_scroll_obj(indev) == obj) break;
indev = lv_indev_get_next(indev);
}
if(indev == NULL) return;
}
lv_coord_t st = lv_obj_get_scroll_top(obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_t sr = lv_obj_get_scroll_right(obj);
lv_dir_t dir = lv_obj_get_scroll_dir(obj);
bool ver_draw = false;
if((dir & LV_DIR_VER) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
ver_draw = true;
}
bool hor_draw = false;
if((dir & LV_DIR_HOR) &&
((sm == LV_SCROLLBAR_MODE_ON) ||
(sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
(sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
hor_draw = true;
}
if(!hor_draw && !ver_draw) return;
bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t end_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
lv_coord_t side_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
lv_coord_t tickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
lv_coord_t obj_h = lv_obj_get_height(obj);
lv_coord_t obj_w = lv_obj_get_width(obj);
lv_coord_t ver_reg_space = ver_draw ? tickness + side_space : 0;
lv_coord_t hor_req_space = hor_draw ? tickness + side_space : 0;
lv_coord_t rem;
if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN &&
lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN) {
return;
}
/*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
lv_coord_t content_h = obj_h + st + sb;
if(ver_draw && content_h) {
ver_area->y1 = obj->coords.y1;
ver_area->y2 = obj->coords.y2;
if(rtl) {
ver_area->x1 = obj->coords.x1 + side_space;
ver_area->x2 = ver_area->x1 + tickness - 1;
}
else {
ver_area->x2 = obj->coords.x2 - side_space;
ver_area->x1 = ver_area->x2 - tickness + 1;
}
lv_coord_t sb_h = ((obj_h - end_space * 2 - hor_req_space) * obj_h) / content_h;
sb_h = LV_MAX(sb_h, SCROLLBAR_MIN_SIZE);
rem = (obj_h - end_space * 2 - hor_req_space) -
sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
lv_coord_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
if(scroll_h <= 0) {
ver_area->y1 = obj->coords.y1 + end_space;
ver_area->y2 = obj->coords.y2 - end_space - hor_req_space - 1;
}
else {
lv_coord_t sb_y = (rem * sb) / scroll_h;
sb_y = rem - sb_y;
ver_area->y1 = obj->coords.y1 + sb_y + end_space;
ver_area->y2 = ver_area->y1 + sb_h - 1;
if(ver_area->y1 < obj->coords.y1 + end_space) {
ver_area->y1 = obj->coords.y1 + end_space;
if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2)ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
}
if(ver_area->y2 > obj->coords.y2 - hor_req_space - end_space) {
ver_area->y2 = obj->coords.y2 - hor_req_space - end_space;
if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1)ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
}
}
}
/*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
lv_coord_t content_w = obj_w + sl + sr;
if(hor_draw && content_w) {
hor_area->y2 = obj->coords.y2 - side_space;
hor_area->y1 = hor_area->y2 - tickness + 1;
hor_area->x1 = obj->coords.x1;
hor_area->x2 = obj->coords.x2;
lv_coord_t sb_w = ((obj_w - end_space * 2 - ver_reg_space) * obj_w) / content_w;
sb_w = LV_MAX(sb_w, SCROLLBAR_MIN_SIZE);
rem = (obj_w - end_space * 2 - ver_reg_space) -
sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
lv_coord_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
if(scroll_w <= 0) {
if(rtl) {
hor_area->x1 = obj->coords.x1 + end_space + ver_reg_space - 1;
hor_area->x2 = obj->coords.x2 - end_space;
}
else {
hor_area->x1 = obj->coords.x1 + end_space;
hor_area->x2 = obj->coords.x2 - end_space - ver_reg_space - 1;
}
}
else {
lv_coord_t sb_x = (rem * sr) / scroll_w;
sb_x = rem - sb_x;
if(rtl) {
hor_area->x1 = obj->coords.x1 + sb_x + end_space + ver_reg_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
if(hor_area->x1 < obj->coords.x1 + end_space + ver_reg_space) {
hor_area->x1 = obj->coords.x1 + end_space + ver_reg_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
if(hor_area->x2 > obj->coords.x2 - end_space) {
hor_area->x2 = obj->coords.x2 - end_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
else {
hor_area->x1 = obj->coords.x1 + sb_x + end_space;
hor_area->x2 = hor_area->x1 + sb_w - 1;
if(hor_area->x1 < obj->coords.x1 + end_space) {
hor_area->x1 = obj->coords.x1 + end_space;
if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
}
if(hor_area->x2 > obj->coords.x2 - ver_reg_space - end_space) {
hor_area->x2 = obj->coords.x2 - ver_reg_space - end_space;
if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
}
}
}
}
}
void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
{
lv_area_t hor_area;
lv_area_t ver_area;
lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
}
void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
/*Be sure the bottom side is not remains scrolled in*/
/*With snapping the content can't be scrolled in*/
if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
lv_coord_t st = lv_obj_get_scroll_top(obj);
lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
if(sb < 0 && st > 0) {
sb = LV_MIN(st, -sb);
lv_obj_scroll_by(obj, 0, sb, anim_en);
}
}
if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
lv_coord_t sl = lv_obj_get_scroll_left(obj);
lv_coord_t sr = lv_obj_get_scroll_right(obj);
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
/*Be sure the left side is not remains scrolled in*/
if(sr < 0 && sl > 0) {
sr = LV_MIN(sl, -sr);
lv_obj_scroll_by(obj, sr, 0, anim_en);
}
}
else {
/*Be sure the right side is not remains scrolled in*/
if(sl < 0 && sr > 0) {
sr = LV_MIN(sr, -sl);
lv_obj_scroll_by(obj, sl, 0, anim_en);
}
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
if(x == 0 && y == 0) return;
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->scroll.x += x;
obj->spec_attr->scroll.y += y;
lv_obj_move_children_by(obj, x, y, true);
lv_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL);
if(res != LV_RES_OK) return;
lv_obj_invalidate(obj);
}
static void scroll_x_anim(void * obj, int32_t v)
{
scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
}
static void scroll_y_anim(void * obj, int32_t v)
{
scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
}
static void scroll_anim_ready_cb(lv_anim_t * a)
{
lv_event_send(a->var, LV_EVENT_SCROLL_END, NULL);
}
static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
lv_anim_enable_t anim_en)
{
lv_obj_t * parent = lv_obj_get_parent(child);
if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
lv_coord_t snap_goal = 0;
lv_coord_t act = 0;
const lv_area_t * area_tmp;
lv_coord_t y_scroll = 0;
lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN) + border_width;
lv_coord_t pbottom = lv_obj_get_style_pad_bottom(parent, LV_PART_MAIN) + border_width;
lv_coord_t top_diff = parent->coords.y1 + ptop - area_tmp->y1 - scroll_value->y;
lv_coord_t bottom_diff = -(parent->coords.y2 - pbottom - area_tmp->y2 - scroll_value->y);
lv_coord_t parent_h = lv_obj_get_height(parent) - ptop - pbottom;
if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
else if(top_diff > 0) {
y_scroll = top_diff;
/*Do not let scrolling in*/
lv_coord_t st = lv_obj_get_scroll_top(parent);
if(st - y_scroll < 0) y_scroll = 0;
}
else if(bottom_diff > 0) {
y_scroll = -bottom_diff;
/*Do not let scrolling in*/
lv_coord_t sb = lv_obj_get_scroll_bottom(parent);
if(sb + y_scroll < 0) y_scroll = 0;
}
switch(snap_y) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.y1 + ptop;
act = area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.y2 - pbottom;
act = area_tmp->y2 + y_scroll;
y_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.y1 + ptop + parent_h / 2;
act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
y_scroll += snap_goal - act;
break;
}
lv_coord_t x_scroll = 0;
lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
else area_tmp = area;
lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN) + border_width;
lv_coord_t pright = lv_obj_get_style_pad_right(parent, LV_PART_MAIN) + border_width;
lv_coord_t left_diff = parent->coords.x1 + pleft - area_tmp->x1 - scroll_value->x;
lv_coord_t right_diff = -(parent->coords.x2 - pright - area_tmp->x2 - scroll_value->x);
if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
else if(left_diff > 0) {
x_scroll = left_diff;
/*Do not let scrolling in*/
lv_coord_t sl = lv_obj_get_scroll_left(parent);
if(sl + x_scroll > 0) x_scroll = 0;
}
else if(right_diff > 0) {
x_scroll = -right_diff;
/*Do not let scrolling in*/
lv_coord_t sr = lv_obj_get_scroll_right(parent);
if(sr + x_scroll < 0) x_scroll = 0;
}
lv_coord_t parent_w = lv_obj_get_width(parent) - pleft - pright;
switch(snap_x) {
case LV_SCROLL_SNAP_START:
snap_goal = parent->coords.x1 + pleft;
act = area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_END:
snap_goal = parent->coords.x2 - pright;
act = area_tmp->x2 + x_scroll;
x_scroll += snap_goal - act;
break;
case LV_SCROLL_SNAP_CENTER:
snap_goal = parent->coords.x1 + pleft + parent_w / 2;
act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
x_scroll += snap_goal - act;
break;
}
/*Remove any pending scroll animations.*/
bool y_del = lv_anim_del(parent, scroll_y_anim);
bool x_del = lv_anim_del(parent, scroll_x_anim);
if(y_del || x_del) {
lv_res_t res;
res = lv_event_send(parent, LV_EVENT_SCROLL_END, NULL);
if(res != LV_RES_OK) return;
}
if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
scroll_value->x += anim_en == LV_ANIM_OFF ? 0 : x_scroll;
scroll_value->y += anim_en == LV_ANIM_OFF ? 0 : y_scroll;
lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
}

View File

@ -0,0 +1,284 @@
/**
* @file lv_obj_scroll.h
*
*/
#ifndef LV_OBJ_SCROLL_H
#define LV_OBJ_SCROLL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_area.h"
#include "../misc/lv_anim.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
/** Scrollbar modes: shows when should the scrollbars be visible*/
enum {
LV_SCROLLBAR_MODE_OFF, /**< Never show scrollbars*/
LV_SCROLLBAR_MODE_ON, /**< Always show scrollbars*/
LV_SCROLLBAR_MODE_ACTIVE, /**< Show scroll bars when object is being scrolled*/
LV_SCROLLBAR_MODE_AUTO, /**< Show scroll bars when the content is large enough to be scrolled*/
};
typedef uint8_t lv_scrollbar_mode_t;
/** Scroll span align options. Tells where to align the snappable children when scroll stops.*/
enum {
LV_SCROLL_SNAP_NONE, /**< Do not align, leave where it is*/
LV_SCROLL_SNAP_START, /**< Align to the left/top*/
LV_SCROLL_SNAP_END, /**< Align to the right/bottom*/
LV_SCROLL_SNAP_CENTER /**< Align to the center*/
};
typedef uint8_t lv_scroll_snap_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*=====================
* Setter functions
*====================*/
/**
* Set how the scrollbars should behave.
* @param obj pointer to an object
* @param mode LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
*/
void lv_obj_set_scrollbar_mode(struct _lv_obj_t * obj, lv_scrollbar_mode_t mode);
/**
* Set the object in which directions can be scrolled
* @param obj pointer to an object
* @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
*/
void lv_obj_set_scroll_dir(struct _lv_obj_t * obj, lv_dir_t dir);
/**
* Set where to snap the children when scrolling ends horizontally
* @param obj pointer to an object
* @param align the snap align to set from `lv_scroll_snap_t`
*/
void lv_obj_set_scroll_snap_x(struct _lv_obj_t * obj, lv_scroll_snap_t align);
/**
* Set where to snap the children when scrolling ends vertically
* @param obj pointer to an object
* @param align the snap align to set from `lv_scroll_snap_t`
*/
void lv_obj_set_scroll_snap_y(struct _lv_obj_t * obj, lv_scroll_snap_t align);
/*=====================
* Getter functions
*====================*/
/**
* Get the current scroll mode (when to hide the scrollbars)
* @param obj pointer to an object
* @return the current scroll mode from `lv_scrollbar_mode_t`
*/
lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const struct _lv_obj_t * obj);
/**
* Get the object in which directions can be scrolled
* @param obj pointer to an object
* @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
*/
lv_dir_t lv_obj_get_scroll_dir(const struct _lv_obj_t * obj);
/**
* Get where to snap the children when scrolling ends horizontally
* @param obj pointer to an object
* @return the current snap align from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_x(const struct _lv_obj_t * obj);
/**
* Get where to snap the children when scrolling ends vertically
* @param obj pointer to an object
* @return the current snap align from `lv_scroll_snap_t`
*/
lv_scroll_snap_t lv_obj_get_scroll_snap_y(const struct _lv_obj_t * obj);
/**
* Get current X scroll position.
* @param obj pointer to an object
* @return the current scroll position from the left edge.
* If the object is not scrolled return 0
* If scrolled return > 0
* If scrolled in (elastic scroll) return < 0
*/
lv_coord_t lv_obj_get_scroll_x(const struct _lv_obj_t * obj);
/**
* Get current Y scroll position.
* @param obj pointer to an object
* @return the current scroll position from the top edge.
* If the object is not scrolled return 0
* If scrolled return > 0
* If scrolled inside return < 0
*/
lv_coord_t lv_obj_get_scroll_y(const struct _lv_obj_t * obj);
/**
* Return the height of the area above the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area above the object in pixels
*/
lv_coord_t lv_obj_get_scroll_top(struct _lv_obj_t * obj);
/**
* Return the height of the area below the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area below the object in pixels
*/
lv_coord_t lv_obj_get_scroll_bottom(struct _lv_obj_t * obj);
/**
* Return the width of the area on the left the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area on the left the object in pixels
*/
lv_coord_t lv_obj_get_scroll_left(struct _lv_obj_t * obj);
/**
* Return the width of the area on the right the object.
* That is the number of pixels the object can be scrolled down.
* Normally positive but can be negative when scrolled inside.
* @param obj pointer to an object
* @return the scrollable area on the right the object in pixels
*/
lv_coord_t lv_obj_get_scroll_right(struct _lv_obj_t * obj);
/**
* Get the X and Y coordinates where the scrolling will end for this object if a scrolling animation is in progress.
* If no scrolling animation, give the current `x` or `y` scroll position.
* @param obj pointer to an object
* @param end pointer to store the result
*/
void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end);
/*=====================
* Other functions
*====================*/
/**
*
* Scroll by a given amount of pixels
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
* @note > 0 value means scroll right/bottom (show the more content on the right/bottom)
* @note
*/
void lv_obj_scroll_by(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to a given coordinate on an object.
* `x` and `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to a given X coordinate on an object.
* `x` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object to scroll
* @param x pixels to scroll horizontally
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_x(struct _lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en);
/**
* Scroll to a given Y coordinate on an object
* `y` will be limited internally to allow scrolling only on the content area.
* @param obj pointer to an object to scroll
* @param y pixels to scroll vertically
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_y(struct _lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en);
/**
* Scroll to an object until it becomes visible on its parent
* @param obj pointer to an object to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Scroll to an object until it becomes visible on its parent.
* Do the same on the parent's parent, and so on.
* Therefore the object will be scrolled into view even it has nested scrollable parents
* @param obj pointer to an object to scroll into view
* @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
*/
void lv_obj_scroll_to_view_recursive(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Tell whether an object is being scrolled or not at this moment
* @param obj pointer to an object
* @return true: `obj` is being scrolled
*/
bool lv_obj_is_scrolling(const struct _lv_obj_t * obj);
/**
* Check the children of `obj` and scroll `obj` to fulfill the scroll_snap settings
* @param obj an object whose children needs to checked and snapped
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_update_snap(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**
* Get the area of the scrollbars
* @param obj pointer to an object
* @param hor pointer to store the area of the horizontal scrollbar
* @param ver pointer to store the area of the vertical scrollbar
*/
void lv_obj_get_scrollbar_area(struct _lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
/**
* Invalidate the area of the scrollbars
* @param obj pointer to an object
*/
void lv_obj_scrollbar_invalidate(struct _lv_obj_t * obj);
/**
* Checked if the content is scrolled "in" and adjusts it to a normal position.
* @param obj pointer to an object
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_obj_readjust_scroll(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_SCROLL_H*/

View File

@ -0,0 +1,825 @@
/**
* @file lv_obj_style.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_disp.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t * obj;
lv_style_prop_t prop;
lv_style_selector_t selector;
lv_style_value_t start_value;
lv_style_value_t end_value;
} trans_t;
typedef enum {
CACHE_ZERO = 0,
CACHE_TRUE = 1,
CACHE_UNSET = 2,
CACHE_255 = 3,
CACHE_NEED_CHECK = 4,
} cache_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector);
static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, uint32_t part);
static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v);
static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v);
static void report_style_change_core(void * style, lv_obj_t * obj);
static void refresh_children_style(lv_obj_t * obj);
static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
static void trans_anim_cb(void * _tr, int32_t v);
static void trans_anim_start_cb(lv_anim_t * a);
static void trans_anim_ready_cb(lv_anim_t * a);
static void fade_anim_cb(void * obj, int32_t v);
static void fade_in_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
static bool style_refr = true;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_obj_style_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(trans_t));
}
void lv_obj_add_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
trans_del(obj, selector, LV_STYLE_PROP_ANY, NULL);
uint32_t i;
/*Go after the transition and local styles*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans) continue;
if(obj->styles[i].is_local) continue;
break;
}
/*Now `i` is at the first normal style. Insert the new style before this*/
/*Allocate space for the new style and shift the rest of the style to the end*/
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
uint32_t j;
for(j = obj->style_cnt - 1; j > i ; j--) {
obj->styles[j] = obj->styles[j - 1];
}
lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
obj->styles[i].style = style;
obj->styles[i].selector = selector;
lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
}
void lv_obj_remove_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
lv_state_t state = lv_obj_style_get_selector_state(selector);
lv_part_t part = lv_obj_style_get_selector_part(selector);
lv_style_prop_t prop = LV_STYLE_PROP_ANY;
if(style && style->prop_cnt == 0) prop = LV_STYLE_PROP_INV;
uint32_t i = 0;
bool deleted = false;
while(i < obj->style_cnt) {
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if((state != LV_STATE_ANY && state_act != state) ||
(part != LV_PART_ANY && part_act != part) ||
(style != NULL && style != obj->styles[i].style)) {
i++;
continue;
}
if(obj->styles[i].is_trans) {
trans_del(obj, part, LV_STYLE_PROP_ANY, NULL);
}
if(obj->styles[i].is_local || obj->styles[i].is_trans) {
lv_style_reset(obj->styles[i].style);
lv_mem_free(obj->styles[i].style);
obj->styles[i].style = NULL;
}
/*Shift the styles after `i` by one*/
uint32_t j;
for(j = i; j < (uint32_t)obj->style_cnt - 1 ; j++) {
obj->styles[j] = obj->styles[j + 1];
}
obj->style_cnt--;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
deleted = true;
/*The style from the current `i` index is removed, so `i` points to the next style.
*Therefore it doesn't needs to be incremented*/
}
if(deleted && prop != LV_STYLE_PROP_INV) {
lv_obj_refresh_style(obj, part, prop);
}
}
void lv_obj_report_style_change(lv_style_t * style)
{
if(!style_refr) return;
lv_disp_t * d = lv_disp_get_next(NULL);
while(d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
report_style_change_core(style, d->screens[i]);
}
d = lv_disp_get_next(d);
}
}
void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!style_refr) return;
lv_obj_invalidate(obj);
lv_part_t part = lv_obj_style_get_selector_part(selector);
if(prop & LV_STYLE_PROP_LAYOUT_REFR) {
if(part == LV_PART_ANY ||
part == LV_PART_MAIN ||
lv_obj_get_style_height(obj, 0) == LV_SIZE_CONTENT ||
lv_obj_get_style_width(obj, 0) == LV_SIZE_CONTENT) {
lv_event_send(obj, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_mark_layout_as_dirty(obj);
}
}
if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY ||
(prop & LV_STYLE_PROP_PARENT_LAYOUT_REFR))) {
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) lv_obj_mark_layout_as_dirty(parent);
}
if(prop == LV_STYLE_PROP_ANY || (prop & LV_STYLE_PROP_EXT_DRAW)) {
lv_obj_refresh_ext_draw_size(obj);
}
lv_obj_invalidate(obj);
if(prop == LV_STYLE_PROP_ANY ||
((prop & LV_STYLE_PROP_INHERIT) && ((prop & LV_STYLE_PROP_EXT_DRAW) || (prop & LV_STYLE_PROP_LAYOUT_REFR)))) {
if(part != LV_PART_SCROLLBAR) {
refresh_children_style(obj);
}
}
}
void lv_obj_enable_style_refresh(bool en)
{
style_refr = en;
}
lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
{
lv_style_value_t value_act;
bool inherit = prop & LV_STYLE_PROP_INHERIT ? true : false;
bool filter = prop & LV_STYLE_PROP_FILTER ? true : false;
if(filter) {
prop &= ~LV_STYLE_PROP_FILTER;
}
bool found = false;
while(obj) {
found = get_prop_core(obj, part, prop, &value_act);
if(found) break;
if(!inherit) break;
/*If not found, check the `MAIN` style first*/
if(part != LV_PART_MAIN) {
part = LV_PART_MAIN;
continue;
}
/*Check the parent too.*/
obj = lv_obj_get_parent(obj);
}
if(!found) {
if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
const lv_obj_class_t * cls = obj->class_p;
while(cls) {
if(prop == LV_STYLE_WIDTH) {
if(cls->width_def != 0) break;
}
else {
if(cls->height_def != 0) break;
}
cls = cls->base_class;
}
value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def;
}
else {
value_act = lv_style_prop_get_default(prop);
}
}
if(filter) value_act = apply_color_filter(obj, part, value_act);
return value_act;
}
void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
lv_style_selector_t selector)
{
lv_style_t * style = get_local_style(obj, selector);
lv_style_set_prop(style, prop, value);
lv_obj_refresh_style(obj, selector, prop);
}
lv_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
return lv_style_get_prop(obj->styles[i].style, prop, value);
}
}
return LV_RES_INV;
}
bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
uint32_t i;
/*Find the style*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
break;
}
}
/*The style is not found*/
if(i == obj->style_cnt) return false;
return lv_style_remove_prop(obj->styles[i].style, prop);
}
void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state,
const _lv_obj_style_transition_dsc_t * tr_dsc)
{
trans_t * tr;
/*Get the previous and current values*/
obj->skip_trans = 1;
obj->state = prev_state;
lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->state = new_state;
lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->skip_trans = 0;
if(v1.ptr == v2.ptr && v1.num == v2.num && v1.color.full == v2.color.full) return;
obj->state = prev_state;
v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
obj->state = new_state;
_lv_obj_style_t * style_trans = get_trans_style(obj, part);
lv_style_set_prop(style_trans->style, tr_dsc->prop, v1); /*Be sure `trans_style` has a valid value*/
if(tr_dsc->prop == LV_STYLE_RADIUS) {
if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
lv_coord_t whalf = lv_obj_get_width(obj) / 2;
lv_coord_t hhalf = lv_obj_get_width(obj) / 2;
if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
}
}
tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
LV_ASSERT_MALLOC(tr);
if(tr == NULL) return;
tr->start_value = v1;
tr->end_value = v2;
if(tr) {
tr->obj = obj;
tr->prop = tr_dsc->prop;
tr->selector = part;
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, tr);
lv_anim_set_exec_cb(&a, trans_anim_cb);
lv_anim_set_start_cb(&a, trans_anim_start_cb);
lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
lv_anim_set_values(&a, 0x00, 0xFF);
lv_anim_set_time(&a, tr_dsc->time);
lv_anim_set_delay(&a, tr_dsc->delay);
lv_anim_set_path_cb(&a, tr_dsc->path_cb);
lv_anim_set_early_apply(&a, false);
#if LV_USE_USER_DATA
a.user_data = tr_dsc->user_data;
#endif
lv_anim_start(&a);
}
}
_lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
{
_lv_style_state_cmp_t res = _LV_STYLE_STATE_CMP_SAME;
/*Are there any new styles for the new state?*/
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans) continue;
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
/*The style is valid for a state but not the other*/
bool valid1 = state_act & (~state1) ? false : true;
bool valid2 = state_act & (~state2) ? false : true;
if(valid1 != valid2) {
lv_style_t * style = obj->styles[i].style;
lv_style_value_t v;
/*If there is layout difference on the main part, return immediately. There is no more serious difference*/
bool layout_diff = false;
if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) layout_diff = true;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) layout_diff = true;
if(layout_diff) {
return _LV_STYLE_STATE_CMP_DIFF_LAYOUT;
}
/*Check for draw pad changes*/
if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_X, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_Y, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
else if(res == _LV_STYLE_STATE_CMP_SAME) res = _LV_STYLE_STATE_CMP_DIFF_REDRAW;
}
}
return res;
}
void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER);
lv_anim_set_exec_cb(&a, fade_anim_cb);
lv_anim_set_ready_cb(&a, fade_in_anim_ready);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_start(&a);
}
void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, LV_OPA_COVER, LV_OPA_TRANSP);
lv_anim_set_exec_cb(&a, fade_anim_cb);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_start(&a);
}
lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
{
return selector & 0xFFFF;
}
lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
{
return selector & 0xFF0000;
}
lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt)
{
lv_text_align_t align = lv_obj_get_style_text_align(obj, part);
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, part);
lv_bidi_calculate_align(&align, &base_dir, txt);
return align;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get the local style of an object for a given part and for a given state.
* If the local style for the part-state pair doesn't exist allocate and return it.
* @param obj pointer to an object
* @param part the part in whose local style to get
* @param state the state in whose local style to get
* @return pointer to the local style
*/
static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_local &&
obj->styles[i].selector == selector) {
return obj->styles[i].style;
}
}
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
LV_ASSERT_MALLOC(obj->styles);
for(i = obj->style_cnt - 1; i > 0 ; i--) {
/*Copy only normal styles (not local and transition).
*The new local style will be added as the last local style*/
if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
obj->styles[i] = obj->styles[i - 1];
}
lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
obj->styles[i].style = lv_mem_alloc(sizeof(lv_style_t));
lv_style_init(obj->styles[i].style);
obj->styles[i].is_local = 1;
obj->styles[i].selector = selector;
return obj->styles[i].style;
}
/**
* Get the transition style of an object for a given part and for a given state.
* If the transition style for the part-state pair doesn't exist allocate and return it.
* @param obj pointer to an object
* @param part the part in whose local style to get
* @param state the state in whose local style to get
* @return pointer to the transition style
*/
static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t selector)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
}
/*Already have a transition style for it*/
if(i != obj->style_cnt) return &obj->styles[i];
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
for(i = obj->style_cnt - 1; i > 0 ; i--) {
obj->styles[i] = obj->styles[i - 1];
}
lv_memset_00(&obj->styles[0], sizeof(_lv_obj_style_t));
obj->styles[0].style = lv_mem_alloc(sizeof(lv_style_t));
lv_style_init(obj->styles[0].style);
obj->styles[0].is_trans = 1;
obj->styles[0].selector = selector;
return &obj->styles[0];
}
static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v)
{
uint8_t group = 1 << _lv_style_get_prop_group(prop);
int32_t weight = -1;
lv_state_t state = obj->state;
lv_state_t state_inv = ~state;
lv_style_value_t value_tmp;
bool skip_trans = obj->skip_trans;
uint32_t i;
bool found;
for(i = 0; i < obj->style_cnt; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
if(obj_style->is_trans == false) break;
if(skip_trans) continue;
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
if(part_act != part) continue;
if((obj_style->style->has_group & group) == 0) continue;
found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
if(found) {
*v = value_tmp;
return true;
}
}
for(; i < obj->style_cnt; i++) {
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
if(part_act != part) continue;
if((obj_style->style->has_group & group) == 0) continue;
/*Be sure the style not specifies other state than the requested.
*E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
if((state_act & state_inv)) continue;
/*Check only better candidates*/
if(state_act <= weight) continue;
found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
if(found) {
if(state_act == state) {
*v = value_tmp;
return true;
}
if(weight < state_act) {
weight = state_act;
*v = value_tmp;
}
}
}
if(weight >= 0) {
*v = value_tmp;
return true;
}
else return false;
}
static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v)
{
if(obj == NULL) return v;
const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
if(f && f->filter_cb) {
lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
}
return v;
}
/**
* Refresh the style of all children of an object. (Called recursively)
* @param style refresh objects only with this
* @param obj pointer to an object
*/
static void report_style_change_core(void * style, lv_obj_t * obj)
{
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(style == NULL || obj->styles[i].style == style) {
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
break;
}
}
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
report_style_change_core(style, obj->spec_attr->children[i]);
}
}
/**
* Recursively refresh the style of the children. Go deeper until a not NULL style is found
* because the NULL styles are inherited from the parent
* @param obj pointer to an object
*/
static void refresh_children_style(lv_obj_t * obj)
{
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_invalidate(child);
lv_event_send(child, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_invalidate(child);
refresh_children_style(child); /*Check children too*/
}
}
/**
* Remove the transition from object's part's property.
* - Remove the transition from `_lv_obj_style_trans_ll` and free it
* - Delete pending transitions
* @param obj pointer to an object which transition(s) should be removed
* @param part a part of object or 0xFF to remove from all parts
* @param prop a property or 0xFF to remove all properties
* @param tr_limit delete transitions only "older" than this. `NULL` if not used
*/
static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
{
trans_t * tr;
trans_t * tr_prev;
bool removed = false;
tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
while(tr != NULL) {
if(tr == tr_limit) break;
/*'tr' might be deleted, so get the next object while 'tr' is valid*/
tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
/*Remove the transitioned property from trans. style
*to allow changing it by normal styles*/
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
lv_style_remove_prop(obj->styles[i].style, tr->prop);
lv_anim_del(tr, NULL);
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
lv_mem_free(tr);
removed = true;
}
}
}
tr = tr_prev;
}
return removed;
}
static void trans_anim_cb(void * _tr, int32_t v)
{
trans_t * tr = _tr;
lv_obj_t * obj = tr->obj;
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
lv_style_value_t value_final;
switch(tr->prop) {
case LV_STYLE_BORDER_SIDE:
case LV_STYLE_BORDER_POST:
case LV_STYLE_BLEND_MODE:
if(v < 255) value_final.num = tr->start_value.num;
else value_final.num = tr->end_value.num;
break;
case LV_STYLE_TRANSITION:
case LV_STYLE_TEXT_FONT:
if(v < 255) value_final.ptr = tr->start_value.ptr;
else value_final.ptr = tr->end_value.ptr;
break;
case LV_STYLE_COLOR_FILTER_DSC:
if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
else if(v < 128) value_final.ptr = tr->start_value.ptr;
else value_final.ptr = tr->end_value.ptr;
break;
case LV_STYLE_BG_COLOR:
case LV_STYLE_BORDER_COLOR:
case LV_STYLE_TEXT_COLOR:
case LV_STYLE_SHADOW_COLOR:
case LV_STYLE_OUTLINE_COLOR:
case LV_STYLE_IMG_RECOLOR:
if(v <= 0) value_final.color = tr->start_value.color;
else if(v >= 255) value_final.color = tr->end_value.color;
else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
break;
default:
if(v == 0) value_final.num = tr->start_value.num;
else if(v == 255) value_final.num = tr->end_value.num;
else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
break;
}
lv_style_value_t old_value;
bool refr = true;
if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
if(value_final.ptr == old_value.ptr && value_final.color.full == old_value.color.full &&
value_final.num == old_value.num) {
refr = false;
}
}
lv_style_set_prop(obj->styles[i].style, tr->prop, value_final);
if(refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
break;
}
}
static void trans_anim_start_cb(lv_anim_t * a)
{
trans_t * tr = a->var;
lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
/*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
lv_style_prop_t prop_tmp = tr->prop;
tr->prop = LV_STYLE_PROP_INV;
/*Delete the related transitions if any*/
trans_del(tr->obj, part, prop_tmp, tr);
tr->prop = prop_tmp;
_lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
lv_style_set_prop(style_trans->style, tr->prop, tr->start_value); /*Be sure `trans_style` has a valid value*/
}
static void trans_anim_ready_cb(lv_anim_t * a)
{
trans_t * tr = a->var;
lv_obj_t * obj = tr->obj;
lv_style_prop_t prop = tr->prop;
/*Remove the transitioned property from trans. style
*if there no more transitions for this property
*It allows changing it by normal styles*/
bool running = false;
trans_t * tr_i;
_LV_LL_READ(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
running = true;
break;
}
}
if(!running) {
uint32_t i;
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
_lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
lv_mem_free(tr);
_lv_obj_style_t * obj_style = &obj->styles[i];
lv_style_remove_prop(obj_style->style, prop);
if(lv_style_is_empty(obj->styles[i].style)) {
lv_obj_remove_style(obj, obj_style->style, obj_style->selector);
}
break;
}
}
}
}
static void fade_anim_cb(void * obj, int32_t v)
{
lv_obj_set_style_opa(obj, v, 0);
}
static void fade_in_anim_ready(lv_anim_t * a)
{
lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
}

View File

@ -0,0 +1,251 @@
/**
* @file lv_obj_style.h
*
*/
#ifndef LV_OBJ_STYLE_H
#define LV_OBJ_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include "../misc/lv_bidi.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
_LV_STYLE_STATE_CMP_SAME, /*The style properties in the 2 states are identical*/
_LV_STYLE_STATE_CMP_DIFF_REDRAW, /*The differences can be shown with a simple redraw*/
_LV_STYLE_STATE_CMP_DIFF_DRAW_PAD, /*The differences can be shown with a simple redraw*/
_LV_STYLE_STATE_CMP_DIFF_LAYOUT, /*The differences can be shown with a simple redraw*/
} _lv_style_state_cmp_t;
typedef uint32_t lv_style_selector_t;
typedef struct {
lv_style_t * style;
uint32_t selector : 24;
uint32_t is_local : 1;
uint32_t is_trans : 1;
} _lv_obj_style_t;
typedef struct {
uint16_t time;
uint16_t delay;
lv_style_selector_t selector;
lv_style_prop_t prop;
lv_anim_path_cb_t path_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
} _lv_obj_style_transition_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the object related style manager module.
* Called by LVGL in `lv_init()`
*/
void _lv_obj_style_init(void);
/**
* Add a style to an object.
* @param obj pointer to an object
* @param style pointer to a style to add
* @param selector OR-ed value of parts and state to which the style should be added
* @example lv_obj_add_style(btn, &style_btn, 0); //Default button style
* @example lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); //Overwrite only some colors to red when pressed
*/
void lv_obj_add_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
/**
* Add a style to an object.
* @param obj pointer to an object
* @param style pointer to a style to remove. Can be NULL to check only the selector
* @param selector OR-ed values of states and a part to remove only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
* @example lv_obj_remove_style(obj, &style, LV_PART_ANY | LV_STATE_ANY); //Remove a specific style
* @example lv_obj_remove_style(obj, NULL, LV_PART_MAIN | LV_STATE_ANY); //Remove all styles from the main part
* @example lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY); //Remove all styles
*/
void lv_obj_remove_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
/**
* Remove all styles from an object
* @param obj pointer to an object
*/
static inline void lv_obj_remove_style_all(struct _lv_obj_t * obj)
{
lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY);
}
/**
* Notify all object if a style is modified
* @param style pointer to a style. Only the objects with this style will be notified
* (NULL to notify all objects)
*/
void lv_obj_report_style_change(lv_style_t * style);
/**
* Notify an object and its children about its style is modified.
* @param obj pointer to an object
* @param part the part whose style was changed. E.g. `LV_PART_ANY`, `LV_PART_MAIN`
* @param prop `LV_STYLE_PROP_ANY` or an `LV_STYLE_...` property.
* It is used to optimize what needs to be refreshed.
* `LV_STYLE_PROP_INV` to perform only a style cache update
*/
void lv_obj_refresh_style(struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Enable or disable automatic style refreshing when a new style is added/removed to/from an object
* or any other style change happens.
* @param en true: enable refreshing; false: disable refreshing
*/
void lv_obj_enable_style_refresh(bool en);
/**
* Get the value of a style property. The current state of the object will be considered.
* Inherited properties will be inherited.
* If a property is not set a default value will be returned.
* @param obj pointer to an object
* @param part a part from which the property should be get
* @param prop the property to get
* @return the value of the property.
* Should be read from the correct field of the `lv_style_value_t` according to the type of the property.
*/
lv_style_value_t lv_obj_get_style_prop(const struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
/**
* Set local style property on an object's part and state.
* @param obj pointer to an object
* @param part a part to which the property should be added
* @param state a state to which the property should be added
* @param prop the property
* @param value value of the property. The correct element should be set according to the type of the property
*/
void lv_obj_set_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
lv_style_selector_t selector);
lv_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
lv_style_selector_t selector);
/**
* Remove a local style property from a part of an object with a given state.
* @param obj pointer to an object
* @param part the part of the object which style property should be removed.
* @param state the state from which the property should be removed.
* @param prop a style property to remove.
* @return true the property was found and removed; false: the property was not found
*/
bool lv_obj_remove_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector);
/**
* Used internally to create a style transition
* @param obj
* @param part
* @param prev_state
* @param new_state
* @param tr
*/
void _lv_obj_style_create_transition(struct _lv_obj_t * obj, lv_part_t part, lv_state_t prev_state,
lv_state_t new_state, const _lv_obj_style_transition_dsc_t * tr);
/**
* Used internally to compare the appearance of an object in 2 states
* @param obj
* @param state1
* @param state2
* @return
*/
_lv_style_state_cmp_t _lv_obj_style_state_compare(struct _lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
/**
* Fade in an an object and all its children.
* @param obj the object to fade in
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_in(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
/**
* Fade out an an object and all its children.
* @param obj the object to fade out
* @param time time of fade
* @param delay delay to start the animation
*/
void lv_obj_fade_out(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector);
lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector);
#include "lv_obj_style_gen.h"
static inline void lv_obj_set_style_pad_all(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_hor(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_left(obj, value, selector);
lv_obj_set_style_pad_right(obj, value, selector);
}
static inline void lv_obj_set_style_pad_ver(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_top(obj, value, selector);
lv_obj_set_style_pad_bottom(obj, value, selector);
}
static inline void lv_obj_set_style_pad_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_obj_set_style_pad_row(obj, value, selector);
lv_obj_set_style_pad_column(obj, value, selector);
}
static inline void lv_obj_set_style_size(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_obj_set_style_width(obj, value, selector);
lv_obj_set_style_height(obj, value, selector);
}
lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt);
static inline lv_coord_t lv_obj_get_x_aligned(const struct _lv_obj_t * obj)
{
return lv_obj_get_style_x(obj, LV_PART_MAIN);
}
static inline lv_coord_t lv_obj_get_y_aligned(const struct _lv_obj_t * obj)
{
return lv_obj_get_style_y(obj, LV_PART_MAIN);
}
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEMPL_H*/

View File

@ -0,0 +1,715 @@
#include "lv_obj.h"
void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_WIDTH, v, selector);
}
void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_WIDTH, v, selector);
}
void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_WIDTH, v, selector);
}
void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_HEIGHT, v, selector);
}
void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_HEIGHT, v, selector);
}
void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_HEIGHT, v, selector);
}
void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_X, v, selector);
}
void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_Y, v, selector);
}
void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ALIGN, v, selector);
}
void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_WIDTH, v, selector);
}
void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_HEIGHT, v, selector);
}
void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_X, v, selector);
}
void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_Y, v, selector);
}
void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ZOOM, v, selector);
}
void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ANGLE, v, selector);
}
void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_TOP, v, selector);
}
void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_BOTTOM, v, selector);
}
void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_LEFT, v, selector);
}
void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RIGHT, v, selector);
}
void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_ROW, v, selector);
}
void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_COLUMN, v, selector);
}
void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_RADIUS, v, selector);
}
void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_CLIP_CORNER, v, selector);
}
void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OPA, v, selector);
}
void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value,
lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_DSC, v, selector);
}
void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_OPA, v, selector);
}
void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_TIME, v, selector);
}
void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_SPEED, v, selector);
}
void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value,
lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSITION, v, selector);
}
void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BLEND_MODE, v, selector);
}
void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LAYOUT, v, selector);
}
void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BASE_DIR, v, selector);
}
void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR, v, selector);
}
void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_OPA, v, selector);
}
void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR, v, selector);
}
void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_DIR, v, selector);
}
void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_STOP, v, selector);
}
void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
}
void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_SRC, v, selector);
}
void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_OPA, v, selector);
}
void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR, v, selector);
}
void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR_FILTERED, v, selector);
}
void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_TILED, v, selector);
}
void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR, v, selector);
}
void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_OPA, v, selector);
}
void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_WIDTH, v, selector);
}
void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_SIDE, v, selector);
}
void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_POST, v, selector);
}
void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR, v, selector);
}
void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OPA, v, selector);
}
void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_FONT, v, selector);
}
void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LETTER_SPACE, v, selector);
}
void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LINE_SPACE, v, selector);
}
void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_DECOR, v, selector);
}
void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_ALIGN, v, selector);
}
void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_OPA, v, selector);
}
void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR, v, selector);
}
void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR_FILTERED, v, selector);
}
void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR_OPA, v, selector);
}
void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_WIDTH, v, selector);
}
void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR, v, selector);
}
void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_OPA, v, selector);
}
void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_PAD, v, selector);
}
void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_WIDTH, v, selector);
}
void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFS_X, v, selector);
}
void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFS_Y, v, selector);
}
void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_SPREAD, v, selector);
}
void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR, v, selector);
}
void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OPA, v, selector);
}
void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_WIDTH, v, selector);
}
void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_WIDTH, v, selector);
}
void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_GAP, v, selector);
}
void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_ROUNDED, v, selector);
}
void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR, v, selector);
}
void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_OPA, v, selector);
}
void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_WIDTH, v, selector);
}
void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_ROUNDED, v, selector);
}
void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR, v, selector);
}
void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.color = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR_FILTERED, v, selector);
}
void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_OPA, v, selector);
}
void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_IMG_SRC, v, selector);
}

View File

@ -0,0 +1,626 @@
static inline lv_coord_t lv_obj_get_style_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_min_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_max_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_min_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_max_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_Y);
return (lv_coord_t)v.num;
}
static inline lv_align_t lv_obj_get_style_align(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ALIGN);
return (lv_align_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_height(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_HEIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_translate_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_translate_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_Y);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_zoom(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ZOOM);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_transform_angle(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ANGLE);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_top(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_bottom(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_BOTTOM);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_left(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_LEFT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_right(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RIGHT);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_row(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_ROW);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_pad_column(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_COLUMN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_radius(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_clip_corner(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_CLIP_CORNER);
return (bool)v.num;
}
static inline lv_opa_t lv_obj_get_style_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj,
uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_DSC);
return (const lv_color_filter_dsc_t *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_color_filter_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_OPA);
return (lv_opa_t)v.num;
}
static inline uint32_t lv_obj_get_style_anim_time(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_TIME);
return (uint32_t)v.num;
}
static inline uint32_t lv_obj_get_style_anim_speed(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_SPEED);
return (uint32_t)v.num;
}
static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSITION);
return (const lv_style_transition_dsc_t *)v.ptr;
}
static inline lv_blend_mode_t lv_obj_get_style_blend_mode(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLEND_MODE);
return (lv_blend_mode_t)v.num;
}
static inline uint16_t lv_obj_get_style_layout(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LAYOUT);
return (uint16_t)v.num;
}
static inline lv_base_dir_t lv_obj_get_style_base_dir(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BASE_DIR);
return (lv_base_dir_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_grad_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR_FILTERED);
return v.color;
}
static inline lv_grad_dir_t lv_obj_get_style_bg_grad_dir(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_DIR);
return (lv_grad_dir_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_bg_main_stop(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_STOP);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_bg_grad_stop(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_STOP);
return (lv_coord_t)v.num;
}
static inline const void * lv_obj_get_style_bg_img_src(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_SRC);
return (const void *)v.ptr;
}
static inline lv_opa_t lv_obj_get_style_bg_img_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_bg_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_bg_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_bg_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline bool lv_obj_get_style_bg_img_tiled(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_TILED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_border_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_border_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_border_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_border_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_border_side_t lv_obj_get_style_border_side(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_SIDE);
return (lv_border_side_t)v.num;
}
static inline bool lv_obj_get_style_border_post(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_POST);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_text_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_text_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_text_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OPA);
return (lv_opa_t)v.num;
}
static inline const lv_font_t * lv_obj_get_style_text_font(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_FONT);
return (const lv_font_t *)v.ptr;
}
static inline lv_coord_t lv_obj_get_style_text_letter_space(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LETTER_SPACE);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_text_line_space(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LINE_SPACE);
return (lv_coord_t)v.num;
}
static inline lv_text_decor_t lv_obj_get_style_text_decor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_DECOR);
return (lv_text_decor_t)v.num;
}
static inline lv_text_align_t lv_obj_get_style_text_align(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_ALIGN);
return (lv_text_align_t)v.num;
}
static inline lv_opa_t lv_obj_get_style_img_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_OPA);
return (lv_opa_t)v.num;
}
static inline lv_color_t lv_obj_get_style_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_outline_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_outline_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_outline_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_outline_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_outline_pad(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_PAD);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_ofs_x(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_X);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_ofs_y(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_Y);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_shadow_spread(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_SPREAD);
return (lv_coord_t)v.num;
}
static inline lv_color_t lv_obj_get_style_shadow_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_shadow_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_dash_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_WIDTH);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_line_dash_gap(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_GAP);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_line_rounded(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_ROUNDED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_line_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_line_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_line_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_OPA);
return (lv_opa_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_arc_width(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_WIDTH);
return (lv_coord_t)v.num;
}
static inline bool lv_obj_get_style_arc_rounded(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_ROUNDED);
return (bool)v.num;
}
static inline lv_color_t lv_obj_get_style_arc_color(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR);
return v.color;
}
static inline lv_color_t lv_obj_get_style_arc_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR_FILTERED);
return v.color;
}
static inline lv_opa_t lv_obj_get_style_arc_opa(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_OPA);
return (lv_opa_t)v.num;
}
static inline const void * lv_obj_get_style_arc_img_src(const struct _lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_IMG_SRC);
return (const void *)v.ptr;
}
void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value,
lv_style_selector_t selector);
void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value,
lv_style_selector_t selector);
void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector);
void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector);
void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector);
void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector);
void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector);
void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);

View File

@ -0,0 +1,460 @@
/**
* @file lv_obj_tree.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdlib.h>
#include "lv_obj.h"
#include "lv_indev.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_gc.h"
#include "../misc/lv_async.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_obj_del_async_cb(void * obj);
static void obj_del_core(lv_obj_t * obj);
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_del(lv_obj_t * obj)
{
LV_LOG_TRACE("begin (delete %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * par = lv_obj_get_parent(obj);
if(par) {
lv_obj_scrollbar_invalidate(par);
}
lv_disp_t * disp = NULL;
bool act_scr_del = false;
if(par == NULL) {
disp = lv_obj_get_disp(obj);
if(!disp) return; /*Shouldn't happen*/
if(disp->act_scr == obj) act_scr_del = true;
}
obj_del_core(obj);
/*Call the ancestor's event handler to the parent to notify it about the child delete*/
if(par) {
lv_obj_readjust_scroll(par, LV_ANIM_OFF);
lv_obj_scrollbar_invalidate(par);
lv_event_send(par, LV_EVENT_CHILD_CHANGED, NULL);
lv_event_send(par, LV_EVENT_CHILD_DELETED, NULL);
}
/*Handle if the active screen was deleted*/
if(act_scr_del) {
LV_LOG_WARN("the active screen was deleted");
disp->act_scr = NULL;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", (void *)obj);
}
void lv_obj_clean(lv_obj_t * obj)
{
LV_LOG_TRACE("begin (delete %p)", (void *)obj);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_del_core(child);
child = lv_obj_get_child(obj, 0);
}
/*Just to remove scroll animations if any*/
lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
if(obj->spec_attr) {
obj->spec_attr->scroll.x = 0;
obj->spec_attr->scroll.y = 0;
}
LV_ASSERT_MEM_INTEGRITY();
LV_LOG_TRACE("finished (delete %p)", (void *)obj);
}
void lv_obj_del_delayed(lv_obj_t * obj, uint32_t delay_ms)
{
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a, NULL);
lv_anim_set_time(&a, 1);
lv_anim_set_delay(&a, delay_ms);
lv_anim_set_ready_cb(&a, lv_obj_del_anim_ready_cb);
lv_anim_start(&a);
}
void lv_obj_del_anim_ready_cb(lv_anim_t * a)
{
lv_obj_del(a->var);
}
void lv_obj_del_async(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_async_call(lv_obj_del_async_cb, obj);
}
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_OBJ(parent, MY_CLASS);
if(obj->parent == NULL) {
LV_LOG_WARN("Can't set the parent of a screen");
return;
}
if(parent == NULL) {
LV_LOG_WARN("Can't set parent == NULL to an object");
return;
}
lv_obj_invalidate(obj);
lv_obj_allocate_spec_attr(parent);
lv_obj_t * old_parent = obj->parent;
lv_point_t old_pos;
old_pos.y = lv_obj_get_y(obj);
lv_base_dir_t new_base_dir = lv_obj_get_style_base_dir(parent, LV_PART_MAIN);
if(new_base_dir != LV_BASE_DIR_RTL) old_pos.x = lv_obj_get_x(obj);
else old_pos.x = old_parent->coords.x2 - obj->coords.x2;
/*Remove the object from the old parent's child list*/
int32_t i;
for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_cnt(old_parent) - 2; i++) {
old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
}
old_parent->spec_attr->child_cnt--;
if(old_parent->spec_attr->child_cnt) {
old_parent->spec_attr->children = lv_mem_realloc(old_parent->spec_attr->children,
old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
}
else {
lv_mem_free(old_parent->spec_attr->children);
old_parent->spec_attr->children = NULL;
}
/*Add the child to the new parent as the last (newest child)*/
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children,
parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
parent->spec_attr->children[lv_obj_get_child_cnt(parent) - 1] = obj;
obj->parent = parent;
if(new_base_dir != LV_BASE_DIR_RTL) {
lv_obj_set_pos(obj, old_pos.x, old_pos.y);
}
else {
/*Align to the right in case of RTL base dir*/
lv_coord_t new_x = lv_obj_get_width(parent) - old_pos.x - lv_obj_get_width(obj);
lv_obj_set_pos(obj, new_x, old_pos.y);
}
/*Notify the original parent because one of its children is lost*/
lv_event_send(old_parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(old_parent, LV_EVENT_CHILD_DELETED, NULL);
/*Notify the new parent about the child*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(parent, LV_EVENT_CHILD_CREATED, NULL);
lv_obj_invalidate(obj);
}
void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const int32_t old_index = lv_obj_get_index(obj);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(index < 0) return;
if(index >= (int32_t) lv_obj_get_child_cnt(parent)) return;
if(index == old_index) return;
int32_t i = old_index;
if(index < old_index) {
while(i > index) {
parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
i--;
}
}
else {
while(i < index) {
parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
i++;
}
}
parent->spec_attr->children[index] = obj;
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, NULL);
lv_obj_invalidate(parent);
}
void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
{
LV_ASSERT_OBJ(obj1, MY_CLASS);
LV_ASSERT_OBJ(obj2, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj1);
lv_obj_t * parent2 = lv_obj_get_parent(obj2);
uint_fast32_t index1 = lv_obj_get_index(obj1);
uint_fast32_t index2 = lv_obj_get_index(obj2);
lv_event_send(parent2, LV_EVENT_CHILD_DELETED, obj2);
lv_event_send(parent, LV_EVENT_CHILD_DELETED, obj1);
parent->spec_attr->children[index1] = obj2;
parent2->spec_attr->children[index2] = obj1;
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj2);
lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj2);
lv_event_send(parent2, LV_EVENT_CHILD_CHANGED, obj1);
lv_event_send(parent2, LV_EVENT_CHILD_CREATED, obj1);
lv_obj_invalidate(parent);
if(parent != parent2) {
lv_obj_invalidate(parent2);
}
lv_group_swap_obj(obj1, obj2);
}
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * par = obj;
const lv_obj_t * act_par;
do {
act_par = par;
par = lv_obj_get_parent(act_par);
} while(par != NULL);
return (lv_obj_t *)act_par;
}
lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_obj_t * scr;
if(obj->parent == NULL) scr = obj; /*`obj` is a screen*/
else scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
lv_disp_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_disp_ll), d) {
uint32_t i;
for(i = 0; i < d->screen_cnt; i++) {
if(d->screens[i] == scr) return d;
}
}
LV_LOG_WARN("No screen found");
return NULL;
}
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
{
if(obj == NULL) return NULL;
LV_ASSERT_OBJ(obj, MY_CLASS);
return obj->parent;
}
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t id)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return NULL;
uint32_t idu;
if(id < 0) {
id = obj->spec_attr->child_cnt + id;
if(id < 0) return NULL;
idu = (uint32_t) id;
}
else {
idu = id;
}
if(idu >= obj->spec_attr->child_cnt) return NULL;
else return obj->spec_attr->children[id];
}
uint32_t lv_obj_get_child_cnt(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr == NULL) return 0;
return obj->spec_attr->child_cnt;
}
uint32_t lv_obj_get_index(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return 0;
uint32_t i = 0;
for(i = 0; i < lv_obj_get_child_cnt(parent); i++) {
if(lv_obj_get_child(parent, i) == obj) return i;
}
return 0xFFFFFFFF; /*Shouldn't happen*/
}
void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
walk_core(start_obj, cb, user_data);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_obj_del_async_cb(void * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_del(obj);
}
static void obj_del_core(lv_obj_t * obj)
{
/*Let the user free the resources used in `LV_EVENT_DELETE`*/
lv_res_t res = lv_event_send(obj, LV_EVENT_DELETE, NULL);
if(res == LV_RES_INV) return;
/*Recursively delete the children*/
lv_obj_t * child = lv_obj_get_child(obj, 0);
while(child) {
obj_del_core(child);
child = lv_obj_get_child(obj, 0);
}
lv_group_t * group = lv_obj_get_group(obj);
/*Reset all input devices if the object to delete is used*/
lv_indev_t * indev = lv_indev_get_next(NULL);
while(indev) {
if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
lv_indev_reset(indev, obj);
}
if(indev->proc.types.pointer.last_pressed == obj) {
indev->proc.types.pointer.last_pressed = NULL;
}
if(indev->group == group && obj == lv_indev_get_obj_act()) {
lv_indev_reset(indev, obj);
}
indev = lv_indev_get_next(indev);
}
/*All children deleted. Now clean up the object specific data*/
_lv_obj_destruct(obj);
/*Remove the screen for the screen list*/
if(obj->parent == NULL) {
lv_disp_t * disp = lv_obj_get_disp(obj);
uint32_t i;
/*Find the screen in the list*/
for(i = 0; i < disp->screen_cnt; i++) {
if(disp->screens[i] == obj) break;
}
uint32_t id = i;
for(i = id; i < disp->screen_cnt - 1; i++) {
disp->screens[i] = disp->screens[i + 1];
}
disp->screen_cnt--;
disp->screens = lv_mem_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
}
/*Remove the object from the child list of its parent*/
else {
uint32_t id = lv_obj_get_index(obj);
uint32_t i;
for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
}
obj->parent->spec_attr->child_cnt--;
obj->parent->spec_attr->children = lv_mem_realloc(obj->parent->spec_attr->children,
obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
}
/*Free the object itself*/
lv_mem_free(obj);
}
static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
{
lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
if(obj == NULL) {
lv_disp_t * disp = lv_disp_get_next(NULL);
while(disp) {
uint32_t i;
for(i = 0; i < disp->screen_cnt; i++) {
walk_core(disp->screens[i], cb, user_data);
}
disp = lv_disp_get_next(disp);
}
return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
}
res = cb(obj, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
}
}
return LV_OBJ_TREE_WALK_NEXT;
}

View File

@ -0,0 +1,172 @@
/**
* @file struct _lv_obj_tree.h
*
*/
#ifndef LV_OBJ_TREE_H
#define LV_OBJ_TREE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _lv_obj_class_t;
typedef enum {
LV_OBJ_TREE_WALK_NEXT,
LV_OBJ_TREE_WALK_SKIP_CHILDREN,
LV_OBJ_TREE_WALK_END,
} lv_obj_tree_walk_res_t;
typedef lv_obj_tree_walk_res_t (*lv_obj_tree_walk_cb_t)(struct _lv_obj_t *, void *);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Delete an object and all of its children.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETED` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_del(struct _lv_obj_t * obj);
/**
* Delete all children of an object.
* Also remove the objects from their group and remove all animations (if any).
* Send `LV_EVENT_DELETED` to deleted objects.
* @param obj pointer to an object
*/
void lv_obj_clean(struct _lv_obj_t * obj);
/**
* Delete an object after some delay
* @param obj pointer to an object
* @param delay_ms time to wait before delete in milliseconds
*/
void lv_obj_del_delayed(struct _lv_obj_t * obj, uint32_t delay_ms);
/**
* A function to be easily used in animation ready callback to delete an object when the animation is ready
* @param a pointer to the animation
*/
void lv_obj_del_anim_ready_cb(lv_anim_t * a);
/**
* Helper function for asynchronously deleting objects.
* Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
* @param obj object to delete
* @see lv_async_call
*/
void lv_obj_del_async(struct _lv_obj_t * obj);
/**
* Move the parent of an object. The relative coordinates will be kept.
*
* @param obj pointer to an object whose parent needs to be changed
* @param parent pointer to the new parent
*/
void lv_obj_set_parent(struct _lv_obj_t * obj, struct _lv_obj_t * parent);
/**
* Swap the positions of two objects.
* When used in listboxes, it can be used to sort the listbox items.
* @param obj1 pointer to the first object
* @param obj2 pointer to the second object
*/
void lv_obj_swap(struct _lv_obj_t * obj1, struct _lv_obj_t * obj2);
/**
* moves the object to the given index in its parent.
* When used in listboxes, it can be used to sort the listbox items.
* @param obj pointer to the object to be moved.
* @param index new index in parent.
* @note to move to the foreground: lv_obj_move_to_index(obj, 0)
* @note to move forward (up): lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)
*/
void lv_obj_move_to_index(struct _lv_obj_t * obj, int32_t index);
/**
* Get the screen of an object
* @param obj pointer to an object
* @return pointer to the object's screen
*/
struct _lv_obj_t * lv_obj_get_screen(const struct _lv_obj_t * obj);
/**
* Get the display of the object
* @param obj pointer to an object
* @return pointer to the object's display
*/
lv_disp_t * lv_obj_get_disp(const struct _lv_obj_t * obj);
/**
* Get the parent of an object
* @param obj pointer to an object
* @return the parent of the object. (NULL if `obj` was a screen)
*/
struct _lv_obj_t * lv_obj_get_parent(const struct _lv_obj_t * obj);
/**
* Get the child of an object by the child's index.
* @param obj pointer to an object whose child should be get
* @param id the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @return pointer to the child or NULL if the index was invalid
*/
struct _lv_obj_t * lv_obj_get_child(const struct _lv_obj_t * obj, int32_t id);
/**
* Get the number of children
* @param obj pointer to an object
* @return the number of children
*/
uint32_t lv_obj_get_child_cnt(const struct _lv_obj_t * obj);
/**
* Get the index of a child.
* @param obj pointer to an object
* @return the child index of the object.
* E.g. 0: the oldest (firstly created child)
*/
uint32_t lv_obj_get_index(const struct _lv_obj_t * obj);
/**
* Iterate through all children of any object.
* @param start_obj start integrating from this object
* @param cb call this callback on the objects
* @param user_data pointer to any user related data (will be passed to `cb`)
*/
void lv_obj_tree_walk(struct _lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OBJ_TREE_H*/

View File

@ -0,0 +1,976 @@
/**
* @file lv_refr.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_refr.h"
#include "lv_disp.h"
#include "../hal/lv_hal_tick.h"
#include "../hal/lv_hal_disp.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_math.h"
#include "../misc/lv_gc.h"
#include "../draw/lv_draw.h"
#include "../font/lv_font_fmt_txt.h"
#if LV_USE_PERF_MONITOR || LV_USE_MEM_MONITOR
#include "../widgets/lv_label.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_refr_join_area(void);
static void lv_refr_areas(void);
static void lv_refr_area(const lv_area_t * area_p);
static void lv_refr_area_part(const lv_area_t * area_p);
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p);
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p);
static void draw_buf_flush(void);
static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
/**********************
* STATIC VARIABLES
**********************/
static uint32_t px_num;
static lv_disp_t * disp_refr; /*Display being refreshed*/
#if LV_USE_PERF_MONITOR
static uint32_t fps_sum_cnt;
static uint32_t fps_sum_all;
#endif
/**********************
* MACROS
**********************/
#if LV_LOG_TRACE_DISP_REFR
#define REFR_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define REFR_TRACE(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void _lv_refr_init(void)
{
/*Nothing to do*/
}
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
* can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
* (e.g. progress bar) this function can be called when the screen should be updated.
* @param disp pointer to display to refresh. NULL to refresh all displays.
*/
void lv_refr_now(lv_disp_t * disp)
{
lv_anim_refr_now();
if(disp) {
_lv_disp_refr_timer(disp->refr_timer);
}
else {
lv_disp_t * d;
d = lv_disp_get_next(NULL);
while(d) {
_lv_disp_refr_timer(d->refr_timer);
d = lv_disp_get_next(d);
}
}
}
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
*/
void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) return;
/*Clear the invalidate buffer if the parameter is NULL*/
if(area_p == NULL) {
disp->inv_p = 0;
return;
}
lv_area_t scr_area;
scr_area.x1 = 0;
scr_area.y1 = 0;
scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
lv_area_t com_area;
bool suc;
suc = _lv_area_intersect(&com_area, area_p, &scr_area);
if(suc == false) return; /*Out of the screen*/
/*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
if(disp->driver->full_refresh) {
disp->inv_areas[0] = scr_area;
disp->inv_p = 1;
lv_timer_resume(disp->refr_timer);
return;
}
if(disp->driver->rounder_cb) disp->driver->rounder_cb(disp->driver, &com_area);
/*Save only if this area is not in one of the saved areas*/
uint16_t i;
for(i = 0; i < disp->inv_p; i++) {
if(_lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
}
/*Save the area*/
if(disp->inv_p < LV_INV_BUF_SIZE) {
lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
}
else { /*If no place for the area add the screen*/
disp->inv_p = 0;
lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
}
disp->inv_p++;
lv_timer_resume(disp->refr_timer);
}
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * _lv_refr_get_disp_refreshing(void)
{
return disp_refr;
}
/**
* Set the display which is being refreshed.
* It shouldn't be used directly by the user.
* It can be used to trick the drawing functions about there is an active display.
* @param the display being refreshed
*/
void _lv_refr_set_disp_refreshing(lv_disp_t * disp)
{
disp_refr = disp;
}
/**
* Called periodically to handle the refreshing
* @param tmr pointer to the timer itself
*/
void _lv_disp_refr_timer(lv_timer_t * tmr)
{
REFR_TRACE("begin");
uint32_t start = lv_tick_get();
volatile uint32_t elaps = 0;
disp_refr = tmr->user_data;
#if LV_USE_PERF_MONITOR == 0 && LV_USE_MEM_MONITOR == 0
/**
* Ensure the timer does not run again automatically.
* This is done before refreshing in case refreshing invalidates something else.
*/
lv_timer_pause(tmr);
#endif
/*Refresh the screen's layout if required*/
lv_obj_update_layout(disp_refr->act_scr);
if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
lv_obj_update_layout(disp_refr->top_layer);
lv_obj_update_layout(disp_refr->sys_layer);
/*Do nothing if there is no active screen*/
if(disp_refr->act_scr == NULL) {
disp_refr->inv_p = 0;
LV_LOG_WARN("there is no active screen");
REFR_TRACE("finished");
return;
}
lv_refr_join_area();
lv_refr_areas();
/*If refresh happened ...*/
if(disp_refr->inv_p != 0) {
if(disp_refr->driver->full_refresh) {
draw_buf_flush();
}
/*Clean up*/
lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
disp_refr->inv_p = 0;
elaps = lv_tick_elaps(start);
/*Call monitor cb if present*/
if(disp_refr->driver->monitor_cb) {
disp_refr->driver->monitor_cb(disp_refr->driver, elaps, px_num);
}
}
lv_mem_buf_free_all();
_lv_font_clean_up_fmt_txt();
#if LV_DRAW_COMPLEX
_lv_draw_mask_cleanup();
#endif
#if LV_USE_PERF_MONITOR && LV_USE_LABEL
static lv_obj_t * perf_label = NULL;
if(perf_label == NULL) {
perf_label = lv_label_create(lv_layer_sys());
lv_obj_set_style_bg_opa(perf_label, LV_OPA_50, 0);
lv_obj_set_style_bg_color(perf_label, lv_color_black(), 0);
lv_obj_set_style_text_color(perf_label, lv_color_white(), 0);
lv_obj_set_style_pad_top(perf_label, 3, 0);
lv_obj_set_style_pad_bottom(perf_label, 3, 0);
lv_obj_set_style_pad_left(perf_label, 3, 0);
lv_obj_set_style_pad_right(perf_label, 3, 0);
lv_obj_set_style_text_align(perf_label, LV_TEXT_ALIGN_RIGHT, 0);
lv_label_set_text(perf_label, "?");
lv_obj_align(perf_label, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
}
static uint32_t perf_last_time = 0;
static uint32_t elaps_sum = 0;
static uint32_t frame_cnt = 0;
if(lv_tick_elaps(perf_last_time) < 300) {
if(px_num > 5000) {
elaps_sum += elaps;
frame_cnt ++;
}
}
else {
perf_last_time = lv_tick_get();
uint32_t fps_limit = 1000 / disp_refr->refr_timer->period;
unsigned int fps;
if(elaps_sum == 0) elaps_sum = 1;
if(frame_cnt == 0) fps = fps_limit;
else fps = (1000 * frame_cnt) / elaps_sum;
elaps_sum = 0;
frame_cnt = 0;
if(fps > fps_limit) fps = fps_limit;
fps_sum_all += fps;
fps_sum_cnt ++;
unsigned int cpu = 100 - lv_timer_get_idle();
lv_label_set_text_fmt(perf_label, "%u FPS\n%u%% CPU", fps, cpu);
}
#endif
#if LV_USE_MEM_MONITOR && LV_MEM_CUSTOM == 0 && LV_USE_LABEL
static lv_obj_t * mem_label = NULL;
if(mem_label == NULL) {
mem_label = lv_label_create(lv_layer_sys());
lv_obj_set_style_bg_opa(mem_label, LV_OPA_50, 0);
lv_obj_set_style_bg_color(mem_label, lv_color_black(), 0);
lv_obj_set_style_text_color(mem_label, lv_color_white(), 0);
lv_obj_set_style_pad_top(mem_label, 3, 0);
lv_obj_set_style_pad_bottom(mem_label, 3, 0);
lv_obj_set_style_pad_left(mem_label, 3, 0);
lv_obj_set_style_pad_right(mem_label, 3, 0);
lv_label_set_text(mem_label, "?");
lv_obj_align(mem_label, LV_ALIGN_BOTTOM_LEFT, 0, 0);
}
static uint32_t mem_last_time = 0;
if(lv_tick_elaps(mem_last_time) > 300) {
mem_last_time = lv_tick_get();
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
uint32_t used_size = mon.total_size - mon.free_size;;
uint32_t used_kb = used_size / 1024;
uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
lv_label_set_text_fmt(mem_label, "%d.%d kB used (%d %%)\n%d%% frag.", used_kb, used_kb_tenth, mon.used_pct,
mon.frag_pct);
}
#endif
REFR_TRACE("finished");
}
#if LV_USE_PERF_MONITOR
uint32_t lv_refr_get_fps_avg(void)
{
return fps_sum_all / fps_sum_cnt;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Join the areas which has got common parts
*/
static void lv_refr_join_area(void)
{
uint32_t join_from;
uint32_t join_in;
lv_area_t joined_area;
for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
if(disp_refr->inv_area_joined[join_in] != 0) continue;
/*Check all areas to join them in 'join_in'*/
for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
/*Handle only unjoined areas and ignore itself*/
if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
continue;
}
/*Check if the areas are on each other*/
if(_lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
continue;
}
_lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
/*Join two area only if the joined area size is smaller*/
if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
/*Mark 'join_form' is joined into 'join_in'*/
disp_refr->inv_area_joined[join_from] = 1;
}
}
}
}
/**
* Refresh the joined areas
*/
static void lv_refr_areas(void)
{
px_num = 0;
if(disp_refr->inv_p == 0) return;
/*Find the last area which will be drawn*/
int32_t i;
int32_t last_i = 0;
for(i = disp_refr->inv_p - 1; i >= 0; i--) {
if(disp_refr->inv_area_joined[i] == 0) {
last_i = i;
break;
}
}
disp_refr->driver->draw_buf->last_area = 0;
disp_refr->driver->draw_buf->last_part = 0;
for(i = 0; i < disp_refr->inv_p; i++) {
/*Refresh the unjoined areas*/
if(disp_refr->inv_area_joined[i] == 0) {
if(i == last_i) disp_refr->driver->draw_buf->last_area = 1;
disp_refr->driver->draw_buf->last_part = 0;
lv_refr_area(&disp_refr->inv_areas[i]);
px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
}
}
}
/**
* Refresh an area if there is Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area(const lv_area_t * area_p)
{
/*With full refresh just redraw directly into the buffer*/
if(disp_refr->driver->full_refresh) {
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
draw_buf->area.x1 = 0;
draw_buf->area.x2 = lv_disp_get_hor_res(disp_refr) - 1;
draw_buf->area.y1 = 0;
draw_buf->area.y2 = lv_disp_get_ver_res(disp_refr) - 1;
disp_refr->driver->draw_buf->last_part = 1;
lv_refr_area_part(area_p);
return;
}
/*Normal refresh: draw the area in parts*/
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
/*Calculate the max row num*/
lv_coord_t w = lv_area_get_width(area_p);
lv_coord_t h = lv_area_get_height(area_p);
lv_coord_t y2 = area_p->y2 >= lv_disp_get_ver_res(disp_refr) ?
lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
int32_t max_row = (uint32_t)draw_buf->size / w;
if(max_row > h) max_row = h;
/*Round down the lines of draw_buf if rounding is added*/
if(disp_refr->driver->rounder_cb) {
lv_area_t tmp;
tmp.x1 = 0;
tmp.x2 = 0;
tmp.y1 = 0;
lv_coord_t h_tmp = max_row;
do {
tmp.y2 = h_tmp - 1;
disp_refr->driver->rounder_cb(disp_refr->driver, &tmp);
/*If this height fits into `max_row` then fine*/
if(lv_area_get_height(&tmp) <= max_row) break;
/*Decrement the height of the area until it fits into `max_row` after rounding*/
h_tmp--;
} while(h_tmp > 0);
if(h_tmp <= 0) {
LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or to "
"small draw_buf)");
return;
}
else {
max_row = tmp.y2 + 1;
}
}
/*Always use the full row*/
lv_coord_t row;
lv_coord_t row_last = 0;
for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
/*Calc. the next y coordinates of draw_buf*/
draw_buf->area.x1 = area_p->x1;
draw_buf->area.x2 = area_p->x2;
draw_buf->area.y1 = row;
draw_buf->area.y2 = row + max_row - 1;
if(draw_buf->area.y2 > y2) draw_buf->area.y2 = y2;
row_last = draw_buf->area.y2;
if(y2 == row_last) disp_refr->driver->draw_buf->last_part = 1;
lv_refr_area_part(area_p);
}
/*If the last y coordinates are not handled yet ...*/
if(y2 != row_last) {
/*Calc. the next y coordinates of draw_buf*/
draw_buf->area.x1 = area_p->x1;
draw_buf->area.x2 = area_p->x2;
draw_buf->area.y1 = row;
draw_buf->area.y2 = y2;
disp_refr->driver->draw_buf->last_part = 1;
lv_refr_area_part(area_p);
}
}
/**
* Refresh a part of an area which is on the actual Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area_part(const lv_area_t * area_p)
{
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
/* Below the `area_p` area will be redrawn into the draw buffer.
* In single buffered mode wait here until the buffer is freed.*/
if(draw_buf->buf1 && !draw_buf->buf2) {
while(draw_buf->flushing) {
if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
}
}
lv_obj_t * top_act_scr = NULL;
lv_obj_t * top_prev_scr = NULL;
/*Get the new mask from the original area and the act. draw_buf
It will be a part of 'area_p'*/
lv_area_t start_mask;
_lv_area_intersect(&start_mask, area_p, &draw_buf->area);
/*Get the most top object which is not covered by others*/
top_act_scr = lv_refr_get_top_obj(&start_mask, lv_disp_get_scr_act(disp_refr));
if(disp_refr->prev_scr) {
top_prev_scr = lv_refr_get_top_obj(&start_mask, disp_refr->prev_scr);
}
/*Draw a display background if there is no top object*/
if(top_act_scr == NULL && top_prev_scr == NULL) {
if(disp_refr->bg_fn) {
disp_refr->bg_fn(&start_mask);
} else if(disp_refr->bg_img) {
lv_draw_img_dsc_t dsc;
lv_draw_img_dsc_init(&dsc);
dsc.opa = disp_refr->bg_opa;
lv_img_header_t header;
lv_res_t res;
res = lv_img_decoder_get_info(disp_refr->bg_img, &header);
if(res == LV_RES_OK) {
lv_area_t a;
lv_area_set(&a, 0, 0, header.w - 1, header.h - 1);
lv_draw_img(&a, &start_mask, disp_refr->bg_img, &dsc);
}
else {
LV_LOG_WARN("Can't draw the background image");
}
}
else {
lv_draw_rect_dsc_t dsc;
lv_draw_rect_dsc_init(&dsc);
dsc.bg_color = disp_refr->bg_color;
dsc.bg_opa = disp_refr->bg_opa;
lv_draw_rect(&start_mask, &start_mask, &dsc);
}
}
/*Refresh the previous screen if any*/
if(disp_refr->prev_scr) {
/*Get the most top object which is not covered by others*/
if(top_prev_scr == NULL) {
top_prev_scr = disp_refr->prev_scr;
}
/*Do the refreshing from the top object*/
lv_refr_obj_and_children(top_prev_scr, &start_mask);
}
if(top_act_scr == NULL) {
top_act_scr = disp_refr->act_scr;
}
/*Do the refreshing from the top object*/
lv_refr_obj_and_children(top_act_scr, &start_mask);
/*Also refresh top and sys layer unconditionally*/
lv_refr_obj_and_children(lv_disp_get_layer_top(disp_refr), &start_mask);
lv_refr_obj_and_children(lv_disp_get_layer_sys(disp_refr), &start_mask);
/*In true double buffered mode flush only once when all areas were rendered.
*In normal mode flush after every area*/
if(disp_refr->driver->full_refresh == false) {
draw_buf_flush();
}
}
/**
* Search the most top object which fully covers an area
* @param area_p pointer to an area
* @param obj the first object to start the searching (typically a screen)
* @return
*/
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
{
lv_obj_t * found_p = NULL;
/*If this object is fully cover the draw area check the children too*/
if(_lv_area_is_in(area_p, &obj->coords, 0) && lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN) == false) {
lv_cover_check_info_t info;
info.res = LV_COVER_RES_COVER;
info.area = area_p;
lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
if(info.res == LV_COVER_RES_MASKED) return NULL;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
found_p = lv_refr_get_top_obj(area_p, child);
/*If a children is ok then break*/
if(found_p != NULL) {
break;
}
}
/*If no better children use this object*/
if(found_p == NULL) {
if(info.res == LV_COVER_RES_COVER) {
found_p = obj;
}
}
}
return found_p;
}
/**
* Make the refreshing from an object. Draw all its children and the youngers too.
* @param top_p pointer to an objects. Start the drawing from it.
* @param mask_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
{
/*Normally always will be a top_obj (at least the screen)
*but in special cases (e.g. if the screen has alpha) it won't.
*In this case use the screen directly*/
if(top_p == NULL) top_p = lv_disp_get_scr_act(disp_refr);
if(top_p == NULL) return; /*Shouldn't happen*/
/*Refresh the top object and its children*/
lv_refr_obj(top_p, mask_p);
/*Draw the 'younger' sibling objects because they can be on top_obj*/
lv_obj_t * par;
lv_obj_t * border_p = top_p;
par = lv_obj_get_parent(top_p);
/*Do until not reach the screen*/
while(par != NULL) {
bool go = false;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(par);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = par->spec_attr->children[i];
if(!go) {
if(child == border_p) go = true;
}
else {
/*Refresh the objects*/
lv_refr_obj(child, mask_p);
}
}
/*Call the post draw draw function of the parents of the to object*/
lv_event_send(par, LV_EVENT_DRAW_POST_BEGIN, (void *)mask_p);
lv_event_send(par, LV_EVENT_DRAW_POST, (void *)mask_p);
lv_event_send(par, LV_EVENT_DRAW_POST_END, (void *)mask_p);
/*The new border will be the last parents,
*so the 'younger' brothers of parent will be refreshed*/
border_p = par;
/*Go a level deeper*/
par = lv_obj_get_parent(par);
}
}
/**
* Refresh an object an all of its children. (Called recursively)
* @param obj pointer to an object to refresh
* @param mask_ori_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
{
/*Do not refresh hidden objects*/
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
bool union_ok; /*Store the return value of area_union*/
/*Truncate the original mask to the coordinates of the parent
*because the parent and its children are visible only here*/
lv_area_t obj_mask;
lv_area_t obj_ext_mask;
lv_area_t obj_area;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_obj_get_coords(obj, &obj_area);
obj_area.x1 -= ext_size;
obj_area.y1 -= ext_size;
obj_area.x2 += ext_size;
obj_area.y2 += ext_size;
union_ok = _lv_area_intersect(&obj_ext_mask, mask_ori_p, &obj_area);
/*Draw the parent and its children only if they ore on 'mask_parent'*/
if(union_ok != false) {
/*Redraw the object*/
lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_MAIN, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, &obj_ext_mask);
#if LV_USE_REFR_DEBUG
lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
lv_draw_rect_dsc_t draw_dsc;
lv_draw_rect_dsc_init(&draw_dsc);
draw_dsc.bg_color.full = debug_color.full;
draw_dsc.bg_opa = LV_OPA_20;
draw_dsc.border_width = 1;
draw_dsc.border_opa = LV_OPA_30;
draw_dsc.border_color = debug_color;
lv_draw_rect(&obj_ext_mask, &obj_ext_mask, &draw_dsc);
#endif
/*Create a new 'obj_mask' without 'ext_size' because the children can't be visible there*/
lv_obj_get_coords(obj, &obj_area);
union_ok = _lv_area_intersect(&obj_mask, mask_ori_p, &obj_area);
if(union_ok != false) {
lv_area_t mask_child; /*Mask from obj and its child*/
lv_area_t child_area;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
lv_obj_get_coords(child, &child_area);
ext_size = _lv_obj_get_ext_draw_size(child);
child_area.x1 -= ext_size;
child_area.y1 -= ext_size;
child_area.x2 += ext_size;
child_area.y2 += ext_size;
/*Get the union (common parts) of original mask (from obj)
*and its child*/
union_ok = _lv_area_intersect(&mask_child, &obj_mask, &child_area);
/*If the parent and the child has common area then refresh the child*/
if(union_ok) {
/*Refresh the next children*/
lv_refr_obj(child, &mask_child);
}
}
}
/*If all the children are redrawn make 'post draw' draw*/
lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_POST, &obj_ext_mask);
lv_event_send(obj, LV_EVENT_DRAW_POST_END, &obj_ext_mask);
}
}
static void draw_buf_rotate_180(lv_disp_drv_t * drv, lv_area_t * area, lv_color_t * color_p)
{
lv_coord_t area_w = lv_area_get_width(area);
lv_coord_t area_h = lv_area_get_height(area);
uint32_t total = area_w * area_h;
/*Swap the beginning and end values*/
lv_color_t tmp;
uint32_t i = total - 1, j = 0;
while(i > j) {
tmp = color_p[i];
color_p[i] = color_p[j];
color_p[j] = tmp;
i--;
j++;
}
lv_coord_t tmp_coord;
tmp_coord = area->y2;
area->y2 = drv->ver_res - area->y1 - 1;
area->y1 = drv->ver_res - tmp_coord - 1;
tmp_coord = area->x2;
area->x2 = drv->hor_res - area->x1 - 1;
area->x1 = drv->hor_res - tmp_coord - 1;
}
static LV_ATTRIBUTE_FAST_MEM void draw_buf_rotate_90(bool invert_i, lv_coord_t area_w, lv_coord_t area_h,
lv_color_t * orig_color_p, lv_color_t * rot_buf)
{
uint32_t invert = (area_w * area_h) - 1;
uint32_t initial_i = ((area_w - 1) * area_h);
for(lv_coord_t y = 0; y < area_h; y++) {
uint32_t i = initial_i + y;
if(invert_i)
i = invert - i;
for(lv_coord_t x = 0; x < area_w; x++) {
rot_buf[i] = *(orig_color_p++);
if(invert_i)
i += area_h;
else
i -= area_h;
}
}
}
/**
* Helper function for draw_buf_rotate_90_sqr. Given a list of four numbers, rotate the entire list to the left.
*/
static inline void draw_buf_rotate4(lv_color_t * a, lv_color_t * b, lv_color_t * c, lv_color_t * d)
{
lv_color_t tmp;
tmp = *a;
*a = *b;
*b = *c;
*c = *d;
*d = tmp;
}
/**
* Rotate a square image 90/270 degrees in place.
* @note inspired by https://stackoverflow.com/a/43694906
*/
static void draw_buf_rotate_90_sqr(bool is_270, lv_coord_t w, lv_color_t * color_p)
{
for(lv_coord_t i = 0; i < w / 2; i++) {
for(lv_coord_t j = 0; j < (w + 1) / 2; j++) {
lv_coord_t inv_i = (w - 1) - i;
lv_coord_t inv_j = (w - 1) - j;
if(is_270) {
draw_buf_rotate4(
&color_p[i * w + j],
&color_p[inv_j * w + i],
&color_p[inv_i * w + inv_j],
&color_p[j * w + inv_i]
);
}
else {
draw_buf_rotate4(
&color_p[i * w + j],
&color_p[j * w + inv_i],
&color_p[inv_i * w + inv_j],
&color_p[inv_j * w + i]
);
}
}
}
}
/**
* Rotate the draw_buf to the display's native orientation.
*/
static void draw_buf_rotate(lv_area_t * area, lv_color_t * color_p)
{
lv_disp_drv_t * drv = disp_refr->driver;
if(disp_refr->driver->full_refresh && drv->sw_rotate) {
LV_LOG_ERROR("cannot rotate a full refreshed display!");
return;
}
if(drv->rotated == LV_DISP_ROT_180) {
draw_buf_rotate_180(drv, area, color_p);
call_flush_cb(drv, area, color_p);
}
else if(drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
/*Allocate a temporary buffer to store rotated image*/
lv_color_t * rot_buf = NULL;
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
lv_coord_t area_w = lv_area_get_width(area);
lv_coord_t area_h = lv_area_get_height(area);
/*Determine the maximum number of rows that can be rotated at a time*/
lv_coord_t max_row = LV_MIN((lv_coord_t)((LV_DISP_ROT_MAX_BUF / sizeof(lv_color_t)) / area_w), area_h);
lv_coord_t init_y_off;
init_y_off = area->y1;
if(drv->rotated == LV_DISP_ROT_90) {
area->y2 = drv->ver_res - area->x1 - 1;
area->y1 = area->y2 - area_w + 1;
}
else {
area->y1 = area->x1;
area->y2 = area->y1 + area_w - 1;
}
/*Rotate the screen in chunks, flushing after each one*/
lv_coord_t row = 0;
while(row < area_h) {
lv_coord_t height = LV_MIN(max_row, area_h - row);
draw_buf->flushing = 1;
if((row == 0) && (area_h >= area_w)) {
/*Rotate the initial area as a square*/
height = area_w;
draw_buf_rotate_90_sqr(drv->rotated == LV_DISP_ROT_270, area_w, color_p);
if(drv->rotated == LV_DISP_ROT_90) {
area->x1 = init_y_off;
area->x2 = init_y_off + area_w - 1;
}
else {
area->x2 = drv->hor_res - 1 - init_y_off;
area->x1 = area->x2 - area_w + 1;
}
}
else {
/*Rotate other areas using a maximum buffer size*/
if(rot_buf == NULL) rot_buf = lv_mem_buf_get(LV_DISP_ROT_MAX_BUF);
draw_buf_rotate_90(drv->rotated == LV_DISP_ROT_270, area_w, height, color_p, rot_buf);
if(drv->rotated == LV_DISP_ROT_90) {
area->x1 = init_y_off + row;
area->x2 = init_y_off + row + height - 1;
}
else {
area->x2 = drv->hor_res - 1 - init_y_off - row;
area->x1 = area->x2 - height + 1;
}
}
/* The original part (chunk of the current area) were split into more parts here.
* Set the original last_part flag on the last part of rotation. */
if(row + height >= area_h && draw_buf->last_area && draw_buf->last_part) {
draw_buf->flushing_last = 1;
}
else {
draw_buf->flushing_last = 0;
}
/*Flush the completed area to the display*/
call_flush_cb(drv, area, rot_buf == NULL ? color_p : rot_buf);
/*FIXME: Rotation forces legacy behavior where rendering and flushing are done serially*/
while(draw_buf->flushing) {
if(drv->wait_cb) drv->wait_cb(drv);
}
color_p += area_w * height;
row += height;
}
/*Free the allocated buffer at the end if necessary*/
if(rot_buf != NULL) lv_mem_buf_release(rot_buf);
}
}
/**
* Flush the content of the draw buffer
*/
static void draw_buf_flush(void)
{
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
lv_color_t * color_p = draw_buf->buf_act;
/*Flush the rendered content to the display*/
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->gpu_wait_cb) disp->driver->gpu_wait_cb(disp->driver);
/* In double buffered mode wait until the other buffer is freed
* and driver is ready to receive the new buffer */
if(draw_buf->buf1 && draw_buf->buf2) {
while(draw_buf->flushing) {
if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
}
}
draw_buf->flushing = 1;
if(disp_refr->driver->draw_buf->last_area && disp_refr->driver->draw_buf->last_part) draw_buf->flushing_last = 1;
else draw_buf->flushing_last = 0;
if(disp->driver->flush_cb) {
/*Rotate the buffer to the display's native orientation if necessary*/
if(disp->driver->rotated != LV_DISP_ROT_NONE && disp->driver->sw_rotate) {
draw_buf_rotate(&draw_buf->area, draw_buf->buf_act);
}
else {
call_flush_cb(disp->driver, &draw_buf->area, color_p);
}
}
if(draw_buf->buf1 && draw_buf->buf2) {
if(draw_buf->buf_act == draw_buf->buf1)
draw_buf->buf_act = draw_buf->buf2;
else
draw_buf->buf_act = draw_buf->buf1;
}
}
static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
{
REFR_TRACE("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer", area->x1, area->y1, area->x2, area->y2,
(void *)color_p);
lv_area_t offset_area = {
.x1 = area->x1 + drv->offset_x,
.y1 = area->y1 + drv->offset_y,
.x2 = area->x2 + drv->offset_x,
.y2 = area->y2 + drv->offset_y
};
drv->flush_cb(drv, &offset_area, color_p);
}

View File

@ -0,0 +1,103 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include <stdbool.h>
/*********************
* DEFINES
*********************/
#define LV_REFR_TASK_PRIO LV_TASK_PRIO_MID
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void _lv_refr_init(void);
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
* can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
* (e.g. progress bar) this function can be called when the screen should be updated.
* @param disp pointer to display to refresh. NULL to refresh all displays.
*/
void lv_refr_now(lv_disp_t * disp);
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
*/
void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p);
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * _lv_refr_get_disp_refreshing(void);
/**
* Set the display which is being refreshed.
* It shouldn't be used directly by the user.
* It can be used to trick the drawing functions about there is an active display.
* @param the display being refreshed
*/
void _lv_refr_set_disp_refreshing(lv_disp_t * disp);
#if LV_USE_PERF_MONITOR
/**
* Get the average FPS since start up
* @return the average FPS
*/
uint32_t lv_refr_get_fps_avg(void);
#endif
/**
* Called periodically to handle the refreshing
* @param timer pointer to the timer itself
*/
void _lv_disp_refr_timer(lv_timer_t * timer);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_REFR_H*/

View File

@ -0,0 +1,118 @@
/**
* @file lv_theme.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void apply_theme(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj)
{
lv_disp_t * disp = obj ? lv_obj_get_disp(obj) : lv_disp_get_default();
return lv_disp_get_theme(disp);
}
/**
* Apply the active theme on an object
* @param obj pointer to an object
* @param name the name of the theme element to apply. E.g. `LV_THEME_BTN`
*/
void lv_theme_apply(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
if(th == NULL) return;
lv_obj_remove_style_all(obj);
apply_theme(th, obj); /*Apply the theme including the base theme(s)*/
}
/**
* Set a base theme for a theme.
* The styles from the base them will be added before the styles of the current theme.
* Arbitrary long chain of themes can be created by setting base themes.
* @param new_theme pointer to theme which base should be set
* @param base pointer to the base theme
*/
void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * base)
{
new_theme->parent = base;
}
/**
* Set a callback for a theme.
* The callback is used to add styles to different objects
* @param theme pointer to theme which callback should be set
* @param cb pointer to the callback
*/
void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb)
{
theme->apply_cb = apply_cb;
}
const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_small : LV_FONT_DEFAULT;
}
const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_normal : LV_FONT_DEFAULT;
}
const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->font_large : LV_FONT_DEFAULT;
}
lv_color_t lv_theme_get_color_primary(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->color_primary : lv_palette_main(LV_PALETTE_BLUE_GREY);
}
lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj)
{
lv_theme_t * th = lv_theme_get_from_obj(obj);
return th ? th->color_secondary : lv_palette_main(LV_PALETTE_BLUE);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void apply_theme(lv_theme_t * th, lv_obj_t * obj)
{
if(th->parent) apply_theme(th->parent, obj);
if(th->apply_cb) th->apply_cb(th, obj);
}

View File

@ -0,0 +1,115 @@
/**
*@file lv_theme.h
*
*/
#ifndef LV_THEME_H
#define LV_THEME_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_theme_t;
struct _lv_disp_t;
typedef void (*lv_theme_apply_cb_t)(struct _lv_theme_t *, lv_obj_t *);
typedef struct _lv_theme_t {
lv_theme_apply_cb_t apply_cb;
struct _lv_theme_t * parent; /**< Apply the current theme's style on top of this theme.*/
void * user_data;
struct _lv_disp_t * disp;
lv_color_t color_primary;
lv_color_t color_secondary;
const lv_font_t * font_small;
const lv_font_t * font_normal;
const lv_font_t * font_large;
uint32_t flags; /*Any custom flag used by the theme*/
} lv_theme_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the theme assigned to the display of the object
* @param obj pointer to object
* @return the theme of the object's display (can be NULL)
*/
lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj);
/**
* Apply the active theme on an object
* @param obj pointer to an object
*/
void lv_theme_apply(lv_obj_t * obj);
/**
* Set a base theme for a theme.
* The styles from the base them will be added before the styles of the current theme.
* Arbitrary long chain of themes can be created by setting base themes.
* @param new_theme pointer to theme which base should be set
* @param parent pointer to the base theme
*/
void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * parent);
/**
* Set an apply callback for a theme.
* The apply callback is used to add styles to different objects
* @param theme pointer to theme which callback should be set
* @param apply_cb pointer to the callback
*/
void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb);
/**
* Get the small font of the theme
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj);
/**
* Get the normal font of the theme
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj);
/**
* Get the subtitle font of the theme
* @return pointer to the font
*/
const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj);
/**
* Get the primary color of the theme
* @return the color
*/
lv_color_t lv_theme_get_color_primary(lv_obj_t * obj);
/**
* Get the secondary color of the theme
* @return the color
*/
lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_H*/

View File

@ -0,0 +1,60 @@
/**
* @file lv_draw.h
*
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_style.h"
#include "../misc/lv_txt.h"
#include "lv_img_decoder.h"
#include "lv_img_cache.h"
#include "lv_draw_rect.h"
#include "lv_draw_label.h"
#include "lv_draw_img.h"
#include "lv_draw_line.h"
#include "lv_draw_triangle.h"
#include "lv_draw_arc.h"
#include "lv_draw_blend.h"
#include "lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* POST INCLUDES
*********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_H*/

View File

@ -0,0 +1,16 @@
CSRCS += lv_draw_arc.c
CSRCS += lv_draw_blend.c
CSRCS += lv_draw_img.c
CSRCS += lv_draw_label.c
CSRCS += lv_draw_line.c
CSRCS += lv_draw_mask.c
CSRCS += lv_draw_rect.c
CSRCS += lv_draw_triangle.c
CSRCS += lv_img_buf.c
CSRCS += lv_img_cache.c
CSRCS += lv_img_decoder.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw"

View File

@ -0,0 +1,569 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_arc.h"
#include "lv_draw_rect.h"
#include "lv_draw_mask.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
#define SPLIT_RADIUS_LIMIT 10 /*With radius greater than this the arc will drawn in quarters. A quarter is drawn only if there is arc in it*/
#define SPLIT_ANGLE_GAP_LIMIT 60 /*With small gaps in the arc don't bother with splitting because there is nothing to skip.*/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_coord_t center_x;
lv_coord_t center_y;
lv_coord_t radius;
uint16_t start_angle;
uint16_t end_angle;
uint16_t start_quarter;
uint16_t end_quarter;
lv_coord_t width;
lv_draw_rect_dsc_t * draw_dsc;
const lv_area_t * draw_area;
const lv_area_t * clip_area;
} quarter_draw_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q);
static void draw_quarter_1(quarter_draw_dsc_t * q);
static void draw_quarter_2(quarter_draw_dsc_t * q);
static void draw_quarter_3(quarter_draw_dsc_t * q);
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc)
{
#if LV_DRAW_COMPLEX
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(start_angle == end_angle) return;
lv_coord_t width = dsc->width;
if(width > radius) width = radius;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
cir_dsc.blend_mode = dsc->blend_mode;
if(dsc->img_src) {
cir_dsc.bg_opa = LV_OPA_TRANSP;
cir_dsc.bg_img_src = dsc->img_src;
cir_dsc.bg_img_opa = dsc->opa;
}
else {
cir_dsc.bg_opa = dsc->opa;
cir_dsc.bg_color = dsc->color;
}
lv_area_t area_out;
area_out.x1 = center_x - radius;
area_out.y1 = center_y - radius;
area_out.x2 = center_x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/
area_out.y2 = center_y + radius - 1;
lv_area_t area_in;
lv_area_copy(&area_in, &area_out);
area_in.x1 += dsc->width;
area_in.y1 += dsc->width;
area_in.x2 -= dsc->width;
area_in.y2 -= dsc->width;
/*Create inner the mask*/
int16_t mask_in_id = LV_MASK_ID_INV;
lv_draw_mask_radius_param_t mask_in_param;
if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
mask_in_id = lv_draw_mask_add(&mask_in_param, NULL);
}
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL);
/*Draw a full ring*/
if(start_angle + 360 == end_angle || start_angle == end_angle + 360) {
cir_dsc.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&area_out, clip_area, &cir_dsc);
lv_draw_mask_remove_id(mask_out_id);
if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id);
return;
}
while(start_angle >= 360) start_angle -= 360;
while(end_angle >= 360) end_angle -= 360;
lv_draw_mask_angle_param_t mask_angle_param;
lv_draw_mask_angle_init(&mask_angle_param, center_x, center_y, start_angle, end_angle);
int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL);
int32_t angle_gap;
if(end_angle > start_angle) {
angle_gap = 360 - (end_angle - start_angle);
}
else {
angle_gap = start_angle - end_angle;
}
if(angle_gap > SPLIT_ANGLE_GAP_LIMIT && radius > SPLIT_RADIUS_LIMIT) {
/*Handle each quarter individually and skip which is empty*/
quarter_draw_dsc_t q_dsc;
q_dsc.center_x = center_x;
q_dsc.center_y = center_y;
q_dsc.radius = radius;
q_dsc.start_angle = start_angle;
q_dsc.end_angle = end_angle;
q_dsc.start_quarter = (start_angle / 90) & 0x3;
q_dsc.end_quarter = (end_angle / 90) & 0x3;
q_dsc.width = width;
q_dsc.draw_dsc = &cir_dsc;
q_dsc.draw_area = &area_out;
q_dsc.clip_area = clip_area;
draw_quarter_0(&q_dsc);
draw_quarter_1(&q_dsc);
draw_quarter_2(&q_dsc);
draw_quarter_3(&q_dsc);
}
else {
lv_draw_rect(&area_out, clip_area, &cir_dsc);
}
lv_draw_mask_free_param(&mask_angle_param);
lv_draw_mask_free_param(&mask_out_param);
lv_draw_mask_free_param(&mask_in_param);
lv_draw_mask_remove_id(mask_angle_id);
lv_draw_mask_remove_id(mask_out_id);
if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id);
if(dsc->rounded) {
lv_draw_mask_radius_param_t mask_end_param;
lv_area_t round_area;
get_rounded_area(start_angle, radius, width, &round_area);
round_area.x1 += center_x;
round_area.x2 += center_x;
round_area.y1 += center_y;
round_area.y2 += center_y;
lv_area_t clip_area2;
if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
lv_draw_rect(&area_out, &clip_area2, &cir_dsc);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
get_rounded_area(end_angle, radius, width, &round_area);
round_area.x1 += center_x;
round_area.x2 += center_x;
round_area.y1 += center_y;
round_area.y2 += center_y;
if(_lv_area_intersect(&clip_area2, clip_area, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
lv_draw_rect(&area_out, &clip_area2, &cir_dsc);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
}
#else
LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0");
LV_UNUSED(center_x);
LV_UNUSED(center_y);
LV_UNUSED(radius);
LV_UNUSED(start_angle);
LV_UNUSED(end_angle);
LV_UNUSED(clip_area);
LV_UNUSED(dsc);
#endif /*LV_DRAW_COMPLEX*/
}
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
lv_coord_t w, bool rounded, lv_area_t * area)
{
lv_coord_t rout = radius;
/*Special case: full arc invalidation */
if(end_angle == start_angle + 360) {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
return;
}
if(start_angle > 360) start_angle -= 360;
if(end_angle > 360) end_angle -= 360;
lv_coord_t rin = radius - w;
lv_coord_t extra_area = rounded ? w / 2 + 1 : 0;
uint8_t start_quarter = start_angle / 90;
uint8_t end_quarter = end_angle / 90;
/*360 deg still counts as quarter 3 (360 / 90 would be 4)*/
if(start_quarter == 4) start_quarter = 3;
if(end_quarter == 4) end_quarter = 3;
if(start_quarter == end_quarter && start_angle <= end_angle) {
if(start_quarter == 0) {
area->y1 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 1) {
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 2) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
}
else if(start_quarter == 0 && end_quarter == 1) {
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle),
lv_trigo_sin(start_angle)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + rout + extra_area;
}
else if(start_quarter == 1 && end_quarter == 2) {
area->x1 = x - rout - extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle + 90),
lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 2 && end_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y - rout - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle) * rin,
lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3 && end_quarter == 0) {
area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle + 90),
lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + rout + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 0 && q->end_quarter == 0 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 0 || q->end_quarter == 0) {
/*Start and/or end arcs here*/
if(q->start_quarter == 0) {
quarter_area.x1 = q->center_x;
quarter_area.y2 = q->center_y + q->radius;
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 0) {
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y1 = q->center_y;
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 0 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 1) ||
(q->start_quarter == 3 && q->end_quarter == 2) ||
(q->start_quarter == 3 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x;
quarter_area.y1 = q->center_y;
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y2 = q->center_y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void draw_quarter_1(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 1 && q->end_quarter == 1 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 1 || q->end_quarter == 1) {
/*Start and/or end arcs here*/
if(q->start_quarter == 1) {
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y1 = q->center_y;
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 1) {
quarter_area.x2 = q->center_x - 1;
quarter_area.y2 = q->center_y + q->radius;
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 1 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 2) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 3 && q->end_quarter == 2)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y1 = q->center_y;
quarter_area.x2 = q->center_x - 1;
quarter_area.y2 = q->center_y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void draw_quarter_2(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 2 && q->end_quarter == 2 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 2 || q->end_quarter == 2) {
/*Start and/or end arcs here*/
if(q->start_quarter == 2) {
quarter_area.x2 = q->center_x - 1;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 2) {
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y2 = q->center_y - 1;
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 2 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 0)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x - q->radius;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x2 = q->center_x - 1;
quarter_area.y2 = q->center_y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void draw_quarter_3(quarter_draw_dsc_t * q)
{
lv_area_t quarter_area;
if(q->start_quarter == 3 && q->end_quarter == 3 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
else if(q->start_quarter == 3 || q->end_quarter == 3) {
/*Start and/or end arcs here*/
if(q->start_quarter == 3) {
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y2 = q->center_y - 1;
quarter_area.x1 = q->center_x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center_y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
if(q->end_quarter == 3) {
quarter_area.x1 = q->center_x;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x2 = q->center_x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center_y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 3 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 0) ||
(q->start_quarter == 1 && q->end_quarter == 0) ||
(q->start_quarter == 2 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center_x;
quarter_area.y1 = q->center_y - q->radius;
quarter_area.x2 = q->center_x + q->radius;
quarter_area.y2 = q->center_y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, q->clip_area);
if(ok) lv_draw_rect(q->draw_area, &quarter_area, q->draw_dsc);
}
}
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area)
{
const uint8_t ps = 8;
const uint8_t pa = 127;
int32_t thick_half = thickness / 2;
uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
int32_t cir_x;
int32_t cir_y;
cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps);
cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
/*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
if(cir_x > 0) {
cir_x = (cir_x - pa) >> ps;
res_area->x1 = cir_x - thick_half + thick_corr;
res_area->x2 = cir_x + thick_half;
}
else {
cir_x = (cir_x + pa) >> ps;
res_area->x1 = cir_x - thick_half;
res_area->x2 = cir_x + thick_half - thick_corr;
}
if(cir_y > 0) {
cir_y = (cir_y - pa) >> ps;
res_area->y1 = cir_y - thick_half + thick_corr;
res_area->y2 = cir_y + thick_half;
}
else {
cir_y = (cir_y + pa) >> ps;
res_area->y1 = cir_y - thick_half;
res_area->y2 = cir_y + thick_half - thick_corr;
}
}
#endif /*LV_DRAW_COMPLEX*/

View File

@ -0,0 +1,76 @@
/**
* @file lv_draw_arc.h
*
*/
#ifndef LV_DRAW_ARC_H
#define LV_DRAW_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_line.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t color;
lv_coord_t width;
const void * img_src;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t rounded : 1;
} lv_draw_arc_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc);
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param mask the arc will be drawn only in this mask
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param clip_area the arc will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc);
/**
* Get an area the should be invalidated when the arcs angle changed between start_angle and end_ange
* @param x the x coordinate of the center of the arc
* @param y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param w width of the arc
* @param rounded true: the arc is rounded
* @param area store the area to invalidate here
*/
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
lv_coord_t w, bool rounded, lv_area_t * area);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_ARC*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
/**
* @file lv_draw_blend.h
*
*/
#ifndef LV_DRAW_BLEND_H
#define LV_DRAW_BLEND_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
#include "lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
//! @cond Doxygen_Suppress
LV_ATTRIBUTE_FAST_MEM void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area, lv_color_t color,
lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode);
LV_ATTRIBUTE_FAST_MEM void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area,
const lv_color_t * map_buf,
lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa, lv_blend_mode_t mode);
//! @endcond
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_BLEND_H*/

View File

@ -0,0 +1,668 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_img.h"
#include "lv_img_cache.h"
#include "../hal/lv_hal_disp.h"
#include "../misc/lv_log.h"
#include "../core/lv_refr.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_math.h"
#if LV_USE_GPU_STM32_DMA2D
#include "../gpu/lv_gpu_stm32_dma2d.h"
#elif LV_USE_GPU_NXP_PXP
#include "../gpu/lv_gpu_nxp_pxp.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_USE_EXTERNAL_RENDERER == 0
LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area,
const void * src,
const lv_draw_img_dsc_t * draw_dsc);
LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const lv_area_t * clip_area,
const uint8_t * map_p,
const lv_draw_img_dsc_t * draw_dsc,
bool chroma_key, bool alpha_byte);
static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg);
static void draw_cleanup(_lv_img_cache_entry_t * cache);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
dsc->recolor = lv_color_black();
dsc->opa = LV_OPA_COVER;
dsc->zoom = LV_IMG_ZOOM_NONE;
dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
}
#if LV_USE_EXTERNAL_RENDERER == 0
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * src, const lv_draw_img_dsc_t * dsc)
{
if(src == NULL) {
LV_LOG_WARN("Image draw: src is NULL");
show_error(coords, mask, "No\ndata");
return;
}
if(dsc->opa <= LV_OPA_MIN) return;
lv_res_t res;
res = lv_img_draw_core(coords, mask, src, dsc);
if(res == LV_RES_INV) {
LV_LOG_WARN("Image draw error");
show_error(coords, mask, "No\ndata");
return;
}
}
#endif //LV_USE_GPU_SDL_RENDER
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
{
uint8_t px_size = 0;
switch(cf) {
case LV_IMG_CF_UNKNOWN:
case LV_IMG_CF_RAW:
px_size = 0;
break;
case LV_IMG_CF_TRUE_COLOR:
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
px_size = LV_COLOR_SIZE;
break;
case LV_IMG_CF_TRUE_COLOR_ALPHA:
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
break;
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_ALPHA_1BIT:
px_size = 1;
break;
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_ALPHA_2BIT:
px_size = 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_ALPHA_4BIT:
px_size = 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_8BIT:
px_size = 8;
break;
default:
px_size = 0;
break;
}
return px_size;
}
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
{
bool is_chroma_keyed = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
case LV_IMG_CF_RAW_CHROMA_KEYED:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
is_chroma_keyed = true;
break;
default:
is_chroma_keyed = false;
break;
}
return is_chroma_keyed;
}
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf)
{
bool has_alpha = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_RAW_ALPHA:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
has_alpha = true;
break;
default:
has_alpha = false;
break;
}
return has_alpha;
}
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src)
{
lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
if(src == NULL) return img_src_type;
const uint8_t * u8_p = src;
/*The first byte shows the type of the image source*/
if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
}
else if(u8_p[0] >= 0x80) {
img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
}
else {
img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
}
if(LV_IMG_SRC_UNKNOWN == img_src_type) {
LV_LOG_WARN("lv_img_src_get_type: unknown image type");
}
return img_src_type;
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_USE_EXTERNAL_RENDERER == 0
LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area,
const void * src,
const lv_draw_img_dsc_t * draw_dsc)
{
if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
if(cdsc == NULL) return LV_RES_INV;
bool chroma_keyed = lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf);
bool alpha_byte = lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf);
if(cdsc->dec_dsc.error_msg != NULL) {
LV_LOG_WARN("Image draw error");
show_error(coords, clip_area, cdsc->dec_dsc.error_msg);
}
/*The decoder could open the image and gave the entire uncompressed image.
*Just draw it!*/
else if(cdsc->dec_dsc.img_data) {
lv_area_t map_area_rot;
lv_area_copy(&map_area_rot, coords);
if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
_lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
map_area_rot.x1 += coords->x1;
map_area_rot.y1 += coords->y1;
map_area_rot.x2 += coords->x1;
map_area_rot.y2 += coords->y1;
}
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&mask_com, clip_area, &map_area_rot);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
lv_draw_map(coords, &mask_com, cdsc->dec_dsc.img_data, draw_dsc, chroma_keyed, alpha_byte);
}
/*The whole uncompressed image is not available. Try to read it line-by-line*/
else {
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&mask_com, clip_area, coords);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
int32_t width = lv_area_get_width(&mask_com);
uint8_t * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/
lv_area_t line;
lv_area_copy(&line, &mask_com);
lv_area_set_height(&line, 1);
int32_t x = mask_com.x1 - coords->x1;
int32_t y = mask_com.y1 - coords->y1;
int32_t row;
lv_res_t read_res;
for(row = mask_com.y1; row <= mask_com.y2; row++) {
lv_area_t mask_line;
union_ok = _lv_area_intersect(&mask_line, clip_area, &line);
if(union_ok == false) continue;
read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
if(read_res != LV_RES_OK) {
lv_img_decoder_close(&cdsc->dec_dsc);
LV_LOG_WARN("Image draw can't read the line");
lv_mem_buf_release(buf);
draw_cleanup(cdsc);
return LV_RES_INV;
}
lv_draw_map(&line, &mask_line, buf, draw_dsc, chroma_keyed, alpha_byte);
line.y1++;
line.y2++;
y++;
}
lv_mem_buf_release(buf);
}
draw_cleanup(cdsc);
return LV_RES_OK;
}
/**
* Draw a color map to the display (image)
* @param cords_p coordinates the color map
* @param mask_p the map will drawn only on this area (truncated to draw_buf area)
* @param map_p pointer to a lv_color_t array
* @param draw_dsc pointer to an initialized `lv_draw_img_dsc_t` variable
* @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
* @param alpha_byte true: extra alpha byte is inserted for every pixel
*/
LV_ATTRIBUTE_FAST_MEM static void lv_draw_map(const lv_area_t * map_area, const lv_area_t * clip_area,
const uint8_t * map_p,
const lv_draw_img_dsc_t * draw_dsc,
bool chroma_key, bool alpha_byte)
{
/*Use the clip area as draw area*/
lv_area_t draw_area;
lv_area_copy(&draw_area, clip_area);
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Now `draw_area` has absolute coordinates.
*Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
bool mask_any = lv_draw_mask_is_any(map_area);
/*The simplest case just copy the pixels into the draw_buf*/
if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE &&
chroma_key == false && alpha_byte == false && draw_dsc->recolor_opa == LV_OPA_TRANSP) {
_lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa,
draw_dsc->blend_mode);
}
#if LV_USE_GPU_NXP_PXP
/*Simple case without masking and transformations*/
else if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && alpha_byte == false &&
chroma_key == true && draw_dsc->recolor_opa == LV_OPA_TRANSP) { /*copy with color keying (+ alpha)*/
lv_gpu_nxp_pxp_enable_color_key();
_lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa,
draw_dsc->blend_mode);
lv_gpu_nxp_pxp_disable_color_key();
}
else if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && alpha_byte == false &&
chroma_key == false && draw_dsc->recolor_opa != LV_OPA_TRANSP) { /*copy with recolor (+ alpha)*/
lv_gpu_nxp_pxp_enable_recolor(draw_dsc->recolor, draw_dsc->recolor_opa);
_lv_blend_map(clip_area, map_area, (lv_color_t *)map_p, NULL, LV_DRAW_MASK_RES_FULL_COVER, draw_dsc->opa,
draw_dsc->blend_mode);
lv_gpu_nxp_pxp_disable_recolor();
}
#endif
/*In the other cases every pixel need to be checked one-by-one*/
else {
//#if LV_DRAW_COMPLEX
/*The pixel size in byte is different if an alpha byte is added too*/
uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
/*Go to the first displayed pixel of the map*/
int32_t map_w = lv_area_get_width(map_area);
const uint8_t * map_buf_tmp = map_p;
map_buf_tmp += map_w * (draw_area.y1 - (map_area->y1 - disp_area->y1)) * px_size_byte;
map_buf_tmp += (draw_area.x1 - (map_area->x1 - disp_area->x1)) * px_size_byte;
lv_color_t c;
lv_color_t chroma_keyed_color = LV_COLOR_CHROMA_KEY;
uint32_t px_i = 0;
const uint8_t * map_px;
lv_area_t blend_area;
blend_area.x1 = draw_area.x1 + disp_area->x1;
blend_area.x2 = blend_area.x1 + lv_area_get_width(&draw_area) - 1;
blend_area.y1 = disp_area->y1 + draw_area.y1;
blend_area.y2 = blend_area.y1;
lv_coord_t draw_area_h = lv_area_get_height(&draw_area);
lv_coord_t draw_area_w = lv_area_get_width(&draw_area);
bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false;
/*Simple ARGB image. Handle it as special case because it's very common*/
if(!mask_any && !transform && !chroma_key && draw_dsc->recolor_opa == LV_OPA_TRANSP && alpha_byte) {
#if LV_USE_GPU_STM32_DMA2D && LV_COLOR_DEPTH == 32
/*Blend ARGB images directly*/
if(lv_area_get_size(&draw_area) > 240) {
int32_t disp_w = lv_area_get_width(disp_area);
lv_color_t * disp_buf = draw_buf->buf_act;
lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area.y1 + draw_area.x1;
lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, (const lv_color_t *)map_buf_tmp, draw_dsc->opa, map_w, draw_area_w,
draw_area_h);
return;
}
#endif
uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp);
uint32_t mask_buf_size = lv_area_get_size(&draw_area) > (uint32_t) hor_res ? hor_res : lv_area_get_size(&draw_area);
lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
int32_t x;
int32_t y;
for(y = 0; y < draw_area_h; y++) {
map_px = map_buf_tmp;
for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) {
lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
mask_buf[px_i] = px_opa;
if(px_opa) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
map2[px_i].full = map_px[0];
#elif LV_COLOR_DEPTH == 16
map2[px_i].full = map_px[0] + (map_px[1] << 8);
#elif LV_COLOR_DEPTH == 32
map2[px_i].full = *((uint32_t *)map_px);
#endif
}
#if LV_COLOR_DEPTH == 32
map2[px_i].ch.alpha = 0xFF;
#endif
}
map_buf_tmp += map_w * px_size_byte;
if(px_i + lv_area_get_width(&draw_area) < mask_buf_size) {
blend_area.y2 ++;
}
else {
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, draw_dsc->blend_mode);
blend_area.y1 = blend_area.y2 + 1;
blend_area.y2 = blend_area.y1;
px_i = 0;
}
}
/*Flush the last part*/
if(blend_area.y1 != blend_area.y2) {
blend_area.y2--;
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, LV_DRAW_MASK_RES_CHANGED, draw_dsc->opa, draw_dsc->blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(map2);
}
/*Most complicated case: transform or other mask or chroma keyed*/
else {
/*Build the image and a mask line-by-line*/
uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(disp);
uint32_t mask_buf_size = lv_area_get_size(&draw_area) > hor_res ? hor_res : lv_area_get_size(&draw_area);
lv_color_t * map2 = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
#if LV_DRAW_COMPLEX
lv_img_transform_dsc_t trans_dsc;
lv_memset_00(&trans_dsc, sizeof(lv_img_transform_dsc_t));
if(transform) {
lv_img_cf_t cf = LV_IMG_CF_TRUE_COLOR;
if(alpha_byte) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
else if(chroma_key) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
trans_dsc.cfg.angle = draw_dsc->angle;
trans_dsc.cfg.zoom = draw_dsc->zoom;
trans_dsc.cfg.src = map_p;
trans_dsc.cfg.src_w = map_w;
trans_dsc.cfg.src_h = lv_area_get_height(map_area);;
trans_dsc.cfg.cf = cf;
trans_dsc.cfg.pivot_x = draw_dsc->pivot.x;
trans_dsc.cfg.pivot_y = draw_dsc->pivot.y;
trans_dsc.cfg.color = draw_dsc->recolor;
trans_dsc.cfg.antialias = draw_dsc->antialias;
_lv_img_buf_transform_init(&trans_dsc);
}
#endif
uint16_t recolor_premult[3] = {0};
lv_opa_t recolor_opa_inv = 255 - draw_dsc->recolor_opa;
if(draw_dsc->recolor_opa != 0) {
lv_color_premult(draw_dsc->recolor, draw_dsc->recolor_opa, recolor_premult);
}
lv_draw_mask_res_t mask_res;
mask_res = (alpha_byte || chroma_key || draw_dsc->angle ||
draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
/*Prepare the `mask_buf`if there are other masks*/
if(mask_any) {
lv_memset_ff(mask_buf, mask_buf_size);
}
int32_t x;
int32_t y;
#if LV_DRAW_COMPLEX
int32_t rot_y = disp_area->y1 + draw_area.y1 - map_area->y1;
#endif
for(y = 0; y < draw_area_h; y++) {
map_px = map_buf_tmp;
#if LV_DRAW_COMPLEX
uint32_t px_i_start = px_i;
int32_t rot_x = disp_area->x1 + draw_area.x1 - map_area->x1;
#endif
for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) {
#if LV_DRAW_COMPLEX
if(transform) {
/*Transform*/
bool ret;
ret = _lv_img_buf_transform(&trans_dsc, rot_x + x, rot_y + y);
if(ret == false) {
mask_buf[px_i] = LV_OPA_TRANSP;
continue;
}
else {
mask_buf[px_i] = trans_dsc.res.opa;
c.full = trans_dsc.res.color.full;
}
}
/*No transform*/
else
#endif
{
if(alpha_byte) {
lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
mask_buf[px_i] = px_opa;
if(px_opa == 0) {
#if LV_COLOR_DEPTH == 32
map2[px_i].full = 0;
#endif
continue;
}
}
else {
mask_buf[px_i] = 0xFF;
}
#if LV_COLOR_DEPTH == 1
c.full = map_px[0];
#elif LV_COLOR_DEPTH == 8
c.full = map_px[0];
#elif LV_COLOR_DEPTH == 16
c.full = map_px[0] + (map_px[1] << 8);
#elif LV_COLOR_DEPTH == 32
c.full = *((uint32_t *)map_px);
c.ch.alpha = 0xFF;
#endif
if(chroma_key) {
if(c.full == chroma_keyed_color.full) {
mask_buf[px_i] = LV_OPA_TRANSP;
#if LV_COLOR_DEPTH == 32
map2[px_i].full = 0;
#endif
continue;
}
}
}
if(draw_dsc->recolor_opa != 0) {
c = lv_color_mix_premult(recolor_premult, c, recolor_opa_inv);
}
map2[px_i].full = c.full;
}
#if LV_DRAW_COMPLEX
/*Apply the masks if any*/
if(mask_any) {
lv_draw_mask_res_t mask_res_sub;
mask_res_sub = lv_draw_mask_apply(mask_buf + px_i_start, draw_area.x1 + draw_buf->area.x1,
y + draw_area.y1 + draw_buf->area.y1,
lv_area_get_width(&draw_area));
if(mask_res_sub == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + px_i_start, lv_area_get_width(&draw_area));
mask_res = LV_DRAW_MASK_RES_CHANGED;
}
else if(mask_res_sub == LV_DRAW_MASK_RES_CHANGED) {
mask_res = LV_DRAW_MASK_RES_CHANGED;
}
}
#endif
map_buf_tmp += map_w * px_size_byte;
if(px_i + lv_area_get_width(&draw_area) < mask_buf_size) {
blend_area.y2 ++;
}
else {
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode);
blend_area.y1 = blend_area.y2 + 1;
blend_area.y2 = blend_area.y1;
px_i = 0;
mask_res = (alpha_byte || chroma_key || draw_dsc->angle ||
draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
/*Prepare the `mask_buf`if there are other masks*/
if(mask_any) {
lv_memset_ff(mask_buf, mask_buf_size);
}
}
}
/*Flush the last part*/
if(blend_area.y1 != blend_area.y2) {
blend_area.y2--;
_lv_blend_map(clip_area, &blend_area, map2, mask_buf, mask_res, draw_dsc->opa, draw_dsc->blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(map2);
}
}
}
static void show_error(const lv_area_t * coords, const lv_area_t * clip_area, const char * msg)
{
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_white();
lv_draw_rect(coords, clip_area, &rect_dsc);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_draw_label(coords, clip_area, &label_dsc, msg, NULL);
}
static void draw_cleanup(_lv_img_cache_entry_t * cache)
{
/*Automatically close images with no caching*/
#if LV_IMG_CACHE_DEF_SIZE == 0
lv_img_decoder_close(&cache->dec_dsc);
#else
LV_UNUSED(cache);
#endif
}
#endif //LV_USE_GPU_SDL_RENDER

View File

@ -0,0 +1,97 @@
/**
* @file lv_draw_img.h
*
*/
#ifndef LV_DRAW_IMG_H
#define LV_DRAW_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "lv_img_buf.h"
#include "lv_draw_blend.h"
/*********************
* DEFINES
*********************/
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint16_t angle;
uint16_t zoom;
lv_point_t pivot;
lv_color_t recolor;
lv_opa_t recolor_opa;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 4;
int32_t frame_id;
uint8_t antialias : 1;
} lv_draw_img_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc);
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * src, const lv_draw_img_dsc_t * dsc);
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src);
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf);
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf);
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_IMG_H*/

View File

@ -0,0 +1,878 @@
/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_label.h"
#include "../misc/lv_math.h"
#include "../hal/lv_hal_disp.h"
#include "../core/lv_refr.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_assert.h"
#if LV_USE_GPU_SDL
#include "../gpu/lv_gpu_sdl.h"
#endif
/*********************
* DEFINES
*********************/
#define LABEL_RECOLOR_PAR_LENGTH 6
#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
/**********************
* TYPEDEFS
**********************/
enum {
CMD_STATE_WAIT,
CMD_STATE_PAR,
CMD_STATE_IN,
};
typedef uint8_t cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_USE_EXTERNAL_RENDERER == 0
LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g,
const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/
#endif /*LV_USE_EXTERNAL_RENDERER*/
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
const uint8_t _lv_bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const uint8_t _lv_bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const uint8_t _lv_bpp3_opa_table[8] = {0, 36, 73, 109, /*Opacity mapping with bpp = 3*/
146, 182, 219, 255
};
const uint8_t _lv_bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119,
136, 153, 170, 187,
204, 221, 238, 255
};
const uint8_t _lv_bpp8_opa_table[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->font = LV_FONT_DEFAULT;
dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_color = lv_color_black();
dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE);
dsc->bidi_dir = LV_BASE_DIR_LTR;
}
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask,
const lv_draw_label_dsc_t * dsc,
const char * txt,
lv_draw_label_hint_t * hint)
{
if(dsc->opa <= LV_OPA_MIN) return;
const lv_font_t * font = dsc->font;
int32_t w;
/*No need to waste processor time if string is empty*/
if(txt == NULL || txt[0] == '\0')
return;
lv_area_t clipped_area;
bool clip_ok = _lv_area_intersect(&clipped_area, coords, mask);
if(!clip_ok) return;
lv_text_align_t align = dsc->align;
lv_base_dir_t base_dir = dsc->bidi_dir;
lv_bidi_calculate_align(&align, &base_dir, txt);
if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
}
else {
/*If EXAPND is enabled then not limit the text's width to the object's width*/
lv_point_t p;
lv_txt_get_size(&p, txt, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
dsc->flag);
w = p.x;
}
int32_t line_height_font = lv_font_get_line_height(font);
int32_t line_height = line_height_font + dsc->line_space;
/*Init variables for the first line*/
int32_t line_width = 0;
lv_point_t pos;
pos.x = coords->x1;
pos.y = coords->y1;
int32_t x_ofs = 0;
int32_t y_ofs = 0;
x_ofs = dsc->ofs_x;
y_ofs = dsc->ofs_y;
pos.y += y_ofs;
uint32_t line_start = 0;
int32_t last_line_start = -1;
/*Check the hint to use the cached info*/
if(hint && y_ofs == 0 && coords->y1 < 0) {
/*If the label changed too much recalculate the hint.*/
if(LV_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
hint->line_start = -1;
}
last_line_start = hint->line_start;
}
/*Use the hint if it's valid*/
if(hint && last_line_start >= 0) {
line_start = last_line_start;
pos.y += hint->y;
}
uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
/*Go the first visible line*/
while(pos.y + line_height_font < mask->y1) {
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
pos.y += line_height;
/*Save at the threshold coordinate*/
if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
hint->line_start = line_start;
hint->y = pos.y - coords->y1;
hint->coord_y = coords->y1;
}
if(txt[line_start] == '\0') return;
}
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
lv_opa_t opa = dsc->opa;
uint32_t sel_start = dsc->sel_start;
uint32_t sel_end = dsc->sel_end;
if(sel_start > sel_end) {
uint32_t tmp = sel_start;
sel_start = sel_end;
sel_end = tmp;
}
lv_draw_line_dsc_t line_dsc;
if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) {
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = dsc->color;
line_dsc.width = font->underline_thickness ? font->underline_thickness : 1;
line_dsc.opa = dsc->opa;
line_dsc.blend_mode = dsc->blend_mode;
}
cmd_state_t cmd_state = CMD_STATE_WAIT;
uint32_t i;
uint32_t par_start = 0;
lv_color_t recolor;
int32_t letter_w;
lv_draw_rect_dsc_t draw_dsc_sel;
lv_draw_rect_dsc_init(&draw_dsc_sel);
draw_dsc_sel.bg_color = dsc->sel_bg_color;
int32_t pos_x_start = pos.x;
/*Write out all lines*/
while(txt[line_start] != '\0') {
pos.x += x_ofs;
/*Write all letter of a line*/
cmd_state = CMD_STATE_WAIT;
i = 0;
#if LV_USE_BIDI
char * bidi_txt = lv_mem_buf_get(line_end - line_start + 1);
_lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
#else
const char * bidi_txt = txt + line_start;
#endif
while(i < line_end - line_start) {
uint32_t logical_char_pos = 0;
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
#if LV_USE_BIDI
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start);
uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i);
logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
#else
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i);
#endif
}
uint32_t letter;
uint32_t letter_next;
_lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i);
/*Handle the re-color command*/
if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
par_start = i;
cmd_state = CMD_STATE_PAR;
continue;
}
else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/
cmd_state = CMD_STATE_WAIT;
}
else if(cmd_state == CMD_STATE_IN) { /*Command end*/
cmd_state = CMD_STATE_WAIT;
continue;
}
}
/*Skip the color parameter and wait the space after it*/
if(cmd_state == CMD_STATE_PAR) {
if(letter == ' ') {
/*Get the parameter*/
if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
int r, g, b;
r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
recolor = lv_color_make(r, g, b);
}
else {
recolor.full = dsc->color.full;
}
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
}
continue;
}
}
lv_color_t color = dsc->color;
if(cmd_state == CMD_STATE_IN) color = recolor;
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
if(logical_char_pos >= sel_start && logical_char_pos < sel_end) {
lv_area_t sel_coords;
sel_coords.x1 = pos.x;
sel_coords.y1 = pos.y;
sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1;
sel_coords.y2 = pos.y + line_height - 1;
lv_draw_rect(&sel_coords, mask, &draw_dsc_sel);
color = dsc->sel_color;
}
}
lv_draw_letter(&pos, mask, font, letter, color, opa, dsc->blend_mode);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
}
}
if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + (dsc->font->line_height / 2) + line_dsc.width / 2;
p2.x = pos.x;
p2.y = p1.y;
lv_draw_line(&p1, &p2, mask, &line_dsc);
}
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position;
p2.x = pos.x;
p2.y = p1.y;
lv_draw_line(&p1, &p2, mask, &line_dsc);
}
#if LV_USE_BIDI
lv_mem_buf_release(bidi_txt);
bidi_txt = NULL;
#endif
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, dsc->flag);
pos.x = coords->x1;
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width =
lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width =
lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > mask->y2) return;
}
LV_ASSERT_MEM_INTEGRITY();
}
#if LV_USE_EXTERNAL_RENDERER == 0
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Draw a letter in the Virtual Display Buffer
* @param pos_p left-top coordinate of the latter
* @param mask_p the letter will be drawn only on this area (truncated to draw_buf area)
* @param font_p pointer to font
* @param letter a letter to draw
* @param color color of letter
* @param opa opacity of letter (0..255)
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
const lv_font_t * font_p,
uint32_t letter,
lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
{
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
if(font_p == NULL) {
LV_LOG_WARN("lv_draw_letter: font is NULL");
return;
}
lv_font_glyph_dsc_t g;
bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found
*but do not print warning for non printable ASCII chars (e.g. '\n')*/
if(letter >= 0x20 &&
letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
// LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", (unsigned int)letter);
}
return;
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) return;
int32_t pos_x = pos_p->x + g.ofs_x;
int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
/*If the letter is completely out of mask don't draw it*/
if(pos_x + g.box_w < clip_area->x1 ||
pos_x > clip_area->x2 ||
pos_y + g.box_h < clip_area->y1 ||
pos_y > clip_area->y2) {
return;
}
const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
if(map_p == NULL) {
LV_LOG_WARN("lv_draw_letter: character's bitmap not found");
return;
}
if(font_p->subpx) {
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
draw_letter_subpx(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
#else
LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h");
#endif
}
else {
draw_letter_normal(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
}
}
LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g,
const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
{
const uint8_t * bpp_opa_table_p;
uint32_t bitmask_init;
uint32_t bitmask;
uint32_t bpp = g->bpp;
uint32_t shades;
if(bpp == 3) bpp = 4;
switch(bpp) {
case 1:
bpp_opa_table_p = _lv_bpp1_opa_table;
bitmask_init = 0x80;
shades = 2;
break;
case 2:
bpp_opa_table_p = _lv_bpp2_opa_table;
bitmask_init = 0xC0;
shades = 4;
break;
case 4:
bpp_opa_table_p = _lv_bpp4_opa_table;
bitmask_init = 0xF0;
shades = 16;
break;
case 8:
bpp_opa_table_p = _lv_bpp8_opa_table;
bitmask_init = 0xFF;
shades = 256;
break; /*No opa table, pixel value will be used directly*/
default:
LV_LOG_WARN("lv_draw_letter: invalid bpp");
return; /*Invalid bpp. Can't render the letter*/
}
static lv_opa_t opa_table[256];
static lv_opa_t prev_opa = LV_OPA_TRANSP;
static uint32_t prev_bpp = 0;
if(opa < LV_OPA_MAX) {
if(prev_opa != opa || prev_bpp != bpp) {
uint32_t i;
for(i = 0; i < shades; i++) {
opa_table[i] = bpp_opa_table_p[i] == LV_OPA_COVER ? opa : ((bpp_opa_table_p[i] * opa) >> 8);
}
}
bpp_opa_table_p = opa_table;
prev_opa = opa;
prev_bpp = bpp;
}
int32_t col, row;
int32_t box_w = g->box_w;
int32_t box_h = g->box_h;
int32_t width_bit = box_w * bpp; /*Letter width in bits*/
/*Calculate the col/row start/end on the map*/
int32_t col_start = pos_x >= clip_area->x1 ? 0 : clip_area->x1 - pos_x;
int32_t col_end = pos_x + box_w <= clip_area->x2 ? box_w : clip_area->x2 - pos_x + 1;
int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y;
int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1;
/*Move on the map too*/
uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
map_p += bit_ofs >> 3;
uint8_t letter_px;
uint32_t col_bit;
col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/
lv_coord_t hor_res = lv_disp_get_hor_res(_lv_refr_get_disp_refreshing());
uint32_t mask_buf_size = box_w * box_h > hor_res ? hor_res : box_w * box_h;
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
int32_t mask_p = 0;
lv_area_t fill_area;
fill_area.x1 = col_start + pos_x;
fill_area.x2 = col_end + pos_x - 1;
fill_area.y1 = row_start + pos_y;
fill_area.y2 = fill_area.y1;
#if LV_DRAW_COMPLEX
bool mask_any = lv_draw_mask_is_any(&fill_area);
#endif
uint32_t col_bit_max = 8 - bpp;
uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp;
for(row = row_start ; row < row_end; row++) {
#if LV_DRAW_COMPLEX
int32_t mask_p_start = mask_p;
#endif
bitmask = bitmask_init >> col_bit;
for(col = col_start; col < col_end; col++) {
/*Load the pixel's opacity into the mask*/
letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit);
if(letter_px) {
mask_buf[mask_p] = bpp_opa_table_p[letter_px];
}
else {
mask_buf[mask_p] = 0;
}
/*Go to the next column*/
if(col_bit < col_bit_max) {
col_bit += bpp;
bitmask = bitmask >> bpp;
}
else {
col_bit = 0;
bitmask = bitmask_init;
map_p++;
}
/*Next mask byte*/
mask_p++;
}
#if LV_DRAW_COMPLEX
/*Apply masks if any*/
if(mask_any) {
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2,
lv_area_get_width(&fill_area));
if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&fill_area));
}
}
#endif
if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) {
fill_area.y2 ++;
}
else {
_lv_blend_fill(clip_area, &fill_area,
color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER,
blend_mode);
fill_area.y1 = fill_area.y2 + 1;
fill_area.y2 = fill_area.y1;
mask_p = 0;
}
col_bit += col_bit_row_ofs;
map_p += (col_bit >> 3);
col_bit = col_bit & 0x7;
}
/*Flush the last part*/
if(fill_area.y1 != fill_area.y2) {
fill_area.y2--;
_lv_blend_fill(clip_area, &fill_area,
color, mask_buf, LV_DRAW_MASK_RES_CHANGED, LV_OPA_COVER,
blend_mode);
mask_p = 0;
}
lv_mem_buf_release(mask_buf);
}
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
static void draw_letter_subpx(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area,
const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
{
const uint8_t * bpp_opa_table;
uint32_t bitmask_init;
uint32_t bitmask;
uint32_t bpp = g->bpp;
if(bpp == 3) bpp = 4;
switch(bpp) {
case 1:
bpp_opa_table = _lv_bpp1_opa_table;
bitmask_init = 0x80;
break;
case 2:
bpp_opa_table = _lv_bpp2_opa_table;
bitmask_init = 0xC0;
break;
case 4:
bpp_opa_table = _lv_bpp4_opa_table;
bitmask_init = 0xF0;
break;
case 8:
bpp_opa_table = _lv_bpp8_opa_table;
bitmask_init = 0xFF;
break; /*No opa table, pixel value will be used directly*/
default:
LV_LOG_WARN("lv_draw_letter: invalid bpp not found");
return; /*Invalid bpp. Can't render the letter*/
}
int32_t col, row;
int32_t box_w = g->box_w;
int32_t box_h = g->box_h;
int32_t width_bit = box_w * bpp; /*Letter width in bits*/
/*Calculate the col/row start/end on the map*/
int32_t col_start = pos_x >= clip_area->x1 ? 0 : (clip_area->x1 - pos_x) * 3;
int32_t col_end = pos_x + box_w / 3 <= clip_area->x2 ? box_w : (clip_area->x2 - pos_x + 1) * 3;
int32_t row_start = pos_y >= clip_area->y1 ? 0 : clip_area->y1 - pos_y;
int32_t row_end = pos_y + box_h <= clip_area->y2 ? box_h : clip_area->y2 - pos_y + 1;
/*Move on the map too*/
int32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
map_p += bit_ofs >> 3;
uint8_t letter_px;
lv_opa_t px_opa;
int32_t col_bit;
col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/
lv_area_t map_area;
map_area.x1 = col_start / 3 + pos_x;
map_area.x2 = col_end / 3 + pos_x - 1;
map_area.y1 = row_start + pos_y;
map_area.y2 = map_area.y1;
if(map_area.x2 <= map_area.x1) return;
int32_t mask_buf_size = box_w * box_h > _LV_MASK_BUF_MAX_SIZE ? _LV_MASK_BUF_MAX_SIZE : g->box_w * g->box_h;
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
int32_t mask_p = 0;
lv_color_t * color_buf = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t));
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
int32_t disp_buf_width = lv_area_get_width(&draw_buf->area);
lv_color_t * disp_buf_buf_tmp = draw_buf->buf_act;
/*Set a pointer on draw_buf to the first pixel of the letter*/
disp_buf_buf_tmp += ((pos_y - draw_buf->area.y1) * disp_buf_width) + pos_x - draw_buf->area.x1;
/*If the letter is partially out of mask the move there on draw_buf*/
disp_buf_buf_tmp += (row_start * disp_buf_width) + col_start / 3;
bool mask_any = lv_draw_mask_is_any(&map_area);
uint8_t font_rgb[3];
#if LV_COLOR_16_SWAP == 0
uint8_t txt_rgb[3] = {color.ch.red, color.ch.green, color.ch.blue};
#else
uint8_t txt_rgb[3] = {color.ch.red, (color.ch.green_h << 3) + color.ch.green_l, color.ch.blue};
#endif
for(row = row_start ; row < row_end; row++) {
uint32_t subpx_cnt = 0;
bitmask = bitmask_init >> col_bit;
int32_t mask_p_start = mask_p;
for(col = col_start; col < col_end; col++) {
/*Load the pixel's opacity into the mask*/
letter_px = (*map_p & bitmask) >> (8 - col_bit - bpp);
if(letter_px != 0) {
if(opa == LV_OPA_COVER) {
px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px];
}
else {
px_opa = bpp == 8 ? (uint32_t)((uint32_t)letter_px * opa) >> 8
: (uint32_t)((uint32_t)bpp_opa_table[letter_px] * opa) >> 8;
}
}
else {
px_opa = 0;
}
font_rgb[subpx_cnt] = px_opa;
subpx_cnt ++;
if(subpx_cnt == 3) {
subpx_cnt = 0;
lv_color_t res_color;
#if LV_COLOR_16_SWAP == 0
uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red, disp_buf_buf_tmp->ch.green, disp_buf_buf_tmp->ch.blue};
#else
uint8_t bg_rgb[3] = {disp_buf_buf_tmp->ch.red,
(disp_buf_buf_tmp->ch.green_h << 3) + disp_buf_buf_tmp->ch.green_l,
disp_buf_buf_tmp->ch.blue
};
#endif
#if LV_FONT_SUBPX_BGR
res_color.ch.blue = (uint32_t)((uint32_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8;
res_color.ch.red = (uint32_t)((uint32_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8;
#else
res_color.ch.red = (uint32_t)((uint16_t)txt_rgb[0] * font_rgb[0] + (bg_rgb[0] * (255 - font_rgb[0]))) >> 8;
res_color.ch.blue = (uint32_t)((uint16_t)txt_rgb[2] * font_rgb[2] + (bg_rgb[2] * (255 - font_rgb[2]))) >> 8;
#endif
#if LV_COLOR_16_SWAP == 0
res_color.ch.green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8;
#else
uint8_t green = (uint32_t)((uint32_t)txt_rgb[1] * font_rgb[1] + (bg_rgb[1] * (255 - font_rgb[1]))) >> 8;
res_color.ch.green_h = green >> 3;
res_color.ch.green_l = green & 0x7;
#endif
#if LV_COLOR_DEPTH == 32
res_color.ch.alpha = 0xff;
#endif
if(font_rgb[0] == 0 && font_rgb[1] == 0 && font_rgb[2] == 0) mask_buf[mask_p] = LV_OPA_TRANSP;
else mask_buf[mask_p] = LV_OPA_COVER;
color_buf[mask_p] = res_color;
/*Next mask byte*/
mask_p++;
disp_buf_buf_tmp++;
}
/*Go to the next column*/
if(col_bit < (int32_t)(8 - bpp)) {
col_bit += bpp;
bitmask = bitmask >> bpp;
}
else {
col_bit = 0;
bitmask = bitmask_init;
map_p++;
}
}
/*Apply masks if any*/
if(mask_any) {
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, map_area.x1, map_area.y2,
lv_area_get_width(&map_area));
if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + mask_p_start, lv_area_get_width(&map_area));
}
}
if((int32_t) mask_p + (col_end - col_start) < mask_buf_size) {
map_area.y2 ++;
}
else {
_lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode);
map_area.y1 = map_area.y2 + 1;
map_area.y2 = map_area.y1;
mask_p = 0;
}
col_bit += ((box_w - col_end) + col_start) * bpp;
map_p += (col_bit >> 3);
col_bit = col_bit & 0x7;
/*Next row in draw_buf*/
disp_buf_buf_tmp += disp_buf_width - (col_end - col_start) / 3;
}
/*Flush the last part*/
if(map_area.y1 != map_area.y2) {
map_area.y2--;
_lv_blend_map(clip_area, &map_area, color_buf, mask_buf, LV_DRAW_MASK_RES_CHANGED, opa, blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(color_buf);
}
#endif /*LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX*/
#endif /*LV_USE_EXTERNAL_RENDERER*/
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
uint8_t result = 0;
if(hex >= '0' && hex <= '9') {
result = hex - '0';
}
else {
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
switch(hex) {
case 'A':
result = 10;
break;
case 'B':
result = 11;
break;
case 'C':
result = 12;
break;
case 'D':
result = 13;
break;
case 'E':
result = 14;
break;
case 'F':
result = 15;
break;
default:
result = 0;
break;
}
}
return result;
}

View File

@ -0,0 +1,108 @@
/**
* @file lv_draw_label.h
*
*/
#ifndef LV_DRAW_LABEL_H
#define LV_DRAW_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_txt.h"
#include "../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF)
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_font_t * font;
uint32_t sel_start;
uint32_t sel_end;
lv_color_t color;
lv_color_t sel_color;
lv_color_t sel_bg_color;
lv_coord_t line_space;
lv_coord_t letter_space;
lv_coord_t ofs_x;
lv_coord_t ofs_y;
lv_opa_t opa;
lv_base_dir_t bidi_dir;
lv_text_align_t align;
lv_text_flag_t flag;
lv_text_decor_t decor : 3;
lv_blend_mode_t blend_mode: 3;
} lv_draw_label_dsc_t;
/** Store some info to speed up drawing of very large texts
* It takes a lot of time to get the first visible character because
* all the previous characters needs to be checked to calculate the positions.
* This structure stores an earlier (e.g. at -1000 px) coordinate and the index of that line.
* Therefore the calculations can start from here.*/
typedef struct _lv_draw_label_hint_t {
/** Index of the line at `y` coordinate*/
int32_t line_start;
/** Give the `y` coordinate of the first letter at `line start` index. Relative to the label's coordinates*/
int32_t y;
/** The 'y1' coordinate of the label when the hint was saved.
* Used to invalidate the hint if the label has moved too much.*/
int32_t coord_y;
} lv_draw_label_hint_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
//! @cond Doxygen_Suppress
LV_ATTRIBUTE_FAST_MEM void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc);
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask,
const lv_draw_label_dsc_t * dsc,
const char * txt, lv_draw_label_hint_t * hint);
LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
const lv_font_t * font_p,
uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
//! @endcond
/***********************
* GLOBAL VARIABLES
***********************/
extern const uint8_t _lv_bpp2_opa_table[];
extern const uint8_t _lv_bpp3_opa_table[];
extern const uint8_t _lv_bpp1_opa_table[];
extern const uint8_t _lv_bpp4_opa_table[];
extern const uint8_t _lv_bpp8_opa_table[];
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LABEL_H*/

View File

@ -0,0 +1,495 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "lv_draw_mask.h"
#include "lv_draw_blend.h"
#include "../core/lv_refr.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_line_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param clip the line will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
if(dsc->width == 0) return;
if(dsc->opa <= LV_OPA_MIN) return;
if(point1->x == point2->x && point1->y == point2->y) return;
lv_area_t clip_line;
clip_line.x1 = LV_MIN(point1->x, point2->x) - dsc->width / 2;
clip_line.x2 = LV_MAX(point1->x, point2->x) + dsc->width / 2;
clip_line.y1 = LV_MIN(point1->y, point2->y) - dsc->width / 2;
clip_line.y2 = LV_MAX(point1->y, point2->y) + dsc->width / 2;
bool is_common;
is_common = _lv_area_intersect(&clip_line, &clip_line, clip);
if(!is_common) return;
if(point1->y == point2->y) draw_line_hor(point1, point2, &clip_line, dsc);
else if(point1->x == point2->x) draw_line_ver(point1, point2, &clip_line, dsc);
else draw_line_skew(point1, point2, &clip_line, dsc);
if(dsc->round_end || dsc->round_start) {
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
cir_dsc.bg_color = dsc->color;
cir_dsc.radius = LV_RADIUS_CIRCLE;
cir_dsc.bg_opa = dsc->opa;
int32_t r = (dsc->width >> 1);
int32_t r_corr = (dsc->width & 1) ? 0 : 1;
lv_area_t cir_area;
if(dsc->round_start) {
cir_area.x1 = point1->x - r;
cir_area.y1 = point1->y - r;
cir_area.x2 = point1->x + r - r_corr;
cir_area.y2 = point1->y + r - r_corr ;
lv_draw_rect(&cir_area, clip, &cir_dsc);
}
if(dsc->round_end) {
cir_area.x1 = point2->x - r;
cir_area.y1 = point2->y - r;
cir_area.x2 = point2->x + r - r_corr;
cir_area.y2 = point2->y + r - r_corr ;
lv_draw_rect(&cir_area, clip, &cir_dsc);
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
LV_ATTRIBUTE_FAST_MEM static void draw_line_hor(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
lv_opa_t opa = dsc->opa;
int32_t w = dsc->width - 1;
int32_t w_half0 = w >> 1;
int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
lv_area_t draw_area;
draw_area.x1 = LV_MIN(point1->x, point2->x);
draw_area.x2 = LV_MAX(point1->x, point2->x) - 1;
draw_area.y1 = point1->y - w_half1;
draw_area.y2 = point1->y + w_half0;
bool dashed = dsc->dash_gap && dsc->dash_width ? true : false;
bool simple_mode = true;
if(lv_draw_mask_is_any(&draw_area)) simple_mode = false;
else if(dashed) simple_mode = false;
/*If there is no mask then simply draw a rectangle*/
if(simple_mode) {
_lv_blend_fill(clip, &draw_area,
dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa,
dsc->blend_mode);
}
#if LV_DRAW_COMPLEX
/*If there other mask apply it*/
else {
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Get clipped fill area which is the real draw area.
*It is always the same or inside `fill_area`*/
bool is_common;
is_common = _lv_area_intersect(&draw_area, clip, &draw_area);
if(!is_common) return;
/*Now `draw_area` has absolute coordinates.
*Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
int32_t draw_area_w = lv_area_get_width(&draw_area);
lv_area_t fill_area;
fill_area.x1 = draw_area.x1 + disp_area->x1;
fill_area.x2 = draw_area.x2 + disp_area->x1;
fill_area.y1 = draw_area.y1 + disp_area->y1;
fill_area.y2 = fill_area.y1;
lv_coord_t dash_start = 0;
if(dashed) {
dash_start = (draw_buf->area.x1 + draw_area.x1) % (dsc->dash_gap + dsc->dash_width);
}
lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w);
int32_t h;
for(h = draw_area.y1; h <= draw_area.y2; h++) {
lv_memset_ff(mask_buf, draw_area_w);
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h,
draw_area_w);
if(dashed) {
if(mask_res != LV_DRAW_MASK_RES_TRANSP) {
lv_coord_t dash_cnt = dash_start;
lv_coord_t i;
for(i = 0; i < draw_area_w; i++, dash_cnt++) {
if(dash_cnt <= dsc->dash_width) {
int16_t diff = dsc->dash_width - dash_cnt;
i += diff;
dash_cnt += diff;
}
else if(dash_cnt >= dsc->dash_gap + dsc->dash_width) {
dash_cnt = 0;
}
else {
mask_buf[i] = 0x00;
}
}
mask_res = LV_DRAW_MASK_RES_CHANGED;
}
}
_lv_blend_fill(clip, &fill_area,
dsc->color, mask_buf, mask_res, dsc->opa,
dsc->blend_mode);
fill_area.y1++;
fill_area.y2++;
}
lv_mem_buf_release(mask_buf);
}
#endif /*LV_DRAW_COMPLEX*/
}
LV_ATTRIBUTE_FAST_MEM static void draw_line_ver(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
lv_opa_t opa = dsc->opa;
int32_t w = dsc->width - 1;
int32_t w_half0 = w >> 1;
int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
lv_area_t draw_area;
draw_area.x1 = point1->x - w_half1;
draw_area.x2 = point1->x + w_half0;
draw_area.y1 = LV_MIN(point1->y, point2->y);
draw_area.y2 = LV_MAX(point1->y, point2->y) - 1;
bool dashed = dsc->dash_gap && dsc->dash_width ? true : false;
bool simple_mode = true;
if(lv_draw_mask_is_any(&draw_area)) simple_mode = false;
else if(dashed) simple_mode = false;
/*If there is no mask then simply draw a rectangle*/
if(simple_mode) {
_lv_blend_fill(clip, &draw_area,
dsc->color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa,
dsc->blend_mode);
}
#if LV_DRAW_COMPLEX
/*If there other mask apply it*/
else {
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Get clipped fill area which is the real draw area.
*It is always the same or inside `fill_area`*/
bool is_common;
is_common = _lv_area_intersect(&draw_area, clip, &draw_area);
if(!is_common) return;
/*Now `draw_area` has absolute coordinates.
*Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= draw_buf->area.x1;
draw_area.y1 -= draw_buf->area.y1;
draw_area.x2 -= draw_buf->area.x1;
draw_area.y2 -= draw_buf->area.y1;
int32_t draw_area_w = lv_area_get_width(&draw_area);
lv_area_t fill_area;
fill_area.x1 = draw_area.x1 + disp_area->x1;
fill_area.x2 = draw_area.x2 + disp_area->x1;
fill_area.y1 = draw_area.y1 + disp_area->y1;
fill_area.y2 = fill_area.y1;
lv_opa_t * mask_buf = lv_mem_buf_get(draw_area_w);
lv_coord_t dash_start = 0;
if(dashed) {
dash_start = (draw_buf->area.y1 + draw_area.y1) % (dsc->dash_gap + dsc->dash_width);
}
lv_coord_t dash_cnt = dash_start;
int32_t h;
for(h = draw_area.y1; h <= draw_area.y2; h++) {
lv_memset_ff(mask_buf, draw_area_w);
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(mask_buf, draw_buf->area.x1 + draw_area.x1, draw_buf->area.y1 + h,
draw_area_w);
if(dashed) {
if(mask_res != LV_DRAW_MASK_RES_TRANSP) {
if(dash_cnt > dsc->dash_width) {
mask_res = LV_DRAW_MASK_RES_TRANSP;
}
if(dash_cnt >= dsc->dash_gap + dsc->dash_width) {
dash_cnt = 0;
}
}
dash_cnt ++;
}
_lv_blend_fill(clip, &fill_area,
dsc->color, mask_buf, mask_res, dsc->opa,
LV_BLEND_MODE_NORMAL);
fill_area.y1++;
fill_area.y2++;
}
lv_mem_buf_release(mask_buf);
}
#endif /*LV_DRAW_COMPLEX*/
}
LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc)
{
#if LV_DRAW_COMPLEX
/*Keep the great y in p1*/
lv_point_t p1;
lv_point_t p2;
if(point1->y < point2->y) {
p1.y = point1->y;
p2.y = point2->y;
p1.x = point1->x;
p2.x = point2->x;
}
else {
p1.y = point2->y;
p2.y = point1->y;
p1.x = point2->x;
p2.x = point1->x;
}
int32_t xdiff = p2.x - p1.x;
int32_t ydiff = p2.y - p1.y;
bool flat = LV_ABS(xdiff) > LV_ABS(ydiff) ? true : false;
static const uint8_t wcorr[] = {
128, 128, 128, 129, 129, 130, 130, 131,
132, 133, 134, 135, 137, 138, 140, 141,
143, 145, 147, 149, 151, 153, 155, 158,
160, 162, 165, 167, 170, 173, 175, 178,
181,
};
int32_t w = dsc->width;
int32_t wcorr_i = 0;
if(flat) wcorr_i = (LV_ABS(ydiff) << 5) / LV_ABS(xdiff);
else wcorr_i = (LV_ABS(xdiff) << 5) / LV_ABS(ydiff);
w = (w * wcorr[wcorr_i] + 63) >> 7; /*+ 63 for rounding*/
int32_t w_half0 = w >> 1;
int32_t w_half1 = w_half0 + (w & 0x1); /*Compensate rounding error*/
lv_area_t draw_area;
draw_area.x1 = LV_MIN(p1.x, p2.x) - w;
draw_area.x2 = LV_MAX(p1.x, p2.x) + w;
draw_area.y1 = LV_MIN(p1.y, p2.y) - w;
draw_area.y2 = LV_MAX(p1.y, p2.y) + w;
/*Get the union of `coords` and `clip`*/
/*`clip` is already truncated to the `draw_buf` size
*in 'lv_refr_area' function*/
bool is_common = _lv_area_intersect(&draw_area, &draw_area, clip);
if(is_common == false) return;
lv_draw_mask_line_param_t mask_left_param;
lv_draw_mask_line_param_t mask_right_param;
lv_draw_mask_line_param_t mask_top_param;
lv_draw_mask_line_param_t mask_bottom_param;
if(flat) {
if(xdiff > 0) {
lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
}
else {
lv_draw_mask_line_points_init(&mask_left_param, p1.x, p1.y + w_half1, p2.x, p2.y + w_half1,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_line_points_init(&mask_right_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
}
}
else {
lv_draw_mask_line_points_init(&mask_left_param, p1.x + w_half1, p1.y, p2.x + w_half1, p2.y,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_line_points_init(&mask_right_param, p1.x - w_half0, p1.y, p2.x - w_half0, p2.y,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
}
/*Use the normal vector for the endings*/
int16_t mask_left_id = lv_draw_mask_add(&mask_left_param, NULL);
int16_t mask_right_id = lv_draw_mask_add(&mask_right_param, NULL);
int16_t mask_top_id = LV_MASK_ID_INV;
int16_t mask_bottom_id = LV_MASK_ID_INV;
if(!dsc->raw_end) {
lv_draw_mask_line_points_init(&mask_top_param, p1.x, p1.y, p1.x - ydiff, p1.y + xdiff, LV_DRAW_MASK_LINE_SIDE_BOTTOM);
lv_draw_mask_line_points_init(&mask_bottom_param, p2.x, p2.y, p2.x - ydiff, p2.y + xdiff, LV_DRAW_MASK_LINE_SIDE_TOP);
mask_top_id = lv_draw_mask_add(&mask_top_param, NULL);
mask_bottom_id = lv_draw_mask_add(&mask_bottom_param, NULL);
}
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp);
const lv_area_t * disp_area = &draw_buf->area;
/*Store the coordinates of the `draw_a` relative to the draw_buf*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
/*The real draw area is around the line.
*It's easy to calculate with steep lines, but the area can be very wide with very flat lines.
*So deal with it only with steep lines.*/
int32_t draw_area_w = lv_area_get_width(&draw_area);
/*Draw the background line by line*/
int32_t h;
uint32_t hor_res = (uint32_t)lv_disp_get_hor_res(disp);
size_t mask_buf_size = LV_MIN(lv_area_get_size(&draw_area), hor_res);
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
lv_area_t fill_area;
fill_area.x1 = draw_area.x1 + disp_area->x1;
fill_area.x2 = draw_area.x2 + disp_area->x1;
fill_area.y1 = draw_area.y1 + disp_area->y1;
fill_area.y2 = fill_area.y1;
int32_t x = draw_buf->area.x1 + draw_area.x1;
uint32_t mask_p = 0;
lv_memset_ff(mask_buf, mask_buf_size);
/*Fill the first row with 'color'*/
for(h = draw_area.y1 + disp_area->y1; h <= draw_area.y2 + disp_area->y1; h++) {
lv_draw_mask_res_t mask_res = lv_draw_mask_apply(&mask_buf[mask_p], x, h, draw_area_w);
if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&mask_buf[mask_p], draw_area_w);
}
mask_p += draw_area_w;
if((uint32_t) mask_p + draw_area_w < mask_buf_size) {
fill_area.y2 ++;
}
else {
_lv_blend_fill(&fill_area, clip,
dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa,
dsc->blend_mode);
fill_area.y1 = fill_area.y2 + 1;
fill_area.y2 = fill_area.y1;
mask_p = 0;
lv_memset_ff(mask_buf, mask_buf_size);
}
}
/*Flush the last part*/
if(fill_area.y1 != fill_area.y2) {
fill_area.y2--;
_lv_blend_fill(&fill_area, clip,
dsc->color, mask_buf, LV_DRAW_MASK_RES_CHANGED, dsc->opa,
dsc->blend_mode);
}
lv_mem_buf_release(mask_buf);
lv_draw_mask_free_param(&mask_left_param);
lv_draw_mask_free_param(&mask_right_param);
if(mask_top_id != LV_MASK_ID_INV) lv_draw_mask_free_param(&mask_top_param);
if(mask_bottom_id != LV_MASK_ID_INV) lv_draw_mask_free_param(&mask_bottom_param);
lv_draw_mask_remove_id(mask_left_id);
lv_draw_mask_remove_id(mask_right_id);
lv_draw_mask_remove_id(mask_top_id);
lv_draw_mask_remove_id(mask_bottom_id);
#else
LV_UNUSED(point1);
LV_UNUSED(point2);
LV_UNUSED(clip);
LV_UNUSED(dsc);
LV_LOG_WARN("Can't draw skewed line with LV_DRAW_COMPLEX == 0");
#endif /*LV_DRAW_COMPLEX*/
}

View File

@ -0,0 +1,64 @@
/**
* @file lv_draw_line.h
*
*/
#ifndef LV_DRAW_LINE_H
#define LV_DRAW_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t color;
lv_coord_t width;
lv_coord_t dash_width;
lv_coord_t dash_gap;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t round_start : 1;
uint8_t round_end : 1;
uint8_t raw_end : 1; /*Do not bother with perpendicular line ending if it's not visible for any reason*/
} lv_draw_line_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
//! @cond Doxygen_Suppress
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param clip the line will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
LV_ATTRIBUTE_FAST_MEM void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * clip,
const lv_draw_line_dsc_t * dsc);
LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
//! @endcond
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LINE_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,384 @@
/**
* @file lv_draw_mask.h
*
*/
#ifndef LV_DRAW_MASK_H
#define LV_DRAW_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_MASK_ID_INV (-1)
#if LV_DRAW_COMPLEX
# define _LV_MASK_MAX_NUM 16
# ifndef _LV_MASK_BUF_MAX_SIZE
# define _LV_MASK_BUF_MAX_SIZE 2048 /*Should be >= than the max hor res*/
# endif
#else
# define _LV_MASK_MAX_NUM 1
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_DRAW_MASK_RES_TRANSP,
LV_DRAW_MASK_RES_FULL_COVER,
LV_DRAW_MASK_RES_CHANGED,
LV_DRAW_MASK_RES_UNKNOWN
};
typedef uint8_t lv_draw_mask_res_t;
typedef struct {
void * param;
void * custom_id;
} _lv_draw_mask_saved_t;
typedef _lv_draw_mask_saved_t _lv_draw_mask_saved_arr_t[_LV_MASK_MAX_NUM];
#if LV_DRAW_COMPLEX == 0
static inline uint8_t lv_draw_mask_get_cnt(void)
{
return 0;
}
static inline bool lv_draw_mask_is_any(const lv_area_t * a)
{
LV_UNUSED(a);
return false;
}
#endif
#if LV_DRAW_COMPLEX
enum {
LV_DRAW_MASK_TYPE_LINE,
LV_DRAW_MASK_TYPE_ANGLE,
LV_DRAW_MASK_TYPE_RADIUS,
LV_DRAW_MASK_TYPE_FADE,
LV_DRAW_MASK_TYPE_MAP,
};
typedef uint8_t lv_draw_mask_type_t;
enum {
LV_DRAW_MASK_LINE_SIDE_LEFT = 0,
LV_DRAW_MASK_LINE_SIDE_RIGHT,
LV_DRAW_MASK_LINE_SIDE_TOP,
LV_DRAW_MASK_LINE_SIDE_BOTTOM,
};
/**
* A common callback type for every mask type.
* Used internally by the library.
*/
typedef lv_draw_mask_res_t (*lv_draw_mask_xcb_t)(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len,
void * p);
typedef uint8_t lv_draw_mask_line_side_t;
typedef struct {
lv_draw_mask_xcb_t cb;
lv_draw_mask_type_t type;
} _lv_draw_mask_common_dsc_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
/*First point*/
lv_point_t p1;
/*Second point*/
lv_point_t p2;
/*Which side to keep?*/
lv_draw_mask_line_side_t side : 2;
} cfg;
/*A point of the line*/
lv_point_t origo;
/*X / (1024*Y) steepness (X is 0..1023 range). What is the change of X in 1024 Y?*/
int32_t xy_steep;
/*Y / (1024*X) steepness (Y is 0..1023 range). What is the change of Y in 1024 X?*/
int32_t yx_steep;
/*Helper which stores yx_steep for flat lines and xy_steep for steep (non flat) lines*/
int32_t steep;
/*Steepness in 1 px in 0..255 range. Used only by flat lines.*/
int32_t spx;
/*1: It's a flat line? (Near to horizontal)*/
uint8_t flat : 1;
/*Invert the mask. The default is: Keep the left part.
*It is used to select left/right/top/bottom*/
uint8_t inv: 1;
} lv_draw_mask_line_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_point_t vertex_p;
lv_coord_t start_angle;
lv_coord_t end_angle;
} cfg;
lv_draw_mask_line_param_t start_line;
lv_draw_mask_line_param_t end_line;
uint16_t delta_deg;
} lv_draw_mask_angle_param_t;
typedef struct {
uint8_t * buf;
lv_opa_t * cir_opa; /*Opacity of values on the circumference of an 1/4 circle*/
uint16_t * x_start_on_y; /*The x coordinate of the circle for each y value*/
uint16_t * opa_start_on_y; /*The index of `cir_opa` for each y value*/
int32_t life; /*How many times the entry way used*/
uint32_t used_cnt; /*Like a semaphore to count the referencing masks*/
lv_coord_t radius; /*The radius of the entry*/
} _lv_draw_mask_radius_circle_dsc_t;
typedef _lv_draw_mask_radius_circle_dsc_t _lv_draw_mask_radius_circle_dsc_arr_t[LV_CIRCLE_CACHE_SIZE];
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t rect;
lv_coord_t radius;
/*Invert the mask. 0: Keep the pixels inside.*/
uint8_t outer: 1;
} cfg;
_lv_draw_mask_radius_circle_dsc_t * circle;
} lv_draw_mask_radius_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
lv_coord_t y_top;
lv_coord_t y_bottom;
lv_opa_t opa_top;
lv_opa_t opa_bottom;
} cfg;
} lv_draw_mask_fade_param_t;
typedef struct _lv_draw_mask_map_param_t {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
const lv_opa_t * map;
} cfg;
} lv_draw_mask_map_param_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
* @param param an initialized mask parameter. Only the pointer is saved.
* @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
* @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
*/
int16_t lv_draw_mask_add(void * param, void * custom_id);
//! @cond Doxygen_Suppress
/**
* Apply the added buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len);
/**
* Apply the specified buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @param ids ID array of added buffers
* @param ids_count number of ID array
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
LV_ATTRIBUTE_FAST_MEM lv_draw_mask_res_t lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len, const int16_t *ids, int16_t ids_count);
//! @endcond
/**
* Remove a mask with a given ID
* @param id the ID of the mask. Returned by `lv_draw_mask_add`
* @return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_id(int16_t id);
/**
* Remove all mask with a given custom ID
* @param custom_id a pointer used in `lv_draw_mask_add`
* @return return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_custom(void * custom_id);
/**
* Free the data from the parameter.
* It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom`
* Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add`
* and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom`
* @param p pointer to a mask parameter
*/
void lv_draw_mask_free_param(void * p);
/**
* Called by LVGL the rendering of a screen is ready to clean up
* the temporal (cache) data of the masks
*/
void _lv_draw_mask_cleanup(void);
//! @cond Doxygen_Suppress
/**
* Count the currently added masks
* @return number of active masks
*/
LV_ATTRIBUTE_FAST_MEM uint8_t lv_draw_mask_get_cnt(void);
/**
* Check if there is any added draw mask
* @param a an area to test for affecting masks.
* @return true: there is t least 1 draw mask; false: there are no draw masks
*/
bool lv_draw_mask_is_any(const lv_area_t * a);
//! @endcond
/**
*Initialize a line mask from two points.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param p1x X coordinate of the first point of the line
* @param p1y Y coordinate of the first point of the line
* @param p2x X coordinate of the second point of the line
* @param p2y y coordinate of the second point of the line
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
lv_coord_t p2y, lv_draw_mask_line_side_t side);
/**
*Initialize a line mask from a point and an angle.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param px X coordinate of a point of the line
* @param py X coordinate of a point of the line
* @param angle right 0 deg, bottom: 90
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
lv_draw_mask_line_side_t side);
/**
* Initialize an angle mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param vertex_x X coordinate of the angle vertex (absolute coordinates)
* @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
* @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
* @param end_angle end angle
*/
void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
lv_coord_t start_angle, lv_coord_t end_angle);
/**
* Initialize a fade mask.
* @param param param pointer to a `lv_draw_mask_param_t` to initialize
* @param rect coordinates of the rectangle to affect (absolute coordinates)
* @param radius radius of the rectangle
* @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
*/
void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv);
/**
* Initialize a fade mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the area to affect (absolute coordinates)
* @param opa_top opacity on the top
* @param y_top at which coordinate start to change to opacity to `opa_bottom`
* @param opa_bottom opacity at the bottom
* @param y_bottom at which coordinate reach `opa_bottom`.
*/
void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
lv_coord_t y_top,
lv_opa_t opa_bottom, lv_coord_t y_bottom);
/**
* Initialize a map mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the map (absolute coordinates)
* @param map array of bytes with the mask values
*/
void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_MASK_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
/**
* @file lv_draw_rect.h
*
*/
#ifndef LV_DRAW_RECT_H
#define LV_DRAW_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
#include "../font/lv_font.h"
/*********************
* DEFINES
*********************/
#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/
LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE);
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_coord_t radius;
lv_blend_mode_t blend_mode;
/*Background*/
lv_color_t bg_color;
lv_color_t bg_grad_color;
uint8_t bg_main_color_stop;
uint8_t bg_grad_color_stop;
lv_opa_t bg_opa;
lv_grad_dir_t bg_grad_dir : 3;
/*Background img*/
const void * bg_img_src;
const void * bg_img_symbol_font;
lv_color_t bg_img_recolor;
lv_opa_t bg_img_opa;
lv_opa_t bg_img_recolor_opa;
uint8_t bg_img_tiled;
/*Border*/
lv_color_t border_color;
lv_coord_t border_width;
lv_opa_t border_opa;
uint8_t border_post : 1; /*There is a border it will be drawn later.*/
lv_border_side_t border_side : 5;
/*Outline*/
lv_color_t outline_color;
lv_coord_t outline_width;
lv_coord_t outline_pad;
lv_opa_t outline_opa;
/*Shadow*/
lv_color_t shadow_color;
lv_coord_t shadow_width;
lv_coord_t shadow_ofs_x;
lv_coord_t shadow_ofs_y;
lv_coord_t shadow_spread;
lv_opa_t shadow_opa;
} lv_draw_rect_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
//! @endcond
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param clip the rectangle will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc);
/**
* Draw a pixel
* @param point the coordinates of the point to draw
* @param mask the pixel will be drawn only in this mask
* @param style pointer to a style
*/
//void lv_draw_px(const lv_point_t * point, const lv_area_t * clip_area, const lv_style_t * style);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_RECT_H*/

View File

@ -0,0 +1,216 @@
/**
* @file lv_draw_triangle.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_triangle.h"
#include "../misc/lv_math.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Draw a triangle
* @param points pointer to an array with 3 points
* @param clip_area the triangle will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip_area, const lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
lv_draw_polygon(points, 3, clip_area, draw_dsc);
#else
LV_UNUSED(points);
LV_UNUSED(clip_area);
LV_UNUSED(draw_dsc);
LV_LOG_WARN("Can't draw triangle with LV_DRAW_COMPLEX == 0");
#endif /*LV_DRAW_COMPLEX*/
}
/**
* Draw a polygon. Only convex polygons are supported
* @param points an array of points
* @param point_cnt number of points
* @param clip_area polygon will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * clip_area,
const lv_draw_rect_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
if(point_cnt < 3) return;
if(points == NULL) return;
/*Join adjacent points if they are on the same coordinate*/
lv_point_t * p = lv_mem_buf_get(point_cnt * sizeof(lv_point_t));
if(p == NULL) return;
uint16_t i;
uint16_t pcnt = 0;
p[0] = points[0];
for(i = 0; i < point_cnt - 1; i++) {
if(points[i].x != points[i + 1].x || points[i].y != points[i + 1].y) {
p[pcnt] = points[i];
pcnt++;
}
}
/*The first and the last points are also adjacent*/
if(points[0].x != points[point_cnt - 1].x || points[0].y != points[point_cnt - 1].y) {
p[pcnt] = points[point_cnt - 1];
pcnt++;
}
point_cnt = pcnt;
if(point_cnt < 3) {
lv_mem_buf_release(p);
return;
}
lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
for(i = 0; i < point_cnt; i++) {
poly_coords.x1 = LV_MIN(poly_coords.x1, p[i].x);
poly_coords.y1 = LV_MIN(poly_coords.y1, p[i].y);
poly_coords.x2 = LV_MAX(poly_coords.x2, p[i].x);
poly_coords.y2 = LV_MAX(poly_coords.y2, p[i].y);
}
bool is_common;
lv_area_t poly_mask;
is_common = _lv_area_intersect(&poly_mask, &poly_coords, clip_area);
if(!is_common) {
lv_mem_buf_release(p);
return;
}
/*Find the lowest point*/
lv_coord_t y_min = p[0].y;
int16_t y_min_i = 0;
for(i = 1; i < point_cnt; i++) {
if(p[i].y < y_min) {
y_min = p[i].y;
y_min_i = i;
}
}
lv_draw_mask_line_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_line_param_t) * point_cnt);
lv_draw_mask_line_param_t * mp_next = mp;
int32_t i_prev_left = y_min_i;
int32_t i_prev_right = y_min_i;
int32_t i_next_left;
int32_t i_next_right;
uint32_t mask_cnt = 0;
/*Get the index of the left and right points*/
i_next_left = y_min_i - 1;
if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
i_next_right = y_min_i + 1;
if(i_next_right > point_cnt - 1) i_next_right = 0;
/**
* Check if the order of points is inverted or not.
* The normal case is when the left point is on `y_min_i - 1`
* Explanation:
* if angle(p_left) < angle(p_right) -> inverted
* dy_left/dx_left < dy_right/dx_right
* dy_left * dx_right < dy_right * dx_left
*/
lv_coord_t dxl = p[i_next_left].x - p[y_min_i].x;
lv_coord_t dxr = p[i_next_right].x - p[y_min_i].x;
lv_coord_t dyl = p[i_next_left].y - p[y_min_i].y;
lv_coord_t dyr = p[i_next_right].y - p[y_min_i].y;
bool inv = false;
if(dyl * dxr < dyr * dxl) inv = true;
do {
if(!inv) {
i_next_left = i_prev_left - 1;
if(i_next_left < 0) i_next_left = point_cnt + i_next_left;
i_next_right = i_prev_right + 1;
if(i_next_right > point_cnt - 1) i_next_right = 0;
}
else {
i_next_left = i_prev_left + 1;
if(i_next_left > point_cnt - 1) i_next_left = 0;
i_next_right = i_prev_right - 1;
if(i_next_right < 0) i_next_right = point_cnt + i_next_right;
}
if(p[i_next_left].y >= p[i_prev_left].y) {
if(p[i_next_left].y != p[i_prev_left].y &&
p[i_next_left].x != p[i_prev_left].x) {
lv_draw_mask_line_points_init(mp_next, p[i_prev_left].x, p[i_prev_left].y,
p[i_next_left].x, p[i_next_left].y,
LV_DRAW_MASK_LINE_SIDE_RIGHT);
lv_draw_mask_add(mp_next, mp);
mp_next++;
}
mask_cnt++;
i_prev_left = i_next_left;
}
if(mask_cnt == point_cnt) break;
if(p[i_next_right].y >= p[i_prev_right].y) {
if(p[i_next_right].y != p[i_prev_right].y &&
p[i_next_right].x != p[i_prev_right].x) {
lv_draw_mask_line_points_init(mp_next, p[i_prev_right].x, p[i_prev_right].y,
p[i_next_right].x, p[i_next_right].y,
LV_DRAW_MASK_LINE_SIDE_LEFT);
lv_draw_mask_add(mp_next, mp);
mp_next++;
}
mask_cnt++;
i_prev_right = i_next_right;
}
} while(mask_cnt < point_cnt);
lv_draw_rect(&poly_coords, clip_area, draw_dsc);
lv_draw_mask_remove_custom(mp);
lv_mem_buf_release(mp);
lv_mem_buf_release(p);
#else
LV_UNUSED(points);
LV_UNUSED(point_cnt);
LV_UNUSED(clip_area);
LV_UNUSED(draw_dsc);
LV_LOG_WARN("Can't draw polygon with LV_DRAW_COMPLEX == 0");
#endif /*LV_DRAW_COMPLEX*/
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,56 @@
/**
* @file lv_draw_triangle.h
*
*/
#ifndef LV_DRAW_TRIANGLE_H
#define LV_DRAW_TRIANGLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw a triangle
* @param points pointer to an array with 3 points
* @param clip_area the triangle will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_triangle(const lv_point_t points[], const lv_area_t * clip, const lv_draw_rect_dsc_t * draw_dsc);
/**
* Draw a polygon. Only convex polygons are supported.
* @param points an array of points
* @param point_cnt number of points
* @param clip_area polygon will be drawn only in this area
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_polygon(const lv_point_t points[], uint16_t point_cnt, const lv_area_t * mask,
const lv_draw_rect_dsc_t * draw_dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRIANGLE_H*/

View File

@ -0,0 +1,772 @@
/**
* @file lv_img_buf.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <string.h>
#include "lv_img_buf.h"
#include "lv_draw_img.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color)
{
lv_color_t p_color = lv_color_black();
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
#if LV_COLOR_SIZE == 32
p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
#endif
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += 4 * 2;
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8, 16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += 4 * 4;
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += 4 * 16;
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2, 4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += 4 * 256;
uint32_t px = dsc->header.w * y + x;
p_color.full = buf_u8[px];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
p_color = color;
}
return p_color;
}
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
const uint8_t opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
return buf_u8[px];
}
return LV_OPA_COVER;
}
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
buf_u8[px + px_size - 1] = opa;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
opa = opa >> 7; /*opa -> [0,1]*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
opa = opa >> 6; /*opa -> [0,3]*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
opa = opa >> 4; /*opa -> [0,15]*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = opa;
}
}
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size);
}
else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8 ,12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = c.full;
}
}
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
{
if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
return;
}
lv_color32_t c32;
c32.full = lv_color_to32(c);
uint8_t * buf = (uint8_t *)dsc->data;
lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
}
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
/*Allocate image descriptor*/
lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
if(dsc == NULL)
return NULL;
lv_memset_00(dsc, sizeof(lv_img_dsc_t));
/*Get image data size*/
dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
if(dsc->data_size == 0) {
lv_mem_free(dsc);
return NULL;
}
/*Allocate raw buffer*/
dsc->data = lv_mem_alloc(dsc->data_size);
if(dsc->data == NULL) {
lv_mem_free(dsc);
return NULL;
}
lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
/*Fill in header*/
dsc->header.always_zero = 0;
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
return dsc;
}
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc)
{
if(dsc != NULL) {
if(dsc->data != NULL)
lv_mem_free((void *)dsc->data);
lv_mem_free(dsc);
}
}
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
switch(cf) {
case LV_IMG_CF_TRUE_COLOR:
return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
case LV_IMG_CF_TRUE_COLOR_ALPHA:
return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
case LV_IMG_CF_ALPHA_1BIT:
return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
case LV_IMG_CF_ALPHA_2BIT:
return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
case LV_IMG_CF_ALPHA_4BIT:
return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
case LV_IMG_CF_ALPHA_8BIT:
return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
case LV_IMG_CF_INDEXED_1BIT:
return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
case LV_IMG_CF_INDEXED_2BIT:
return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
case LV_IMG_CF_INDEXED_4BIT:
return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
case LV_IMG_CF_INDEXED_8BIT:
return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
default:
return 0;
}
}
#if LV_DRAW_COMPLEX
/**
* Initialize a descriptor to transform an image
* @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized
*/
void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc)
{
dsc->tmp.pivot_x_256 = dsc->cfg.pivot_x * 256;
dsc->tmp.pivot_y_256 = dsc->cfg.pivot_y * 256;
int32_t angle_low = dsc->cfg.angle / 10;
int32_t angle_high = angle_low + 1;
int32_t angle_rem = dsc->cfg.angle - (angle_low * 10);
int32_t s1 = lv_trigo_sin(-angle_low);
int32_t s2 = lv_trigo_sin(-angle_high);
int32_t c1 = lv_trigo_sin(-angle_low + 90);
int32_t c2 = lv_trigo_sin(-angle_high + 90);
dsc->tmp.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
dsc->tmp.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
/*Use smaller value to avoid overflow*/
dsc->tmp.sinma = dsc->tmp.sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
dsc->tmp.cosma = dsc->tmp.cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
dsc->tmp.chroma_keyed = lv_img_cf_is_chroma_keyed(dsc->cfg.cf) ? 1 : 0;
dsc->tmp.has_alpha = lv_img_cf_has_alpha(dsc->cfg.cf) ? 1 : 0;
if(dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR || dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
dsc->tmp.native_color = 1;
}
else {
dsc->tmp.native_color = 0;
}
dsc->tmp.img_dsc.data = dsc->cfg.src;
dsc->tmp.img_dsc.header.always_zero = 0;
dsc->tmp.img_dsc.header.cf = dsc->cfg.cf;
dsc->tmp.img_dsc.header.w = dsc->cfg.src_w;
dsc->tmp.img_dsc.header.h = dsc->cfg.src_h;
/*The inverse of the zoom will be sued during the transformation
* + dsc->cfg.zoom / 2 for rounding*/
dsc->tmp.zoom_inv = (((256 * 256) << _LV_ZOOM_INV_UPSCALE) + dsc->cfg.zoom / 2) / dsc->cfg.zoom;
dsc->res.opa = LV_OPA_COVER;
dsc->res.color = dsc->cfg.color;
}
#endif
/**
* Get the area of a rectangle if its rotated and scaled
* @param res store the coordinates here
* @param w width of the rectangle to transform
* @param h height of the rectangle to transform
* @param angle angle of rotation
* @param zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot)
{
#if LV_DRAW_COMPLEX
if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
return;
}
res->x1 = (((int32_t)(-pivot->x) * zoom) >> 8) - 1;
res->y1 = (((int32_t)(-pivot->y) * zoom) >> 8) - 1;
res->x2 = (((int32_t)(w - pivot->x) * zoom) >> 8) + 2;
res->y2 = (((int32_t)(h - pivot->y) * zoom) >> 8) + 2;
if(angle == 0) {
res->x1 += pivot->x;
res->y1 += pivot->y;
res->x2 += pivot->x;
res->y2 += pivot->y;
return;
}
int32_t angle_low = angle / 10;
int32_t angle_high = angle_low + 1;
int32_t angle_rem = angle - (angle_low * 10);
int32_t s1 = lv_trigo_sin(angle_low);
int32_t s2 = lv_trigo_sin(angle_high);
int32_t c1 = lv_trigo_sin(angle_low + 90);
int32_t c2 = lv_trigo_sin(angle_high + 90);
int32_t sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10;
int32_t cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10;
/*Use smaller value to avoid overflow*/
sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT);
lv_point_t lt;
lv_point_t rt;
lv_point_t lb;
lv_point_t rb;
lv_coord_t xt;
lv_coord_t yt;
xt = res->x1;
yt = res->y1;
lt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
lt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x2;
yt = res->y1;
rt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
rt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x1;
yt = res->y2;
lb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
lb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
xt = res->x2;
yt = res->y2;
rb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x;
rb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y;
res->x1 = LV_MIN4(lb.x, lt.x, rb.x, rt.x);
res->x2 = LV_MAX4(lb.x, lt.x, rb.x, rt.x);
res->y1 = LV_MIN4(lb.y, lt.y, rb.y, rt.y);
res->y2 = LV_MAX4(lb.y, lt.y, rb.y, rt.y);
#else
LV_UNUSED(angle);
LV_UNUSED(zoom);
LV_UNUSED(pivot);
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
#endif
}
#if LV_DRAW_COMPLEX
/**
* Get which color and opa would come to a pixel if it were rotated
* @param dsc a descriptor initialized by `lv_img_buf_rotate_init`
* @param x the coordinate which color and opa should be get
* @param y the coordinate which color and opa should be get
* @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image
* @note the result is written back to `dsc->res_color` and `dsc->res_opa`
*/
bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
const uint8_t * src_u8 = (const uint8_t *)dsc->cfg.src;
/*Get the target point relative coordinates to the pivot*/
int32_t xt = x - dsc->cfg.pivot_x;
int32_t yt = y - dsc->cfg.pivot_y;
int32_t xs;
int32_t ys;
if(dsc->cfg.zoom == LV_IMG_ZOOM_NONE) {
/*Get the source pixel from the upscaled image*/
xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_x_256;
ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_y_256;
}
else if(dsc->cfg.angle == 0) {
xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
xs = xt + dsc->tmp.pivot_x_256;
ys = yt + dsc->tmp.pivot_y_256;
}
else {
xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE;
xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_x_256;
ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_y_256;
}
/*Get the integer part of the source pixel*/
int32_t xs_int = xs >> 8;
int32_t ys_int = ys >> 8;
if(xs_int >= dsc->cfg.src_w) return false;
else if(xs_int < 0) return false;
if(ys_int >= dsc->cfg.src_h) return false;
else if(ys_int < 0) return false;
uint8_t px_size;
uint32_t pxi;
if(dsc->tmp.native_color) {
if(dsc->tmp.has_alpha == 0) {
px_size = LV_COLOR_SIZE >> 3;
pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size;
lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size);
}
else {
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE;
pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size;
lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size - 1);
dsc->res.opa = src_u8[pxi + px_size - 1];
}
}
else {
pxi = 0; /*unused*/
px_size = 0; /*unused*/
dsc->res.color = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, xs_int, ys_int, dsc->cfg.color);
dsc->res.opa = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, xs_int, ys_int);
}
if(dsc->tmp.chroma_keyed) {
lv_color_t ct = LV_COLOR_CHROMA_KEY;
if(dsc->res.color.full == ct.full) return false;
}
if(dsc->cfg.antialias == false) return true;
dsc->tmp.xs = xs;
dsc->tmp.ys = ys;
dsc->tmp.xs_int = xs_int;
dsc->tmp.ys_int = ys_int;
dsc->tmp.pxi = pxi;
dsc->tmp.px_size = px_size;
bool ret;
ret = _lv_img_buf_transform_anti_alias(dsc);
return ret;
}
/**
* Continue transformation by taking the neighbors into account
* @param dsc pointer to the transformation descriptor
*/
bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc)
{
const uint8_t * src_u8 = dsc->cfg.src;
/*Get the fractional part of the source pixel*/
int xs_fract = dsc->tmp.xs & 0xff;
int ys_fract = dsc->tmp.ys & 0xff;
int32_t xn; /*x neighbor*/
lv_opa_t xr; /*x mix ratio*/
if(xs_fract < 0x70) {
xn = - 1;
if(dsc->tmp.xs_int + xn < 0) xn = 0;
xr = xs_fract + 0x80;
}
else if(xs_fract > 0x90) {
xn = 1;
if(dsc->tmp.xs_int + xn >= dsc->cfg.src_w) xn = 0;
xr = (0xFF - xs_fract) + 0x80;
}
else {
xn = 0;
xr = 0xFF;
}
int32_t yn; /*x neighbor*/
lv_opa_t yr; /*x mix ratio*/
if(ys_fract < 0x70) {
yn = - 1;
if(dsc->tmp.ys_int + yn < 0) yn = 0;
yr = ys_fract + 0x80;
}
else if(ys_fract > 0x90) {
yn = 1;
if(dsc->tmp.ys_int + yn >= dsc->cfg.src_h) yn = 0;
yr = (0xFF - ys_fract) + 0x80;
}
else {
yn = 0;
yr = 0xFF;
}
lv_color_t c00 = dsc->res.color;
lv_color_t c01;
lv_color_t c10;
lv_color_t c11;
lv_opa_t a00 = dsc->res.opa;
lv_opa_t a10 = 0;
lv_opa_t a01 = 0;
lv_opa_t a11 = 0;
if(dsc->tmp.native_color) {
lv_memcpy_small(&c01, &src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn], sizeof(lv_color_t));
lv_memcpy_small(&c10, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn], sizeof(lv_color_t));
lv_memcpy_small(&c11, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn],
sizeof(lv_color_t));
if(dsc->tmp.has_alpha) {
a10 = src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1];
a01 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size - 1];
a11 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1];
}
}
else {
c01 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int, dsc->cfg.color);
c10 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn, dsc->cfg.color);
c11 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn, dsc->cfg.color);
if(dsc->tmp.has_alpha) {
a10 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int);
a01 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn);
a11 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn);
}
}
lv_opa_t xr0 = xr;
lv_opa_t xr1 = xr;
if(dsc->tmp.has_alpha) {
lv_opa_t a0 = (a00 * xr + (a10 * (255 - xr))) >> 8;
lv_opa_t a1 = (a01 * xr + (a11 * (255 - xr))) >> 8;
dsc->res.opa = (a0 * yr + (a1 * (255 - yr))) >> 8;
if(a0 <= LV_OPA_MIN && a1 <= LV_OPA_MIN) return false;
if(a0 <= LV_OPA_MIN) yr = LV_OPA_TRANSP;
if(a1 <= LV_OPA_MIN) yr = LV_OPA_COVER;
if(a00 <= LV_OPA_MIN) xr0 = LV_OPA_TRANSP;
if(a10 <= LV_OPA_MIN) xr0 = LV_OPA_COVER;
if(a01 <= LV_OPA_MIN) xr1 = LV_OPA_TRANSP;
if(a11 <= LV_OPA_MIN) xr1 = LV_OPA_COVER;
}
else {
xr0 = xr;
xr1 = xr;
dsc->res.opa = LV_OPA_COVER;
}
lv_color_t c0;
if(xr0 == LV_OPA_TRANSP) c0 = c01;
else if(xr0 == LV_OPA_COVER) c0 = c00;
else c0 = lv_color_mix(c00, c01, xr0);
lv_color_t c1;
if(xr1 == LV_OPA_TRANSP) c1 = c11;
else if(xr1 == LV_OPA_COVER) c1 = c10;
else c1 = lv_color_mix(c10, c11, xr1);
if(yr == LV_OPA_TRANSP) dsc->res.color = c1;
else if(yr == LV_OPA_COVER) dsc->res.color = c0;
else dsc->res.color = lv_color_mix(c0, c1, yr);
return true;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/

View File

@ -0,0 +1,310 @@
/**
* @file lv_img_buf.h
*
*/
#ifndef LV_IMG_BUF_H
#define LV_IMG_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/*If image pixels contains alpha we need to know how much byte is a pixel*/
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
#define LV_IMG_PX_SIZE_ALPHA_BYTE 2
#elif LV_COLOR_DEPTH == 16
#define LV_IMG_PX_SIZE_ALPHA_BYTE 3
#elif LV_COLOR_DEPTH == 32
#define LV_IMG_PX_SIZE_ALPHA_BYTE 4
#endif
#define LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h)
/*+ 1: to be sure no fractional row*/
#define LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h))
/*4 * X: for palette*/
#define LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2)
#define LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4)
#define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16)
#define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256)
#define _LV_TRANSFORM_TRIGO_SHIFT 10
#define _LV_ZOOM_INV_UPSCALE 5
/**********************
* TYPEDEFS
**********************/
/*Image color format*/
enum {
LV_IMG_CF_UNKNOWN = 0,
LV_IMG_CF_RAW, /**< Contains the file as it is. Needs custom decoder function*/
LV_IMG_CF_RAW_ALPHA, /**< Contains the file as it is. The image has alpha. Needs custom decoder
function*/
LV_IMG_CF_RAW_CHROMA_KEYED, /**< Contains the file as it is. The image is chroma keyed. Needs
custom decoder function*/
LV_IMG_CF_TRUE_COLOR, /**< Color format and depth should match with LV_COLOR settings*/
LV_IMG_CF_TRUE_COLOR_ALPHA, /**< Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /**< Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels
will be transparent*/
LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_ALPHA_1BIT, /**< Can have one color and it can be drawn or not*/
LV_IMG_CF_ALPHA_2BIT, /**< Can have one color but 4 different alpha value*/
LV_IMG_CF_ALPHA_4BIT, /**< Can have one color but 16 different alpha value*/
LV_IMG_CF_ALPHA_8BIT, /**< Can have one color but 256 different alpha value*/
LV_IMG_CF_RESERVED_15, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_16, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_17, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_18, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_19, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_20, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_21, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_22, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_23, /**< Reserved for further use.*/
LV_IMG_CF_USER_ENCODED_0, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_1, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_2, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_3, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_4, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_5, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_6, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_7, /**< User holder encoding format.*/
};
typedef uint8_t lv_img_cf_t;
/**
* The first 8 bit is very important to distinguish the different source types.
* For more info see `lv_img_get_src_type()` in lv_img.c
* On big endian systems the order is reversed so cf and always_zero must be at
* the end of the struct.
*/
#if LV_BIG_ENDIAN_SYSTEM
typedef struct {
uint32_t h : 11; /*Height of the image map*/
uint32_t w : 11; /*Width of the image map*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
} lv_img_header_t;
#else
typedef struct {
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t w : 11; /*Width of the image map*/
uint32_t h : 11; /*Height of the image map*/
} lv_img_header_t;
#endif
/** Image header it is compatible with
* the result from image converter utility*/
typedef struct {
lv_img_header_t header; /**< A header describing the basics of the image*/
uint32_t data_size; /**< Size of the image in bytes*/
const uint8_t * data; /**< Pointer to the data of the image*/
} lv_img_dsc_t;
typedef struct {
struct {
const void * src; /*image source (array of pixels)*/
lv_coord_t src_w; /*width of the image source*/
lv_coord_t src_h; /*height of the image source*/
lv_coord_t pivot_x; /*pivot x*/
lv_coord_t pivot_y; /*pivot y*/
int16_t angle; /*angle to rotate*/
uint16_t zoom; /*256 no zoom, 128 half size, 512 double size*/
lv_color_t color; /*a color used for `LV_IMG_CF_INDEXED_1/2/4/8BIT` color formats*/
lv_img_cf_t cf; /*color format of the image to rotate*/
bool antialias;
} cfg;
struct {
lv_color_t color;
lv_opa_t opa;
} res;
struct {
lv_img_dsc_t img_dsc;
int32_t pivot_x_256;
int32_t pivot_y_256;
int32_t sinma;
int32_t cosma;
uint8_t chroma_keyed : 1;
uint8_t has_alpha : 1;
uint8_t native_color : 1;
uint32_t zoom_inv;
/*Runtime data*/
lv_coord_t xs;
lv_coord_t ys;
lv_coord_t xs_int;
lv_coord_t ys_int;
uint32_t pxi;
uint8_t px_size;
} tmp;
} lv_img_transform_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color);
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c);
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa);
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c);
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc);
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
#if LV_DRAW_COMPLEX
/**
* Initialize a descriptor to rotate an image
* @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized
*/
void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc);
/**
* Continue transformation by taking the neighbors into account
* @param dsc pointer to the transformation descriptor
*/
bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc);
/**
* Get which color and opa would come to a pixel if it were rotated
* @param dsc a descriptor initialized by `lv_img_buf_rotate_init`
* @param x the coordinate which color and opa should be get
* @param y the coordinate which color and opa should be get
* @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image
* @note the result is written back to `dsc->res_color` and `dsc->res_opa`
*/
bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
#endif
/**
* Get the area of a rectangle if its rotated and scaled
* @param res store the coordinates here
* @param w width of the rectangle to transform
* @param h height of the rectangle to transform
* @param angle angle of rotation
* @param zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_BUF_H*/

View File

@ -0,0 +1,215 @@
/**
* @file lv_img_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_assert.h"
#include "lv_img_cache.h"
#include "lv_img_decoder.h"
#include "lv_draw_img.h"
#include "../hal/lv_hal_tick.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/*Decrement life with this value on every open*/
#define LV_IMG_CACHE_AGING 1
/*Boost life by this factor (multiply time_to_open with this value)*/
#define LV_IMG_CACHE_LIFE_GAIN 1
/*Don't let life to be greater than this limit because it would require a lot of time to
* "die" from very high values*/
#define LV_IMG_CACHE_LIFE_LIMIT 1000
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static uint16_t entry_cnt;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color color The color of the image with `LV_IMG_CF_ALPHA_...`
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
{
/*Is the image cached?*/
_lv_img_cache_entry_t * cached_src = NULL;
#if LV_IMG_CACHE_DEF_SIZE
if(entry_cnt == 0) {
LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
return NULL;
}
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
/*Decrement all lifes. Make the entries older*/
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
cache[i].life -= LV_IMG_CACHE_AGING;
}
}
for(i = 0; i < entry_cnt; i++) {
if(color.full == cache[i].dec_dsc.color.full &&
frame_id == cache[i].dec_dsc.frame_id &&
lv_img_cache_match(src, cache[i].dec_dsc.src)) {
/*If opened increment its life.
*Image difficult to open should live longer to keep avoid frequent their recaching.
*Therefore increase `life` with `time_to_open`*/
cached_src = &cache[i];
cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;
if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
LV_LOG_TRACE("image source found in the cache");
break;
}
}
/*The image is not cached then cache it now*/
if(cached_src) return cached_src;
/*Find an entry to reuse. Select the entry with the least life*/
cached_src = &cache[0];
for(i = 1; i < entry_cnt; i++) {
if(cache[i].life < cached_src->life) {
cached_src = &cache[i];
}
}
/*Close the decoder to reuse if it was opened (has a valid source)*/
if(cached_src->dec_dsc.src) {
lv_img_decoder_close(&cached_src->dec_dsc);
LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
}
else {
LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
}
#else
cached_src = &LV_GC_ROOT(_lv_img_cache_single);
#endif
/*Open the image and measure the time to open*/
uint32_t t_start = lv_tick_get();
lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color, frame_id);
if(open_res == LV_RES_INV) {
LV_LOG_WARN("Image draw cannot open the image resource");
lv_memset_00(cached_src, sizeof(_lv_img_cache_entry_t));
cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its us*/
return NULL;
}
cached_src->life = 0;
/*If `time_to_open` was not set in the open function set it here*/
if(cached_src->dec_dsc.time_to_open == 0) {
cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
}
if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
return cached_src;
}
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_entry_cnt)
{
#if LV_IMG_CACHE_DEF_SIZE == 0
LV_UNUSED(new_entry_cnt);
LV_LOG_WARN("Can't change cache size because it's disabled by LV_IMG_CACHE_DEF_SIZE = 0");
#else
if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
/*Clean the cache before free it*/
lv_img_cache_invalidate_src(NULL);
lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
}
/*Reallocate the cache*/
LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(_lv_img_cache_entry_t) * new_entry_cnt);
LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_img_cache_array));
if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
entry_cnt = 0;
return;
}
entry_cnt = new_entry_cnt;
/*Clean the cache*/
lv_memset_00(LV_GC_ROOT(_lv_img_cache_array), entry_cnt * sizeof(_lv_img_cache_entry_t));
#endif
}
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src)
{
LV_UNUSED(src);
#if LV_IMG_CACHE_DEF_SIZE
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(src == NULL || lv_img_cache_match(src, cache[i].dec_dsc.src)) {
if(cache[i].dec_dsc.src != NULL) {
lv_img_decoder_close(&cache[i].dec_dsc);
}
lv_memset_00(&cache[i], sizeof(_lv_img_cache_entry_t));
}
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2)
{
lv_img_src_t src_type = lv_img_src_get_type(src1);
if(src_type == LV_IMG_SRC_VARIABLE)
return src1 == src2;
if(src_type != LV_IMG_SRC_FILE)
return false;
if(lv_img_src_get_type(src2) != LV_IMG_SRC_FILE)
return false;
return strcmp(src1, src2) == 0;
}
#endif

View File

@ -0,0 +1,78 @@
/**
* @file lv_img_cache.h
*
*/
#ifndef LV_IMG_CACHE_H
#define LV_IMG_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* When loading images from the network it can take a long time to download and decode the image.
*
* To avoid repeating this heavy load images can be cached.
*/
typedef struct {
lv_img_decoder_dsc_t dec_dsc; /**< Image information*/
/** Count the cache entries's life. Add `time_to_open` to `life` when the entry is used.
* Decrement all lifes by one every in every ::lv_img_cache_open.
* If life == 0 the entry can be reused*/
int32_t life;
} _lv_img_cache_entry_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id);
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_slot_num);
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_CACHE_H*/

View File

@ -0,0 +1,704 @@
/**
* @file lv_img_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw_img.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR
#define CF_BUILT_IN_LAST LV_IMG_CF_ALPHA_8BIT
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t f;
lv_color_t * palette;
lv_opa_t * opa;
} lv_img_decoder_built_in_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_img_decoder_ll), sizeof(lv_img_decoder_t));
lv_img_decoder_t * decoder;
/*Create a decoder for the built in color format*/
decoder = lv_img_decoder_create();
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) {
LV_LOG_WARN("lv_img_decoder_init: out of memory");
return;
}
lv_img_decoder_set_info_cb(decoder, lv_img_decoder_built_in_info);
lv_img_decoder_set_open_cb(decoder, lv_img_decoder_built_in_open);
lv_img_decoder_set_read_line_cb(decoder, lv_img_decoder_built_in_read_line);
lv_img_decoder_set_close_cb(decoder, lv_img_decoder_built_in_close);
}
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. E.g. file name or variable.
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
{
lv_memset_00(header, sizeof(lv_img_header_t));
if(src == NULL) return LV_RES_INV;
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return LV_RES_INV;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) {
if(d->info_cb) {
res = d->info_cb(d, src, header);
if(res == LV_RES_OK) break;
}
}
return res;
}
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
{
lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));
if(src == NULL) return LV_RES_INV;
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return LV_RES_INV;
}
dsc->color = color;
dsc->src_type = src_type;
dsc->frame_id = frame_id;
if(dsc->src_type == LV_IMG_SRC_FILE) {
size_t fnlen = strlen(src);
dsc->src = lv_mem_alloc(fnlen + 1);
LV_ASSERT_MALLOC(dsc->src);
if(dsc->src == NULL) {
LV_LOG_WARN("lv_img_decoder_open: out of memory");
return LV_RES_INV;
}
strcpy((char *)dsc->src, src);
}
else {
dsc->src = src;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * decoder;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) {
/*Info and Open callbacks are required*/
if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;
res = decoder->info_cb(decoder, src, &dsc->header);
if(res != LV_RES_OK) continue;
dsc->decoder = decoder;
res = decoder->open_cb(decoder, dsc);
/*Opened successfully. It is a good decoder to for this image source*/
if(res == LV_RES_OK) return res;
/*Prepare for the next loop*/
lv_memset_00(&dsc->header, sizeof(lv_img_header_t));
dsc->error_msg = NULL;
dsc->img_data = NULL;
dsc->user_data = NULL;
dsc->time_to_open = 0;
}
if(dsc->src_type == LV_IMG_SRC_FILE)
lv_mem_free((void *)dsc->src);
return res;
}
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
lv_res_t res = LV_RES_INV;
if(dsc->decoder->read_line_cb) res = dsc->decoder->read_line_cb(dsc->decoder, dsc, x, y, len, buf);
return res;
}
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc)
{
if(dsc->decoder) {
if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_mem_free((void *)dsc->src);
dsc->src = NULL;
}
}
}
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void)
{
lv_img_decoder_t * decoder;
decoder = _lv_ll_ins_head(&LV_GC_ROOT(_lv_img_decoder_ll));
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) return NULL;
lv_memset_00(decoder, sizeof(lv_img_decoder_t));
return decoder;
}
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder)
{
_lv_ll_remove(&LV_GC_ROOT(_lv_img_decoder_ll), decoder);
lv_mem_free(decoder);
}
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb)
{
decoder->info_cb = info_cb;
}
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb)
{
decoder->open_cb = open_cb;
}
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb)
{
decoder->read_line_cb = read_line_cb;
}
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb)
{
decoder->close_cb = close_cb;
}
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
lv_img_cf_t cf = ((lv_img_dsc_t *)src)->header.cf;
if(cf < CF_BUILT_IN_FIRST || cf > CF_BUILT_IN_LAST) return LV_RES_INV;
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
}
else if(src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
if(res == LV_FS_RES_OK) {
uint32_t rn;
res = lv_fs_read(&f, header, sizeof(lv_img_header_t), &rn);
lv_fs_close(&f);
if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
LV_LOG_WARN("Image get info get read file header");
return LV_RES_INV;
}
}
if(header->cf < CF_BUILT_IN_FIRST || header->cf > CF_BUILT_IN_LAST) return LV_RES_INV;
}
else if(src_type == LV_IMG_SRC_SYMBOL) {
/*The size depend on the font but it is unknown here. It should be handled outside of the
*function*/
header->w = 1;
header->h = 1;
/*Symbols always have transparent parts. Important because of cover check in the draw
*function. The actual value doesn't matter because lv_draw_label will draw it*/
header->cf = LV_IMG_CF_ALPHA_1BIT;
}
else {
LV_LOG_WARN("Image get info found unknown src type");
return LV_RES_INV;
}
return LV_RES_OK;
}
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
/*Open the file if it's a file*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder can't open the file");
return LV_RES_INV;
}
/*If the file was open successfully save the file descriptor*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_fs_close(&f);
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_memcpy_small(&user_data->f, &f, sizeof(f));
}
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*The variables should have valid data*/
if(((lv_img_dsc_t *)dsc->src)->data == NULL) {
return LV_RES_INV;
}
}
lv_img_cf_t cf = dsc->header.cf;
/*Process true color formats*/
if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
return LV_RES_OK;
}
else {
/*If it's a file it need to be read line by line later*/
return LV_RES_OK;
}
}
/*Process indexed images. Build a palette*/
else if(cf == LV_IMG_CF_INDEXED_1BIT || cf == LV_IMG_CF_INDEXED_2BIT || cf == LV_IMG_CF_INDEXED_4BIT ||
cf == LV_IMG_CF_INDEXED_8BIT) {
uint8_t px_size = lv_img_cf_get_px_size(cf);
uint32_t palette_size = 1 << px_size;
/*Allocate the palette*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
user_data->palette = lv_mem_alloc(palette_size * sizeof(lv_color_t));
LV_ASSERT_MALLOC(user_data->palette);
user_data->opa = lv_mem_alloc(palette_size * sizeof(lv_opa_t));
LV_ASSERT_MALLOC(user_data->opa);
if(user_data->palette == NULL || user_data->opa == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_img_decoder_built_in_close(decoder, dsc);
return LV_RES_INV;
}
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Read the palette from file*/
lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*Skip the header*/
lv_color32_t cur_color;
uint32_t i;
for(i = 0; i < palette_size; i++) {
lv_fs_read(&user_data->f, &cur_color, sizeof(lv_color32_t), NULL);
user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue);
user_data->opa[i] = cur_color.ch.alpha;
}
}
else {
/*The palette begins in the beginning of the image data. Just point to it.*/
lv_color32_t * palette_p = (lv_color32_t *)((lv_img_dsc_t *)dsc->src)->data;
uint32_t i;
for(i = 0; i < palette_size; i++) {
user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
user_data->opa[i] = palette_p[i].ch.alpha;
}
}
return LV_RES_OK;
}
/*Alpha indexed images.*/
else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT ||
cf == LV_IMG_CF_ALPHA_8BIT) {
return LV_RES_OK; /*Nothing to process*/
}
/*Unknown format. Can't decode it.*/
else {
/*Free the potentially allocated memories*/
lv_img_decoder_built_in_close(decoder, dsc);
LV_LOG_WARN("Image decoder open: unknown color format");
return LV_RES_INV;
}
}
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
(void)decoder; /*Unused*/
lv_res_t res = LV_RES_INV;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
/*For TRUE_COLOR images read line required only for files.
*For variables the image data was returned in `open`*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
res = lv_img_decoder_built_in_line_true_color(dsc, x, y, len, buf);
}
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
res = lv_img_decoder_built_in_line_alpha(dsc, x, y, len, buf);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT || dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
dsc->header.cf == LV_IMG_CF_INDEXED_4BIT || dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
res = lv_img_decoder_built_in_line_indexed(dsc, x, y, len, buf);
}
else {
LV_LOG_WARN("Built-in image decoder read not supports the color format");
return LV_RES_INV;
}
return res;
}
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
(void)decoder; /*Unused*/
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
if(user_data) {
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_fs_close(&user_data->f);
}
if(user_data->palette) lv_mem_free(user_data->palette);
if(user_data->opa) lv_mem_free(user_data->opa);
lv_mem_free(user_data);
dsc->user_data = NULL;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_fs_res_t res;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint32_t pos = ((y * dsc->header.w + x) * px_size) >> 3;
pos += 4; /*Skip the header*/
res = lv_fs_seek(&user_data->f, pos, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder seek failed");
return LV_RES_INV;
}
uint32_t btr = len * (px_size >> 3);
uint32_t br = 0;
res = lv_fs_read(&user_data->f, buf, btr, &br);
if(res != LV_FS_RES_OK || btr != br) {
LV_LOG_WARN("Built-in image decoder read failed");
return LV_RES_INV;
}
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
/*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
lv_color_t bg_color = dsc->color;
lv_coord_t i;
for(i = 0; i < len; i++) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
}
const lv_opa_t * opa_table = NULL;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
uint32_t ofs = 0;
int8_t pos = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_ALPHA_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
pos = 7 - (x & 0x7);
opa_table = alpha1_opa_table;
break;
case LV_IMG_CF_ALPHA_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
pos = 6 - (x & 0x3) * 2;
opa_table = alpha2_opa_table;
break;
case LV_IMG_CF_ALPHA_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
pos = 4 - (x & 0x1) * 4;
opa_table = alpha4_opa_table;
break;
case LV_IMG_CF_ALPHA_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if(fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
dsc->header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
int8_t pos = 0;
uint32_t ofs = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_INDEXED_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
ofs += 8; /*Skip the palette*/
pos = 7 - (x & 0x7);
break;
case LV_IMG_CF_INDEXED_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
ofs += 16; /*Skip the palette*/
pos = 6 - (x & 0x3) * 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
ofs += 64; /*Skip the palette*/
pos = 4 - (x & 0x1) * 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
ofs += 1024; /*Skip the palette*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if(fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
lv_coord_t i;
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
lv_color_t color = user_data->palette[val_act];
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}

View File

@ -0,0 +1,274 @@
/**
* @file lv_img_decoder.h
*
*/
#ifndef LV_IMG_DECODER_H
#define LV_IMG_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include "lv_img_buf.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Source of image.*/
enum {
LV_IMG_SRC_VARIABLE, /** Binary/C variable*/
LV_IMG_SRC_FILE, /** File in filesystem*/
LV_IMG_SRC_SYMBOL, /** Symbol (@ref lv_symbol_def.h)*/
LV_IMG_SRC_UNKNOWN, /** Unknown source*/
};
typedef uint8_t lv_img_src_t;
/*Decoder function definitions*/
struct _lv_img_decoder_dsc_t;
struct _lv_img_decoder_t;
/**
* Get info from an image and store in the `header`
* @param src the image source. Can be a pointer to a C array or a file name (Use
* `lv_img_src_get_type` to determine the type)
* @param header store the info here
* @return LV_RES_OK: info written correctly; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_info_f_t)(struct _lv_img_decoder_t * decoder, const void * src,
lv_img_header_t * header);
/**
* Open an image for decoding. Prepare it as it is required to read it later
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
*/
typedef lv_res_t (*lv_img_decoder_open_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_read_line_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc,
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
typedef void (*lv_img_decoder_close_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
typedef struct _lv_img_decoder_t {
lv_img_decoder_info_f_t info_cb;
lv_img_decoder_open_f_t open_cb;
lv_img_decoder_read_line_f_t read_line_cb;
lv_img_decoder_close_f_t close_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_img_decoder_t;
/**Describe an image decoding session. Stores data about the decoding*/
typedef struct _lv_img_decoder_dsc_t {
/**The decoder which was able to open the image source*/
lv_img_decoder_t * decoder;
/**The image source. A file path like "S:my_img.png" or pointer to an `lv_img_dsc_t` variable*/
const void * src;
/**Color to draw the image. USed when the image has alpha channel only*/
lv_color_t color;
/**Frame of the image, using with animated images*/
int32_t frame_id;
/**Type of the source: file or variable. Can be set in `open` function if required*/
lv_img_src_t src_type;
/**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
lv_img_header_t header;
/** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format.
* MUST be set in `open` function*/
const uint8_t * img_data;
/** How much time did it take to open the image. [ms]
* If not set `lv_img_cache` will measure and set the time to open*/
uint32_t time_to_open;
/**A text to display instead of the image when the image can't be opened.
* Can be set in `open` function or set NULL.*/
const char * error_msg;
/**Store any custom data here is required*/
void * user_data;
} lv_img_decoder_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void);
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_add_drv()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header);
/**
* Open an image.
* Try the created image decoder one by one. Once one is able to open the image that decoder is save in `dsc`
* @param dsc describe a decoding session. Simply a pointer to an `lv_img_decoder_dsc_t` variable.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_add_drv()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return LV_RES_OK: opened the image. `dsc->img_data` and `dsc->header` are set.
* LV_RES_INV: none of the registered image decoders were able to open the image.
*/
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id);
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len,
uint8_t * buf);
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc);
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void);
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder);
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb);
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb);
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb);
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb);
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_DECODER_H*/

View File

@ -0,0 +1,30 @@
# Extra components
This directory contains extra (optional) components to lvgl.
It's a good place for contributions as there are less strict expectations about the completeness and flexibility of the components here.
In other words, if you have created a complex widget from other widgets, or modified an existing widget with special events, styles or animations, or have a new feature that could work as a plugin to lvgl feel free to the share it here.
## How to contribute
- Create a [Pull request](https://docs.lvgl.io/8.0/CONTRIBUTING.html#pull-request) with your new content
- Please and follow the [Coding style](https://github.com/lvgl/lvgl/blob/master/docs/CODING_STYLE.md) of LVGL
- Add setter/getter functions in pair
- Update [lv_conf_template.h](https://github.com/lvgl/lvgl/blob/master/lv_conf_template.h)
- Add description in the [docs](https://github.com/lvgl/lvgl/tree/master/docs)
- Add [examples](https://github.com/lvgl/lvgl/tree/master/examples)
- Update the [changelog](https://github.com/lvgl/lvgl/tree/master/docs/CHANGELOG.md)
- Add yourself to the [Contributors](#contributors) section below.
## Ideas
Here some ideas as inspiration feel free to contribute with ideas too.
- New [Calendar headers](https://github.com/lvgl/lvgl/tree/master/src/extra/widgets/calendar)
- Color picker with RGB and or HSV bars
- Ruler, horizontal or vertical with major and minor ticks and labels
- New [List items types](https://github.com/lvgl/lvgl/tree/master/src/extra/widgets/list)
- [Preloaders](https://www.google.com/search?q=preloader&sxsrf=ALeKk01ddA4YB0WEgLLN1bZNSm8YER7pkg:1623080551559&source=lnms&tbm=isch&sa=X&ved=2ahUKEwiwoN6d7oXxAhVuw4sKHVedBB4Q_AUoAXoECAEQAw&biw=952&bih=940)
- Drop-down list with a container to which an content can be added
- 9 patch button: Similar to [lv_imgbtn](https://docs.lvgl.io/8.0/widgets/extra/imgbtn.html) but 9 images for 4 corner, 4 sides and the center
## Contributors
- lv_animimg: @ZhaoQiang-b45475
- lv_span: @guoweilkd

View File

@ -0,0 +1 @@
CSRCS += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra -name \*.c)

View File

@ -0,0 +1,595 @@
/**
* @file lv_flex.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_layouts.h"
#if LV_USE_FLEX
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_flex_align_t main_place;
lv_flex_align_t cross_place;
lv_flex_align_t track_place;
uint8_t row : 1;
uint8_t wrap : 1;
uint8_t rev : 1;
} flex_t;
typedef struct {
lv_obj_t * item;
lv_coord_t min_size;
lv_coord_t max_size;
lv_coord_t final_size;
uint32_t grow_value;
uint32_t clamped : 1;
} grow_dsc_t;
typedef struct {
lv_coord_t track_cross_size;
lv_coord_t track_main_size; /*For all items*/
lv_coord_t track_fix_main_size; /*For non grow items*/
uint32_t item_cnt;
grow_dsc_t * grow_dsc;
uint32_t grow_item_cnt;
uint32_t grow_dsc_calc : 1;
} track_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void flex_update(lv_obj_t * cont, void * user_data);
static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, lv_coord_t item_gap,
lv_coord_t max_main_size, track_t * t);
static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t);
static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt,
lv_coord_t * start_pos, lv_coord_t * gap);
static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id);
/**********************
* GLOBAL VARIABLES
**********************/
uint32_t LV_LAYOUT_FLEX;
lv_style_prop_t LV_STYLE_FLEX_FLOW;
lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE;
lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE;
lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE;
lv_style_prop_t LV_STYLE_FLEX_GROW;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_flex_init(void)
{
LV_LAYOUT_FLEX = lv_layout_register(flex_update, NULL);
LV_STYLE_FLEX_FLOW = lv_style_register_prop();
LV_STYLE_FLEX_MAIN_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_FLEX_CROSS_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_FLEX_TRACK_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
}
void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow)
{
lv_obj_set_style_flex_flow(obj, flow, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
}
void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place,
lv_flex_align_t track_place)
{
lv_obj_set_style_flex_main_place(obj, main_place, 0);
lv_obj_set_style_flex_cross_place(obj, cross_place, 0);
lv_obj_set_style_flex_track_place(obj, track_place, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
}
void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow)
{
lv_obj_set_style_flex_grow(obj, grow, 0);
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_FLOW, v);
}
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_MAIN_PLACE, v);
}
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_CROSS_PLACE, v);
}
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_TRACK_PLACE, v);
}
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_GROW, v);
}
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_FLOW, v, selector);
}
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_MAIN_PLACE, v, selector);
}
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_CROSS_PLACE, v, selector);
}
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_TRACK_PLACE, v, selector);
}
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_GROW, v, selector);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void flex_update(lv_obj_t * cont, void * user_data)
{
LV_LOG_INFO("update %p container", (void *)cont);
LV_UNUSED(user_data);
flex_t f;
lv_flex_flow_t flow = lv_obj_get_style_flex_flow(cont, LV_PART_MAIN);
f.row = flow & _LV_FLEX_COLUMN ? 0 : 1;
f.wrap = flow & _LV_FLEX_WRAP ? 1 : 0;
f.rev = flow & _LV_FLEX_REVERSE ? 1 : 0;
f.main_place = lv_obj_get_style_flex_main_place(cont, LV_PART_MAIN);
f.cross_place = lv_obj_get_style_flex_cross_place(cont, LV_PART_MAIN);
f.track_place = lv_obj_get_style_flex_track_place(cont, LV_PART_MAIN);
bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t track_gap = !f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont,
LV_PART_MAIN);
lv_coord_t item_gap = f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont,
LV_PART_MAIN);
lv_coord_t max_main_size = (f.row ? lv_obj_get_content_width(cont) : lv_obj_get_content_height(cont));
lv_coord_t border_width = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
lv_coord_t abs_y = cont->coords.y1 + lv_obj_get_style_pad_top(cont,
LV_PART_MAIN) + border_width - lv_obj_get_scroll_y(cont);
lv_coord_t abs_x = cont->coords.x1 + lv_obj_get_style_pad_left(cont,
LV_PART_MAIN) + border_width - lv_obj_get_scroll_x(cont);
lv_flex_align_t track_cross_place = f.track_place;
lv_coord_t * cross_pos = (f.row ? &abs_y : &abs_x);
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
/*Content sized objects should squeezed the gap between the children, therefore any alignment will look like `START`*/
if((f.row && h_set == LV_SIZE_CONTENT && cont->h_layout == 0) ||
(!f.row && w_set == LV_SIZE_CONTENT && cont->w_layout == 0)) {
track_cross_place = LV_FLEX_ALIGN_START;
}
if(rtl && !f.row) {
if(track_cross_place == LV_FLEX_ALIGN_START) track_cross_place = LV_FLEX_ALIGN_END;
else if(track_cross_place == LV_FLEX_ALIGN_END) track_cross_place = LV_FLEX_ALIGN_START;
}
lv_coord_t total_track_cross_size = 0;
lv_coord_t gap = 0;
uint32_t track_cnt = 0;
int32_t track_first_item;
int32_t next_track_first_item;
if(track_cross_place != LV_FLEX_ALIGN_START) {
track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
track_t t;
while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
/*Search the first item of the next row*/
t.grow_dsc_calc = 0;
next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
total_track_cross_size += t.track_cross_size + track_gap;
track_cnt++;
track_first_item = next_track_first_item;
}
if(track_cnt) total_track_cross_size -= track_gap; /*No gap after the last track*/
/*Place the tracks to get the start position*/
lv_coord_t max_cross_size = (f.row ? lv_obj_get_content_height(cont) : lv_obj_get_content_width(cont));
place_content(track_cross_place, max_cross_size, total_track_cross_size, track_cnt, cross_pos, &gap);
}
track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
if(rtl && !f.row) {
*cross_pos += total_track_cross_size;
}
while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
track_t t;
t.grow_dsc_calc = 1;
/*Search the first item of the next row*/
next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
if(rtl && !f.row) {
*cross_pos -= t.track_cross_size;
}
children_repos(cont, &f, track_first_item, next_track_first_item, abs_x, abs_y, max_main_size, item_gap, &t);
track_first_item = next_track_first_item;
lv_mem_buf_release(t.grow_dsc);
t.grow_dsc = NULL;
if(rtl && !f.row) {
*cross_pos -= gap + track_gap;
}
else {
*cross_pos += t.track_cross_size + gap + track_gap;
}
}
LV_ASSERT_MEM_INTEGRITY();
if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
lv_obj_refr_size(cont);
}
lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
LV_TRACE_LAYOUT("finished");
}
/**
* Find the last item of a track
*/
static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, lv_coord_t max_main_size,
lv_coord_t item_gap, track_t * t)
{
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
/*Can't wrap if the size if auto (i.e. the size depends on the children)*/
if(f->wrap && ((f->row && w_set == LV_SIZE_CONTENT) || (!f->row && h_set == LV_SIZE_CONTENT))) {
f->wrap = false;
}
lv_coord_t(*get_main_size)(const lv_obj_t *) = (f->row ? lv_obj_get_width : lv_obj_get_height);
lv_coord_t(*get_cross_size)(const lv_obj_t *) = (!f->row ? lv_obj_get_width : lv_obj_get_height);
t->track_main_size = 0;
t->track_fix_main_size = 0;
t->grow_item_cnt = 0;
t->track_cross_size = 0;
t->item_cnt = 0;
t->grow_dsc = NULL;
int32_t item_id = item_start_id;
lv_obj_t * item = lv_obj_get_child(cont, item_id);
while(item) {
if(item_id != item_start_id && lv_obj_has_flag(item, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)) break;
if(!lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
uint8_t grow_value = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
if(grow_value) {
t->grow_item_cnt++;
t->track_fix_main_size += item_gap;
if(t->grow_dsc_calc) {
grow_dsc_t * new_dsc = lv_mem_buf_get(sizeof(grow_dsc_t) * (t->grow_item_cnt));
LV_ASSERT_MALLOC(new_dsc);
if(new_dsc == NULL) return item_id;
if(t->grow_dsc) {
lv_memcpy(new_dsc, t->grow_dsc, sizeof(grow_dsc_t) * (t->grow_item_cnt - 1));
lv_mem_buf_release(t->grow_dsc);
}
new_dsc[t->grow_item_cnt - 1].item = item;
new_dsc[t->grow_item_cnt - 1].min_size = f->row ? lv_obj_get_style_min_width(item,
LV_PART_MAIN) : lv_obj_get_style_min_height(item, LV_PART_MAIN);
new_dsc[t->grow_item_cnt - 1].max_size = f->row ? lv_obj_get_style_max_width(item,
LV_PART_MAIN) : lv_obj_get_style_max_height(item, LV_PART_MAIN);
new_dsc[t->grow_item_cnt - 1].grow_value = grow_value;
new_dsc[t->grow_item_cnt - 1].clamped = 0;
t->grow_dsc = new_dsc;
}
}
else {
lv_coord_t item_size = get_main_size(item);
if(f->wrap && t->track_fix_main_size + item_size > max_main_size) break;
t->track_fix_main_size += item_size + item_gap;
}
t->track_cross_size = LV_MAX(get_cross_size(item), t->track_cross_size);
t->item_cnt++;
}
item_id += f->rev ? -1 : +1;
if(item_id < 0) break;
item = lv_obj_get_child(cont, item_id);
}
if(t->track_fix_main_size > 0) t->track_fix_main_size -= item_gap; /*There is no gap after the last item*/
/*If there is at least one "grow item" the track takes the full space*/
t->track_main_size = t->grow_item_cnt ? max_main_size : t->track_fix_main_size;
/*Have at least one item in a row*/
if(item && item_id == item_start_id) {
item = cont->spec_attr->children[item_id];
get_next_item(cont, f->rev, &item_id);
if(item) {
t->track_cross_size = get_cross_size(item);
t->track_main_size = get_main_size(item);
t->item_cnt = 1;
}
}
return item_id;
}
/**
* Position the children in the same track
*/
static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t)
{
void (*area_set_main_size)(lv_area_t *, lv_coord_t) = (f->row ? lv_area_set_width : lv_area_set_height);
lv_coord_t (*area_get_main_size)(const lv_area_t *) = (f->row ? lv_area_get_width : lv_area_get_height);
lv_coord_t (*area_get_cross_size)(const lv_area_t *) = (!f->row ? lv_area_get_width : lv_area_get_height);
/*Calculate the size of grow items first*/
uint32_t i;
bool grow_reiterate = true;
while(grow_reiterate) {
grow_reiterate = false;
lv_coord_t grow_value_sum = 0;
lv_coord_t grow_max_size = t->track_main_size - t->track_fix_main_size;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].clamped == 0) {
grow_value_sum += t->grow_dsc[i].grow_value;
}
else {
grow_max_size -= t->grow_dsc[i].final_size;
}
}
lv_coord_t grow_unit;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].clamped == 0) {
grow_unit = grow_max_size / grow_value_sum;
lv_coord_t size = grow_unit * t->grow_dsc[i].grow_value;
lv_coord_t size_clamp = LV_CLAMP(t->grow_dsc[i].min_size, size, t->grow_dsc[i].max_size);
if(size_clamp != size) {
t->grow_dsc[i].clamped = 1;
grow_reiterate = true;
}
t->grow_dsc[i].final_size = size_clamp;
grow_value_sum -= t->grow_dsc[i].grow_value;
grow_max_size -= t->grow_dsc[i].final_size;
}
}
}
bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t main_pos = 0;
lv_coord_t place_gap = 0;
place_content(f->main_place, max_main_size, t->track_main_size, t->item_cnt, &main_pos, &place_gap);
if(f->row && rtl) main_pos += lv_obj_get_content_width(cont);
lv_obj_t * item = lv_obj_get_child(cont, item_first_id);
/*Reposition the children*/
while(item && item_first_id != item_last_id) {
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
item = get_next_item(cont, f->rev, &item_first_id);
continue;
}
lv_coord_t grow_size = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
if(grow_size) {
lv_coord_t s = 0;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].item == item) {
s = t->grow_dsc[i].final_size;
break;
}
}
if(f->row) item->w_layout = 1;
else item->h_layout = 1;
if(s != area_get_main_size(&item->coords)) {
lv_obj_invalidate(item);
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
area_set_main_size(&item->coords, s);
lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
lv_obj_invalidate(item);
}
}
else {
item->w_layout = 0;
item->h_layout = 0;
}
lv_coord_t cross_pos = 0;
switch(f->cross_place) {
case LV_FLEX_ALIGN_CENTER:
/*Round up the cross size to avoid rounding error when dividing by 2
*The issue comes up e,g, with column direction with center cross direction if an element's width changes*/
cross_pos = (((t->track_cross_size + 1) & (~1)) - area_get_cross_size(&item->coords)) / 2;
break;
case LV_FLEX_ALIGN_END:
cross_pos = t->track_cross_size - area_get_cross_size(&item->coords);
break;
default:
break;
}
if(f->row && rtl) main_pos -= area_get_main_size(&item->coords);
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(item);
lv_coord_t h = lv_obj_get_height(item);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
lv_coord_t diff_x = abs_x - item->coords.x1 + tr_x;
lv_coord_t diff_y = abs_y - item->coords.y1 + tr_y;
diff_x += f->row ? main_pos : cross_pos;
diff_y += f->row ? cross_pos : main_pos;
if(diff_x || diff_y) {
lv_obj_invalidate(item);
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
lv_obj_invalidate(item);
lv_obj_move_children_by(item, diff_x, diff_y, true);
}
if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap;
else main_pos -= item_gap + place_gap;
item = get_next_item(cont, f->rev, &item_first_id);
}
}
/**
* Tell a start coordinate and gap for a placement type.
*/
static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt,
lv_coord_t * start_pos, lv_coord_t * gap)
{
if(item_cnt <= 1) {
switch(place) {
case LV_FLEX_ALIGN_SPACE_BETWEEN:
case LV_FLEX_ALIGN_SPACE_AROUND:
case LV_FLEX_ALIGN_SPACE_EVENLY:
place = LV_FLEX_ALIGN_CENTER;
break;
default:
break;
}
}
switch(place) {
case LV_FLEX_ALIGN_CENTER:
*gap = 0;
*start_pos += (max_size - content_size) / 2;
break;
case LV_FLEX_ALIGN_END:
*gap = 0;
*start_pos += max_size - content_size;
break;
case LV_FLEX_ALIGN_SPACE_BETWEEN:
*gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt - 1);
break;
case LV_FLEX_ALIGN_SPACE_AROUND:
*gap += (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt);
*start_pos += *gap / 2;
break;
case LV_FLEX_ALIGN_SPACE_EVENLY:
*gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt + 1);
*start_pos += *gap;
break;
default:
*gap = 0;
}
}
static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id)
{
if(rev) {
(*item_id)--;
if(*item_id >= 0) return cont->spec_attr->children[*item_id];
else return NULL;
}
else {
(*item_id)++;
if((*item_id) < (int32_t)cont->spec_attr->child_cnt) return cont->spec_attr->children[*item_id];
else return NULL;
}
}
#endif /*LV_USE_FLEX*/

View File

@ -0,0 +1,153 @@
/**
* @file lv_flex.h
*
*/
#ifndef LV_FLEX_H
#define LV_FLEX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_FLEX
/*********************
* DEFINES
*********************/
#define LV_OBJ_FLAG_FLEX_IN_NEW_TRACK LV_OBJ_FLAG_LAYOUT_1
LV_EXPORT_CONST_INT(LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
#define _LV_FLEX_COLUMN (1 << 0)
#define _LV_FLEX_WRAP (1 << 2)
#define _LV_FLEX_REVERSE (1 << 3)
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_END,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_SPACE_AROUND,
LV_FLEX_ALIGN_SPACE_BETWEEN,
} lv_flex_align_t;
typedef enum {
LV_FLEX_FLOW_ROW = 0x00,
LV_FLEX_FLOW_COLUMN = _LV_FLEX_COLUMN,
LV_FLEX_FLOW_ROW_WRAP = LV_FLEX_FLOW_ROW | _LV_FLEX_WRAP,
LV_FLEX_FLOW_ROW_REVERSE = LV_FLEX_FLOW_ROW | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_ROW_WRAP_REVERSE = LV_FLEX_FLOW_ROW | _LV_FLEX_WRAP | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_COLUMN_WRAP = LV_FLEX_FLOW_COLUMN | _LV_FLEX_WRAP,
LV_FLEX_FLOW_COLUMN_REVERSE = LV_FLEX_FLOW_COLUMN | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_COLUMN_WRAP_REVERSE = LV_FLEX_FLOW_COLUMN | _LV_FLEX_WRAP | _LV_FLEX_REVERSE,
} lv_flex_flow_t;
/**********************
* GLOBAL VARIABLES
**********************/
extern uint32_t LV_LAYOUT_FLEX;
extern lv_style_prop_t LV_STYLE_FLEX_FLOW;
extern lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_GROW;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a flex layout the default values
* @param flex pointer to a flex layout descriptor
*/
void lv_flex_init(void);
/**
* Set hot the item should flow
* @param flex pointer to a flex layout descriptor
* @param flow an element of `lv_flex_flow_t`.
*/
void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow);
/**
* Set how to place (where to align) the items and tracks
* @param flex pointer: to a flex layout descriptor
* @param main_place where to place the items on main axis (in their track). Any value of `lv_flex_align_t`.
* @param cross_place where to place the item in their track on the cross axis. `LV_FLEX_ALIGN_START/END/CENTER`
* @param track_place where to place the tracks in the cross direction. Any value of `lv_flex_align_t`.
*/
void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place,
lv_flex_align_t track_cross_place);
/**
* Sets the width or height (on main axis) to grow the object in order fill the free space
* @param obj pointer to an object. The parent must have flex layout else nothing will happen.
* @param grow a value to set how much free space to take proportionally to other growing items.
*/
void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow);
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value);
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value);
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector);
static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_FLOW);
return (lv_flex_flow_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_main_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_MAIN_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_cross_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_CROSS_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_track_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_TRACK_PLACE);
return (lv_flex_align_t)v.num;
}
static inline uint8_t lv_obj_get_style_flex_grow(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_GROW);
return (uint8_t)v.num;
}
/**********************
* MACROS
**********************/
#endif /*LV_USE_FLEX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FLEX_H*/

View File

@ -0,0 +1,780 @@
/**
* @file lv_grid.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_layouts.h"
#if LV_USE_GRID
/*********************
* DEFINES
*********************/
/**
* Some helper defines
*/
#define IS_FR(x) (x >= LV_COORD_MAX - 100)
#define IS_CONTENT(x) (x == LV_COORD_MAX - 101)
#define GET_FR(x) (x - (LV_COORD_MAX - 100))
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint32_t col;
uint32_t row;
lv_point_t grid_abs;
} item_repos_hint_t;
typedef struct {
lv_coord_t * x;
lv_coord_t * y;
lv_coord_t * w;
lv_coord_t * h;
uint32_t col_num;
uint32_t row_num;
lv_coord_t grid_w;
lv_coord_t grid_h;
} _lv_grid_calc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void grid_update(lv_obj_t * cont, void * user_data);
static void calc(lv_obj_t * obj, _lv_grid_calc_t * calc);
static void calc_free(_lv_grid_calc_t * calc);
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c);
static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c);
static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint);
static lv_coord_t grid_align(lv_coord_t cont_size, bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num,
lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse);
static uint32_t count_tracks(const lv_coord_t * templ);
static inline const lv_coord_t * get_col_dsc(lv_obj_t * obj)
{
return lv_obj_get_style_grid_column_dsc_array(obj, 0);
}
static inline const lv_coord_t * get_row_dsc(lv_obj_t * obj)
{
return lv_obj_get_style_grid_row_dsc_array(obj, 0);
}
static inline uint8_t get_col_pos(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_column_pos(obj, 0);
}
static inline uint8_t get_row_pos(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_row_pos(obj, 0);
}
static inline uint8_t get_col_span(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_column_span(obj, 0);
}
static inline uint8_t get_row_span(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_row_span(obj, 0);
}
static inline uint8_t get_cell_col_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_x_align(obj, 0);
}
static inline uint8_t get_cell_row_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_y_align(obj, 0);
}
static inline uint8_t get_grid_col_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_column_align(obj, 0);
}
static inline uint8_t get_grid_row_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_row_align(obj, 0);
}
/**********************
* GLOBAL VARIABLES
**********************/
uint32_t LV_LAYOUT_GRID;
lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_grid_init(void)
{
LV_LAYOUT_GRID = lv_layout_register(grid_update, NULL);
LV_STYLE_GRID_COLUMN_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_ROW_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_COLUMN_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_ROW_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_ROW_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_ROW_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_COLUMN_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_COLUMN_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_X_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_Y_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
}
void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[])
{
lv_obj_set_style_grid_column_dsc_array(obj, col_dsc, 0);
lv_obj_set_style_grid_row_dsc_array(obj, row_dsc, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_GRID, 0);
}
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align)
{
lv_obj_set_style_grid_column_align(obj, column_align, 0);
lv_obj_set_style_grid_row_align(obj, row_align, 0);
}
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t x_align, uint8_t col_pos, uint8_t col_span,
lv_grid_align_t y_align, uint8_t row_pos, uint8_t row_span)
{
lv_obj_set_style_grid_cell_column_pos(obj, col_pos, 0);
lv_obj_set_style_grid_cell_row_pos(obj, row_pos, 0);
lv_obj_set_style_grid_cell_x_align(obj, x_align, 0);
lv_obj_set_style_grid_cell_column_span(obj, col_span, 0);
lv_obj_set_style_grid_cell_row_span(obj, row_span, 0);
lv_obj_set_style_grid_cell_y_align(obj, y_align, 0);
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[])
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_style_set_prop(style, LV_STYLE_GRID_ROW_DSC_ARRAY, v);
}
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[])
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v);
}
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value)
{
lv_style_value_t v = {
.num = (lv_grid_align_t)value
};
lv_style_set_prop(style, LV_STYLE_GRID_ROW_ALIGN, v);
}
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value)
{
lv_style_value_t v = {
.num = (lv_grid_align_t)value
};
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_ALIGN, v);
}
void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_POS, v);
}
void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_SPAN, v);
}
void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_POS, v);
}
void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_SPAN, v);
}
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_X_ALIGN, v);
}
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_Y_ALIGN, v);
}
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_ALIGN, v, selector);
}
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_POS, v, selector);
}
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_POS, v, selector);
}
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_X_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_Y_ALIGN, v, selector);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void grid_update(lv_obj_t * cont, void * user_data)
{
LV_LOG_INFO("update %p container", (void *)cont);
LV_UNUSED(user_data);
const lv_coord_t * col_templ = get_col_dsc(cont);
const lv_coord_t * row_templ = get_row_dsc(cont);
if(col_templ == NULL || row_templ == NULL) return;
_lv_grid_calc_t c;
calc(cont, &c);
item_repos_hint_t hint;
lv_memset_00(&hint, sizeof(hint));
/*Calculate the grids absolute x and y coordinates.
*It will be used as helper during item repositioning to avoid calculating this value for every children*/
lv_coord_t border_widt = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(cont, LV_PART_MAIN) + border_widt;
lv_coord_t pad_top = lv_obj_get_style_pad_top(cont, LV_PART_MAIN) + border_widt;
hint.grid_abs.x = pad_left + cont->coords.x1 - lv_obj_get_scroll_x(cont);
hint.grid_abs.y = pad_top + cont->coords.y1 - lv_obj_get_scroll_y(cont);
uint32_t i;
for(i = 0; i < cont->spec_attr->child_cnt; i++) {
lv_obj_t * item = cont->spec_attr->children[i];
item_repos(item, &c, &hint);
}
calc_free(&c);
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
lv_obj_refr_size(cont);
}
lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
LV_TRACE_LAYOUT("finished");
}
/**
* Calculate the grid cells coordinates
* @param cont an object that has a grid
* @param calc store the calculated cells sizes here
* @note `_lv_grid_calc_free(calc_out)` needs to be called when `calc_out` is not needed anymore
*/
static void calc(lv_obj_t * cont, _lv_grid_calc_t * calc_out)
{
if(lv_obj_get_child(cont, 0) == NULL) {
lv_memset_00(calc_out, sizeof(_lv_grid_calc_t));
return;
}
calc_rows(cont, calc_out);
calc_cols(cont, calc_out);
lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
bool rev = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
bool auto_w = (w_set == LV_SIZE_CONTENT && !cont->w_layout) ? true : false;
lv_coord_t cont_w = lv_obj_get_content_width(cont);
calc_out->grid_w = grid_align(cont_w, auto_w, get_grid_col_align(cont), col_gap, calc_out->col_num, calc_out->w,
calc_out->x, rev);
bool auto_h = (h_set == LV_SIZE_CONTENT && !cont->h_layout) ? true : false;
lv_coord_t cont_h = lv_obj_get_content_height(cont);
calc_out->grid_h = grid_align(cont_h, auto_h, get_grid_row_align(cont), row_gap, calc_out->row_num, calc_out->h,
calc_out->y, false);
LV_ASSERT_MEM_INTEGRITY();
}
/**
* Free the a grid calculation's data
* @param calc pointer to the calculated gtrid cell coordinates
*/
static void calc_free(_lv_grid_calc_t * calc)
{
lv_mem_buf_release(calc->x);
lv_mem_buf_release(calc->y);
lv_mem_buf_release(calc->w);
lv_mem_buf_release(calc->h);
}
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c)
{
const lv_coord_t * col_templ = get_col_dsc(cont);
lv_coord_t cont_w = lv_obj_get_content_width(cont);
c->col_num = count_tracks(col_templ);
c->x = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
c->w = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
/*Set sizes for CONTENT cells*/
uint32_t i;
for(i = 0; i < c->col_num; i++) {
lv_coord_t size = LV_COORD_MIN;
if(IS_CONTENT(col_templ[i])) {
/*Check the size of children of this cell*/
uint32_t ci;
for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
lv_obj_t * item = lv_obj_get_child(cont, ci);
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
uint32_t col_span = get_col_span(item);
if(col_span != 1) continue;
uint32_t col_pos = get_col_pos(item);
if(col_pos != i) continue;
size = LV_MAX(size, lv_obj_get_width(item));
}
if(size >= 0) c->w[i] = size;
else c->w[i] = 0;
}
}
uint32_t col_fr_cnt = 0;
lv_coord_t grid_w = 0;
for(i = 0; i < c->col_num; i++) {
lv_coord_t x = col_templ[i];
if(IS_FR(x)) {
col_fr_cnt += GET_FR(x);
}
else if(IS_CONTENT(x)) {
grid_w += c->w[i];
}
else {
c->w[i] = x;
grid_w += x;
}
}
lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
cont_w -= col_gap * (c->col_num - 1);
lv_coord_t free_w = cont_w - grid_w;
if(free_w < 0) free_w = 0;
int32_t last_fr_i = -1;
int32_t last_fr_x = 0;
for(i = 0; i < c->col_num; i++) {
lv_coord_t x = col_templ[i];
if(IS_FR(x)) {
lv_coord_t f = GET_FR(x);
c->w[i] = (free_w * f) / col_fr_cnt;
last_fr_i = i;
last_fr_x = f;
}
}
/*To avoid rounding errors set the last FR track to the remaining size */
if(last_fr_i >= 0) {
c->w[last_fr_i] = free_w - ((free_w * (col_fr_cnt - last_fr_x)) / col_fr_cnt);
}
}
static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c)
{
uint32_t i;
const lv_coord_t * row_templ = get_row_dsc(cont);
c->row_num = count_tracks(row_templ);
c->y = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
c->h = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
/*Set sizes for CONTENT cells*/
for(i = 0; i < c->row_num; i++) {
lv_coord_t size = LV_COORD_MIN;
if(IS_CONTENT(row_templ[i])) {
/*Check the size of children of this cell*/
uint32_t ci;
for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
lv_obj_t * item = lv_obj_get_child(cont, ci);
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
uint32_t row_span = get_row_span(item);
if(row_span != 1) continue;
uint32_t row_pos = get_row_pos(item);
if(row_pos != i) continue;
size = LV_MAX(size, lv_obj_get_height(item));
}
if(size >= 0) c->h[i] = size;
else c->h[i] = 0;
}
}
uint32_t row_fr_cnt = 0;
lv_coord_t grid_h = 0;
for(i = 0; i < c->row_num; i++) {
lv_coord_t x = row_templ[i];
if(IS_FR(x)) {
row_fr_cnt += GET_FR(x);
}
else if(IS_CONTENT(x)) {
grid_h += c->h[i];
}
else {
c->h[i] = x;
grid_h += x;
}
}
lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t cont_h = lv_obj_get_content_height(cont) - row_gap * (c->row_num - 1);
lv_coord_t free_h = cont_h - grid_h;
if(free_h < 0) free_h = 0;
int32_t last_fr_i = -1;
int32_t last_fr_x = 0;
for(i = 0; i < c->row_num; i++) {
lv_coord_t x = row_templ[i];
if(IS_FR(x)) {
lv_coord_t f = GET_FR(x);
c->h[i] = (free_h * f) / row_fr_cnt;
}
}
/*To avoid rounding errors set the last FR track to the remaining size */
if(last_fr_i >= 0) {
c->h[last_fr_i] = free_h - ((free_h * (row_fr_cnt - last_fr_x)) / row_fr_cnt);
}
}
/**
* Reposition a grid item in its cell
* @param item a grid item to reposition
* @param calc the calculated grid of `cont`
* @param child_id_ext helper value if the ID of the child is know (order from the oldest) else -1
* @param grid_abs helper value, the absolute position of the grid, NULL if unknown
*/
static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint)
{
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) return;
uint32_t col_span = get_col_span(item);
uint32_t row_span = get_row_span(item);
if(row_span == 0 || col_span == 0) return;
uint32_t col_pos = get_col_pos(item);
uint32_t row_pos = get_row_pos(item);
lv_grid_align_t col_align = get_cell_col_align(item);
lv_grid_align_t row_align = get_cell_row_align(item);
lv_coord_t col_x1 = c->x[col_pos];
lv_coord_t col_x2 = c->x[col_pos + col_span - 1] + c->w[col_pos + col_span - 1];
lv_coord_t col_w = col_x2 - col_x1;
lv_coord_t row_y1 = c->y[row_pos];
lv_coord_t row_y2 = c->y[row_pos + row_span - 1] + c->h[row_pos + row_span - 1];
lv_coord_t row_h = row_y2 - row_y1;
/*If the item has RTL base dir switch start and end*/
if(lv_obj_get_style_base_dir(item, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(col_align == LV_GRID_ALIGN_START) col_align = LV_GRID_ALIGN_END;
else if(col_align == LV_GRID_ALIGN_END) col_align = LV_GRID_ALIGN_START;
}
lv_coord_t x;
lv_coord_t y;
lv_coord_t item_w = lv_area_get_width(&item->coords);
lv_coord_t item_h = lv_area_get_height(&item->coords);
switch(col_align) {
default:
case LV_GRID_ALIGN_START:
x = c->x[col_pos];
item->w_layout = 0;
break;
case LV_GRID_ALIGN_STRETCH:
x = c->x[col_pos];
item_w = col_w;
item->w_layout = 1;
break;
case LV_GRID_ALIGN_CENTER:
x = c->x[col_pos] + (col_w - item_w) / 2;
item->w_layout = 0;
break;
case LV_GRID_ALIGN_END:
x = c->x[col_pos] + col_w - lv_obj_get_width(item);
item->w_layout = 0;
break;
}
switch(row_align) {
default:
case LV_GRID_ALIGN_START:
y = c->y[row_pos];
item->h_layout = 0;
break;
case LV_GRID_ALIGN_STRETCH:
y = c->y[row_pos];
item_h = row_h;
item->h_layout = 1;
break;
case LV_GRID_ALIGN_CENTER:
y = c->y[row_pos] + (row_h - item_h) / 2;
item->h_layout = 0;
break;
case LV_GRID_ALIGN_END:
y = c->y[row_pos] + row_h - lv_obj_get_height(item);
item->h_layout = 0;
break;
}
/*Set a new size if required*/
if(lv_obj_get_width(item) != item_w || lv_obj_get_height(item) != item_h) {
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
lv_obj_invalidate(item);
lv_area_set_width(&item->coords, item_w);
lv_area_set_height(&item->coords, item_h);
lv_obj_invalidate(item);
lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
}
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(item);
lv_coord_t h = lv_obj_get_height(item);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
x += tr_x;
y += tr_y;
lv_coord_t diff_x = hint->grid_abs.x + x - item->coords.x1;
lv_coord_t diff_y = hint->grid_abs.y + y - item->coords.y1;
if(diff_x || diff_y) {
lv_obj_invalidate(item);
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
lv_obj_invalidate(item);
lv_obj_move_children_by(item, diff_x, diff_y, true);
}
}
/**
* Place the grid track according to align methods. It keeps the track sizes but sets their position.
* It can process both columns or rows according to the passed parameters.
* @param cont_size size of the containers content area (width/height)
* @param auto_size true: the container has auto size in the current direction
* @param align align method
* @param gap grid gap
* @param track_num number of tracks
* @param size_array array with the track sizes
* @param pos_array write the positions of the tracks here
* @return the total size of the grid
*/
static lv_coord_t grid_align(lv_coord_t cont_size, bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num,
lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse)
{
lv_coord_t grid_size = 0;
uint32_t i;
if(auto_size) {
pos_array[0] = 0;
}
else {
/*With spaced alignment gap will be calculated from the remaining space*/
if(align == LV_GRID_ALIGN_SPACE_AROUND || align == LV_GRID_ALIGN_SPACE_BETWEEN || align == LV_GRID_ALIGN_SPACE_EVENLY) {
gap = 0;
if(track_num == 1) align = LV_GRID_ALIGN_CENTER;
}
/*Get the full grid size with gap*/
for(i = 0; i < track_num; i++) {
grid_size += size_array[i] + gap;
}
grid_size -= gap;
/*Calculate the position of the first item and set gap is necessary*/
switch(align) {
case LV_GRID_ALIGN_START:
pos_array[0] = 0;
break;
case LV_GRID_ALIGN_CENTER:
pos_array[0] = (cont_size - grid_size) / 2;
break;
case LV_GRID_ALIGN_END:
pos_array[0] = cont_size - grid_size;
break;
case LV_GRID_ALIGN_SPACE_BETWEEN:
pos_array[0] = 0;
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num - 1);
break;
case LV_GRID_ALIGN_SPACE_AROUND:
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num);
pos_array[0] = gap / 2;
break;
case LV_GRID_ALIGN_SPACE_EVENLY:
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num + 1);
pos_array[0] = gap;
break;
}
}
/*Set the position of all tracks from the start position, gaps and track sizes*/
for(i = 0; i < track_num - 1; i++) {
pos_array[i + 1] = pos_array[i] + size_array[i] + gap;
}
lv_coord_t total_gird_size = pos_array[track_num - 1] + size_array[track_num - 1] - pos_array[0];
if(reverse) {
for(i = 0; i < track_num; i++) {
pos_array[i] = cont_size - pos_array[i] - size_array[i];
}
}
/*Return the full size of the grid*/
return total_gird_size;
}
static uint32_t count_tracks(const lv_coord_t * templ)
{
uint32_t i;
for(i = 0; templ[i] != LV_GRID_TEMPLATE_LAST; i++);
return i;
}
#endif /*LV_USE_GRID*/

View File

@ -0,0 +1,194 @@
/**
* @file lv_grid.h
*
*/
#ifndef LV_GRID_H
#define LV_GRID_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_GRID
/*********************
* DEFINES
*********************/
/**
* Can be used track size to make the track fill the free space.
* @param x how much space to take proportionally to other FR tracks
* @return a special track size
*/
#define LV_GRID_FR(x) (LV_COORD_MAX - 100 + x)
#define LV_GRID_CONTENT (LV_COORD_MAX - 101)
LV_EXPORT_CONST_INT(LV_GRID_CONTENT);
#define LV_GRID_TEMPLATE_LAST (LV_COORD_MAX)
LV_EXPORT_CONST_INT(LV_GRID_TEMPLATE_LAST);
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
LV_GRID_ALIGN_START,
LV_GRID_ALIGN_CENTER,
LV_GRID_ALIGN_END,
LV_GRID_ALIGN_STRETCH,
LV_GRID_ALIGN_SPACE_EVENLY,
LV_GRID_ALIGN_SPACE_AROUND,
LV_GRID_ALIGN_SPACE_BETWEEN,
} lv_grid_align_t;
/**********************
* GLOBAL VARIABLES
**********************/
extern uint32_t LV_LAYOUT_GRID;
extern lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
extern lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
extern lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
extern lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
extern lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_grid_init(void);
void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[]);
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align);
/**
* Set the cell of an object. The object's parent needs to have grid layout, else nothing will happen
* @param obj pointer to an object
* @param column_align the vertical alignment in the cell. `LV_GRID_START/END/CENTER/STRETCH`
* @param col_pos column ID
* @param col_span number of columns to take (>= 1)
* @param row_align the horizontal alignment in the cell. `LV_GRID_START/END/CENTER/STRETCH`
* @param row_pos row ID
* @param row_span number of rows to take (>= 1)
*/
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t column_align, uint8_t col_pos, uint8_t col_span,
lv_grid_align_t row_align, uint8_t row_pos, uint8_t row_span);
/**
* Just a wrapper to `LV_GRID_FR` for bindings.
*/
static inline lv_coord_t lv_grid_fr(uint8_t x)
{
return LV_GRID_FR(x);
}
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[]);
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[]);
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value);
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value);
void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value);
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector);
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector);
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
static inline const lv_coord_t * lv_obj_get_style_grid_row_dsc_array(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_DSC_ARRAY);
return (const lv_coord_t *)v.ptr;
}
static inline const lv_coord_t * lv_obj_get_style_grid_column_dsc_array(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_DSC_ARRAY);
return (const lv_coord_t *)v.ptr;
}
static inline lv_grid_align_t lv_obj_get_style_grid_row_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline lv_grid_align_t lv_obj_get_style_grid_column_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_column_pos(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_POS);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_column_span(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_SPAN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_row_pos(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_POS);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_row_span(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_SPAN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_x_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_X_ALIGN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_y_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_Y_ALIGN);
return (lv_coord_t)v.num;
}
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GRID*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GRID_H*/

View File

@ -0,0 +1,44 @@
/**
* @file lv_layouts.h
*
*/
#ifndef LV_LAYOUTS_H
#define LV_LAYOUTS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "flex/lv_flex.h"
#include "grid/lv_grid.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_LAYOUT
# define LV_TRACE_LAYOUT(...) LV_LOG_TRACE(__VA_ARGS__)
#else
# define LV_TRACE_LAYOUT(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LAYOUTS_H*/

Some files were not shown because too many files have changed in this diff Show More