colourpicker

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

Commit: 3f0f969f6d944a420ea837870e9fac71ae9952d2
Parent: 02d0364c845c794a1dd41db1dca96cf6efefef53
Author: Randy Palamar
Date:   Sat, 10 May 2025 09:49:11 -0600

introduce new interaction model, hook up colour format switch to it

Diffstat:
Mcolourpicker.c | 344++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mmain.c | 14+++++++-------
Mutil.c | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
3 files changed, 338 insertions(+), 153 deletions(-)

diff --git a/colourpicker.c b/colourpicker.c @@ -6,11 +6,6 @@ global f32 dt_for_frame; -global str8 mode_labels[CM_LAST][4] = { - [CM_RGB] = { str8("R"), str8("G"), str8("B"), str8("A") }, - [CM_HSV] = { str8("H"), str8("S"), str8("V"), str8("A") }, -}; - #ifdef _DEBUG enum clock_counts { CC_WHOLE_RUN, @@ -47,6 +42,14 @@ mem_move(u8 *dest, u8 *src, sz n) else while (n) { n--; dest[n] = src[n]; } } +function b32 +point_in_rect(v2 p, Rect r) +{ + v2 end = add_v2(r.pos, r.size); + b32 result = BETWEEN(p.x, r.pos.x, end.x) & BETWEEN(p.y, r.pos.y, end.y); + return result; +} + function f32 move_towards_f32(f32 current, f32 target, f32 delta) { @@ -182,6 +185,30 @@ cut_rect_right(Rect r, f32 fraction) return r; } +function b32 +hover_rect(v2 mouse, Rect rect, f32 *hover_t) +{ + b32 result = point_in_rect(mouse, rect); + if (result) *hover_t += HOVER_SPEED * dt_for_frame; + else *hover_t -= HOVER_SPEED * dt_for_frame; + CLAMP01(*hover_t); + return result; +} + +function b32 +hover_var(ColourPickerCtx *ctx, v2 mouse, Rect rect, Variable *var) +{ + b32 result = 0; + if (ctx->interaction.kind != InteractionKind_Drag || var == ctx->interaction.active) { + result = hover_rect(mouse, rect, &var->parameter); + if (result) { + ctx->interaction.next_hot = var; + ctx->interaction.hot_rect = rect; + } + } + return result; +} + function void draw_cardinal_triangle(v2 midpoint, v2 size, v2 scale, enum cardinal_direction direction, Color colour) @@ -224,66 +251,44 @@ draw_cardinal_triangle(v2 midpoint, v2 size, v2 scale, enum cardinal_direction d } function v4 -get_formatted_colour(ColourPickerCtx *ctx, enum colour_mode format) +convert_colour(v4 colour, ColourKind current, ColourKind target) { - switch (ctx->colour_mode) { - case CM_RGB: - switch (format) { - case CM_RGB: return ctx->colour; - case CM_HSV: return rgb_to_hsv(ctx->colour); - case CM_LAST: ASSERT(0); break; + v4 result = colour; + switch (current) { + case ColourKind_RGB: { + switch (target) { + case ColourKind_RGB: break; + case ColourKind_HSV: result = rgb_to_hsv(colour); break; + InvalidDefaultCase; } - break; - case CM_HSV: - switch (format) { - case CM_RGB: return hsv_to_rgb(ctx->colour); - case CM_HSV: return ctx->colour; - case CM_LAST: ASSERT(0); break; + } break; + case ColourKind_HSV: { + switch (target) { + case ColourKind_RGB: result = hsv_to_rgb(colour); break; + case ColourKind_HSV: break; + InvalidDefaultCase; } - break; - case CM_LAST: ASSERT(0); break; + } break; + InvalidDefaultCase; } - return (v4){0}; + return result; } -function void -store_formatted_colour(ColourPickerCtx *ctx, v4 colour, enum colour_mode format) +function v4 +get_formatted_colour(ColourPickerCtx *ctx, ColourKind format) { - switch (ctx->colour_mode) { - case CM_RGB: - switch (format) { - case CM_RGB: ctx->colour = colour; break; - case CM_HSV: ctx->colour = hsv_to_rgb(colour); break; - case CM_LAST: ASSERT(0); break; - } - break; - case CM_HSV: - switch (format) { - case CM_RGB: ctx->colour = rgb_to_hsv(colour); break; - case CM_HSV: ctx->colour = colour; break; - case CM_LAST: ASSERT(0); break; - } - ctx->flags |= CPF_REFILL_TEXTURE; - break; - case CM_LAST: ASSERT(0); break; - } + v4 result = convert_colour(ctx->colour, ctx->stored_colour_kind, format); + return result; } function void -step_colour_mode(ColourPickerCtx *ctx, s32 inc) +store_formatted_colour(ColourPickerCtx *ctx, v4 colour, ColourKind format) { - ASSERT(inc == 1 || inc == -1); - - enum colour_mode last_mode = ctx->colour_mode; - - ctx->colour_mode += inc; - CLAMP(ctx->colour_mode, CM_RGB, CM_LAST); - if (ctx->colour_mode == CM_LAST) { - if (inc == 1) ctx->colour_mode = 0; - else ctx->colour_mode = CM_LAST + inc; - } + ctx->colour = convert_colour(colour, format, ctx->stored_colour_kind); - store_formatted_colour(ctx, ctx->colour, last_mode); + /* TODO(rnp): what exactly was going on here? shouldn't we always redraw the texture ? */ + if (ctx->stored_colour_kind == ColourKind_HSV) + ctx->flags |= ColourPickerFlag_RefillTexture; } function void @@ -303,14 +308,14 @@ parse_and_store_text_input(ColourPickerCtx *ctx) { str8 input = {.len = ctx->text_input_state.count, .data = ctx->text_input_state.buf}; v4 new_colour = {0}; - enum colour_mode new_mode = CM_LAST; + ColourKind new_mode = ColourKind_Last; if (ctx->text_input_state.idx == -1) { return; } else if (ctx->text_input_state.idx == INPUT_HEX) { new_colour = normalize_colour(parse_hex_u32(input)); - new_mode = CM_RGB; + new_mode = ColourKind_RGB; } else { - new_mode = ctx->colour_mode; + new_mode = ctx->stored_colour_kind; new_colour = ctx->colour; f32 fv = parse_f64(input); CLAMP01(fv); @@ -323,7 +328,7 @@ parse_and_store_text_input(ColourPickerCtx *ctx) } } - if (new_mode != CM_LAST) + if (new_mode != ColourKind_Last) store_formatted_colour(ctx, new_colour, new_mode); } @@ -335,7 +340,7 @@ set_text_input_idx(ColourPickerCtx *ctx, enum input_indices idx, Rect r, v2 mous Stream in = {.data = ctx->text_input_state.buf, .cap = countof(ctx->text_input_state.buf)}; if (idx == INPUT_HEX) { - stream_append_colour(&in, rl_colour_from_normalized(get_formatted_colour(ctx, CM_RGB))); + stream_append_colour(&in, rl_colour_from_normalized(get_formatted_colour(ctx, ColourKind_RGB))); } else { f32 fv = 0; switch (idx) { @@ -495,7 +500,7 @@ do_rect_button(ButtonState *btn, v2 mouse, Rect r, Color bg, f32 hover_speed, f3 function s32 do_text_button(ColourPickerCtx *ctx, ButtonState *btn, v2 mouse, Rect r, str8 text, v4 fg, Color bg) { - s32 pressed_mask = do_rect_button(btn, mouse, r, bg, TEXT_HOVER_SPEED, 1, 1); + s32 pressed_mask = do_rect_button(btn, mouse, r, bg, HOVER_SPEED, 1, 1); v2 tpos = center_align_text_in_rect(r, text, ctx->font); v2 spos = {.x = tpos.x + 1.75, .y = tpos.y + 2}; @@ -508,7 +513,7 @@ do_text_button(ColourPickerCtx *ctx, ButtonState *btn, v2 mouse, Rect r, str8 te } function void -do_slider(ColourPickerCtx *ctx, Rect r, s32 label_idx, v2 relative_mouse) +do_slider(ColourPickerCtx *ctx, Rect r, s32 label_idx, v2 relative_mouse, str8 name) { Rect lr, sr, vr; get_slider_subrects(r, &lr, &sr, &vr); @@ -526,7 +531,7 @@ do_slider(ColourPickerCtx *ctx, Rect r, s32 label_idx, v2 relative_mouse) current += wheel / 255; CLAMP01(current); ctx->colour.E[ctx->held_idx] = current; - ctx->flags |= CPF_REFILL_TEXTURE; + ctx->flags |= ColourPickerFlag_RefillTexture; } if (IsMouseButtonUp(MOUSE_BUTTON_LEFT)) @@ -554,9 +559,9 @@ do_slider(ColourPickerCtx *ctx, Rect r, s32 label_idx, v2 relative_mouse) SliderState *s = &ctx->ss; b32 collides = CheckCollisionPointRec(relative_mouse.rv, vr.rr); if (collides && ctx->text_input_state.idx != (label_idx + 1)) { - s->colour_t[label_idx] += TEXT_HOVER_SPEED * dt_for_frame; + s->colour_t[label_idx] += HOVER_SPEED * dt_for_frame; } else { - s->colour_t[label_idx] -= TEXT_HOVER_SPEED * dt_for_frame; + s->colour_t[label_idx] -= HOVER_SPEED * dt_for_frame; } CLAMP01(s->colour_t[label_idx]); @@ -583,9 +588,7 @@ do_slider(ColourPickerCtx *ctx, Rect r, s32 label_idx, v2 relative_mouse) do_text_input(ctx, vr, colour_rl, 4); } } - - str8 label = mode_labels[ctx->colour_mode][label_idx]; - draw_text(ctx->font, label, center_align_text_in_rect(lr, label, ctx->font), ctx->fg); + draw_text(ctx->font, name, center_align_text_in_rect(lr, name, ctx->font), ctx->fg); } function void @@ -596,23 +599,21 @@ do_status_bar(ColourPickerCtx *ctx, Rect r, v2 relative_mouse) get_slider_subrects(r, 0, 0, &mode_r); str8 mode_txt = str8(""); - switch (ctx->colour_mode) { - case CM_RGB: mode_txt = str8("RGB"); break; - case CM_HSV: mode_txt = str8("HSV"); break; - case CM_LAST: ASSERT(0); break; + switch (ctx->stored_colour_kind) { + case ColourKind_RGB: mode_txt = str8("RGB"); break; + case ColourKind_HSV: mode_txt = str8("HSV"); break; + InvalidDefaultCase; } v2 mode_ts = measure_text(ctx->font, mode_txt); mode_r.pos.y += (mode_r.size.h - mode_ts.h) / 2; mode_r.size.w = mode_ts.w; - s32 mouse_mask = do_button(&ctx->sbs.mode, relative_mouse, mode_r, TEXT_HOVER_SPEED); - if (mouse_mask & MOUSE_LEFT) step_colour_mode(ctx, 1); - if (mouse_mask & MOUSE_RIGHT) step_colour_mode(ctx, -1); + hover_var(ctx, relative_mouse, mode_r, &ctx->slider_mode_state.colour_kind_cycler); u8 hbuf[8]; Stream hstream = {.data = hbuf, .cap = countof(hbuf)}; - stream_append_colour(&hstream, rl_colour_from_normalized(get_formatted_colour(ctx, CM_RGB))); + stream_append_colour(&hstream, rl_colour_from_normalized(get_formatted_colour(ctx, ColourKind_RGB))); str8 hex = {.len = hstream.widx, .data = hbuf}; str8 label = str8("RGB: "); @@ -631,7 +632,7 @@ do_status_bar(ColourPickerCtx *ctx, Rect r, v2 relative_mouse) IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { set_text_input_idx(ctx, -1, hex_r, relative_mouse); hstream.widx = 0; - stream_append_colour(&hstream, rl_colour_from_normalized(get_formatted_colour(ctx, CM_RGB))); + stream_append_colour(&hstream, rl_colour_from_normalized(get_formatted_colour(ctx, ColourKind_RGB))); hex.len = hstream.widx; } @@ -639,14 +640,14 @@ do_status_bar(ColourPickerCtx *ctx, Rect r, v2 relative_mouse) set_text_input_idx(ctx, INPUT_HEX, hex_r, relative_mouse); if (hex_collides && ctx->text_input_state.idx != INPUT_HEX) - ctx->sbs.hex_hover_t += TEXT_HOVER_SPEED * dt_for_frame; + ctx->sbs.hex_hover_t += HOVER_SPEED * dt_for_frame; else - ctx->sbs.hex_hover_t -= TEXT_HOVER_SPEED * dt_for_frame; + ctx->sbs.hex_hover_t -= HOVER_SPEED * dt_for_frame; CLAMP01(ctx->sbs.hex_hover_t); v4 fg = normalize_colour(pack_rl_colour(ctx->fg)); v4 hex_colour = lerp_v4(fg, ctx->hover_colour, ctx->sbs.hex_hover_t); - v4 mode_colour = lerp_v4(fg, ctx->hover_colour, ctx->sbs.mode.hover_t); + v4 mode_colour = lerp_v4(fg, ctx->hover_colour, ctx->slider_mode_state.colour_kind_cycler.parameter); draw_text(ctx->font, label, left_align_text_in_rect(label_r, label, ctx->font), ctx->fg); @@ -696,7 +697,7 @@ do_colour_stack(ColourPickerCtx *ctx, Rect sa) 1 - css->fade_param * fade_scale[i]); if (pressed) { v4 hsv = rgb_to_hsv(css->items[cidx]); - store_formatted_colour(ctx, hsv, CM_HSV); + store_formatted_colour(ctx, hsv, ColourKind_HSV); if (ctx->mode == CPM_PICKER) { ctx->pms.base_hue = hsv.x; ctx->pms.fractional_hue = 0; @@ -726,7 +727,7 @@ do_colour_stack(ColourPickerCtx *ctx, Rect sa) if (push_pressed) { css->fade_param = 1.0; css->last = css->items[css->widx]; - css->items[css->widx++] = get_formatted_colour(ctx, CM_RGB); + css->items[css->widx++] = get_formatted_colour(ctx, ColourKind_RGB); if (css->widx == countof(css->items)) css->widx = 0; } @@ -735,7 +736,7 @@ do_colour_stack(ColourPickerCtx *ctx, Rect sa) function void do_colour_selector(ColourPickerCtx *ctx, Rect r) { - Color colour = rl_colour_from_normalized(get_formatted_colour(ctx, CM_RGB)); + Color colour = rl_colour_from_normalized(get_formatted_colour(ctx, ColourKind_RGB)); Color pcolour = rl_colour_from_normalized(ctx->previous_colour); Rect cs[2] = {cut_rect_left(r, 0.5), cut_rect_right(r, 0.5)}; @@ -748,12 +749,12 @@ do_colour_selector(ColourPickerCtx *ctx, Rect r) s32 pressed_idx = -1; for (u32 i = 0; i < countof(cs); i++) { if (CheckCollisionPointRec(ctx->mouse_pos.rv, cs[i].rr) && ctx->held_idx == -1) { - ctx->selection_hover_t[i] += TEXT_HOVER_SPEED * dt_for_frame; + ctx->selection_hover_t[i] += HOVER_SPEED * dt_for_frame; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) pressed_idx = i; } else { - ctx->selection_hover_t[i] -= TEXT_HOVER_SPEED * dt_for_frame; + ctx->selection_hover_t[i] -= HOVER_SPEED * dt_for_frame; } CLAMP01(ctx->selection_hover_t[i]); @@ -776,11 +777,11 @@ do_colour_selector(ColourPickerCtx *ctx, Rect r) end.y += cs[1].size.h; DrawLineEx(start.rv, end.rv, SELECTOR_BORDER_WIDTH, SELECTOR_BORDER_COLOUR); - if (pressed_idx == 0) store_formatted_colour(ctx, ctx->previous_colour, CM_RGB); - else if (pressed_idx == 1) ctx->previous_colour = get_formatted_colour(ctx, CM_RGB); + if (pressed_idx == 0) store_formatted_colour(ctx, ctx->previous_colour, ColourKind_RGB); + else if (pressed_idx == 1) ctx->previous_colour = get_formatted_colour(ctx, ColourKind_RGB); if (pressed_idx != -1) { - ctx->pms.base_hue = get_formatted_colour(ctx, CM_HSV).x; + ctx->pms.base_hue = get_formatted_colour(ctx, ColourKind_HSV).x; ctx->pms.fractional_hue = 0; } } @@ -832,8 +833,14 @@ do_slider_mode(ColourPickerCtx *ctx, v2 relative_mouse) f32 r_bound = sr.pos.x + sr.size.w; f32 y_step = 1.525 * ss.size.h; + + local_persist str8 colour_slider_labels[ColourKind_Last][4] = { + [ColourKind_RGB] = { str8("R"), str8("G"), str8("B"), str8("A") }, + [ColourKind_HSV] = { str8("H"), str8("S"), str8("V"), str8("A") }, + }; for (s32 i = 0; i < 4; i++) { - do_slider(ctx, ss, i, relative_mouse); + str8 name = colour_slider_labels[ctx->stored_colour_kind][i]; + do_slider(ctx, ss, i, relative_mouse, name); ss.pos.y += y_step; } @@ -846,7 +853,7 @@ do_slider_mode(ColourPickerCtx *ctx, v2 relative_mouse) sr.pos.x, start_y + 0 * y_step, r_bound, end_y + 0 * y_step }; v4 colours[3] = {ctx->colour}; - do_slider_shader(ctx, tr, ctx->colour_mode, regions, (f32 *)colours); + do_slider_shader(ctx, tr, ctx->stored_colour_kind, regions, (f32 *)colours); EndTextureMode(); @@ -902,7 +909,7 @@ do_picker_mode(ColourPickerCtx *ctx, v2 relative_mouse) { BEGIN_CYCLE_COUNT(CC_DO_PICKER); - v4 colour = get_formatted_colour(ctx, CM_HSV); + v4 colour = get_formatted_colour(ctx, ColourKind_HSV); colour.x = ctx->pms.base_hue + ctx->pms.fractional_hue; Rect tr = { @@ -948,7 +955,7 @@ do_picker_mode(ColourPickerCtx *ctx, v2 relative_mouse) hs2.pos.x, hs2.pos.y, hs2.pos.x + hs2.size.w, hs2.pos.y + hs2.size.h, sv.pos.x, sv.pos.y, sv.pos.x + sv.size.w, sv.pos.y + sv.size.h }; - do_slider_shader(ctx, tr, CM_HSV, regions, (f32 *)hsv); + do_slider_shader(ctx, tr, ColourKind_HSV, regions, (f32 *)hsv); } b32 hovering = CheckCollisionPointRec(relative_mouse.rv, sv.rr); @@ -1011,7 +1018,7 @@ do_picker_mode(ColourPickerCtx *ctx, v2 relative_mouse) if (IsMouseButtonUp(MOUSE_BUTTON_LEFT)) ctx->held_idx = -1; - store_formatted_colour(ctx, colour, CM_HSV); + store_formatted_colour(ctx, colour, ColourKind_HSV); END_CYCLE_COUNT(CC_DO_PICKER); } @@ -1026,7 +1033,7 @@ debug_dump_info(ColourPickerCtx *ctx) (void)ctx; #ifdef _DEBUG if (IsKeyPressed(KEY_F1)) - ctx->flags ^= CPF_PRINT_DEBUG; + ctx->flags ^= ColourPickerFlag_PrintDebug; DrawFPS(20, 20); @@ -1049,7 +1056,7 @@ debug_dump_info(ColourPickerCtx *ctx) g_debug_clock_counts.total_cycles[i] = 0; } - if (!(ctx->flags & CPF_PRINT_DEBUG)) + if (!(ctx->flags & ColourPickerFlag_PrintDebug)) return; local_persist u32 fcount; @@ -1067,6 +1074,121 @@ debug_dump_info(ColourPickerCtx *ctx) #endif } +function void +colour_picker_begin_interact(ColourPickerCtx *ctx, b32 scroll) +{ + InteractionState *is = &ctx->interaction; + if (is->hot) { + switch (is->hot->kind) { + case VariableKind_Cycler: { + if (scroll) is->kind = InteractionKind_Scroll; + else is->kind = InteractionKind_Set; + } break; + InvalidDefaultCase; + } + } + + if (is->kind != InteractionKind_None) { + is->active = is->hot; + is->rect = is->hot_rect; + } +} + +function void +colour_picker_end_interact(ColourPickerCtx *ctx, b32 mouse_left_pressed, b32 mouse_right_pressed) +{ + InteractionState *is = &ctx->interaction; + switch (is->kind) { + case InteractionKind_Scroll: { + f32 delta = GetMouseWheelMoveV().y; + switch (is->active->kind) { + case VariableKind_Cycler: { + is->active->cycler.state += delta; + is->active->cycler.state %= is->active->cycler.length; + } break; + InvalidDefaultCase; + } + } break; + case InteractionKind_Set: { + switch (is->active->kind) { + case VariableKind_Cycler: { + s32 delta = (s32)mouse_left_pressed - (s32)mouse_right_pressed; + is->active->cycler.state += delta; + is->active->cycler.state %= is->active->cycler.length; + } break; + InvalidDefaultCase; + } + } break; + InvalidDefaultCase; + } + + if (is->active->flags & VariableFlag_UpdateStoredMode) { + ASSERT(is->active->kind == VariableKind_Cycler); + ctx->colour = convert_colour(ctx->colour, ctx->stored_colour_kind, + is->active->cycler.state); + ctx->stored_colour_kind = is->active->cycler.state; + } + + is->kind = InteractionKind_None; + is->active = 0; +} + +function void +colour_picker_interact(ColourPickerCtx *ctx, v2 mouse) +{ + InteractionState *is = &ctx->interaction; + if (!is->active) is->hot = is->next_hot; + is->next_hot = 0; + + b32 mouse_left_pressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT); + b32 mouse_right_pressed = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT); + b32 wheel_moved = GetMouseWheelMoveV().y != 0; + if (mouse_left_pressed || mouse_right_pressed || wheel_moved) { + if (is->kind != InteractionKind_None) + colour_picker_end_interact(ctx, mouse_left_pressed, mouse_right_pressed); + colour_picker_begin_interact(ctx, wheel_moved); + } + + switch (is->kind) { + case InteractionKind_None: break; + case InteractionKind_Scroll: + case InteractionKind_Set: { + colour_picker_end_interact(ctx, mouse_left_pressed, mouse_right_pressed); + } break; + InvalidDefaultCase; + } + + ctx->last_mouse = mouse; +} + +function void +colour_picker_init(ColourPickerCtx *ctx) +{ +#ifdef _DEBUG + ctx->picker_shader = LoadShader(0, HSV_LERP_SHADER_NAME); +#else + ctx->picker_shader = LoadShaderFromMemory(0, _binary_slider_lerp_glsl_start); +#endif + ctx->mode_id = GetShaderLocation(ctx->picker_shader, "u_mode"); + ctx->colour_mode_id = GetShaderLocation(ctx->picker_shader, "u_colour_mode"); + ctx->colours_id = GetShaderLocation(ctx->picker_shader, "u_colours"); + ctx->regions_id = GetShaderLocation(ctx->picker_shader, "u_regions"); + ctx->radius_id = GetShaderLocation(ctx->picker_shader, "u_radius"); + ctx->border_thick_id = GetShaderLocation(ctx->picker_shader, "u_border_thick"); + + local_persist str8 colour_kind_labels[ColourKind_Last] = { + [ColourKind_RGB] = str8("RGB"), + [ColourKind_HSV] = str8("HSV"), + }; + ctx->slider_mode_state.colour_kind_cycler.kind = VariableKind_Cycler; + ctx->slider_mode_state.colour_kind_cycler.flags = VariableFlag_UpdateStoredMode; + ctx->slider_mode_state.colour_kind_cycler.cycler.state = ctx->stored_colour_kind; + ctx->slider_mode_state.colour_kind_cycler.cycler.length = countof(colour_kind_labels); + ctx->slider_mode_state.colour_kind_cycler.cycler.labels = colour_kind_labels; + + ctx->flags |= ColourPickerFlag_Ready; +} + DEBUG_EXPORT void do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse_pos) { @@ -1084,23 +1206,14 @@ do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse else ctx->font = LoadFont_lora_sb_0_inc(); } - if (!IsShaderReady(ctx->picker_shader)) { -#ifdef _DEBUG - ctx->picker_shader = LoadShader(0, HSV_LERP_SHADER_NAME); -#else - ctx->picker_shader = LoadShaderFromMemory(0, _binary_slider_lerp_glsl_start); -#endif - ctx->mode_id = GetShaderLocation(ctx->picker_shader, "u_mode"); - ctx->colour_mode_id = GetShaderLocation(ctx->picker_shader, "u_colour_mode"); - ctx->colours_id = GetShaderLocation(ctx->picker_shader, "u_colours"); - ctx->regions_id = GetShaderLocation(ctx->picker_shader, "u_regions"); - ctx->radius_id = GetShaderLocation(ctx->picker_shader, "u_radius"); - ctx->border_thick_id = GetShaderLocation(ctx->picker_shader, "u_border_thick"); - } + if (!(ctx->flags & ColourPickerFlag_Ready)) + colour_picker_init(ctx); ctx->mouse_pos.rv = mouse_pos; ctx->window_pos.rv = window_pos; + colour_picker_interact(ctx, ctx->mouse_pos); + uv2 ws = ctx->window_size; DrawRectangle(ctx->window_pos.x, ctx->window_pos.y, ws.w, ws.h, ctx->bg); @@ -1126,6 +1239,7 @@ do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse ma_relative_mouse.y -= ma.pos.y; { + /* TODO(rnp): move this into single resize function */ if (ctx->picker_texture.texture.width != (s32)(ma.size.w)) { s32 w = ma.size.w; s32 h = ma.size.h; @@ -1200,12 +1314,8 @@ do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse f32 offset = lower.size.w - (CPM_LAST + 1) * (mb.size.w + 0.5 * mode_x_pad); mb.pos.x += 0.5 * offset; - Rect tr = { - .size = { - .w = ctx->slider_texture.texture.width, - .h = -ctx->slider_texture.texture.height - } - }; + Rect tr = {.size.w = ctx->slider_texture.texture.width, + .size.h = -ctx->slider_texture.texture.height}; NPatchInfo tnp = {tr.rr, 0, 0, 0, 0, NPATCH_NINE_PATCH }; for (u32 i = 0; i < CPM_LAST; i++) { @@ -1220,11 +1330,11 @@ do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse ctx->mode = ctx->mcs.next_mode; ctx->mcs.next_mode = -1; if (ctx->mode == CPM_PICKER) { - v4 hsv = get_formatted_colour(ctx, CM_HSV); + v4 hsv = get_formatted_colour(ctx, ColourKind_HSV); ctx->pms.base_hue = hsv.x; ctx->pms.fractional_hue = 0; } - ctx->flags |= CPF_REFILL_TEXTURE; + ctx->flags |= ColourPickerFlag_RefillTexture; } } else { ctx->mcs.mode_visible_t += 2 * dt_for_frame; @@ -1253,7 +1363,7 @@ do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse } v4 fg = normalize_colour(pack_rl_colour(ctx->fg)); - Color bg = rl_colour_from_normalized(get_formatted_colour(ctx, CM_RGB)); + Color bg = rl_colour_from_normalized(get_formatted_colour(ctx, ColourKind_RGB)); Rect btn_r = mb; btn_r.size.h *= 0.46; @@ -1270,9 +1380,9 @@ do_colour_picker(ColourPickerCtx *ctx, f32 dt, Vector2 window_pos, Vector2 mouse str8 txt = str8_from_c_str((char *)GetClipboardText()); if (txt.len) { v4 new_colour = normalize_colour(parse_hex_u32(txt)); - store_formatted_colour(ctx, new_colour, CM_RGB); + ctx->colour = convert_colour(new_colour, ColourKind_RGB, ctx->stored_colour_kind); if (ctx->mode == CPM_PICKER) { - f32 hue = rgb_to_hsv(new_colour).x; + f32 hue = get_formatted_colour(ctx, ColourKind_HSV).x; ctx->pms.base_hue = hue; ctx->pms.fractional_hue = 0; } diff --git a/main.c b/main.c @@ -82,9 +82,9 @@ main(s32 argc, char *argv[]) ColourPickerCtx ctx = { .window_size = { .w = 640, .h = 860 }, - .mode = CPM_PICKER, - .colour_mode = CM_HSV, - .flags = CPF_REFILL_TEXTURE, + .mode = CPM_PICKER, + .stored_colour_kind = ColourKind_HSV, + .flags = ColourPickerFlag_RefillTexture, .text_input_state = {.idx = -1, .cursor_t = 1, .cursor_t_target = 0}, .mcs = {.mode_visible_t = 1, .next_mode = -1}, @@ -185,10 +185,10 @@ main(s32 argc, char *argv[]) } v4 rgba = {0}; - switch (ctx.colour_mode) { - case CM_RGB: rgba = ctx.colour; break; - case CM_HSV: rgba = hsv_to_rgb(ctx.colour); break; - default: ASSERT(0); break; + switch (ctx.stored_colour_kind) { + case ColourKind_RGB: rgba = ctx.colour; break; + case ColourKind_HSV: rgba = hsv_to_rgb(ctx.colour); break; + InvalidDefaultCase; } u32 packed_rgba = pack_rl_colour(rl_colour_from_normalized(rgba)); diff --git a/util.c b/util.c @@ -54,7 +54,7 @@ rdtsc(void) #endif #ifdef _DEBUG -#define ASSERT(c) do { if (!(c)) debugbreak(); } while (0); +#define ASSERT(c) do { if (!(c)) debugbreak(); } while (0) #define DEBUG_EXPORT #else #define ASSERT(c) @@ -97,11 +97,11 @@ typedef union { Rectangle rr; } Rect; -enum colour_mode { - CM_RGB = 0, - CM_HSV = 1, - CM_LAST -}; +typedef enum c{ + ColourKind_RGB, + ColourKind_HSV, + ColourKind_Last, +} ColourKind; enum colour_picker_mode { CPM_PICKER = 0, @@ -109,10 +109,11 @@ enum colour_picker_mode { CPM_LAST }; -enum colour_picker_flags { - CPF_REFILL_TEXTURE = 1 << 0, - CPF_PRINT_DEBUG = 1 << 30, -}; +typedef enum { + ColourPickerFlag_Ready = 1 << 0, + ColourPickerFlag_RefillTexture = 1 << 1, + ColourPickerFlag_PrintDebug = 1 << 30, +} ColourPickerFlags; enum input_indices { INPUT_HEX, @@ -147,7 +148,7 @@ enum cardinal_direction { NORTH, EAST, SOUTH, WEST }; #define RECT_BTN_BORDER_WIDTH (SLIDER_BORDER_WIDTH + 3.0f) -#define TEXT_HOVER_SPEED 5.0f +#define HOVER_SPEED 5.0f typedef struct { f32 hover_t; @@ -197,23 +198,85 @@ typedef struct { } TextInputState; typedef struct { + str8 *labels; + u32 state; + u32 length; +} Cycler; + +typedef struct Variable Variable; +typedef enum { + InteractionKind_None, + InteractionKind_Set, + InteractionKind_Text, + InteractionKind_Drag, + InteractionKind_Scroll, +} InteractionKind; + +typedef struct { + Variable *active; + Variable *hot; + Variable *next_hot; + + Rect rect; + Rect hot_rect; + + InteractionKind kind; +} InteractionState; + +typedef enum { + VariableFlag_Text = 1 << 0, + VariableFlag_UpdateStoredMode = 1 << 30, +} VariableFlags; + +typedef enum { + VariableKind_F32, + VariableKind_U32, + VariableKind_F32Reference, + VariableKind_Button, + VariableKind_Cycler, + VariableKind_HexColourInput, +} VariableKind; + +struct Variable { + union { + u32 U32; + f32 F32; + f32 *F32Reference; + void *generic; + Cycler cycler; + }; + VariableKind kind; + VariableFlags flags; + f32 parameter; +}; + +typedef struct { + Variable colour_kind_cycler; +} SliderModeState; + +typedef struct { v4 colour, previous_colour; ColourStackState colour_stack; uv2 window_size; v2 window_pos; v2 mouse_pos; + v2 last_mouse; Font font; Color bg, fg; - TextInputState text_input_state; + TextInputState text_input_state; + InteractionState interaction; + ModeChangeState mcs; PickerModeState pms; SliderState ss; StatusBarState sbs; ButtonState buttons[2]; + SliderModeState slider_mode_state; + s32 held_idx; f32 selection_hover_t[2]; @@ -227,32 +290,24 @@ typedef struct { s32 mode_id, colour_mode_id, colours_id; s32 regions_id, radius_id, border_thick_id; - u32 flags; - enum colour_mode colour_mode; + ColourPickerFlags flags; + ColourKind stored_colour_kind; enum colour_picker_mode mode; } ColourPickerCtx; #define countof(a) (s64)(sizeof(a) / sizeof(*a)) -#define ABS(x) ((x) < 0 ? (-x) : (x)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)) -#define CLAMP01(a) CLAMP(a, 0, 1) +#define ABS(x) ((x) < 0 ? (-x) : (x)) +#define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)) +#define CLAMP01(a) CLAMP(a, 0, 1) #define ISDIGIT(a) ((a) >= '0' && (a) <= '9') #define ISHEX(a) (ISDIGIT(a) || ((a) >= 'a' && (a) <= 'f') || ((a) >= 'A' && (a) <= 'F')) -function Color -rl_colour_from_normalized(v4 colour) -{ - Color result; - result.r = colour.r * 255; - result.g = colour.g * 255; - result.b = colour.b * 255; - result.a = colour.a * 255; - return result; -} +#define InvalidDefaultCase default: ASSERT(0); break function v4 rgb_to_hsv(v4 rgb) @@ -307,6 +362,26 @@ pack_rl_colour(Color colour) return colour.r << 24 | colour.g << 16 | colour.b << 8 | colour.a << 0; } +function Color +rl_colour_from_normalized(v4 colour) +{ + Color result; + result.r = colour.r * 255; + result.g = colour.g * 255; + result.b = colour.b * 255; + result.a = colour.a * 255; + return result; +} + +function v2 +add_v2(v2 a, v2 b) +{ + v2 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + return result; +} + function u32 parse_hex_u32(str8 s) {