(#788) Make ColorPicker work across all of the layers
parent
2eee928605
commit
6c48bc6475
26
src/color.c
26
src/color.c
|
@ -127,3 +127,29 @@ int color_hex_to_stream(Color color, FILE *stream)
|
|||
SDL_Color sdl = color_for_sdl(color);
|
||||
return fprintf(stream, "%02x%02x%02x", sdl.r, sdl.g, sdl.b);
|
||||
}
|
||||
|
||||
Color rgba_to_hsla(Color color)
|
||||
{
|
||||
const float max = fmaxf(color.r, fmaxf(color.g, color.b));
|
||||
const float min = fminf(color.r, fminf(color.g, color.b));
|
||||
const float c = max - min;
|
||||
float hue = 0.0f;
|
||||
float saturation = 0.0f;
|
||||
const float lightness = (max + min) * 0.5f;
|
||||
|
||||
if (fabsf(c) > 1e-6) {
|
||||
if (fabs(max - color.r) <= 1e-6) {
|
||||
hue = 60.0f * fmodf((color.g - color.b) / c, 6.0f);
|
||||
} else if (fabs(max - color.g) <= 1e-6) {
|
||||
hue = 60.0f * ((color.b - color.r) / c + 2.0f);
|
||||
} else {
|
||||
hue = 60.0f * ((color.r - color.g) / c + 4.0f);
|
||||
}
|
||||
|
||||
saturation = c / (1.0f - fabsf(2.0f * lightness - 1.0f));
|
||||
}
|
||||
|
||||
// TODO: Color struct is used not only for RGBA
|
||||
// But also for HSLA. We should make another similar struct but for HSLA
|
||||
return rgba(hue, saturation, lightness, color.a);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ typedef struct Color {
|
|||
|
||||
Color rgba(float r, float g, float b, float a);
|
||||
Color hsla(float h, float s, float l, float a);
|
||||
Color rgba_to_hsla(Color color);
|
||||
Color hexstr(const char *hexstr);
|
||||
SDL_Color color_for_sdl(Color color);
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ Level *create_level_from_level_editor(const LevelEditor *level_editor,
|
|||
|
||||
level->background = PUSH_LT(
|
||||
lt,
|
||||
create_background(level_editor->background_layer.color),
|
||||
create_background(color_picker_rgba(&level_editor->background_layer)),
|
||||
destroy_background);
|
||||
if (level->background == NULL) {
|
||||
RETURN_LT(lt, NULL);
|
||||
|
|
|
@ -54,7 +54,7 @@ LevelEditor *create_level_editor(void)
|
|||
RETURN_LT(lt, NULL);
|
||||
}
|
||||
|
||||
level_editor->background_layer.color = hexstr("fffda5");
|
||||
level_editor->background_layer = create_color_picker_from_rgba(hexstr("fffda5"));
|
||||
|
||||
level_editor->player_layer = PUSH_LT(
|
||||
lt,
|
||||
|
@ -300,7 +300,7 @@ int level_editor_render(const LevelEditor *level_editor,
|
|||
trace_assert(level_editor);
|
||||
trace_assert(camera);
|
||||
|
||||
if (camera_clear_background(camera, level_editor->background_layer.color) < 0) {
|
||||
if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,16 +9,8 @@
|
|||
#include "color_picker.h"
|
||||
#include "color.h"
|
||||
|
||||
#define COLOR_CELL_WIDTH 50.0f
|
||||
#define COLOR_CELL_HEIGHT 50.0f
|
||||
|
||||
// TODO(#788): Colors of ColorPicker are poor
|
||||
static Color colors[] = {
|
||||
{1.0f, 0.0f, 0.0f, 1.0f},
|
||||
{0.0f, 1.0f, 0.0f, 1.0f},
|
||||
{0.0f, 0.0f, 1.0f, 1.0f}
|
||||
};
|
||||
static const size_t colors_count = sizeof(colors) / sizeof(Color);
|
||||
#define COLOR_SLIDER_HEIGHT 50.0f
|
||||
#define COLOR_SLIDER_WIDTH 300.0f
|
||||
|
||||
LayerPtr color_picker_as_layer(ColorPicker *color_picker)
|
||||
{
|
||||
|
@ -29,6 +21,17 @@ LayerPtr color_picker_as_layer(ColorPicker *color_picker)
|
|||
return layer;
|
||||
}
|
||||
|
||||
ColorPicker create_color_picker_from_rgba(Color color)
|
||||
{
|
||||
Color color_hsla = rgba_to_hsla(color);
|
||||
ColorPicker color_picker = {
|
||||
.hue = {0, color_hsla.r, 360.0f},
|
||||
.saturation = {0, color_hsla.g, 1.0f},
|
||||
.lightness = {0, color_hsla.b, 1.0f}
|
||||
};
|
||||
return color_picker;
|
||||
}
|
||||
|
||||
int color_picker_read_from_line_stream(ColorPicker *color_picker,
|
||||
LineStream *line_stream)
|
||||
{
|
||||
|
@ -42,14 +45,7 @@ int color_picker_read_from_line_stream(ColorPicker *color_picker,
|
|||
log_fail("Could not read color\n");
|
||||
}
|
||||
|
||||
color_picker->color = hexstr(color);
|
||||
|
||||
color_picker->hue.value = 0.0f;
|
||||
color_picker->hue.max_value = 360.0f;
|
||||
color_picker->saturation.value = 0.0f;
|
||||
color_picker->saturation.max_value = 1.0f;
|
||||
color_picker->lightness.value = 0.0f;
|
||||
color_picker->lightness.max_value = 1.0f;
|
||||
*color_picker = create_color_picker_from_rgba(hexstr(color));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -60,40 +56,29 @@ int color_picker_render(const ColorPicker *color_picker,
|
|||
trace_assert(color_picker);
|
||||
trace_assert(camera);
|
||||
|
||||
for (size_t i = 0; i < colors_count; ++i) {
|
||||
if (camera_fill_rect_screen(
|
||||
camera,
|
||||
rect(COLOR_CELL_WIDTH * (float) i, 0,
|
||||
COLOR_CELL_WIDTH,
|
||||
COLOR_CELL_HEIGHT),
|
||||
colors[i]) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Color Picker sliders don't have any labels */
|
||||
|
||||
if (slider_render(
|
||||
&color_picker->hue,
|
||||
camera,
|
||||
rect(0.0f, COLOR_CELL_HEIGHT,
|
||||
300.0f, COLOR_CELL_HEIGHT)) < 0) {
|
||||
rect(0.0f, COLOR_SLIDER_HEIGHT,
|
||||
COLOR_SLIDER_WIDTH, COLOR_SLIDER_HEIGHT)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slider_render(
|
||||
&color_picker->saturation,
|
||||
camera,
|
||||
rect(0.0f, COLOR_CELL_HEIGHT * 2.0f,
|
||||
300.0f, COLOR_CELL_HEIGHT)) < 0) {
|
||||
rect(0.0f, COLOR_SLIDER_HEIGHT * 2.0f,
|
||||
COLOR_SLIDER_WIDTH, COLOR_SLIDER_HEIGHT)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slider_render(
|
||||
&color_picker->lightness,
|
||||
camera,
|
||||
rect(0.0f, COLOR_CELL_HEIGHT * 3.0f,
|
||||
300.0f, COLOR_CELL_HEIGHT)) < 0) {
|
||||
rect(0.0f, COLOR_SLIDER_HEIGHT * 3.0f,
|
||||
COLOR_SLIDER_WIDTH, COLOR_SLIDER_HEIGHT)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -101,60 +86,54 @@ int color_picker_render(const ColorPicker *color_picker,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int color_picker_event(ColorPicker *color_picker, const SDL_Event *event, int *selected)
|
||||
// TODO: the `selected` event propagation control is cumbersome
|
||||
int color_picker_event(ColorPicker *color_picker, const SDL_Event *event, int *selected_out)
|
||||
{
|
||||
trace_assert(color_picker);
|
||||
trace_assert(event);
|
||||
|
||||
int selected = 0;
|
||||
|
||||
if (slider_event(&color_picker->hue,
|
||||
event,
|
||||
rect(0.0f, COLOR_CELL_HEIGHT,
|
||||
300.0f, COLOR_CELL_HEIGHT)) < 0) {
|
||||
rect(0.0f, COLOR_SLIDER_HEIGHT,
|
||||
COLOR_SLIDER_WIDTH, COLOR_SLIDER_HEIGHT),
|
||||
&selected) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slider_event(&color_picker->saturation,
|
||||
event,
|
||||
rect(0.0f, COLOR_CELL_HEIGHT * 2.0f,
|
||||
300.0f, COLOR_CELL_HEIGHT)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slider_event(&color_picker->lightness,
|
||||
event,
|
||||
rect(0.0f, COLOR_CELL_HEIGHT * 3.0f,
|
||||
300.0f, COLOR_CELL_HEIGHT)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
color_picker->color = hsla(
|
||||
color_picker->hue.value,
|
||||
color_picker->saturation.value,
|
||||
color_picker->lightness.value,
|
||||
1.0f);
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
switch (event->button.button) {
|
||||
case SDL_BUTTON_LEFT: {
|
||||
for (size_t i = 0; i < colors_count; ++i) {
|
||||
const Vec mouse_position = vec((float) event->button.x, (float) event->button.y);
|
||||
const Rect color_cell =
|
||||
rect(COLOR_CELL_WIDTH * (float) i, 0,
|
||||
COLOR_CELL_WIDTH,
|
||||
COLOR_CELL_HEIGHT);
|
||||
if (rect_contains_point(color_cell, mouse_position)) {
|
||||
color_picker->color = colors[i];
|
||||
if (selected) {
|
||||
*selected = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
if (!selected) {
|
||||
if (slider_event(&color_picker->saturation,
|
||||
event,
|
||||
rect(0.0f, COLOR_SLIDER_HEIGHT * 2.0f,
|
||||
COLOR_SLIDER_WIDTH, COLOR_SLIDER_HEIGHT),
|
||||
&selected) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!selected) {
|
||||
if (slider_event(&color_picker->lightness,
|
||||
event,
|
||||
rect(0.0f, COLOR_SLIDER_HEIGHT * 3.0f,
|
||||
COLOR_SLIDER_WIDTH, COLOR_SLIDER_HEIGHT),
|
||||
&selected) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_out) {
|
||||
*selected_out = selected;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Color color_picker_rgba(const ColorPicker *color_picker)
|
||||
{
|
||||
return hsla(
|
||||
color_picker->hue.value,
|
||||
color_picker->saturation.value,
|
||||
color_picker->lightness.value,
|
||||
1.0f);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "ui/slider.h"
|
||||
|
||||
typedef struct {
|
||||
Color color;
|
||||
// TODO: ColorPicker should use array of sliders
|
||||
Slider hue;
|
||||
Slider saturation;
|
||||
|
@ -15,6 +14,8 @@ typedef struct {
|
|||
|
||||
typedef struct LineStream LineStream;
|
||||
|
||||
ColorPicker create_color_picker_from_rgba(Color color);
|
||||
|
||||
int color_picker_read_from_line_stream(ColorPicker *color_picker,
|
||||
LineStream *line_stream);
|
||||
|
||||
|
@ -24,4 +25,6 @@ int color_picker_render(const ColorPicker *color_picker,
|
|||
Camera *camera);
|
||||
int color_picker_event(ColorPicker *color_picker, const SDL_Event *event, int *selected);
|
||||
|
||||
Color color_picker_rgba(const ColorPicker *color_picker);
|
||||
|
||||
#endif // COLOR_PICKER_H_
|
||||
|
|
|
@ -65,7 +65,7 @@ int layer_dump_stream(LayerPtr layer,
|
|||
return player_layer_dump_stream(layer.ptr, stream);
|
||||
|
||||
case LAYER_COLOR_PICKER: {
|
||||
color_hex_to_stream(((ColorPicker*)layer.ptr)->color, stream);
|
||||
color_hex_to_stream(color_picker_rgba(layer.ptr), stream);
|
||||
return fprintf(stream, "\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ PlayerLayer *create_player_layer(Vec position, Color color)
|
|||
player_layer->lt = lt;
|
||||
|
||||
player_layer->position = position;
|
||||
player_layer->color_picker.color = color;
|
||||
player_layer->color_picker = create_color_picker_from_rgba(color);
|
||||
|
||||
return player_layer;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ int player_layer_render(const PlayerLayer *player_layer,
|
|||
player_layer->position,
|
||||
vec(25.0f, 25.0f)),
|
||||
color_scale(
|
||||
player_layer->color_picker.color,
|
||||
color_picker_rgba(&player_layer->color_picker),
|
||||
rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.0f))) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ int player_layer_dump_stream(const PlayerLayer *player_layer,
|
|||
trace_assert(filedump);
|
||||
|
||||
fprintf(filedump, "%f %f ", player_layer->position.x, player_layer->position.y);
|
||||
color_hex_to_stream(player_layer->color_picker.color, filedump);
|
||||
color_hex_to_stream(color_picker_rgba(&player_layer->color_picker), filedump);
|
||||
fprintf(filedump, "\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -119,7 +119,7 @@ PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
|
|||
|
||||
point_layer->selected = -1;
|
||||
|
||||
point_layer->color_picker.color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
point_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
return point_layer;
|
||||
}
|
||||
|
@ -203,6 +203,7 @@ static int point_layer_mouse_button(PointLayer *point_layer,
|
|||
const int n = (int) dynarray_count(point_layer->points);
|
||||
const Point *points = dynarray_data(point_layer->points);
|
||||
const Point point = camera_map_screen(camera, event->x, event->y);
|
||||
const Color color = color_picker_rgba(&point_layer->color_picker);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (vec_length(vec_sub(points[i], point)) < POINT_LAYER_ELEMENT_RADIUS) {
|
||||
|
@ -219,7 +220,7 @@ static int point_layer_mouse_button(PointLayer *point_layer,
|
|||
id[ID_MAX_SIZE - 1] = '\0';
|
||||
|
||||
dynarray_push(point_layer->points, &point);
|
||||
dynarray_push(point_layer->colors, &point_layer->color_picker.color);
|
||||
dynarray_push(point_layer->colors, &color);
|
||||
dynarray_push(point_layer->ids, id);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
#define PROTO_AREA_THRESHOLD 10.0
|
||||
|
||||
int proto_rect_render(const ProtoRect *proto_rect,
|
||||
Camera *camera)
|
||||
Camera *camera,
|
||||
Color color)
|
||||
{
|
||||
trace_assert(proto_rect);
|
||||
trace_assert(proto_rect->color_current);
|
||||
trace_assert(camera);
|
||||
|
||||
if (proto_rect->active) {
|
||||
|
@ -24,7 +24,7 @@ int proto_rect_render(const ProtoRect *proto_rect,
|
|||
rect_from_points(
|
||||
proto_rect->begin,
|
||||
proto_rect->end),
|
||||
*proto_rect->color_current) < 0) {
|
||||
color) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +34,14 @@ int proto_rect_render(const ProtoRect *proto_rect,
|
|||
|
||||
int proto_rect_event(ProtoRect *proto_rect,
|
||||
const SDL_Event *event,
|
||||
const Camera *camera)
|
||||
const Camera *camera,
|
||||
Color color,
|
||||
RectLayer *layer)
|
||||
{
|
||||
trace_assert(proto_rect);
|
||||
trace_assert(proto_rect->color_current);
|
||||
trace_assert(proto_rect->layer_current);
|
||||
trace_assert(event);
|
||||
trace_assert(camera);
|
||||
trace_assert(layer);
|
||||
|
||||
if (proto_rect->active) {
|
||||
// Active
|
||||
|
@ -55,7 +56,7 @@ int proto_rect_event(ProtoRect *proto_rect,
|
|||
const float area = real_rect.w * real_rect.h;
|
||||
|
||||
if (area >= PROTO_AREA_THRESHOLD) {
|
||||
rect_layer_add_rect(proto_rect->layer_current, real_rect, *proto_rect->color_current);
|
||||
rect_layer_add_rect(layer, real_rect, color);
|
||||
} else {
|
||||
log_info("The area is too small %f. Such small box won't be created.\n", area);
|
||||
}
|
||||
|
|
|
@ -7,15 +7,15 @@ typedef struct {
|
|||
bool active;
|
||||
Sint32 x, y;
|
||||
Vec begin, end;
|
||||
|
||||
Color *color_current;
|
||||
RectLayer *layer_current;
|
||||
} ProtoRect;
|
||||
|
||||
int proto_rect_render(const ProtoRect *proto_rect,
|
||||
Camera *camera);
|
||||
Camera *camera,
|
||||
Color color);
|
||||
int proto_rect_event(ProtoRect *proto_rect,
|
||||
const SDL_Event *event,
|
||||
const Camera *camera);
|
||||
const Camera *camera,
|
||||
Color color,
|
||||
RectLayer *layer);
|
||||
|
||||
#endif // PROTO_RECT_H_
|
||||
|
|
|
@ -67,9 +67,7 @@ RectLayer *create_rect_layer(void)
|
|||
RETURN_LT(lt, NULL);
|
||||
}
|
||||
|
||||
layer->color_picker.color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
layer->proto_rect.color_current = &layer->color_picker.color;
|
||||
layer->proto_rect.layer_current = layer;
|
||||
layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
@ -148,7 +146,10 @@ int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
|
|||
}
|
||||
}
|
||||
|
||||
if (proto_rect_render(&layer->proto_rect, camera) < 0) {
|
||||
if (proto_rect_render(
|
||||
&layer->proto_rect,
|
||||
camera,
|
||||
color_picker_rgba(&layer->color_picker)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -169,7 +170,13 @@ int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *cam
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (!selected && proto_rect_event(&layer->proto_rect, event, camera) < 0) {
|
||||
if (!selected &&
|
||||
proto_rect_event(
|
||||
&layer->proto_rect,
|
||||
event,
|
||||
camera,
|
||||
color_picker_rgba(&layer->color_picker),
|
||||
layer) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ Player *create_player_from_player_layer(const PlayerLayer *player_layer,
|
|||
player->dying_body = PUSH_LT(
|
||||
lt,
|
||||
create_explosion(
|
||||
player_layer->color_picker.color,
|
||||
color_picker_rgba(&player_layer->color_picker),
|
||||
PLAYER_DEATH_DURATION),
|
||||
destroy_explosion);
|
||||
if (player->dying_body == NULL) {
|
||||
|
@ -83,7 +83,7 @@ Player *create_player_from_player_layer(const PlayerLayer *player_layer,
|
|||
}
|
||||
|
||||
player->jump_threshold = 0;
|
||||
player->color = player_layer->color_picker.color;
|
||||
player->color = color_picker_rgba(&player_layer->color_picker);
|
||||
player->checkpoint = player_layer->position;
|
||||
player->play_die_cue = 0;
|
||||
player->state = PLAYER_STATE_ALIVE;
|
||||
|
|
|
@ -34,7 +34,7 @@ int slider_render(const Slider *slider, Camera *camera, Rect boundary)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int slider_event(Slider *slider, const SDL_Event *event, Rect boundary)
|
||||
int slider_event(Slider *slider, const SDL_Event *event, Rect boundary, int *selected)
|
||||
{
|
||||
trace_assert(slider);
|
||||
trace_assert(event);
|
||||
|
@ -44,11 +44,17 @@ int slider_event(Slider *slider, const SDL_Event *event, Rect boundary)
|
|||
Point position = vec((float) event->button.x, (float) event->button.y);
|
||||
if (rect_contains_point(boundary, position)) {
|
||||
slider->drag = 1;
|
||||
if (selected) {
|
||||
*selected = 1;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
slider->drag = 0;
|
||||
if (selected) {
|
||||
*selected = 1;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_MOUSEMOTION: {
|
||||
|
@ -56,6 +62,9 @@ int slider_event(Slider *slider, const SDL_Event *event, Rect boundary)
|
|||
const float x = fminf(fmaxf((float) event->button.x - boundary.x, 0.0f), (float) boundary.w);
|
||||
const float ratio = x / (float) boundary.w;
|
||||
slider->value = ratio * slider->max_value;
|
||||
if (selected) {
|
||||
*selected = 1;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ typedef struct Camera Camera;
|
|||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
int slider_render(const Slider *slider, Camera *camera, Rect boundary);
|
||||
int slider_event(Slider *slider, const SDL_Event *event, Rect boundary);
|
||||
int slider_event(Slider *slider, const SDL_Event *event, Rect boundary, int *selected);
|
||||
|
||||
#endif // SLIDER_H_
|
||||
|
|
Loading…
Reference in New Issue