1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-21 06:53:01 +08:00
lvgl/src/lv_objx/lv_cpicker.c

1815 lines
62 KiB
C
Raw Normal View History

/**
* @file lv_cpicker.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cpicker.h"
#if LV_USE_CPICKER != 0
#include "../lv_draw/lv_draw_arc.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_core/lv_indev.h"
#include "../lv_core/lv_refr.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#ifndef LV_CPICKER_DEF_TYPE
#define LV_CPICKER_DEF_TYPE LV_CPICKER_TYPE_DISC
#endif
#ifndef LV_CPICKER_DEF_HUE
#define LV_CPICKER_DEF_HUE 0
#endif
#ifndef LV_CPICKER_DEF_SATURATION
#define LV_CPICKER_DEF_SATURATION 100
#endif
#ifndef LV_CPICKER_DEF_VALUE
#define LV_CPICKER_DEF_VALUE 100
#endif
#ifndef LV_CPICKER_DEF_INDICATOR_TYPE
#define LV_CPICKER_DEF_INDICATOR_TYPE LV_CPICKER_INDICATOR_CIRCLE
#endif
#ifndef LV_CPICKER_DEF_QF /*quantization factor*/
2019-09-25 23:16:12 -07:00
#define LV_CPICKER_DEF_QF 3
#endif
/*for rectangular mode the QF can be down to 1*/
/*
#define LV_CPICKER_MINIMUM_QF 4
#if LV_CPICKER_DEF_QF < LV_CPICKER_MINIMUM_QF
#undef LV_CPICKER_DEF_QF
#define LV_CPICKER_DEF_QF LV_CPICKER_MINIMUM_QF
#endif
*/
#ifndef LV_CPICKER_USE_TRI /*Use triangle approximation instead of arc*/
#define LV_CPICKER_USE_TRI 1
#endif
#define TRI_OFFSET 4
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
2019-09-26 10:50:34 -07:00
static bool lv_cpicker_design(lv_obj_t * cpicker, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_cb_t ancestor_signal;
static lv_design_cb_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a color_picker object
* @param par pointer to an object, it will be the parent of the new color_picker
* @param copy pointer to a color_picker object, if not NULL then the new object will be copied from it
* @return pointer to the created color_picker
*/
lv_obj_t * lv_cpicker_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("color_picker create started");
lv_obj_t * new_cpicker = lv_obj_create(par, copy);
lv_mem_assert(new_cpicker);
if(new_cpicker == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_cpicker);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_cpicker);
/*Allocate the extended data*/
lv_cpicker_ext_t * ext = lv_obj_allocate_ext_attr(new_cpicker, sizeof(lv_cpicker_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->hue = LV_CPICKER_DEF_HUE;
ext->prev_hue = ext->hue;
ext->saturation = LV_CPICKER_DEF_SATURATION;
ext->value = LV_CPICKER_DEF_VALUE;
ext->indicator.style = &lv_style_plain;
ext->indicator.type = LV_CPICKER_DEF_INDICATOR_TYPE;
ext->color_mode = LV_CPICKER_COLOR_MODE_HUE;
ext->color_mode_fixed = 0;
ext->last_click = 0;
/*The signal and design functions are not copied so set them here*/
2019-09-26 10:50:34 -07:00
lv_obj_set_signal_cb(new_cpicker, lv_cpicker_signal);
lv_obj_set_design_cb(new_cpicker, lv_cpicker_design);
/*If no copy do the basic initialization*/
if(copy == NULL) {
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_cpicker_set_style(new_cpicker, LV_CPICKER_STYLE_MAIN, th->style.bg);
} else {
lv_cpicker_set_style(new_cpicker, LV_CPICKER_STYLE_MAIN, &lv_style_plain);
}
}
/*Copy 'copy'*/
else {
lv_cpicker_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_cpicker);
}
LV_LOG_INFO("color_picker created");
return new_cpicker;
}
/*=====================
* Setter functions
*====================*/
2019-09-24 21:06:51 -07:00
/**
* Set a new type for a cpicker
* @param cpicker pointer to a cpicker object
* @param type new type of the cpicker (from 'lv_cpicker_type_t' enum)
*/
void lv_cpicker_set_type(lv_obj_t * cpicker, lv_cpicker_type_t type)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
if(ext->type == type) return;
ext->type = type;
lv_obj_invalidate(cpicker);
}
/**
* Set a style of a colorpicker.
* @param cpicker pointer to colorpicker object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_cpicker_set_style(lv_obj_t * cpicker, lv_cpicker_style_t type, lv_style_t * style)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
switch(type) {
case LV_CPICKER_STYLE_MAIN:
lv_obj_set_style(cpicker, style);
break;
case LV_CPICKER_STYLE_INDICATOR:
ext->indicator.style = style;
lv_obj_invalidate(cpicker);
break;
}
}
/**
* Set a type of a colorpicker indicator.
* @param cpicker pointer to colorpicker object
* @param type indicator type
*/
void lv_cpicker_set_indicator_type(lv_obj_t * cpicker, lv_cpicker_indicator_type_t type)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
ext->indicator.type = type;
lv_obj_invalidate(cpicker);
}
/**
* Set the current hue of a colorpicker.
* @param cpicker pointer to colorpicker object
* @param hue current selected hue [0..360]
*/
void lv_cpicker_set_hue(lv_obj_t * cpicker, uint16_t hue)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
ext->hue = hue % 360;
lv_obj_invalidate(cpicker);
}
/**
* Set the current saturation of a colorpicker.
* @param cpicker pointer to colorpicker object
* @param saturation current selected saturation [0..100]
*/
void lv_cpicker_set_saturation(lv_obj_t * cpicker, uint8_t saturation)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
ext->saturation = saturation % 100;
lv_obj_invalidate(cpicker);
}
/**
* Set the current value of a colorpicker.
* @param cpicker pointer to colorpicker object
* @param val current selected value [0..100]
*/
void lv_cpicker_set_value(lv_obj_t * cpicker, uint8_t val)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
ext->value = val % 100;
lv_obj_invalidate(cpicker);
}
/**
* Set the current color of a colorpicker.
* @param cpicker pointer to colorpicker object
* @param color current selected color
*/
void lv_cpicker_set_color(lv_obj_t * cpicker, lv_color_t color)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
lv_color_hsv_t hsv = lv_color_rgb_to_hsv(color.ch.red, color.ch.green, color.ch.blue);
ext->hue = hsv.h;
lv_obj_invalidate(cpicker);
}
/**
* Set the current color mode.
* @param cpicker pointer to colorpicker object
* @param mode color mode (hue/sat/val)
*/
void lv_cpicker_set_color_mode(lv_obj_t * cpicker, lv_cpicker_color_mode_t mode)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
ext->color_mode = mode;
}
/**
* Set if the color mode is changed on long press on center
* @param cpicker pointer to colorpicker object
* @param fixed color mode cannot be changed on long press
*/
void lv_cpicker_set_color_mode_fixed(lv_obj_t * cpicker, bool fixed)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
ext->color_mode_fixed = fixed;
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current color mode.
* @param cpicker pointer to colorpicker object
* @return color mode (hue/sat/val)
*/
lv_cpicker_color_mode_t lv_cpicker_get_color_mode(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
return ext->color_mode;
}
/**
* Get if the color mode is changed on long press on center
* @param cpicker pointer to colorpicker object
* @return mode cannot be changed on long press
*/
bool lv_cpicker_get_color_mode_fixed(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
return ext->color_mode_fixed;
}
/**
* Get style of a color_picker.
* @param cpicker pointer to color_picker object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_cpicker_get_style(const lv_obj_t * cpicker, lv_cpicker_style_t type)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
switch(type) {
case LV_CPICKER_STYLE_MAIN:
return lv_obj_get_style(cpicker);
case LV_CPICKER_STYLE_INDICATOR:
return ext->indicator.style;
default:
return NULL;
}
/*To avoid warning*/
return NULL;
}
/**
* Get the current hue of a colorpicker.
* @param cpicker pointer to colorpicker object
* @return hue current selected hue
*/
uint16_t lv_cpicker_get_hue(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
return ext->hue;
}
/**
* Get the current saturation of a colorpicker.
* @param cpicker pointer to colorpicker object
* @return current selected saturation
*/
uint8_t lv_cpicker_get_saturation(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
return ext->saturation;
}
/**
* Get the current hue of a colorpicker.
* @param cpicker pointer to colorpicker object
* @return current selected value
*/
uint8_t lv_cpicker_get_value(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
return ext->value;
}
/**
* Get the current selected color of a colorpicker.
* @param cpicker pointer to colorpicker object
* @return color current selected color
*/
lv_color_t lv_cpicker_get_color(lv_obj_t * cpicker)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
return lv_color_hsv_to_rgb(ext->hue, ext->saturation, ext->value);
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_cpicker_disc_design(lv_obj_t * cpicker,
lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h,
lv_coord_t cx, lv_coord_t cy, uint16_t r);
static void lv_cpicker_rect_design(lv_obj_t * cpicker,
lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h);
/**
* Handle the drawing related tasks of the color_picker
* @param cpicker pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @return true/false, depends on 'mode'
*/
static bool lv_cpicker_design(lv_obj_t * cpicker, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK)
{
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN)
{
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
lv_style_t * style = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN);
static lv_style_t styleCopy;
lv_style_copy(&styleCopy, style);
lv_opa_t opa_scale = lv_obj_get_opa_scale(cpicker);
lv_coord_t w = lv_obj_get_width(cpicker);
lv_coord_t h = lv_obj_get_height(cpicker);
if(ext->type == LV_CPICKER_TYPE_DISC)
{
lv_coord_t cx = cpicker->coords.x1 + (w / 2);
lv_coord_t cy = cpicker->coords.y1 + (h / 2);
uint16_t r = LV_MATH_MIN(w, h) / 2;
lv_cpicker_disc_design(cpicker, mask, &styleCopy, opa_scale, ext, w, h, cx, cy, r);
}
else if(ext->type == LV_CPICKER_TYPE_RECT)
{
lv_cpicker_rect_design(cpicker, mask, &styleCopy, opa_scale, ext, w, h);
}
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST)
{
}
return true;
}
static lv_color_t angle_to_mode_color(lv_cpicker_ext_t * ext, uint16_t angle)
{
lv_color_t color;
switch(ext->color_mode)
{
default:
case LV_CPICKER_COLOR_MODE_HUE:
color = lv_color_hsv_to_rgb(angle % 360, ext->saturation, ext->value);
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
color = lv_color_hsv_to_rgb(ext->hue, (angle % 360) / 360.0 * 100.0, ext->value);
break;
case LV_CPICKER_COLOR_MODE_VALUE:
color = lv_color_hsv_to_rgb(ext->hue, ext->saturation, (angle % 360) / 360.0 * 100.0);
break;
}
return color;
}
static uint16_t mode_color_to_angle(lv_cpicker_ext_t * ext)
{
uint16_t angle;
switch(ext->color_mode)
{
default:
case LV_CPICKER_COLOR_MODE_HUE:
angle = ext->hue;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
angle = ext->saturation / 100.0 * 360.0;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
angle = ext->value / 100.0 * 360.0;
break;
}
return angle;
}
static lv_coord_t lv_cpicker_get_indicator_coord(lv_style_t * style, lv_cpicker_ext_t * ext)
{
lv_coord_t ind_pos = style->line.rounded ? ext->rect_gradient_h / 2 : 0;
switch(ext->color_mode)
{
default:
case LV_CPICKER_COLOR_MODE_HUE:
ind_pos += ext->hue / 360.0 * ext->rect_gradient_w;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ind_pos += ext->saturation / 100.0 * ext->rect_gradient_w;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ind_pos += ext->value / 100.0 * ext->rect_gradient_w;
break;
}
return ind_pos;
}
/**
* Should roughly match up with `lv_cpicker_invalidate_disc_indicator_line`
*/
static void lv_cpicker_draw_disc_indicator_line(lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, uint16_t r,
uint16_t angle)
2019-09-25 23:38:27 -07:00
{
2019-09-26 12:32:05 -07:00
lv_point_t start;
lv_point_t end;
start.x = cx + ((r - style->line.width + ext->indicator.style->body.padding.inner + ext->indicator.style->line.width/2) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
start.y = cy + ((r - style->line.width + ext->indicator.style->body.padding.inner + ext->indicator.style->line.width/2) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
end.x = cx + ((r - ext->indicator.style->body.padding.inner - ext->indicator.style->line.width/2) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
end.y = cy + ((r - ext->indicator.style->body.padding.inner - ext->indicator.style->line.width/2) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
2019-09-26 12:32:05 -07:00
lv_draw_line(&start, &end, mask, ext->indicator.style, opa_scale);
if(ext->indicator.style->line.rounded)
{
lv_area_t circle_area;
circle_area.x1 = start.x - ((ext->indicator.style->line.width - 1) >> 1) - ((ext->indicator.style->line.width - 1) & 0x1);
circle_area.y1 = start.y - ((ext->indicator.style->line.width - 1) >> 1) - ((ext->indicator.style->line.width - 1) & 0x1);
circle_area.x2 = start.x + ((ext->indicator.style->line.width - 1) >> 1);
circle_area.y2 = start.y + ((ext->indicator.style->line.width - 1) >> 1);
lv_draw_rect(&circle_area, mask, ext->indicator.style, opa_scale);
circle_area.x1 = end.x - ((ext->indicator.style->line.width - 1) >> 1) - ((ext->indicator.style->line.width - 1) & 0x1);
circle_area.y1 = end.y - ((ext->indicator.style->line.width - 1) >> 1) - ((ext->indicator.style->line.width - 1) & 0x1);
circle_area.x2 = end.x + ((ext->indicator.style->line.width - 1) >> 1);
circle_area.y2 = end.y + ((ext->indicator.style->line.width - 1) >> 1);
lv_draw_rect(&circle_area, mask, ext->indicator.style, opa_scale);
}
2019-09-25 23:38:27 -07:00
}
/**
* Should roughly match up with `lv_cpicker_invalidate_disc_indicator_circle`
*/
static void lv_cpicker_draw_disc_indicator_circle(lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, uint16_t r,
uint16_t angle)
2019-09-25 23:38:27 -07:00
{
uint32_t ind_cx = cx + ((r - style->line.width/2) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
uint32_t ind_cy = cy + ((r - style->line.width/2) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
2019-09-26 12:32:05 -07:00
lv_area_t ind_area;
ind_area.x1 = ind_cx - style->line.width/2;
ind_area.y1 = ind_cy - style->line.width/2;
ind_area.x2 = ind_cx + style->line.width/2;
ind_area.y2 = ind_cy + style->line.width/2;
2019-09-26 12:32:05 -07:00
lv_style_t styleCopy;
lv_style_copy(&styleCopy, ext->indicator.style);
styleCopy.body.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&ind_area, mask, &styleCopy, opa_scale);
2019-09-25 23:38:27 -07:00
}
/**
* Should roughly match up with `lv_cpicker_invalidate_disc_indicator_in`
*/
static void lv_cpicker_draw_disc_indicator_in(lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, uint16_t r,
uint16_t rin, uint16_t angle)
2019-09-25 23:38:27 -07:00
{
2019-09-26 12:32:05 -07:00
uint16_t ind_radius = lv_sqrt((4*rin*rin)/2)/2 + 1 - style->body.padding.inner;
ind_radius = (ind_radius + rin) / 2;
uint32_t ind_cx = cx + ((ind_radius) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
uint32_t ind_cy = cy + ((ind_radius) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
2019-09-26 12:32:05 -07:00
lv_area_t ind_area;
ind_area.x1 = ind_cx - r;
ind_area.y1 = ind_cy - r;
ind_area.x2 = ind_cx + r;
ind_area.y2 = ind_cy + r;
2019-09-26 12:32:05 -07:00
lv_style_t styleCopy;
lv_style_copy(&styleCopy, ext->indicator.style);
styleCopy.body.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&ind_area, mask, &styleCopy, opa_scale);
2019-09-25 23:38:27 -07:00
}
static void lv_cpicker_draw_disc_indicator(lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, lv_coord_t r,
uint16_t rin, uint16_t radius, lv_area_t center_ind_area)
2019-09-25 23:38:27 -07:00
{
2019-09-26 12:32:05 -07:00
/*draw center background*/
static lv_style_t styleCenterBackground;
lv_theme_t * th = lv_theme_get_current();
if (th) {
lv_style_copy(&styleCenterBackground, th->style.bg);
} else {
lv_style_copy(&styleCenterBackground, &lv_style_plain);
}
lv_area_t center_area;
center_area.x1 = cx - rin;
center_area.y1 = cy - rin;
center_area.x2 = cx + rin;
center_area.y2 = cy + rin;
2019-09-26 12:32:05 -07:00
styleCenterBackground.body.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&center_area, mask, &styleCenterBackground, opa_scale);
/*draw the center color indicator*/
style->body.main_color = lv_color_hsv_to_rgb(ext->hue, ext->saturation, ext->value);
style->body.grad_color = style->body.main_color;
style->body.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&center_ind_area, mask, style, opa_scale);
uint16_t angle = mode_color_to_angle(ext);
/*save the angle to refresh the area later*/
ext->prev_pos = angle;
2019-09-26 12:32:05 -07:00
/*draw the current hue indicator*/
switch(ext->indicator.type)
{
case LV_CPICKER_INDICATOR_NONE:
/*no indicator*/
2019-09-26 12:32:05 -07:00
break;
case LV_CPICKER_INDICATOR_LINE:
lv_cpicker_draw_disc_indicator_line(mask, style, opa_scale, ext, cx, cy, r, angle);
2019-09-26 12:32:05 -07:00
break;
case LV_CPICKER_INDICATOR_CIRCLE:
lv_cpicker_draw_disc_indicator_circle(mask, style, opa_scale, ext, cx, cy, r, angle);
2019-09-26 12:32:05 -07:00
break;
case LV_CPICKER_INDICATOR_IN:
lv_cpicker_draw_disc_indicator_in(mask, style, opa_scale, ext, cx, cy, (rin - radius) / 3, rin, angle);
2019-09-26 12:32:05 -07:00
break;
}
2019-09-25 23:38:27 -07:00
}
static void lv_cpicker_draw_disc_gradient(lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, uint16_t r)
{
int16_t start_angle = 0; /*Default*/
int16_t end_angle = 360 - LV_CPICKER_DEF_QF; /*Default*/
2019-09-26 12:32:05 -07:00
/*if the mask does not include the center of the object
* redrawing all the wheel is not necessary;
* only a given angular range
*/
lv_point_t center = {cx, cy};
if(!lv_area_is_point_on(mask, &center)
/*
&& (mask->x1 != cpicker->coords.x1 || mask->x2 != cpicker->coords.x2
|| mask->y1 != cpicker->coords.y1 || mask->y2 != cpicker->coords.y2)
*/
)
{
/*get angle from center of object to each corners of the area*/
int16_t dr, ur, ul, dl;
dr = lv_atan2(mask->x2 - cx, mask->y2 - cy);
ur = lv_atan2(mask->x2 - cx, mask->y1 - cy);
ul = lv_atan2(mask->x1 - cx, mask->y1 - cy);
dl = lv_atan2(mask->x1 - cx, mask->y2 - cy);
/*check area position from object axis*/
uint8_t left = (mask->x2 < cx && mask->x1 < cx);
uint8_t onYaxis = (mask->x2 > cx && mask->x1 < cx);
uint8_t right = (mask->x2 > cx && mask->x1 > cx);
uint8_t top = (mask->y2 < cy && mask->y1 < cy);
uint8_t onXaxis = (mask->y2 > cy && mask->y1 < cy);
uint8_t bottom = (mask->y2 > cy && mask->y1 > cy);
/*store angular range*/
if(right && bottom)
2019-09-26 10:50:34 -07:00
{
start_angle = dl;
end_angle = ur;
}
else if(right && onXaxis)
2019-09-26 10:50:34 -07:00
{
start_angle = dl;
end_angle = ul;
}
else if(right && top)
{
start_angle = dr;
end_angle = ul;
}
else if(onYaxis && top)
{
start_angle = dr;
end_angle = dl;
}
else if(left && top)
{
start_angle = ur;
end_angle = dl;
}
else if(left && onXaxis)
{
start_angle = ur;
end_angle = dr;
}
else if(left && bottom)
{
start_angle = ul;
end_angle = dr;
}
else if(onYaxis && bottom)
{
start_angle = ul;
end_angle = ur;
2019-09-26 10:50:34 -07:00
}
/*rollover angle*/
if(start_angle > end_angle)
{
end_angle += 360;
}
/*round to QF factor*/
start_angle = start_angle/LV_CPICKER_DEF_QF*LV_CPICKER_DEF_QF;
end_angle = end_angle/LV_CPICKER_DEF_QF*LV_CPICKER_DEF_QF;;
2019-09-26 11:24:47 -07:00
/*shift angle if necessary before adding offset*/
if((start_angle - LV_CPICKER_DEF_QF) < 0)
{
start_angle += 360;
end_angle += 360;
}
2019-09-26 11:24:47 -07:00
/*ensure overlapping by adding offset*/
start_angle -= LV_CPICKER_DEF_QF;
end_angle += LV_CPICKER_DEF_QF;
2019-09-26 11:24:47 -07:00
}
2019-09-26 11:24:47 -07:00
lv_point_t triangle_points[3];
if(ext->color_mode == LV_CPICKER_COLOR_MODE_HUE)
2019-09-26 11:24:47 -07:00
{
for(uint16_t i = start_angle; i <= end_angle; i+= LV_CPICKER_DEF_QF)
2019-09-26 11:24:47 -07:00
{
style->body.main_color = angle_to_mode_color(ext, i);
style->body.grad_color = style->body.main_color;
triangle_points[0].x = cx;
triangle_points[0].y = cy;
triangle_points[1].x = cx + (r * lv_trigo_sin(i) >> LV_TRIGO_SHIFT);
triangle_points[1].y = cy + (r * lv_trigo_sin(i + 90) >> LV_TRIGO_SHIFT);
if(i == end_angle || i == (360 - LV_CPICKER_DEF_QF))
{
/*the last triangle is drawn without additional overlapping pixels*/
triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF) >> LV_TRIGO_SHIFT);
triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + 90) >> LV_TRIGO_SHIFT);
}
else
2019-09-26 11:24:47 -07:00
{
triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET) >> LV_TRIGO_SHIFT);
triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET + 90) >> LV_TRIGO_SHIFT);
}
lv_draw_triangle(triangle_points, mask, style, LV_OPA_COVER);
2019-09-26 11:24:47 -07:00
}
}
else if(ext->color_mode == LV_CPICKER_COLOR_MODE_SATURATION)
{
for(uint16_t i = start_angle; i <= end_angle; i += LV_CPICKER_DEF_QF)
2019-09-26 10:50:34 -07:00
{
style->body.main_color = angle_to_mode_color(ext, i);
style->body.grad_color = style->body.main_color;
triangle_points[0].x = cx;
triangle_points[0].y = cy;
triangle_points[1].x = cx + (r * lv_trigo_sin(i) >> LV_TRIGO_SHIFT);
triangle_points[1].y = cy + (r * lv_trigo_sin(i + 90) >> LV_TRIGO_SHIFT);
if(i == end_angle || i == (360 - LV_CPICKER_DEF_QF))
{
/*the last triangle is drawn without additional overlapping pixels*/
triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF) >> LV_TRIGO_SHIFT);
triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + 90) >> LV_TRIGO_SHIFT);
}
else
{
triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET) >> LV_TRIGO_SHIFT);
triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET + 90) >> LV_TRIGO_SHIFT);
}
lv_draw_triangle(triangle_points, mask, style, LV_OPA_COVER);
}
}
else if(ext->color_mode == LV_CPICKER_COLOR_MODE_VALUE)
{
for(uint16_t i = start_angle; i <= end_angle; i += LV_CPICKER_DEF_QF)
{
style->body.main_color = angle_to_mode_color(ext, i);
style->body.grad_color = style->body.main_color;
triangle_points[0].x = cx;
triangle_points[0].y = cy;
2019-09-26 11:24:47 -07:00
triangle_points[1].x = cx + (r * lv_trigo_sin(i) >> LV_TRIGO_SHIFT);
triangle_points[1].y = cy + (r * lv_trigo_sin(i + 90) >> LV_TRIGO_SHIFT);
2019-09-26 10:50:34 -07:00
if(i == end_angle || i == (360 - LV_CPICKER_DEF_QF))
{
/*the last triangle is drawn without additional overlapping pixels*/
triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF) >> LV_TRIGO_SHIFT);
triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + 90) >> LV_TRIGO_SHIFT);
}
else
{
triangle_points[2].x = cx + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET) >> LV_TRIGO_SHIFT);
triangle_points[2].y = cy + (r * lv_trigo_sin(i + LV_CPICKER_DEF_QF + TRI_OFFSET + 90) >> LV_TRIGO_SHIFT);
}
lv_draw_triangle(triangle_points, mask, style, LV_OPA_COVER);
}
}
}
/**
* Should roughly match up with `lv_cpicker_invalidate_disc`
*/
static void lv_cpicker_disc_design(lv_obj_t * cpicker,
lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h,
lv_coord_t cx, lv_coord_t cy, uint16_t r)
{
uint16_t rin = r - style->line.width;
/*
the square area (a and b being sides) should fit into the center of diameter d
we have:
a^2+b^2<=d^2
2a^2 <= d^2
a^2<=(d^2)/2
a <= sqrt((d^2)/2)
*/
uint16_t radius = lv_sqrt((4*rin*rin)/2)/2 - style->body.padding.inner;
lv_area_t center_ind_area;
center_ind_area.x1 = cx - radius;
center_ind_area.y1 = cy - radius;
center_ind_area.x2 = cx + radius;
center_ind_area.y2 = cy + radius;
2019-09-26 10:50:34 -07:00
/*redraw the wheel only if the mask intersect with the wheel*/
if(mask->x1 < center_ind_area.x1 || mask->x2 > center_ind_area.x2
|| mask->y1 < center_ind_area.y1 || mask->y2 > center_ind_area.y2)
{
lv_cpicker_draw_disc_gradient(mask, style, opa_scale, ext, cx, cy, r);
2019-09-26 10:50:34 -07:00
}
lv_cpicker_draw_disc_indicator(mask, style, opa_scale, ext, cx, cy, r, rin, radius, center_ind_area);
2019-09-26 11:24:47 -07:00
/*
//code to color the drawn area
static uint32_t c = 0;
lv_style_t style2;
lv_style_copy(&style2, &lv_style_plain);
style2.body.main_color.full = c;
style2.body.grad_color.full = c;
2019-09-26 10:50:34 -07:00
c += 0x123445678;
lv_draw_rect(mask, mask, &style2, opa_scale);
*/
}
static uint16_t lv_cpicker_calculate_rect_preview_area(lv_obj_t * cpicker,
lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t w)
{
2019-09-26 11:24:47 -07:00
lv_coord_t x1 = cpicker->coords.x1;
lv_coord_t y1 = cpicker->coords.y1;
lv_coord_t x2 = cpicker->coords.x2;
lv_coord_t y2 = cpicker->coords.y2;
/*prepare the color preview area*/
uint16_t preview_offset = style->line.width;
uint16_t style_body_padding_ver = style->body.padding.top + style->body.padding.bottom;
uint16_t style_body_padding_hor = style->body.padding.left + style->body.padding.right;
if(style_body_padding_ver == 0)
{
/*draw the color preview rect to the side of the gradient*/
if(style_body_padding_hor >= 0)
{
2019-09-26 11:24:47 -07:00
/*draw the preview to the right*/
ext->rect_gradient_w = w - preview_offset - (LV_MATH_ABS(style_body_padding_hor) - 1);
ext->rect_gradient_h = y2 - y1;
ext->rect_gradient_area.x1 = x1;
ext->rect_gradient_area.x2 = ext->rect_gradient_area.x1 + ext->rect_gradient_w;
ext->rect_gradient_area.y1 = y1;
ext->rect_gradient_area.y2 = y2;
ext->rect_preview_area.x1 = x2 - preview_offset;
ext->rect_preview_area.y1 = y1;
ext->rect_preview_area.x2 = x2 ;
ext->rect_preview_area.y2 = y2;
}
else
{
2019-09-26 11:24:47 -07:00
/*draw the preview to the left*/
ext->rect_gradient_w = w - preview_offset - (LV_MATH_ABS(style_body_padding_hor) - 1);
ext->rect_gradient_h = y2 - y1;
ext->rect_gradient_area.x1 = x2 - ext->rect_gradient_w;
ext->rect_gradient_area.x2 = x2;
ext->rect_gradient_area.y1 = y1;
ext->rect_gradient_area.y2 = y2;
ext->rect_preview_area.x1 = x1;
ext->rect_preview_area.y1 = y1;
ext->rect_preview_area.x2 = x1 + preview_offset;
ext->rect_preview_area.y2 = y2;
}
2019-09-26 11:24:47 -07:00
}
else
{
/*draw the color preview rect on top or below the gradient*/
if(style_body_padding_ver >= 0)
{
2019-09-26 11:24:47 -07:00
/*draw the preview on top*/
ext->rect_gradient_w = w;
ext->rect_gradient_h = (y2 - y1) - preview_offset - (LV_MATH_ABS(style_body_padding_ver) - 1);
ext->rect_gradient_area.x1 = x1;
ext->rect_gradient_area.x2 = x2;
ext->rect_gradient_area.y1 = y2 - ext->rect_gradient_h;
ext->rect_gradient_area.y2 = y2;
ext->rect_preview_area.x1 = x1;
ext->rect_preview_area.y1 = y1;
ext->rect_preview_area.x2 = x2;
ext->rect_preview_area.y2 = y1 + preview_offset;
}
else
{
/*draw the preview below the gradient*/
ext->rect_gradient_w = w;
ext->rect_gradient_h = (y2 - y1) - preview_offset - (LV_MATH_ABS(style_body_padding_ver) - 1);
ext->rect_gradient_area.x1 = x1;
ext->rect_gradient_area.x2 = x2;
ext->rect_gradient_area.y1 = y1;
ext->rect_gradient_area.y2 = y1 + ext->rect_gradient_h;
ext->rect_preview_area.x1 = x1;
ext->rect_preview_area.y1 = y2 - preview_offset;
ext->rect_preview_area.x2 = x2;
ext->rect_preview_area.y2 = y2;
}
}
return style_body_padding_hor;
}
/**
* Should roughly match up with `lv_cpicker_invalidate_rect_indicator_line`
*/
static void lv_cpicker_draw_rect_indicator_line(lv_area_t * mask, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext, lv_coord_t ind_pos)
{
lv_point_t p1, p2;
p1.x = ext->rect_gradient_area.x1 + ind_pos;
p1.y = ext->rect_gradient_area.y1;
p2.x = p1.x;
p2.y = ext->rect_gradient_area.y2;
lv_draw_line(&p1, &p2, mask, ext->indicator.style, opa_scale);
}
/**
* Should roughly match up with `lv_cpicker_invalidate_rect_indicator_circle`
*/
static void lv_cpicker_draw_rect_indicator_circle(lv_area_t * mask, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext, lv_coord_t ind_pos)
{
lv_area_t circle_ind_area;
circle_ind_area.x1 = ext->rect_gradient_area.x1 + ind_pos - ext->rect_gradient_h/2;
circle_ind_area.x2 = circle_ind_area.x1 + ext->rect_gradient_h;
circle_ind_area.y1 = ext->rect_gradient_area.y1;
circle_ind_area.y2 = ext->rect_gradient_area.y2;
lv_style_t styleCopy;
lv_style_copy(&styleCopy, ext->indicator.style);
styleCopy.body.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&circle_ind_area, mask, &styleCopy, opa_scale);
}
/**
* Should roughly match up with `lv_cpicker_invalidate_rect_indicator_in`
*/
static void lv_cpicker_draw_rect_indicator_in(lv_area_t * mask, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext, lv_coord_t ind_pos)
{
/*draw triangle at top and bottom of gradient*/
lv_point_t triangle_points[3];
triangle_points[0].x = ext->rect_gradient_area.x1 + ind_pos;
triangle_points[0].y = ext->rect_gradient_area.y1 + (ext->rect_gradient_h/3);
triangle_points[1].x = triangle_points[0].x - ext->indicator.style->line.width * 3;
triangle_points[1].y = ext->rect_gradient_area.y1 - 1;
triangle_points[2].x = triangle_points[0].x + ext->indicator.style->line.width * 3;
triangle_points[2].y = triangle_points[1].y;
lv_draw_triangle(triangle_points, mask, ext->indicator.style, LV_OPA_COVER);
triangle_points[0].y = ext->rect_gradient_area.y2 - (ext->rect_gradient_h/3);
triangle_points[1].y = ext->rect_gradient_area.y2;
triangle_points[2].y = triangle_points[1].y;
lv_draw_triangle(triangle_points, mask, ext->indicator.style, LV_OPA_COVER);
}
static void lv_cpicker_draw_rect_indicator(lv_obj_t * cpicker,
lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
uint16_t style_body_padding_hor)
{
2019-09-26 10:50:34 -07:00
/*draw the color preview indicator*/
style->body.main_color = lv_cpicker_get_color(cpicker);
style->body.grad_color = style->body.main_color;
if(style->line.rounded && style_body_padding_hor == 0)
{
style->body.radius = ext->rect_gradient_h;
}
lv_draw_rect(&(ext->rect_preview_area), mask, style, opa_scale);
2019-09-26 10:50:34 -07:00
/*
styleCopy.line.width = 10;
2019-09-26 11:24:47 -07:00
lv_draw_arc(cpicker->coords.x1 + 3*ext->rect_gradient_h/2, cpicker->coords.y1 + ext->rect_gradient_h/2, ext->rect_gradient_h / 2 + styleCopy.line.width + 2, mask, 180, 360, &styleCopy, opa_scale);
//lv_draw_arc(cpicker->coords.x1 + ext->rect_gradient_w - ext->rect_gradient_h/2, cpicker->coords.y1 + ext->rect_gradient_h/2, ext->rect_gradient_h / 2 + styleCopy.line.width + 2, mask, 0, 180, &styleCopy, opa_scale);
*/
2019-09-26 11:24:47 -07:00
/*draw the color position indicator*/
lv_coord_t ind_pos = lv_cpicker_get_indicator_coord(style, ext);
2019-09-26 11:24:47 -07:00
/*save to refresh the area later*/
ext->prev_pos = ind_pos;
2019-09-26 11:24:47 -07:00
switch(ext->indicator.type)
{
case LV_CPICKER_INDICATOR_NONE:
/*no indicator*/
break;
case LV_CPICKER_INDICATOR_LINE:
lv_cpicker_draw_rect_indicator_line(mask, opa_scale, ext, ind_pos);
2019-09-26 11:24:47 -07:00
break;
case LV_CPICKER_INDICATOR_CIRCLE:
lv_cpicker_draw_rect_indicator_circle(mask, opa_scale, ext, ind_pos);
break;
case LV_CPICKER_INDICATOR_IN:
lv_cpicker_draw_rect_indicator_in(mask, opa_scale, ext, ind_pos);
break;
default:
break;
}
}
static void lv_cpicker_draw_rect_gradient(lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext)
{
if(style->line.rounded)
2019-09-26 11:24:47 -07:00
{
/*draw rounded edges to the gradient*/
lv_area_t rounded_edge_area;
rounded_edge_area.x1 = ext->rect_gradient_area.x1;
rounded_edge_area.x2 = ext->rect_gradient_area.x1 + ext->rect_gradient_h;
rounded_edge_area.y1 = ext->rect_gradient_area.y1;
rounded_edge_area.y2 = ext->rect_gradient_area.y2;
ext->rect_gradient_area.x1 += ext->rect_gradient_h/2;
ext->rect_gradient_area.x2 -= ext->rect_gradient_h/2;
ext->rect_gradient_w -= ext->rect_gradient_h;
style->body.main_color = angle_to_mode_color(ext, 0);
style->body.grad_color = style->body.main_color;
2019-09-26 10:50:34 -07:00
style->body.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(&rounded_edge_area, mask, style, opa_scale);
rounded_edge_area.x1 += ext->rect_gradient_w - 1;
rounded_edge_area.x2 += ext->rect_gradient_w - 1;
style->body.main_color = angle_to_mode_color(ext, 360);
style->body.grad_color = style->body.main_color;
lv_draw_rect(&rounded_edge_area, mask, style, opa_scale);
2019-09-26 10:50:34 -07:00
}
for(uint16_t i = 0; i < 360; i += LV_MATH_MAX(LV_CPICKER_DEF_QF, 360/ext->rect_gradient_w))
2019-09-26 11:24:47 -07:00
{
style->body.main_color = angle_to_mode_color(ext, i);
style->body.grad_color = style->body.main_color;
/*the following attribute might need changing between index to add border, shadow, radius etc*/
style->body.radius = 0;
style->body.border.width = 0;
style->body.shadow.width = 0;
style->body.opa = LV_OPA_COVER;
lv_area_t rect_area;
/*scale angle (hue/sat/val) to linear coordinate*/
lv_coord_t xi = i / 360.0 * ext->rect_gradient_w;
rect_area.x1 = LV_MATH_MIN(ext->rect_gradient_area.x1 + xi, ext->rect_gradient_area.x1 + ext->rect_gradient_w - LV_MATH_MAX(LV_CPICKER_DEF_QF, 360/ext->rect_gradient_w));
rect_area.y1 = ext->rect_gradient_area.y1;
rect_area.x2 = rect_area.x1 + LV_MATH_MAX(LV_CPICKER_DEF_QF, 360/ext->rect_gradient_w);
rect_area.y2 = ext->rect_gradient_area.y2;
lv_draw_rect(&rect_area, mask, style, opa_scale);
2019-09-26 11:24:47 -07:00
}
if(style->line.rounded)
{
/*Restore gradient area to take rounded end in account*/
ext->rect_gradient_area.x1 -= ext->rect_gradient_h/2;
ext->rect_gradient_area.x2 += ext->rect_gradient_h/2;
//ext->rect_gradient_w += ext->rect_gradient_h;
}
}
/**
* Should roughly match up with `lv_cpicker_invalidate_rect`
*/
static void lv_cpicker_rect_design(lv_obj_t * cpicker,
lv_area_t * mask, lv_style_t * style, lv_opa_t opa_scale,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h)
{
uint16_t style_body_padding_hor = lv_cpicker_calculate_rect_preview_area(cpicker, style, ext, w);
lv_cpicker_draw_rect_gradient(mask, style, opa_scale, ext);
2019-09-26 10:50:34 -07:00
lv_cpicker_draw_rect_indicator(cpicker, mask, style, opa_scale, ext, style_body_padding_hor);
}
static void lv_cpicker_invalidate(lv_obj_t * cpicker, bool all);
static lv_res_t lv_cpicker_disc_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param,
lv_style_t * style, lv_cpicker_ext_t * ext,
lv_coord_t r_out, lv_coord_t r_in, lv_coord_t x, lv_coord_t y);
static lv_res_t lv_cpicker_rect_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param,
lv_style_t * style, lv_cpicker_ext_t * ext);
2019-09-26 10:50:34 -07:00
/**
2019-09-26 10:50:34 -07:00
* Signal function of the color_picker
* @param cpicker pointer to a color_picker object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
2019-09-26 10:50:34 -07:00
static lv_res_t lv_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param)
{
/* Include the ancient signal function */
2019-09-26 10:50:34 -07:00
lv_res_t res = ancestor_signal(cpicker, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_cpicker";
2019-09-26 10:50:34 -07:00
} else {
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
if(sign == LV_SIGNAL_CONTROL) {
uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->hue = (ext->hue + 1) % 360;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->saturation = (ext->saturation + 1) % 100;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->value = (ext->value + 1) % 100;
break;
}
lv_cpicker_invalidate(cpicker, false);
res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
else if(c == LV_KEY_LEFT)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->hue = ext->hue > 0?(ext->hue - 1):360;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->saturation = ext->saturation > 0?(ext->saturation - 1):100;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->value = ext->value > 0?(ext->value - 1):100;
break;
}
lv_cpicker_invalidate(cpicker, false);
res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
else if(c == LV_KEY_UP)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->hue = (ext->hue + 1) % 360;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->saturation = (ext->saturation + 1) % 100;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->value = (ext->value + 1) % 100;
break;
}
lv_cpicker_invalidate(cpicker, false);
res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
else if(c == LV_KEY_DOWN)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->hue = ext->hue > 0?(ext->hue - 1):360;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->saturation = ext->saturation > 0?(ext->saturation - 1):100;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->value = ext->value > 0?(ext->value - 1):100;
break;
}
2019-09-26 07:23:23 +02:00
lv_cpicker_invalidate(cpicker, false);
res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
2019-09-26 07:23:23 +02:00
}
} else {
lv_style_t * style = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN);
if(ext->type == LV_CPICKER_TYPE_DISC)
{
lv_coord_t r_out = (LV_MATH_MIN(lv_obj_get_width(cpicker), lv_obj_get_height(cpicker))) / 2;
lv_coord_t r_in = r_out - style->line.width - style->body.padding.inner;
lv_coord_t x = cpicker->coords.x1 + lv_obj_get_width(cpicker) / 2;
lv_coord_t y = cpicker->coords.y1 + lv_obj_get_height(cpicker) / 2;
res = lv_cpicker_disc_signal(cpicker, sign, param, style, ext, r_out, r_in, x, y);
if(res != LV_RES_OK) return res;
}
else if(ext->type == LV_CPICKER_TYPE_RECT)
{
res = lv_cpicker_rect_signal(cpicker, sign, param, style, ext);
if(res != LV_RES_OK) return res;
}
}
}
return res;
}
static void lv_cpicker_prev_hsv_save(lv_cpicker_ext_t * ext)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->prev_hue = ext->hue;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->prev_saturation = ext->saturation;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->prev_value = ext->value;
break;
}
}
static void lv_cpicker_prev_hsv_restore(lv_obj_t * cpicker,
lv_cpicker_ext_t * ext)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->prev_hue = ext->hue;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->prev_saturation = ext->saturation;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->prev_value = ext->value;
break;
}
lv_cpicker_invalidate(cpicker, false);
}
static void lv_cpicker_reset_hsv_if_double_clicked(lv_obj_t * cpicker,
lv_cpicker_ext_t * ext)
{
if(lv_tick_elaps(ext->last_click) < 400)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->hue = 0;
ext->prev_hue = ext->hue;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->saturation = 100;
ext->prev_saturation = ext->saturation;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->value = 100;
ext->prev_value = ext->value;
break;
}
lv_cpicker_invalidate(cpicker, false);
}
ext->last_click = lv_tick_get();
}
static void lv_cpicker_set_next_color_mode(lv_obj_t * cpicker,
lv_cpicker_ext_t * ext)
{
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
ext->prev_hue = ext->hue;
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
ext->prev_saturation = ext->saturation;
break;
case LV_CPICKER_COLOR_MODE_VALUE:
ext->prev_value = ext->value;
break;
}
ext->color_mode = (ext->color_mode + 1) % 3;
lv_cpicker_invalidate(cpicker, true);
}
static lv_res_t lv_cpicker_set_hsv_percent(lv_obj_t * cpicker,
lv_cpicker_ext_t * ext,
float percent)
{
lv_res_t res;
bool changed = false;
uint16_t hsv;
switch(ext->color_mode)
{
case LV_CPICKER_COLOR_MODE_HUE:
hsv = percent * 360.0;
changed = hsv != ext->hue;
if (changed)
{
ext->hue = hsv;
ext->prev_hue = ext->hue;
}
break;
case LV_CPICKER_COLOR_MODE_SATURATION:
hsv = percent * 100.0;
changed = hsv != ext->saturation;
if (changed)
{
ext->saturation = hsv;
ext->prev_saturation = ext->saturation;
}
break;
case LV_CPICKER_COLOR_MODE_VALUE:
hsv = percent * 100.0;
changed = hsv != ext->value;
if (changed)
{
ext->value = hsv;
ext->prev_value = ext->value;
}
break;
}
if (changed)
{
lv_cpicker_invalidate(cpicker, false);
res = lv_event_send(cpicker, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
return res;
}
static lv_res_t lv_cpicker_disc_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param,
lv_style_t * style, lv_cpicker_ext_t * ext,
lv_coord_t r_out, lv_coord_t r_in, lv_coord_t x, lv_coord_t y)
{
lv_res_t res;
if(sign == LV_SIGNAL_PRESSED)
{
lv_cpicker_prev_hsv_save(ext);
lv_indev_t * indev = param;
lv_coord_t xp = indev->proc.types.pointer.act_point.x - x;
lv_coord_t yp = indev->proc.types.pointer.act_point.y - y;
if((xp*xp + yp*yp) < (r_in*r_in))
{
lv_cpicker_reset_hsv_if_double_clicked(cpicker, ext);
}
}
else if(sign == LV_SIGNAL_PRESSING)
{
lv_indev_t * indev = param;
lv_coord_t xp = indev->proc.types.pointer.act_point.x - x;
lv_coord_t yp = indev->proc.types.pointer.act_point.y - y;
if((xp*xp + yp*yp) < (r_out*r_out) && (xp*xp + yp*yp) >= (r_in*r_in))
{
float percent = lv_atan2(xp, yp) / 360.0;
res = lv_cpicker_set_hsv_percent(cpicker, ext, percent);
if(res != LV_RES_OK) return res;
}
}
else if(sign == LV_SIGNAL_PRESS_LOST)
{
lv_cpicker_prev_hsv_restore(cpicker, ext);
}
else if(sign == LV_SIGNAL_RELEASED)
{
lv_indev_t * indev = param;
lv_coord_t xp = indev->proc.types.pointer.act_point.x - x;
lv_coord_t yp = indev->proc.types.pointer.act_point.y - y;
if((xp*xp + yp*yp) < (r_out*r_out) && (xp*xp + yp*yp) >= (r_in*r_in))
{
float percent = lv_atan2(xp, yp) / 360.0;
res = lv_cpicker_set_hsv_percent(cpicker, ext, percent);
if(res != LV_RES_OK) return res;
}
}
else if(sign == LV_SIGNAL_LONG_PRESS)
{
if(!ext->color_mode_fixed)
{
lv_indev_t * indev = param;
lv_coord_t xp = indev->proc.types.pointer.act_point.x - x;
lv_coord_t yp = indev->proc.types.pointer.act_point.y - y;
if((xp*xp + yp*yp) < (r_in*r_in))
{
lv_cpicker_set_next_color_mode(cpicker, ext);
}
}
}
return res;
}
static lv_res_t lv_cpicker_rect_signal(lv_obj_t * cpicker, lv_signal_t sign, void * param,
lv_style_t * style, lv_cpicker_ext_t * ext)
{
lv_res_t res;
2019-09-26 10:50:34 -07:00
if(sign == LV_SIGNAL_PRESSED)
{
lv_cpicker_prev_hsv_save(ext);
lv_indev_t * indev = param;
if(lv_area_is_point_on(&(ext->rect_preview_area), &indev->proc.types.pointer.act_point))
{
lv_cpicker_reset_hsv_if_double_clicked(cpicker, ext);
}
}
else if(sign == LV_SIGNAL_PRESSING)
{
lv_indev_t * indev = param;
if(lv_area_is_point_on(&(ext->rect_gradient_area), &indev->proc.types.pointer.act_point))
{
uint16_t width = ext->rect_gradient_area.x2 - ext->rect_gradient_area.x1;
uint16_t distance = indev->proc.types.pointer.act_point.x - ext->rect_gradient_area.x1;
float percent = distance / (float) width;
res = lv_cpicker_set_hsv_percent(cpicker, ext, percent);
if(res != LV_RES_OK) return res;
}
}
else if(sign == LV_SIGNAL_PRESS_LOST)
{
lv_cpicker_prev_hsv_restore(cpicker, ext);
}
else if(sign == LV_SIGNAL_RELEASED)
{
lv_indev_t * indev = param;
if(lv_area_is_point_on(&(ext->rect_gradient_area), &indev->proc.types.pointer.act_point))
{
uint16_t width = ext->rect_gradient_area.x2 - ext->rect_gradient_area.x1;
uint16_t distance = indev->proc.types.pointer.act_point.x - ext->rect_gradient_area.x1;
float percent = distance / (float) width;
res = lv_cpicker_set_hsv_percent(cpicker, ext, percent);
if(res != LV_RES_OK) return res;
}
}
else if(sign == LV_SIGNAL_LONG_PRESS)
{
if(!ext->color_mode_fixed)
{
lv_indev_t * indev = param;
if(lv_area_is_point_on(&(ext->rect_preview_area), &indev->proc.types.pointer.act_point))
{
lv_cpicker_set_next_color_mode(cpicker, ext);
}
}
}
return res;
}
static void lv_cpicker_invalidate_disc(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h,
lv_coord_t cx, lv_coord_t cy, uint16_t r);
static void lv_cpicker_invalidate_rect(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h);
/**
* Indicator points need to match those set in lv_cpicker_disc_design/lv_cpicker_rect_design
*/
static void lv_cpicker_invalidate(lv_obj_t * cpicker, bool all)
{
if (all)
{
lv_obj_invalidate(cpicker);
return;
}
lv_disp_t * disp = lv_obj_get_disp(cpicker);
lv_cpicker_ext_t * ext = lv_obj_get_ext_attr(cpicker);
lv_style_t * style = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN);
lv_style_t styleCopy;
lv_style_copy(&styleCopy, style);
lv_coord_t w = lv_obj_get_width(cpicker);
lv_coord_t h = lv_obj_get_height(cpicker);
if(ext->type == LV_CPICKER_TYPE_DISC)
{
lv_coord_t cx = cpicker->coords.x1 + w / 2;
lv_coord_t cy = cpicker->coords.y1 + h / 2;
uint16_t r = LV_MATH_MIN(w, h) / 2;
lv_cpicker_invalidate_disc(disp, &styleCopy, ext, w, h, cx, cy, r);
}
else if(ext->type == LV_CPICKER_TYPE_RECT)
{
lv_cpicker_invalidate_rect(disp, &styleCopy, ext, w, h);
}
}
/**
* Should roughly match up with `lv_cpicker_draw_disc_indicator_line`
*/
static void lv_cpicker_invalidate_disc_indicator_line(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, uint16_t r,
uint16_t angle)
{
lv_coord_t x1 = cx + ((r - style->line.width + ext->indicator.style->body.padding.inner + ext->indicator.style->line.width/2) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
lv_coord_t y1 = cy + ((r - style->line.width + ext->indicator.style->body.padding.inner + ext->indicator.style->line.width/2) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
lv_coord_t x2 = cx + ((r - ext->indicator.style->body.padding.inner - ext->indicator.style->line.width/2) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
lv_coord_t y2 = cy + ((r - ext->indicator.style->body.padding.inner - ext->indicator.style->line.width/2) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
lv_point_t point1, point2;
point1.x = x1;
point1.y = y1;
point2.x = x2;
point2.y = y2;
lv_area_t line_area;
//if(LV_MATH_ABS(point1.x - point2.x) > LV_MATH_ABS(point1.y - point2.y))
//{
/*Steps less in y than x -> rather horizontal*/
if(point1.x < point2.x) {
line_area.x1 = point1.x;
//line_area.y1 = point1.y;
line_area.x2 = point2.x;
//line_area.y2 = point2.y;
} else {
line_area.x1 = point2.x;
//line_area.y1 = point2.y;
line_area.x2 = point1.x;
//line_area.y2 = point1.y;
}
//} else {
/*Steps less in x than y -> rather vertical*/
if(point1.y < point2.y) {
//line_area.x1 = point1.x;
line_area.y1 = point1.y;
//line_area.x2 = point2.x;
line_area.y2 = point2.y;
} else {
//line_area.x1 = point2.x;
line_area.y1 = point2.y;
//line_area.x2 = point1.x;
line_area.y2 = point1.y;
}
//}
line_area.x1 -= 2*ext->indicator.style->line.width;
line_area.y1 -= 2*ext->indicator.style->line.width;
line_area.x2 += 2*ext->indicator.style->line.width;
line_area.y2 += 2*ext->indicator.style->line.width;
lv_inv_area(disp, &line_area);
}
/**
* Should roughly match up with `lv_cpicker_draw_disc_indicator_circle`
*/
static void lv_cpicker_invalidate_disc_indicator_circle(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t cx, lv_coord_t cy, uint16_t r,
uint16_t angle)
{
uint32_t ind_cx = cx + ((r - style->line.width/2) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
uint32_t ind_cy = cy + ((r - style->line.width/2) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
lv_area_t ind_area;
ind_area.x1 = ind_cx - style->line.width/2;
ind_area.y1 = ind_cy - style->line.width/2;
ind_area.x2 = ind_cx + style->line.width/2;
ind_area.y2 = ind_cy + style->line.width/2;
lv_inv_area(disp, &ind_area);
}
/**
* Should roughly match up with `lv_cpicker_draw_disc_indicator_in`
*/
static void lv_cpicker_invalidate_disc_indicator_in(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t x, lv_coord_t y, uint16_t r,
uint16_t rin, uint16_t angle)
{
uint16_t ind_radius = lv_sqrt((4*rin*rin)/2)/2 + 1 - style->body.padding.inner;
ind_radius = (ind_radius + rin) / 2;
uint16_t ind_cx = x + ((ind_radius) * lv_trigo_sin(angle) >> LV_TRIGO_SHIFT);
uint16_t ind_cy = y + ((ind_radius) * lv_trigo_sin(angle + 90) >> LV_TRIGO_SHIFT);
lv_area_t ind_area;
ind_area.x1 = ind_cx - r;
ind_area.y1 = ind_cy - r;
ind_area.x2 = ind_cx + r;
ind_area.y2 = ind_cy + r;
lv_inv_area(disp, &ind_area);
}
/**
* Should roughly match up with `lv_cpicker_disc_design`
*/
static void lv_cpicker_invalidate_disc(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h,
lv_coord_t cx, lv_coord_t cy, uint16_t r)
{
/*invalidate center color area*/
uint16_t rin = r - style->line.width;
/*
the square area (a and b being sides) should fit into the center of diameter d
we have:
a^2+b^2<=d^2
2a^2 <= d^2
a^2<=(d^2)/2
a <= sqrt((d^2)/2)
*/
uint16_t radius = lv_sqrt((4*rin*rin)/2)/2 + 1 - style->body.padding.inner;
lv_area_t center_color_area;
center_color_area.x1 = cx - radius;
center_color_area.y1 = cy - radius;
center_color_area.x2 = cx + radius;
center_color_area.y2 = cy + radius;
lv_inv_area(disp, &center_color_area);
/*invalidate indicator*/
uint16_t angle = mode_color_to_angle(ext);
switch(ext->indicator.type)
{
case LV_CPICKER_INDICATOR_LINE:
lv_cpicker_invalidate_disc_indicator_line(disp, style, ext, cx, cy, r, angle);
lv_cpicker_invalidate_disc_indicator_line(disp, style, ext, cx, cy, r, ext->prev_pos);
break;
case LV_CPICKER_INDICATOR_CIRCLE:
lv_cpicker_invalidate_disc_indicator_circle(disp, style, ext, cx, cy, r, angle);
lv_cpicker_invalidate_disc_indicator_circle(disp, style, ext, cx, cy, r, ext->prev_pos);
break;
case LV_CPICKER_INDICATOR_IN:
lv_cpicker_invalidate_disc_indicator_in(disp, style, ext, cx, cy, (rin - radius) / 3, rin, angle);
lv_cpicker_invalidate_disc_indicator_in(disp, style, ext, cx, cy, (rin - radius) / 3, rin, ext->prev_pos);
break;
}
}
/**
* Should roughly match up with `lv_cpicker_draw_rect_indicator_line`
*/
static void lv_cpicker_invalidate_rect_indicator_line(lv_disp_t * disp,
lv_cpicker_ext_t * ext,
lv_coord_t ind_pos)
{
lv_area_t line_area;
line_area.x1 = ext->rect_gradient_area.x1 + ind_pos - ext->indicator.style->line.width;
line_area.y1 = ext->rect_gradient_area.y1;
line_area.x2 = ext->rect_gradient_area.x1 + ind_pos + ext->indicator.style->line.width;
line_area.y2 = ext->rect_gradient_area.y2;
lv_inv_area(disp, &line_area);
}
/**
* Should roughly match up with `lv_cpicker_draw_rect_indicator_circle`
*/
static void lv_cpicker_invalidate_rect_indicator_circle(lv_disp_t * disp,
lv_cpicker_ext_t * ext,
lv_coord_t ind_pos)
{
lv_area_t circle_ind_area;
circle_ind_area.x1 = ext->rect_gradient_area.x1 + ind_pos - ext->rect_gradient_h/2;
circle_ind_area.x2 = circle_ind_area.x1 + ext->rect_gradient_h;
circle_ind_area.y1 = ext->rect_gradient_area.y1;
circle_ind_area.y2 = ext->rect_gradient_area.y2;
lv_inv_area(disp, &circle_ind_area);
}
/**
* Should roughly match up with `lv_cpicker_draw_rect_indicator_in`
*/
static void lv_cpicker_invalidate_rect_indicator_in(lv_disp_t * disp,
lv_cpicker_ext_t * ext,
lv_coord_t ind_pos)
{
lv_coord_t center = ext->rect_gradient_area.x1 + ind_pos;
lv_area_t ind_area;
ind_area.x1 = center - ext->indicator.style->line.width * 3;
ind_area.y1 = ext->rect_gradient_area.y1 - 1;
ind_area.x2 = center + ext->indicator.style->line.width * 3;
ind_area.y2 = ext->rect_gradient_area.y2;
lv_inv_area(disp, &ind_area);
}
/**
* Should roughly match up with `lv_cpicker_rect_design`
*/
static void lv_cpicker_invalidate_rect(lv_disp_t * disp, lv_style_t * style,
lv_cpicker_ext_t * ext,
lv_coord_t w, lv_coord_t h)
{
/*invalidate color preview indicator*/
lv_inv_area(disp, &ext->rect_preview_area);
/*invalidate indicator*/
lv_coord_t ind_pos = lv_cpicker_get_indicator_coord(style, ext);
switch(ext->indicator.type)
{
case LV_CPICKER_INDICATOR_LINE:
lv_cpicker_invalidate_rect_indicator_line(disp, ext, ind_pos);
lv_cpicker_invalidate_rect_indicator_line(disp, ext, ext->prev_pos);
break;
case LV_CPICKER_INDICATOR_CIRCLE:
lv_cpicker_invalidate_rect_indicator_circle(disp, ext, ind_pos);
lv_cpicker_invalidate_rect_indicator_circle(disp, ext, ext->prev_pos);
break;
case LV_CPICKER_INDICATOR_IN:
lv_cpicker_invalidate_rect_indicator_in(disp, ext, ind_pos);
lv_cpicker_invalidate_rect_indicator_in(disp, ext, ext->prev_pos);
break;
}
}
#endif /* LV_USE_CPICKER != 0 */