diff options
| author | spl3g <spleefer6@yandex.ru> | 2025-10-31 19:25:30 +0300 |
|---|---|---|
| committer | spl3g <spleefer6@yandex.ru> | 2025-10-31 19:28:29 +0300 |
| commit | 35265935522ac3730e26f8682704315516f7cbe5 (patch) | |
| tree | a005d707c6347fd2d21827484e44cb7dbf379a76 /src/ui.c | |
| parent | 99fb0374e9352ebb61e7eea134784bd26f61a892 (diff) | |
Add ui knobs
Diffstat (limited to 'src/ui.c')
| -rw-r--r-- | src/ui.c | 207 |
1 files changed, 193 insertions, 14 deletions
@@ -1,5 +1,91 @@ #include "ui.h" +bool point_is_inside_circle(Clay_Vector2 point, Clay_BoundingBox circle) { + float center_x = circle.x + circle.width / 2; + float center_y = circle.y + circle.height / 2; + float min_diameter = circle.width > circle.height ? circle.height : circle.width; + float radius = min_diameter / 2; + + return (point.x - center_x) * (point.x - center_x) + (point.y - center_y) * (point.y - center_y) <= radius * radius; +} + +float point_angle_on_circle(Clay_Vector2 point, Clay_BoundingBox circle) { + float center_x = circle.x + circle.width / 2; + float center_y = circle.y + circle.width / 2; + + float point_rad = SDL_atan2f(point.y - center_y, point.x - center_x); + + return point_rad * 180.0f / SDL_PI_F; +} + +float point_value_on_circle(Clay_Vector2 point, Clay_BoundingBox circle, float start_angle) { + float point_on_circle = point_angle_on_circle(point, circle); + float value = SDL_fmodf((point_on_circle - start_angle + 360), 360) / 360; + return value; +} + + +static inline float normalize_value(float value, float range_start, float range_end) +{ + if (range_end == range_start) return 0.0f; // avoid divide-by-zero + float normalized = (value - range_start) / (range_end - range_start); + if (normalized < 0.0f) normalized = 0.0f; + if (normalized > 1.0f) normalized = 1.0f; + return normalized; +} + +static inline float denormalize_value(float normalized, float range_start, float range_end) +{ + if (normalized < 0.0f) normalized = 0.0f; + if (normalized > 1.0f) normalized = 1.0f; + return range_start + normalized * (range_end - range_start); +} + + +bool circle_hovered(void) { + if (!Clay_Hovered()) { + return false; + } + + Clay_PointerData pointer = Clay_GetPointerState(); + + Clay_ElementId element_id = Clay_GetCurrentElementId(); + Clay_ElementData element_data = Clay_GetElementData(element_id); + + return point_is_inside_circle(pointer.position, element_data.boundingBox); +} + +void handle_knob_press(Clay_ElementId element_id, Clay_PointerData pointer_info, intptr_t user_data) { + Clay_ElementData element_data = Clay_GetElementData(element_id); + + if (!point_is_inside_circle(pointer_info.position, element_data.boundingBox)) { + return; + } + + UIKnobData *knob = (UIKnobData *)user_data; + UIData *ui_data = knob->ui_data; + + if (pointer_info.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME + || pointer_info.state == CLAY_POINTER_DATA_PRESSED) { + + float normalized_value = point_value_on_circle(pointer_info.position, + element_data.boundingBox, + knob->start_angle); + float start = knob->info->range_start; + float end = knob->info->range_end; + float value = denormalize_value(normalized_value, start, end); + + knob->info->value = value; + mqueue_push(ui_data->msg_queue, (synth_message){ + .type = MSG_PARAM_CHANGE, + .param_change = { + .param_type = knob->info->param_type, + .value = value, + }, + }); + } +} + void handle_key_press(Clay_ElementId element_id, Clay_PointerData pointer_info, intptr_t user_data) { UIData *ui_data = (UIData *)user_data; int idx = element_id.offset; @@ -67,7 +153,7 @@ void draw_white_key(size_t idx, UIData *ui_data) { } CLAY(CLAY_IDI("white_key", idx), { .layout = { - .sizing = {CLAY_SIZING_FIXED(40), .height = CLAY_SIZING_FIXED(100)}, + .sizing = {CLAY_SIZING_FIXED(40), CLAY_SIZING_FIXED(100)}, }, .backgroundColor = fill_color, .border = { .width = {1, 1, 1, 1, 0}, .color = border_color}, @@ -120,11 +206,11 @@ void draw_black_key(size_t idx, UIData *ui_data) { fill_color = COLOR_BG; border_color = COLOR_FG; } + CLAY(CLAY_IDI("black_key", idx), { .layout = { - .sizing = {CLAY_SIZING_FIXED(25), .height = CLAY_SIZING_FIXED(65)}, + .sizing = {CLAY_SIZING_FIXED(25), CLAY_SIZING_FIXED(65)}, }, - .backgroundColor = fill_color, .border = { .width = {1, 1, 0, 1, 0}, .color = border_color}, }); @@ -132,9 +218,11 @@ void draw_black_key(size_t idx, UIData *ui_data) { } void draw_keyboard(UIData *ui_data) { - CLAY(CLAY_ID("keyboard"), { + CLAY(CLAY_ID("keyboard_container"), { .layout = { + .sizing = {CLAY_SIZING_FIXED(280), CLAY_SIZING_FIXED(100)}, .layoutDirection = CLAY_LEFT_TO_RIGHT, + .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, }, }) { for (size_t i = 0; i < ui_data->keys_amount; i++) { @@ -156,25 +244,116 @@ void draw_keyboard(UIData *ui_data) { } } -void draw_screen() {} -void draw_nob() {} +void draw_knob(Clay_ElementId id, UIData *ui_data, KnobInfo* knob_info, Clay_SizingAxis outer_size, Clay_SizingAxis inner_size) { + CLAY(id) { + bool hovered = circle_hovered(); + UIKnobData *knob_data = arena_alloc(ui_data->arena, sizeof(UIKnobData)); + knob_data->ui_data = ui_data; + knob_data->start_angle = -90; + knob_data->info = knob_info; + float value = normalize_value(knob_info->value, knob_info->range_start, knob_info->range_end); + + + Clay_OnHover(handle_knob_press, (intptr_t)knob_data); + + CustomElementData *knob_element_data = arena_alloc(ui_data->arena, sizeof(CustomElementData)); + knob_element_data->type = CUSTOM_ELEMENT_TYPE_CIRCLE; + knob_element_data->circle = (CircleData){ + .start_angle = -90, + .value = value, + .color = hovered ? COLOR_FG_INTER : COLOR_FG, + }; + CLAY_AUTO_ID({ + .layout = { + .sizing = {outer_size, outer_size}, + .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, + }, + .custom = { + .customData = knob_element_data, + }, + }) { + CustomElementData *inner_data = arena_alloc(ui_data->arena, sizeof(CustomElementData)); + inner_data->type = CUSTOM_ELEMENT_TYPE_CIRCLE; + inner_data->circle = (CircleData){ + .start_angle = 0, + .value = 1.0f, + .color = COLOR_ACCENT, + }; + CLAY_AUTO_ID({ + .layout = { + .sizing = {inner_size, inner_size}, + }, + .custom = { + .customData = inner_data, + }, + }); + }; + } +} + +void draw_panel(UIData *ui_data) { + CLAY(CLAY_ID("panel_container"), { + .layout = { + .sizing = {CLAY_SIZING_FIXED(450), CLAY_SIZING_FIXED(100)}, + .childGap = 30, + .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, + }, + }) { + CLAY(CLAY_ID("volume_knob_container")) { + draw_knob(CLAY_ID("volume_knob"), ui_data, &ui_data->knob_settings->volume, CLAY_SIZING_FIXED(85), CLAY_SIZING_FIXED(45)); + } + + CLAY(CLAY_ID("wave_screen"), { + .layout = { + .sizing = {CLAY_SIZING_FIXED(200), CLAY_SIZING_FIXED(100)}, + }, + .border = { .width = {1, 1, 1, 1, 0}, .color = COLOR_FG }, + }); + + CLAY(CLAY_ID("envelope_knobs_container"), { + .layout = { + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .childGap = 5, + }, + }) { + CLAY(CLAY_ID("envelope_knobs_upper"), { + .layout = { + .childGap = 5, + }, + }) { + draw_knob(CLAY_ID("attack_knob"), ui_data, &ui_data->knob_settings->attack, CLAY_SIZING_FIXED(40), CLAY_SIZING_FIXED(21)); + draw_knob(CLAY_ID("decay_knob"), ui_data, &ui_data->knob_settings->decay, CLAY_SIZING_FIXED(40), CLAY_SIZING_FIXED(21)); + } + CLAY(CLAY_ID("envelope_knobs_lower"), { + .layout = { + .childGap = 5, + }, + }) { + draw_knob(CLAY_ID("sustain_knob"), ui_data, &ui_data->knob_settings->sustain, CLAY_SIZING_FIXED(40), CLAY_SIZING_FIXED(21)); + draw_knob(CLAY_ID("release_knob"), ui_data, &ui_data->knob_settings->release, CLAY_SIZING_FIXED(40), CLAY_SIZING_FIXED(21)); + } + } + }; +} void draw_ui(UIData *ui_data) { CLAY(CLAY_ID("outer_container"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, - .padding = CLAY_PADDING_ALL(16), - .childGap = 16, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, }, .backgroundColor = COLOR_BG, + }) { + CLAY(CLAY_ID("ui_container"), { + .layout = { + .sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_FIT(0)}, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, + .childGap = 30, + }, }) { - /* CLAY(CLAY_ID("app_container"), { */ - /* .layout = { */ - /* .sizing = {CLAY_SIZING_PERCENT(0.75), CLAY_SIZING_PERCENT(0.75)} */ - /* }, */ - /* }) { */ + draw_panel(ui_data); draw_keyboard(ui_data); - /* }; */ + }; }; } |
