#define sloth_test_assert(c) if (!(c)) { do{ *((volatile int*)0) = 0xFFFF; }while(0); } #define sloth_r32_equals(a,b) (fabsf((a) - (b)) < 0.001f) Sloth_U32 sloth_test_string_len(char* s) { char* at = s; while (*at != 0) at++; return (Sloth_U32)(at - s); } bool sloth_test_strings_equal(char* a, char* b) { Sloth_U32 a_len = sloth_test_string_len(a); Sloth_U32 b_len = sloth_test_string_len(b); if (a_len != b_len) return false; for (Sloth_U32 i = 0; i < a_len; i++) { if (a[i] != b[i]) return false; } return true; } static Sloth_U32 sloth_test_widget_order_count = 0; void sloth_test_widget_order(Sloth_Ctx* ctx, Sloth_Widget* widget, Sloth_U8* user_data) { Sloth_ID* ids = (Sloth_ID*)user_data; Sloth_ID id_at = ids[sloth_test_widget_order_count++]; sloth_test_assert(sloth_ids_equal(widget->id, id_at)); } // Naive string sizing Sloth_V2 sloth_test_get_text_size(Sloth_Widget* widget) { Sloth_V2 result = { .x = widget->text_len * 14, .y = 14, }; return result; } // The error this is trying to catch takes place across three frames // Frame 1: all widgets are drawn // Frame 2: a widget early in teh tree is removed // Frame 3: a widget late in the tree is removed // importantly, this widget comes after a widget that // has children. ie. The tree must look like: // root // // widget // child // void sloth_test_multi_frame_removal_frame(Sloth_Ctx* sloth, int num_to_remove) { Sloth_Widget_Desc d = {}; sloth_frame_prepare(sloth); sloth_push_widget(sloth, d, "Root"); // root { if (num_to_remove < 1) { sloth_push_widget(sloth, d, "remove 1"); sloth_pop_widget(sloth); } sloth_push_widget(sloth, d, "bar_bounds_inner"); { sloth_push_widget(sloth, d, "fg_bar"); sloth_pop_widget(sloth); } sloth_pop_widget(sloth); if (num_to_remove < 2) { sloth_push_widget(sloth, d, "remove 2"); sloth_pop_widget(sloth); } } sloth_pop_widget(sloth); sloth_frame_advance(sloth); } void sloth_test_multi_frame_removal_frame_b(Sloth_Ctx* sloth, int num_to_remove) { Sloth_Widget_Desc d = {}; sloth_frame_prepare(sloth); sloth_push_widget(sloth, d, "Root"); // root { if (num_to_remove < 1) { sloth_push_widget(sloth, d, "remove 1"); sloth_pop_widget(sloth); } sloth_push_widget(sloth, d, "a"); { sloth_push_widget(sloth, d, "bar_bounds_inner"); { sloth_push_widget(sloth, d, "fg_bar"); sloth_pop_widget(sloth); } sloth_pop_widget(sloth); if (num_to_remove < 2) { sloth_push_widget(sloth, d, "remove 2"); sloth_pop_widget(sloth); } } sloth_pop_widget(sloth); } sloth_pop_widget(sloth); sloth_frame_advance(sloth); } void sloth_test_multi_frame_removal() { Sloth_Ctx sloth = {}; sloth_test_multi_frame_removal_frame(&sloth, 0); sloth_test_multi_frame_removal_frame(&sloth, 1); sloth_test_multi_frame_removal_frame(&sloth, 2); sloth_ctx_free(&sloth); sloth = (Sloth_Ctx){}; sloth_test_multi_frame_removal_frame_b(&sloth, 0); sloth_test_multi_frame_removal_frame_b(&sloth, 1); sloth_test_multi_frame_removal_frame_b(&sloth, 2); sloth_ctx_free(&sloth); } void sloth_tests() { sloth_test_assert(sloth_is_pow2(2048)); sloth_test_assert(!sloth_is_pow2(1920)); // ID Creation Tests char test_id_str[] = "Test id##53"; int test_id_str_len = (sizeof(test_id_str) / sizeof(char)) - 1; Sloth_ID_Result id0 = sloth_make_id(test_id_str); sloth_test_assert(id0.id.value != 0); sloth_test_assert(id0.display_len == test_id_str_len - 4); Sloth_ID_Result id1 = sloth_make_id_len(11, "Test id##53"); sloth_test_assert(id0.id.value == id1.id.value); Sloth_ID_Result id2 = sloth_make_id_f("Test id###%d", 53); sloth_test_assert(id2.id.value != 0); sloth_test_assert(id2.id.value != id0.id.value); sloth_test_assert(id2.display_len == 7); // Vectors Sloth_V2 va = { 25, 32.1f }; Sloth_V2 vb = { 19, 18.1f }; Sloth_V2 rv0 = sloth_v2_add(va, vb); sloth_test_assert(sloth_r32_equals(rv0.x, 44)); sloth_test_assert(sloth_r32_equals(rv0.y, 50.2f)); Sloth_V2 rv1 = sloth_v2_sub(va, vb); sloth_test_assert(sloth_r32_equals(rv1.x, 6)); sloth_test_assert(sloth_r32_equals(rv1.y, 14)); Sloth_V2 rv2 = sloth_v2_mulf(va, 2); sloth_test_assert(sloth_r32_equals(rv2.x, 50)); sloth_test_assert(sloth_r32_equals(rv2.y, 64.2f)); // Rects // baseline rect Sloth_Rect rect0 = { .value_min = { 0, 0 }, .value_max = { 100, 100 }, }; // overlaps rect0 to right and top Sloth_Rect rect1 = { .value_min = { 50, 50 }, .value_max = { 150, 150 }, }; // overlaps rect1 to the left and bottom Sloth_Rect rect2 = { .value_min = { -50, -50 }, .value_max = { 50, 50 }, }; // no overlap with rect0 to the left and bottom Sloth_Rect rect3 = { .value_min = { -250, -250 }, .value_max = { -200, -200 } }; // no overlap with rect0 to the right and top Sloth_Rect rect4 = { .value_min = { 250, 250 }, .value_max = { 200, 200 } }; // contains rect0 Sloth_Rect rect5 = { .value_min = { -50, -50 }, .value_max = { 200, 200 } }; Sloth_Rect rr0 = sloth_rect_union(rect0, rect1); sloth_test_assert(rr0.value_min.x == 50 && rr0.value_min.y == 50); sloth_test_assert(rr0.value_max.x == 100 && rr0.value_max.y == 100); Sloth_Rect rr1 = sloth_rect_union(rect0, rect2); sloth_test_assert(rr1.value_min.x == 0 && rr1.value_min.y == 0); sloth_test_assert(rr1.value_max.x == 50 && rr1.value_max.y == 50); Sloth_Rect rr2 = sloth_rect_union(rect0, rect3); sloth_test_assert(rr2.value_min.x == 0 && rr2.value_min.y == 0); sloth_test_assert(rr2.value_max.x == 0 && rr2.value_max.y == 0); Sloth_Rect rr3 = sloth_rect_union(rect0, rect4); sloth_test_assert(rr3.value_min.x == 0 && rr3.value_min.y == 0); sloth_test_assert(rr3.value_max.x == 0 && rr3.value_max.y == 0); Sloth_Rect rr4 = sloth_rect_union(rect0, rect5); sloth_test_assert(rr4.value_min.x == 0 && rr4.value_min.y == 0); sloth_test_assert(rr4.value_max.x == 100 && rr4.value_max.y == 100); // contained by rect0 Sloth_V2 rectp0 = { 25, 25 }; // not contained by rect0 to the right and top Sloth_V2 rectp1 = { 150, 150 }; // not contained by rect0 to the left and bottom Sloth_V2 rectp2 = { -25, -25 }; sloth_test_assert(sloth_rect_contains(rect0, rectp0)); sloth_test_assert(!sloth_rect_contains(rect0, rectp1)); sloth_test_assert(!sloth_rect_contains(rect0, rectp2)); // Hashtable Tests { Sloth_Hashtable table = {}; sloth_hashtable_add(&table, 256, (Sloth_U8*)1); sloth_hashtable_add(&table, 394, (Sloth_U8*)2); sloth_hashtable_add(&table, 81932, (Sloth_U8*)3); // this should force chaining sloth_hashtable_add(&table, table.cap + 256, (Sloth_U8*)4); Sloth_U64 v0 = (Sloth_U64)sloth_hashtable_get(&table, 256); sloth_test_assert(v0 == 1); Sloth_U64 v1 = (Sloth_U64)sloth_hashtable_get(&table, 394); sloth_test_assert(v1 == 2); Sloth_U64 v2 = (Sloth_U64)sloth_hashtable_get(&table, 81932); sloth_test_assert(v2 == 3); Sloth_U64 v3 = (Sloth_U64)sloth_hashtable_get(&table, table.cap + 256); sloth_test_assert(v3 == 4); // getting a value that's not present Sloth_U64 vi = (Sloth_U64)sloth_hashtable_get(&table, 3333); sloth_test_assert(vi == 0); Sloth_Bool r0 = sloth_hashtable_rem(&table, 256); sloth_test_assert(r0); v0 = (Sloth_U64)sloth_hashtable_get(&table, 256); sloth_test_assert(v0 == 0); } { // Arena Tests Sloth_Arena arena = {}; Sloth_U32* array_0 = sloth_arena_push_array(&arena, Sloth_U32, 32); for (Sloth_U32 i = 0; i < 32; i++) array_0[i] = i; sloth_test_assert(array_0 != 0); Sloth_Arena_Loc old_at = sloth_arena_at(&arena); sloth_test_assert(old_at.bucket_at == sizeof(Sloth_U32) * 32); Sloth_U32* array_1 = sloth_arena_push_array(&arena, Sloth_U32, 32); for (Sloth_U32 i = 0; i < 32; i++) array_1[i] = (i + 32); sloth_test_assert(array_1 >= (array_0 + 32)); sloth_test_assert(array_1 != 0); sloth_test_assert(array_0[31] == 31); sloth_test_assert(array_1[0] == 32); // testing memory reuse after popping sloth_arena_pop(&arena, old_at); // test that in debug mode, popped memory is cleared // NOTE: that if we aren't in debug mode, sloth_test_assert evaluates to // nothing, so the test won't run for (Sloth_U32 i = 0; i < 32; i++) sloth_test_assert(array_1[i] == 0); Sloth_U32* array_1b = sloth_arena_push_array(&arena, Sloth_U32, 32); sloth_test_assert(array_1 == array_1b); // testing memory reuse after clearing sloth_arena_clear(&arena); Sloth_U32* array_0b = sloth_arena_push_array(&arena, Sloth_U32, 32); sloth_test_assert(array_0 == array_0b); sloth_arena_free(&arena); sloth_test_assert(!arena.buckets); sloth_test_assert(!arena.buckets_len); sloth_test_assert(!arena.buckets_cap); sloth_test_assert(!arena.bucket_cap); sloth_test_assert(!arena.curr_bucket_len); } { // Gamma correction Sloth_R32 r_in = 0.2f; Sloth_R32 g_in = 0.5f; Sloth_R32 b_in = 0.9f; Sloth_R32 a_in = 0.1f; Sloth_U32 color = (((Sloth_U32)(r_in * 255) << 24) | ((Sloth_U32)(g_in * 255) << 16) | ((Sloth_U32)(b_in * 255) << 8) | ((Sloth_U32)(a_in * 255))); // gamma = 1, no change Sloth_U32 color_out0 = sloth_color_apply_gamma(color, 1); sloth_assert(color_out0 == color); // gamma = 2.2, verify changes Sloth_U32 color_out1 = sloth_color_apply_gamma(color, 2.2f); Sloth_R32 r = (Sloth_R32)((color_out1 >> 24) & 0xFF) / 255.0f; Sloth_R32 g = (Sloth_R32)((color_out1 >> 16) & 0xFF) / 255.0f; Sloth_R32 b = (Sloth_R32)((color_out1 >> 8) & 0xFF) / 255.0f; Sloth_R32 a = (Sloth_R32)((color_out1 ) & 0xFF) / 255.0f; Sloth_R32 delta_r = fabsf(r - powf(r_in, 2.2f)); Sloth_R32 delta_g = fabsf(g - powf(g_in, 2.2f)); Sloth_R32 delta_b = fabsf(b - powf(b_in, 2.2f)); Sloth_R32 delta_a = fabsf(a - powf(a_in, 2.2f)); sloth_assert(delta_r < 0.01f); sloth_assert(delta_g < 0.01f); sloth_assert(delta_b < 0.01f); sloth_assert(delta_a < 0.01f); } { // Atlas Tests Sloth_U32 test_icon[] = { 0xFFFFFFFF, 0x000000FF, 0xFFFFFFFF, 0x000000FF, 0x000000FF, 0xFFFFFFFF, 0x000000FF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000FF, 0xFFFFFFFF, 0x000000FF, 0x000000FF, 0xFFFFFFFF, 0x000000FF, 0xFFFFFFFF, }; Sloth_Glyph_Atlas atlas = {}; sloth_glyph_atlas_resize(&atlas, 32); Sloth_Glyph_Desc gd0 = { .family = 1, .id = 25, .data = (Sloth_U8*)test_icon, .width = 4, .height = 4, .stride = 4, .format = Sloth_GlyphData_RGBA8, }; Sloth_Glyph_ID id_0 = sloth_glyph_atlas_register(&atlas, gd0); Sloth_U32 last_glyph = atlas.last_glyph; sloth_test_assert(atlas.glyphs_table.used == 1); // testing adding the same glyph a second time. Sloth_Glyph_ID id_01 = sloth_glyph_atlas_register(&atlas, gd0); sloth_test_assert(id_01.value == id_0.value); sloth_test_assert(atlas.last_glyph == last_glyph); sloth_test_assert(atlas.glyphs_table.used == 1); // no sprite was added Sloth_Glyph_Desc gd2 = gd0; gd2.id = 26; Sloth_Glyph_ID id_2 = sloth_glyph_atlas_register(&atlas, gd2); sloth_test_assert(id_2.value != 0); Sloth_Glyph_Desc gd3 = gd0; gd3.id = 27; Sloth_Glyph_ID id_3 = sloth_glyph_atlas_register(&atlas, gd3); sloth_test_assert(id_3.value != 0); Sloth_Glyph_Desc gd4 = gd0; gd4.id = 28; Sloth_Glyph_ID id_4 = sloth_glyph_atlas_register(&atlas, gd4); sloth_test_assert(id_4.value != 0); sloth_test_assert(id_4.family == gd0.family); sloth_test_assert(id_4.id[0] == 28 && id_4.id[1] == 0 && id_4.id[2] == 0); Sloth_Glyph_Desc gd5 = gd0; gd5.id = 29; Sloth_Glyph_ID id_5 = sloth_glyph_atlas_register(&atlas, gd5); sloth_test_assert(id_5.value != 0); sloth_test_assert(id_5.family == 1); sloth_test_assert(id_5.id[0] == 29 && id_5.id[1] == 0 && id_5.id[2] == 0); sloth_glyph_atlas_free(&atlas); // Glyph ID Tests Sloth_Glyph_ID g_id = sloth_make_glyph_id(24, 'G'); Sloth_Glyph_ID newline_id = sloth_make_glyph_id(32, '\n'); Sloth_Glyph_ID space_id = sloth_make_glyph_id(127, ' '); sloth_test_assert(sloth_glyph_id_matches_charcode(g_id, 'G')); sloth_test_assert(sloth_glyph_id_matches_charcode(newline_id, '\n')); sloth_test_assert(sloth_glyph_id_matches_charcode(space_id, ' ')); } { // Sloth_Size tests // see @Maintenance tag in Sloth_Size_Box if this fails Sloth_Size_Box b = { .left = sloth_size_pixels(0), .right = sloth_size_pixels(5), .top = sloth_size_pixels(10), .bottom = sloth_size_pixels(15), }; // testing to make sure left corresponds to E[Axis_X].min // and so on sloth_test_assert(b.E[Sloth_Axis_X].min.value == 0); sloth_test_assert(b.E[Sloth_Axis_X].max.value == 5); sloth_test_assert(b.E[Sloth_Axis_Y].min.value == 10); sloth_test_assert(b.E[Sloth_Axis_Y].max.value == 15); } // Widget Tree Construction { Sloth_Ctx sloth = { .per_frame_memory.name = "pfm", .scratch.name = "scratch", }; Sloth_Widget_Desc d = {}; // these tests don't depend on the desc at all Sloth_ID ids0_preorder[] = { sloth_make_id_f("Root").id, sloth_make_id_f("W1").id, sloth_make_id_f("W11").id, sloth_make_id_f("W12").id, sloth_make_id_f("W2").id, sloth_make_id_f("W3").id, sloth_make_id_f("W31").id }; Sloth_ID ids0_postorder[] = { sloth_make_id_f("W11").id, sloth_make_id_f("W12").id, sloth_make_id_f("W1").id, sloth_make_id_f("W2").id, sloth_make_id_f("W31").id, sloth_make_id_f("W3").id, sloth_make_id_f("Root").id, }; printf("Frame 1\n"); sloth_frame_prepare(&sloth); sloth_push_widget(&sloth, d, "Root"); // root sloth_push_widget(&sloth, d, "W1"); sloth_push_widget(&sloth, d, "W11"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W12"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W2"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W3"); sloth_push_widget(&sloth, d, "W31"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); // root - won't pop // walking the tree sloth_test_assert(sloth.widget_tree_root != 0); sloth_test_assert(sloth.widget_tree_depth_max == 3); sloth_test_widget_order_count = 0; // reset test sloth_tree_walk_preorder(&sloth, sloth_test_widget_order, (Sloth_U8*)&ids0_preorder); sloth_test_widget_order_count = 0; // reset test sloth_tree_walk_postorder(&sloth, sloth_test_widget_order, (Sloth_U8*)&ids0_postorder); //sloth_widget_tree_print(&sloth); sloth_frame_advance(&sloth); printf("Frame 2\n"); sloth_frame_prepare(&sloth); // Same Frame as above sloth_push_widget(&sloth, d, "Root"); // root sloth_push_widget(&sloth, d, "W1"); sloth_push_widget(&sloth, d, "W11"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W12"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W2"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W3"); sloth_push_widget(&sloth, d, "W31"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); // root - won't pop // walking the tree sloth_test_assert(sloth.widget_tree_root != 0); sloth_test_widget_order_count = 0; // reset test sloth_tree_walk_preorder(&sloth, sloth_test_widget_order, (Sloth_U8*)&ids0_preorder); sloth_frame_advance(&sloth); sloth_frame_prepare(&sloth); // Different frame from above Sloth_ID ids1[] = { sloth_make_id_f("Root").id, sloth_make_id_f("W1").id, sloth_make_id_f("W11").id, sloth_make_id_f("W13").id, sloth_make_id_f("W14").id, sloth_make_id_f("W12").id, sloth_make_id_f("W2").id, sloth_make_id_f("W21").id, sloth_make_id_f("W3").id, }; sloth_push_widget(&sloth, d, "Root"); // root sloth_push_widget(&sloth, d, "W1"); sloth_push_widget(&sloth, d, "W11"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W13"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W14"); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W12"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W2"); sloth_push_widget(&sloth, d, "W21"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); sloth_push_widget(&sloth, d, "W3"); // old child should get freed sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); // root - won't pop sloth_test_widget_order_count = 0; // reset test sloth_tree_walk_preorder(&sloth, sloth_test_widget_order, (Sloth_U8*)&ids1); sloth_ctx_free(&sloth); } // Widget Tree - Removing Expected Next Sibling sloth_test_multi_frame_removal(); // Widget Sizing { Sloth_Ctx sloth = { }; sloth_frame_prepare(&sloth); Sloth_Widget_Desc ele_desc; Sloth_Widget_Desc root_desc = { .layout = { .width = sloth_size_pixels(800), .height = sloth_size_pixels(900), //.margin.top = sloth_size_pixels(32), .direction = Sloth_LayoutDirection_TopDown, }, .style.color_bg = 0x333333FF, }; sloth_push_widget(&sloth, root_desc, "root"); ele_desc = (Sloth_Widget_Desc){ .layout = { .width = sloth_size_pixels(850), .height = sloth_size_pixels(200), }, .style.color_bg = 0xFFFFFFFF, }; sloth_push_widget(&sloth, ele_desc, "ele0"); sloth_pop_widget(&sloth); ele_desc.style.color_bg = 0xFF00FFFF; sloth_push_widget(&sloth, ele_desc, "ele1"); sloth_pop_widget(&sloth); sloth_pop_widget(&sloth); printf("==/==\n"); sloth_frame_advance(&sloth); sloth_frame_prepare(&sloth); Sloth_Widget* root = sloth.widget_tree_next_child; sloth_test_assert(root->cached->offset.x == 0 && root->cached->offset.y == 0); sloth_test_assert(root->cached->dim.x == 800 && root->cached->dim.y == 900); Sloth_Widget* ele0 = root->child_first; sloth_test_assert(ele0->cached->offset.x == 0 && ele0->cached->offset.y == 0); sloth_test_assert(ele0->cached->dim.x == 800 && ele0->cached->dim.y == 200); Sloth_Widget* ele1 = ele0->sibling_next; sloth_test_assert(ele1->cached->offset.x == 0 && ele1->cached->offset.y == 200); sloth_test_assert(ele1->cached->dim.x == 800 && ele1->cached->dim.y == 200); sloth_ctx_free(&sloth); } return; } bool sloth_test_button_f(Sloth_Ctx* sloth, char* fmt, ...) { Sloth_Widget_Desc desc = { .layout = { .width = sloth_size_text_content(), .height = sloth_size_text_content(), .margin = { .left = sloth_size_pixels(12), .right = sloth_size_pixels(12), .top = sloth_size_pixels(0), .bottom = sloth_size_pixels(8), }, }, .style = { .color_bg = 0x333333FF, .color_text = 0xFFFFFFFF, .color_outline = 0xFFFFFFFF, .outline_thickness = 1, .text_style = Sloth_TextStyle_Align_Center, }, }; va_list args; va_start(args, fmt); Sloth_Widget_Result r = sloth_push_widget_v(sloth, desc, fmt, args); va_end(args); sloth_pop_widget(sloth); if (r.clicked) { r.widget->style.color_bg = 0xFFFFFFFF; r.widget->style.color_text = 0x333333FF; } return r.clicked; } Sloth_R32 sloth_test_slider_f(Sloth_Ctx* sloth, Sloth_R32 min, Sloth_R32 max, Sloth_R32 v, char* fmt, ...) { Sloth_Widget_Desc desc = { .layout = { .width = sloth_size_pixels(128), .height = sloth_size_pixels(32), }, .style = { .color_bg = 0x333333FF, .color_outline = 0xFFFFFFFF, .outline_thickness = 1, }, .input.flags = Sloth_WidgetInput_Draggable }; va_list args; va_start(args, fmt); Sloth_Widget_Result r = sloth_push_widget_v(sloth, desc, fmt, args); va_end(args); // background text Sloth_Widget_Desc bg_text_desc = { .layout = { .width = sloth_size_percent_parent(1), .height = sloth_size_percent_parent(1), .position = { .kind= Sloth_LayoutPosition_FixedInParent, .left = sloth_size_pixels(0), .top = sloth_size_pixels(0), }, .margin.left = sloth_size_pixels(8), }, .style = { .color_text = 0xFFFFFFFF, .text_style = Sloth_TextStyle_NoWrapText }, .input.flags = Sloth_WidgetInput_DoNotCaptureMouse, }; sloth_push_widget_f(sloth, bg_text_desc, "%f###%d_bg", v, r.widget->id.value); sloth_pop_widget(sloth); // slider bar Sloth_R32 pct = (v - min) / (max - min); Sloth_Widget_Desc slider_desc = { .layout = { .width = sloth_size_percent_parent(pct), .height = sloth_size_percent_parent(1), .margin.left = sloth_size_pixels(8), }, .style = { .color_bg = 0x00FFFFFF, .color_text = 0x000000FF, .text_style = Sloth_TextStyle_NoWrapText }, .input.flags = Sloth_WidgetInput_DoNotCaptureMouse }; Sloth_Widget_Result rs = sloth_push_widget_f(sloth, slider_desc, "%f###%d_slider", v, r.widget->id.value); //printf("%d\n", r.widget->id.value); sloth_pop_widget(sloth); sloth_pop_widget(sloth); if (r.clicked) { rs.widget->style.color_bg = 0xFFFFFFFF; } Sloth_R32 result = v; if (r.held) { Sloth_R32 width = sloth_rect_dim(r.widget->cached->bounds).x; Sloth_R32 init_pct = (sloth->mouse_down_pos.x - r.widget->cached->bounds.value_min.x) / width; Sloth_R32 dx = r.drag_offset_pixels.x; Sloth_R32 px = dx / width; result = ((px + init_pct) * (max - min)) + min; result = Sloth_Max(min, Sloth_Min(max, result)); } return result; }