colourpicker

Simple Colour Picker written in C
git clone anongit@rnpnr.xyz:colourpicker.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 07a3b88bd7910e9ed110c532909224c51e407e50
Parent: 260bc109e49488e6d556a5caabafc7a87b2cc872
Author: Randy Palamar
Date:   Sun,  9 Jun 2024 16:24:48 -0600

scale clickable areas when hovering

Diffstat:
Mcolourpicker.c | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mmain.c | 3++-
Mutil.c | 3+++
3 files changed, 93 insertions(+), 31 deletions(-)

diff --git a/colourpicker.c b/colourpicker.c @@ -1,5 +1,4 @@ /* See LICENSE for copyright details */ -#include <math.h> #include <raylib.h> #include <stdio.h> @@ -10,14 +9,38 @@ static const char *mode_labels[CPM_LAST][4] = { [CPM_HSV] = { "H:", "S:", "V:", "A:" }, }; +static f32 +move_towards_f32(f32 current, f32 target, f32 delta) +{ + f32 remaining = target - current; + f32 result; + if (target < current) + delta *= -1; + + if (ABS(remaining) < ABS(delta)) + result = target; + else + result = current + delta; + + return result; +} + static v2 -center_text_in_rect(Rect r, const char *text, Font font, f32 fontsize) +left_align_text_in_rect(Rect r, const char *text, Font font, f32 fontsize) { v2 ts = { .rv = MeasureTextEx(font, text, fontsize, 0) }; - v2 delta = { .w = r.size.w - ts.w, .h = r.size.h - ts.h }; + v2 delta = { .h = r.size.h - ts.h }; + return (v2) { .x = r.pos.x, .y = r.pos.y + 0.5 * delta.h, }; +} + +static v2 +right_align_text_in_rect(Rect r, const char *text, Font font, f32 fontsize) +{ + v2 ts = { .rv = MeasureTextEx(font, text, fontsize, 0) }; + v2 delta = { .h = r.size.h - ts.h }; return (v2) { - .x = r.pos.x + 0.5 * delta.w, - .y = r.pos.y + 0.5 * delta.h, + .x = r.pos.x + r.size.w - ts.w, + .y = r.pos.y + 0.5 * delta.h, }; } @@ -110,17 +133,17 @@ fill_hsv_image(Image img, v4 hsv) static void get_slider_subrects(Rect r, Rect *label, Rect *slider, Rect *value) { - if (label) *label = cut_rect_left(r, 0.08); - if (value) *value = cut_rect_right(r, 0.85); + if (label) *label = cut_rect_left(r, 0.1); + if (value) *value = cut_rect_right(r, 0.87); if (slider) { *slider = cut_rect_middle(r, 0.1, 0.85); - slider->size.h *= 0.75; - slider->pos.y += r.size.h * 0.125; + slider->size.h *= 0.7; + slider->pos.y += r.size.h * 0.15; } } static void -do_slider(ColourPickerCtx *ctx, Rect r, i32 label_idx) +do_slider(ColourPickerCtx *ctx, Rect r, i32 label_idx, f32 dt) { static i32 held_idx = -1; @@ -128,12 +151,13 @@ do_slider(ColourPickerCtx *ctx, Rect r, i32 label_idx) get_slider_subrects(r, &lr, &sr, &vr); const char *label = mode_labels[ctx->mode][label_idx]; - v2 fpos = center_text_in_rect(lr, label, ctx->font, 48); - DrawTextEx(ctx->font, label, fpos.rv, 48, 0, ctx->fg); + v2 fpos = left_align_text_in_rect(lr, label, ctx->font, ctx->font_size); + DrawTextEx(ctx->font, label, fpos.rv, ctx->font_size, 0, ctx->fg); v2 mouse = { .rv = GetMousePosition() }; + b32 hovering = CheckCollisionPointRec(mouse.rv, sr.rr); - if (CheckCollisionPointRec(mouse.rv, sr.rr) && held_idx == -1) + if (hovering && held_idx == -1) held_idx = label_idx; if (held_idx != -1) { @@ -190,6 +214,18 @@ do_slider(ColourPickerCtx *ctx, Rect r, i32 label_idx) DrawRectangleRoundedLinesEx(sr.rr, 1, 0, 3, Fade(BLACK, 0.8)); { + static f32 slider_scale[4] = { 1, 1, 1, 1 }; + f32 scale_target = 1.5; + f32 scale_delta = (scale_target - 1.0) * 8 * dt; + + b32 should_scale = (held_idx == -1 && hovering) || + (held_idx != -1 && label_idx == held_idx); + f32 scale = slider_scale[label_idx]; + scale = move_towards_f32(scale, + should_scale? scale_target : 1.0, + scale_delta); + slider_scale[label_idx] = scale; + f32 half_tri_w = 8; f32 tri_h = 12; v2 t_mid = { @@ -197,35 +233,36 @@ do_slider(ColourPickerCtx *ctx, Rect r, i32 label_idx) .y = sr.pos.y, }; v2 t_left = { - .x = t_mid.x - half_tri_w, - .y = t_mid.y - tri_h, + .x = (t_mid.x - scale * half_tri_w), + .y = (t_mid.y - scale * tri_h), }; v2 t_right = { - .x = t_mid.x + half_tri_w, - .y = t_mid.y - tri_h, + .x = (t_mid.x + scale * half_tri_w), + .y = (t_mid.y - scale * tri_h), }; DrawTriangle(t_right.rv, t_left.rv, t_mid.rv, ctx->fg); t_mid.y += sr.size.h; - t_left.y += sr.size.h + 2 * tri_h; - t_right.y += sr.size.h + 2 * tri_h; + t_left.y += sr.size.h + 2 * tri_h * scale; + t_right.y += sr.size.h + 2 * tri_h * scale; DrawTriangle(t_mid.rv, t_left.rv, t_right.rv, ctx->fg); } const char *value = TextFormat("%0.02f", current); - fpos = center_text_in_rect(vr, value, ctx->font, 36); - DrawTextEx(ctx->font, value, fpos.rv, 36, 0, ctx->fg); + fpos = right_align_text_in_rect(vr, value, ctx->font, 0.9 * ctx->font_size); + DrawTextEx(ctx->font, value, fpos.rv, 0.9 * ctx->font_size, 0, ctx->fg); } static void -do_status_bar(ColourPickerCtx *ctx, Rect r) +do_status_bar(ColourPickerCtx *ctx, Rect r, f32 dt) { Rect hex_r = cut_rect_left(r, 0.5); Rect mode_r = cut_rect_right(r, 0.7); v2 mouse = { .rv = GetMousePosition() }; - if (CheckCollisionPointRec(mouse.rv, mode_r.rr)) { + b32 mode_collides = CheckCollisionPointRec(mouse.rv, mode_r.rr); + if (mode_collides) { if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { if (ctx->mode == CPM_HSV) { ctx->mode = CPM_RGB; @@ -248,6 +285,11 @@ do_status_bar(ColourPickerCtx *ctx, Rect r) } } + const char *label = TextFormat("RGB: "); + v2 label_size = {.rv = MeasureTextEx(ctx->font, label, ctx->font_size, 0)}; + Rect label_r = cut_rect_left(hex_r, label_size.x / hex_r.size.w); + hex_r = cut_rect_right(hex_r, label_size.x / hex_r.size.w); + i32 hex_collides = CheckCollisionPointRec(mouse.rv, hex_r.rr); if (hex_collides && IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { const char *new = TextToLower(GetClipboardText()); @@ -256,30 +298,46 @@ do_status_bar(ColourPickerCtx *ctx, Rect r) ctx->colour.rv = ColorNormalize((Color){ .r = r, .g = g, .b = b, .a = a }); } + static f32 scale_hex = 1.0; + f32 scale_target = 1.05; + f32 scale_delta = (scale_target - 1.0) * 8 * dt; + scale_hex = move_towards_f32(scale_hex, hex_collides? scale_target : 1.0, scale_delta); + Color hc = ColorFromNormalized(ctx->colour.rv); - const char *hex = TextFormat("%02x%02x%02x%02x", hc.r, hc.g, hc.b, hc.a); - v2 fpos = center_text_in_rect(hex_r, hex, ctx->font, 48); - DrawTextEx(ctx->font, hex, fpos.rv, 48, 0, ctx->fg); + const char *hex = TextFormat("%02x%02x%02x%02x", hc.r, hc.g, hc.b, hc.a); + + v2 fpos = left_align_text_in_rect(label_r, label, ctx->font, ctx->font_size); + DrawTextEx(ctx->font, label, fpos.rv, ctx->font_size, 0, ctx->fg); + + fpos = left_align_text_in_rect(hex_r, hex, ctx->font, scale_hex * ctx->font_size); + DrawTextEx(ctx->font, hex, fpos.rv, scale_hex * ctx->font_size, 0, ctx->fg); if (hex_collides && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) SetClipboardText(hex); + static f32 scale_mode = 1.0; + scale_mode = move_towards_f32(scale_mode, mode_collides? scale_target : 1.0, scale_delta); + char *mtext; switch (ctx->mode) { case CPM_RGB: mtext = "RGB"; break; case CPM_HSV: mtext = "HSV"; break; case CPM_LAST: ASSERT(0); break; } - fpos = center_text_in_rect(mode_r, mtext, ctx->font, 48); - DrawTextEx(ctx->font, mtext, fpos.rv, 48, 0, ctx->fg); + fpos = right_align_text_in_rect(mode_r, mtext, ctx->font, scale_mode * ctx->font_size); + DrawTextEx(ctx->font, mtext, fpos.rv, scale_mode * ctx->font_size, 0, ctx->fg); } void do_colour_picker(ColourPickerCtx *ctx) { ClearBackground(ctx->bg); + +#ifdef _DEBUG DrawFPS(20, 20); +#endif + f32 dt = GetFrameTime(); uv2 ws = ctx->window_size; { @@ -299,7 +357,7 @@ do_colour_picker(ColourPickerCtx *ctx) Rect sb = subregion; sb.size.h *= 0.25; - do_status_bar(ctx, sb); + do_status_bar(ctx, sb, dt); Rect r = subregion; r.pos.y += 0.25 * ((f32)ws.h / 2); @@ -322,7 +380,7 @@ do_colour_picker(ColourPickerCtx *ctx) } for (i32 i = 0; i < 4; i++) { - do_slider(ctx, r, i); + do_slider(ctx, r, i, dt); r.pos.y += r.size.h + 0.03 * ((f32)ws.h / 2); } } diff --git a/main.c b/main.c @@ -73,7 +73,8 @@ main(void) SetConfigFlags(FLAG_VSYNC_HINT); InitWindow(ctx.window_size.w, ctx.window_size.h, "Colour Picker"); - ctx.font = LoadFontEx(fontpath, 128, 0, 0); + ctx.font = LoadFontEx(fontpath, 128, 0, 0); + ctx.font_size = 44; ctx.hsv_img = GenImageColor(360, 360, BLACK); ctx.hsv_texture = LoadTextureFromImage(ctx.hsv_img); diff --git a/util.c b/util.c @@ -13,6 +13,7 @@ typedef int32_t i32; typedef uint32_t u32; +typedef uint32_t b32; typedef float f32; typedef double f64; typedef ptrdiff_t size; @@ -55,6 +56,7 @@ enum colour_picker_flags { typedef struct { v4 colour; Font font; + i32 font_size; uv2 window_size; Color bg, fg; @@ -65,6 +67,7 @@ typedef struct { enum colour_picker_mode mode; } ColourPickerCtx; +#define ABS(x) ((x) < 0 ? (-x) : (x)) #define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)) #endif /* _UTIL_C_ */