Lumenarium/src_v2/libs/sloth/sloth_tests.c

697 lines
22 KiB
C

#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
// <removed on frame 1>
// widget
// child
// <removed on frame 2>
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;
}