Updated GUI library
This commit is contained in:
441
code/gui/gui.cpp
441
code/gui/gui.cpp
@@ -35,9 +35,9 @@ void gui_context_init(Gui_Context *ctx)
|
||||
ctx-> last_frame_time = 0;
|
||||
ctx->current_frame_time = 0;
|
||||
|
||||
ctx-> active = NULL;
|
||||
ctx-> hot = NULL;
|
||||
ctx->possibly_hot = NULL;
|
||||
ctx-> active = 0;
|
||||
ctx-> hot = 0;
|
||||
ctx->possibly_hot = 0;
|
||||
ctx->active_start_time = 0;
|
||||
ctx-> hot_start_time = 0;
|
||||
ctx->active_status = 0;
|
||||
@@ -45,14 +45,12 @@ void gui_context_init(Gui_Context *ctx)
|
||||
ctx->text_cursor_position = 0;
|
||||
ctx->text_length = 0;
|
||||
|
||||
ctx->windows = NULL;
|
||||
ctx->window_count = 0;
|
||||
ctx->window_capacity = 0;
|
||||
ctx->windows.reserve(2);
|
||||
ctx->current_window = NULL;
|
||||
|
||||
ctx->id_count = 0;
|
||||
ctx->id_capacity = 8;
|
||||
ctx->id_stack = (Gui_Id*)p_alloc(sizeof(Gui_Id) * ctx->id_capacity);
|
||||
ctx->clipping.reserve();
|
||||
|
||||
ctx->id_stack.reserve();
|
||||
|
||||
ctx->input.pointer_position = {0, 0};
|
||||
ctx->input.absolute_pointer_position = {0, 0};
|
||||
@@ -61,6 +59,7 @@ void gui_context_init(Gui_Context *ctx)
|
||||
ctx->input.mouse_released_this_frame = false;
|
||||
ctx->input.text_cursor_move = 0;
|
||||
ctx->input.text[0] = '\0';
|
||||
ctx->input.scroll_move = 0;
|
||||
|
||||
ctx->input.absolute_pointer_position_last_frame = {0, 0};
|
||||
|
||||
@@ -89,6 +88,13 @@ void gui_context_init(Gui_Context *ctx)
|
||||
ctx->style.window_corner_radius = 5;
|
||||
ctx->style.window_titlebar_color = ctx->style.window_border_color;
|
||||
ctx->style.window_titlebar_color_inactive = {0.1,0.1,0.1,0.1};
|
||||
|
||||
ctx->style.scrollbar_size = 10;
|
||||
ctx->style.scrollbar_corner_radius = 4;
|
||||
ctx->style.scrollbar_inner_bar_size = 10;
|
||||
ctx->style.scrollbar_inner_bar_corner_radius = 4;
|
||||
ctx->style.scrollbar_color = v4{.1f,.1f,.1f,1.0f}*0.2f;
|
||||
ctx->style.scrollbar_inner_bar_color = v4{.5f,.5f,.5f,1.0f};
|
||||
}
|
||||
|
||||
void gui_context_select(Gui_Context *ctx)
|
||||
@@ -110,7 +116,7 @@ void gui_frame_begin(f64 curr_time)
|
||||
void gui_frame_end(Gui_Context *ctx)
|
||||
{
|
||||
// Render windows
|
||||
for(u32 i = 0; i < ctx->window_count; i++)
|
||||
for(u32 i = 0; i < ctx->windows.count; i++)
|
||||
{
|
||||
Gui_Window *w = &ctx->windows[i];
|
||||
if(w->still_open)
|
||||
@@ -137,7 +143,9 @@ void gui_frame_end(Gui_Context *ctx)
|
||||
ctx->input.text[0] = '\0';
|
||||
ctx->input.text_cursor_move = 0;
|
||||
|
||||
ctx->possibly_hot = NULL;
|
||||
ctx->input.scroll_move = 0;
|
||||
|
||||
ctx->possibly_hot = 0;
|
||||
}
|
||||
|
||||
void gui_frame_end()
|
||||
@@ -166,6 +174,14 @@ void gui_handle_event(Gui_Context *ctx, Event *e)
|
||||
else
|
||||
ctx->input.mouse_released_this_frame = true;
|
||||
} break;
|
||||
case KEY_MOUSE_WHEEL_UP: {
|
||||
if(e->key.pressed)
|
||||
ctx->input.scroll_move--;
|
||||
} break;
|
||||
case KEY_MOUSE_WHEEL_DOWN: {
|
||||
if(e->key.pressed)
|
||||
ctx->input.scroll_move++;
|
||||
} break;
|
||||
case KEY_ARROW_LEFT: {
|
||||
if(e->key.pressed)
|
||||
ctx->input.text_cursor_move--;
|
||||
@@ -198,6 +214,7 @@ void gui_handle_event(Event *e)
|
||||
// Text
|
||||
void gui_text(Gui_Context *ctx, Rect r, const char *text)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return;
|
||||
// @Feature: Clip text to Rect r
|
||||
gui_text_draw(r, text, ctx->style.font_size, ctx->style.text_color);
|
||||
}
|
||||
@@ -209,6 +226,7 @@ void gui_text(Rect r, const char *text)
|
||||
|
||||
void gui_text_aligned(Gui_Context *ctx, Rect r, const char *text, Gui_Text_Align alignment)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return;
|
||||
// @Cleanup: this should not depend on setting state. We should have a function that gets alignment as an argument
|
||||
Gui_Text_Align old_alignment = ctx->style.text_align;
|
||||
ctx->style.text_align = alignment;
|
||||
@@ -235,9 +253,10 @@ v2 gui_text_compute_size(const char *text)
|
||||
// Button
|
||||
bool gui_button(Gui_Context *ctx, Rect r, const char *text)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return false;
|
||||
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, text);
|
||||
bool behaviuor = gui_button_behaviuor(ctx, widget_id, r);
|
||||
|
||||
// Compute colors
|
||||
v4 button_color = ctx->style.button_color;
|
||||
v4 text_color = ctx->style.button_text_color;
|
||||
@@ -289,82 +308,46 @@ void gui_button_draw_inner_text(Gui_Context *ctx, Rect r, const char *text, v4 c
|
||||
}
|
||||
|
||||
// Slider
|
||||
bool gui_slider(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value)
|
||||
bool gui_slider_range(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return false;
|
||||
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, value);
|
||||
bool behaviour = gui_button_behaviuor(ctx, widget_id, r);
|
||||
|
||||
if(ctx->active == widget_id)
|
||||
{
|
||||
f32 pointer_ratio = (ctx->input.pointer_position.x - r.position.x) / r.size.x;
|
||||
*value = clamp(0.0f, 1.0f, pointer_ratio) * (max - min) + min;
|
||||
}
|
||||
|
||||
// Colors
|
||||
v4 button_color = ctx->style.button_color;
|
||||
v4 text_color = ctx->style.button_text_color;
|
||||
{
|
||||
f64 delta_t = (ctx->current_frame_time - ctx->hot_start_time);
|
||||
f32 interpolation = sin(10 * delta_t) * 0.5 + 0.5;
|
||||
if(ctx->hot == widget_id)
|
||||
{
|
||||
button_color = lerp(ctx->style.button_color, ctx->style.button_color_hovered, interpolation);
|
||||
text_color = lerp(ctx->style.button_text_color, ctx->style.button_text_color_hovered, interpolation);
|
||||
}
|
||||
if(ctx->active == widget_id)
|
||||
{
|
||||
button_color = lerp(ctx->style.button_color_hovered, ctx->style.button_color_pressed, interpolation * 0.4 + 0.6);
|
||||
text_color = lerp(ctx->style.button_text_color_hovered, ctx->style.button_text_color_pressed, interpolation * 0.4 + 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw
|
||||
f32 border = 2;
|
||||
f32 radius = ctx->style.button_radius;
|
||||
|
||||
// Draw background
|
||||
v4 background_color = ctx->style.button_color;
|
||||
r_2d_immediate_rounded_rectangle(r, radius, background_color); // Background
|
||||
|
||||
// Draw fill
|
||||
f32 ratio = (*value - min) / (max - min);
|
||||
Rect fill_r = r;
|
||||
fill_r.position += v2{border, border};
|
||||
fill_r.size = v2{maximum(0, fill_r.size.x - 2*border), maximum(0, fill_r.size.y - 2*border)};
|
||||
f32 fill_radius = maximum(0, radius - border);
|
||||
fill_r.size.x = fill_r.size.x * ratio;
|
||||
r_2d_immediate_rounded_rectangle(fill_r, fill_radius, ctx->style.slider_fill_color);
|
||||
|
||||
// Draw border
|
||||
v4 border_color = ctx->style.button_color_pressed;
|
||||
Rect border_r = r;
|
||||
border_r.position += v2{border, border} * 0.5;
|
||||
border_r.size = v2{maximum(0, border_r.size.x - border), maximum(0, border_r.size.y - border)};
|
||||
f32 border_radius = maximum(0, radius - border*0.5);
|
||||
r_2d_immediate_rounded_rectangle_outline(border_r, border_radius, border_color, border);
|
||||
|
||||
// Draw value text
|
||||
gui_id_stack_push(ctx, widget_id);
|
||||
// Value text
|
||||
char text[64];
|
||||
sprintf(text, "%f", *value);
|
||||
gui_button_draw_inner_text(ctx, r, text, text_color);
|
||||
snprintf(text, 64, "%f", *value);
|
||||
|
||||
return behaviour || ctx->active == widget_id;
|
||||
// Convert value from min-max to 0-1 range
|
||||
f32 ratio = (*value - min) / (max - min);
|
||||
|
||||
// Do slider
|
||||
bool behaviour = gui_slider_text(ctx, r, &ratio, text);
|
||||
|
||||
// Re-convert value from 0-1 to min-max range
|
||||
*value = clamp(0.0f, 1.0f, ratio) * (max - min) + min;
|
||||
|
||||
gui_id_stack_pop(ctx);
|
||||
return behaviour;
|
||||
}
|
||||
|
||||
bool gui_slider(Rect r, f32 min, f32 max, f32 *value)
|
||||
bool gui_slider_range(Rect r, f32 min, f32 max, f32 *value)
|
||||
{
|
||||
return gui_slider(&global_gui_state.default_context, r, min, max, value);
|
||||
return gui_slider_range(&global_gui_state.default_context, r, min, max, value);
|
||||
}
|
||||
|
||||
bool gui_slider_text(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value, const char *text)
|
||||
// ratio must be between 0 and 1.
|
||||
bool gui_slider_text(Gui_Context *ctx, Rect r, f32 *ratio, const char *text)
|
||||
{
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, value);
|
||||
if(gui_is_clipped(ctx, r)) return false;
|
||||
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, ratio);
|
||||
bool behaviour = gui_button_behaviuor(ctx, widget_id, r);
|
||||
|
||||
if(ctx->active == widget_id)
|
||||
{
|
||||
f32 pointer_ratio = (ctx->input.pointer_position.x - r.position.x) / r.size.x;
|
||||
*value = clamp(0.0f, 1.0f, pointer_ratio) * (max - min) + min;
|
||||
*ratio = clamp(0.0f, 1.0f, pointer_ratio);
|
||||
}
|
||||
|
||||
// Colors
|
||||
@@ -394,12 +377,11 @@ bool gui_slider_text(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value, con
|
||||
r_2d_immediate_rounded_rectangle(r, radius, background_color); // Background
|
||||
|
||||
// Draw fill
|
||||
f32 ratio = (*value - min) / (max - min);
|
||||
Rect fill_r = r;
|
||||
fill_r.position += v2{border, border};
|
||||
fill_r.size = v2{maximum(0, fill_r.size.x - 2*border), maximum(0, fill_r.size.y - 2*border)};
|
||||
f32 fill_radius = maximum(0, radius - border);
|
||||
fill_r.size.x = fill_r.size.x * ratio;
|
||||
fill_r.size.x = fill_r.size.x * (*ratio);
|
||||
r_2d_immediate_rounded_rectangle(fill_r, fill_radius, ctx->style.slider_fill_color);
|
||||
|
||||
// Draw border
|
||||
@@ -416,15 +398,17 @@ bool gui_slider_text(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value, con
|
||||
return behaviour || ctx->active == widget_id;
|
||||
}
|
||||
|
||||
bool gui_slider_text(Rect r, f32 min, f32 max, f32 *value, const char *text)
|
||||
bool gui_slider_text(Rect r, f32 *ratio, const char *text)
|
||||
{
|
||||
return gui_slider_text(&global_gui_state.default_context, r, min, max, value, text);
|
||||
return gui_slider_text(&global_gui_state.default_context, r, ratio, text);
|
||||
}
|
||||
|
||||
|
||||
// Images
|
||||
bool gui_image(Gui_Context *ctx, Rect r, r_texture *texture)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return false;
|
||||
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, texture);
|
||||
|
||||
v4 color = {1,1,1,1};
|
||||
@@ -440,6 +424,8 @@ bool gui_image(Rect r, r_texture *texture)
|
||||
|
||||
bool gui_image(Gui_Context *ctx, Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return false;
|
||||
|
||||
r_texture texture = r_texture_create((u8*)bmp, {width, height}, flags | R_TEXTURE_DONT_OWN);
|
||||
bool result = gui_image(ctx, r, &texture);
|
||||
r_texture_destroy(&texture);
|
||||
@@ -455,6 +441,8 @@ bool gui_image(Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 f
|
||||
// Text input
|
||||
bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size)
|
||||
{
|
||||
if(gui_is_clipped(ctx, r)) return false;
|
||||
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, text);
|
||||
bool behaviour = gui_text_input_behaviuor(ctx, widget_id, r);
|
||||
bool edited = false;
|
||||
@@ -501,6 +489,11 @@ bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size)
|
||||
{
|
||||
if(ctx->text_cursor_position > 0)
|
||||
{
|
||||
|
||||
// Panels
|
||||
// void gui_panel(Gui_Context *ctx, Rect r);
|
||||
// void gui_panel(Rect r);
|
||||
//
|
||||
u32 codepoint_bytes = utf8_bytes_to_prev_valid_codepoint(text, ctx->text_cursor_position);
|
||||
u64 from_index = ctx->text_cursor_position;
|
||||
u64 to_index = ctx->text_cursor_position - codepoint_bytes;
|
||||
@@ -536,6 +529,8 @@ bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size)
|
||||
edited = true;
|
||||
}
|
||||
|
||||
gui_clip_start(ctx, r);
|
||||
|
||||
r_2d_immediate_rounded_rectangle(r, ctx->style.button_radius, ctx->style.button_color);
|
||||
Rect text_rect;
|
||||
gui_button_draw_inner_text(ctx, r, text, ctx->style.button_text_color, &text_rect);
|
||||
@@ -562,6 +557,8 @@ bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size)
|
||||
r_2d_immediate_rectangle(cursor_rect, cursor_color);
|
||||
}
|
||||
|
||||
gui_clip_end(ctx);
|
||||
|
||||
return edited;
|
||||
}
|
||||
|
||||
@@ -593,6 +590,7 @@ void gui_panel(Rect r)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Windows
|
||||
bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id)
|
||||
{
|
||||
@@ -600,20 +598,20 @@ bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id)
|
||||
Gui_Window *window = gui_window_by_id(ctx, r, id);
|
||||
window->still_open = true;
|
||||
gui_window_update_rect(ctx, window, r);
|
||||
u32 window_index = window - ctx->windows;
|
||||
u32 window_index = window - ctx->windows.data;
|
||||
|
||||
bool hovered = gui_is_hovered(ctx, id, r);
|
||||
if(hovered && ctx->input.mouse_pressed_this_frame)
|
||||
{
|
||||
// Bring window on top
|
||||
u32 move_count = ctx->window_count - 1 - window_index;
|
||||
u32 move_count = ctx->windows.count - 1 - window_index;
|
||||
if(move_count > 0)
|
||||
{
|
||||
Gui_Window tmp = *window;
|
||||
memmove(ctx->windows + window_index, ctx->windows + window_index + 1, sizeof(Gui_Window) * move_count);
|
||||
memmove(ctx->windows.data + window_index, ctx->windows.data + window_index + 1, sizeof(Gui_Window) * move_count);
|
||||
|
||||
ctx->windows[ctx->window_count - 1] = tmp;
|
||||
window_index = ctx->window_count - 1;
|
||||
ctx->windows.last() = tmp;
|
||||
window_index = ctx->windows.count - 1;
|
||||
window = &ctx->windows[window_index];
|
||||
}
|
||||
}
|
||||
@@ -625,7 +623,7 @@ bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id)
|
||||
r_framebuffer_select(&window->framebuffer);
|
||||
|
||||
|
||||
bool is_inactive = window_index != ctx->window_count-1;
|
||||
bool is_inactive = window_index != ctx->windows.count-1;
|
||||
v4 background_color = is_inactive ? ctx->style.window_background_color_inactive :
|
||||
ctx->style.window_background_color;
|
||||
v4 border_color = is_inactive ? ctx->style.window_border_color_inactive :
|
||||
@@ -643,6 +641,29 @@ bool gui_window_start(Rect r, Gui_Id id)
|
||||
return gui_window_start(&global_gui_state.default_context, r, id);
|
||||
}
|
||||
|
||||
bool gui_window_with_titlebar_start(Gui_Context *ctx, Rect r, const char *title, Gui_Window_Titlebar_State *state)
|
||||
{
|
||||
Gui_Id id = gui_id_from_pointer(ctx, title);
|
||||
gui_window_start(ctx, r, id);
|
||||
|
||||
Gui_Titlebar_State *titlebar_state = state ? &state->titlebar : NULL;
|
||||
Rect titlebar_r = {0, 0, r.w, ctx->style.font_size};
|
||||
bool result = gui_window_titlebar(ctx, titlebar_r, title, titlebar_state);
|
||||
|
||||
if(state)
|
||||
{
|
||||
state->inner_r.position = {0, titlebar_r.h};
|
||||
state->inner_r.size = r.size - v2{0, titlebar_r.h};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool gui_window_with_titlebar_start(Rect r, const char *title, Gui_Window_Titlebar_State *state)
|
||||
{
|
||||
return gui_window_with_titlebar_start(&global_gui_state.default_context, r, title, state);
|
||||
}
|
||||
|
||||
void gui_window_end(Gui_Context *ctx)
|
||||
{
|
||||
gui_id_stack_pop(ctx);
|
||||
@@ -658,18 +679,20 @@ void gui_window_end()
|
||||
}
|
||||
|
||||
|
||||
bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, bool *close, v2 *move)
|
||||
bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, Gui_Titlebar_State *state)
|
||||
{
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, title);
|
||||
bool behaviour = gui_button_behaviuor(ctx, widget_id, r);
|
||||
if(close)
|
||||
*close = false;
|
||||
if(move)
|
||||
*move = {0,0};
|
||||
|
||||
if(state)
|
||||
{
|
||||
state->close = false;
|
||||
state->move = {0, 0};
|
||||
}
|
||||
|
||||
// Background
|
||||
v4 titlebar_color = ctx->style.window_titlebar_color_inactive;
|
||||
if(ctx->current_window && ctx->current_window - ctx->windows == ctx->window_count - 1)
|
||||
if(ctx->current_window == &ctx->windows.last())
|
||||
{
|
||||
titlebar_color = ctx->style.window_titlebar_color;
|
||||
}
|
||||
@@ -698,35 +721,155 @@ bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, bool *clos
|
||||
Rect exit_button_r = {r.x + r.w - exit_size, r.y, exit_size, exit_size};
|
||||
if(gui_button(ctx, exit_button_r, "⨯"))
|
||||
{
|
||||
if(close)
|
||||
*close = true;
|
||||
if(state)
|
||||
state->close = true;
|
||||
behaviour = true;
|
||||
}
|
||||
ctx->style = old_style;
|
||||
|
||||
// Move
|
||||
if(ctx->active == widget_id && !ctx->input.mouse_pressed_this_frame)
|
||||
if(state && ctx->active == widget_id)
|
||||
{
|
||||
if(move)
|
||||
{
|
||||
*move = ctx->input.absolute_pointer_position - ctx->input.absolute_pointer_position_last_frame;
|
||||
}
|
||||
if(ctx->input.mouse_pressed_this_frame)
|
||||
state->anchor_point = ctx->input.pointer_position;
|
||||
|
||||
state->move = ctx->input.pointer_position - state->anchor_point;
|
||||
}
|
||||
|
||||
return behaviour || ctx->active == widget_id;
|
||||
}
|
||||
|
||||
bool gui_window_titlebar(Rect r, const char *title, bool *close, v2 *move)
|
||||
bool gui_window_titlebar(Rect r, const char *title, Gui_Titlebar_State *state)
|
||||
{
|
||||
return gui_window_titlebar(&global_gui_state.default_context, r, title, close, move);
|
||||
return gui_window_titlebar(&global_gui_state.default_context, r, title, state);
|
||||
}
|
||||
|
||||
|
||||
bool gui_scrollable_area_start(Gui_Context *ctx, Rect r, v2 area_size, Rect *displayed_r)
|
||||
{
|
||||
bool behaviour = false;
|
||||
Gui_Id widget_id = gui_id_from_pointer(ctx, displayed_r);
|
||||
gui_id_stack_push(ctx, widget_id);
|
||||
|
||||
Rect displayed = {0,0,0,0};
|
||||
displayed.size = r.size;
|
||||
if(displayed_r)
|
||||
displayed.position = displayed_r->position;
|
||||
|
||||
Rect vertical_scrollbar = {0,0,0,0};
|
||||
Rect horizontal_scrollbar = {0,0,0,0};
|
||||
if(area_size.y > r.h)
|
||||
{
|
||||
vertical_scrollbar.w = ctx->style.scrollbar_size;
|
||||
vertical_scrollbar.h = r.h;
|
||||
vertical_scrollbar.x = r.x + r.w - vertical_scrollbar.w;
|
||||
vertical_scrollbar.y = r.y;
|
||||
}
|
||||
if(area_size.x > r.w)
|
||||
{
|
||||
horizontal_scrollbar.w = r.w;
|
||||
horizontal_scrollbar.h = ctx->style.scrollbar_size;
|
||||
horizontal_scrollbar.x = r.x;
|
||||
horizontal_scrollbar.y = r.y + r.h - horizontal_scrollbar.h;
|
||||
}
|
||||
if(vertical_scrollbar.w && horizontal_scrollbar.h)
|
||||
{
|
||||
vertical_scrollbar.h -= horizontal_scrollbar.h;
|
||||
horizontal_scrollbar.w -= vertical_scrollbar.w;
|
||||
}
|
||||
|
||||
displayed.size -= v2{vertical_scrollbar.w, horizontal_scrollbar.h};
|
||||
|
||||
if(vertical_scrollbar.w)
|
||||
{
|
||||
f32 relative_y = -(displayed.y - r.y) / area_size.y;
|
||||
f32 relative_h = displayed.h / area_size.y;
|
||||
Gui_Id vertical_id = gui_id_from_pointer(ctx, &vertical_scrollbar);
|
||||
behaviour = behaviour || gui_button_behaviuor(ctx, vertical_id, vertical_scrollbar);
|
||||
|
||||
if(gui_is_hovered(ctx, widget_id, r))
|
||||
{
|
||||
behaviour = true;
|
||||
f32 scroll_amount = relative_h / 3;
|
||||
relative_y += ctx->input.scroll_move * scroll_amount;
|
||||
}
|
||||
|
||||
if(ctx->active == vertical_id)
|
||||
{
|
||||
behaviour = true;
|
||||
f32 relative_pointer = ctx->input.pointer_position.y - vertical_scrollbar.y - 0.5*relative_h*vertical_scrollbar.h;
|
||||
relative_pointer /= vertical_scrollbar.h;
|
||||
relative_y = relative_pointer;
|
||||
}
|
||||
|
||||
relative_y = clamp(0, 1 - relative_h, relative_y);
|
||||
displayed.y = r.y - relative_y * area_size.y;
|
||||
|
||||
// Render
|
||||
r_2d_immediate_rounded_rectangle(vertical_scrollbar, ctx->style.scrollbar_corner_radius, ctx->style.scrollbar_color);
|
||||
Rect inner_bar_r = {
|
||||
.x = vertical_scrollbar.x + (vertical_scrollbar.w - ctx->style.scrollbar_inner_bar_size) / 2,
|
||||
.y = vertical_scrollbar.y + relative_y * vertical_scrollbar.h,
|
||||
.w = ctx->style.scrollbar_inner_bar_size,
|
||||
.h = relative_h * vertical_scrollbar.h
|
||||
};
|
||||
r_2d_immediate_rounded_rectangle(inner_bar_r, ctx->style.scrollbar_inner_bar_corner_radius, ctx->style.scrollbar_inner_bar_color);
|
||||
}
|
||||
if(horizontal_scrollbar.h)
|
||||
{
|
||||
f32 relative_x = -(displayed.x - r.x) / area_size.x;
|
||||
f32 relative_w = displayed.w / area_size.x;
|
||||
Gui_Id horizontal_id = gui_id_from_pointer(ctx, &horizontal_scrollbar);
|
||||
behaviour = behaviour || gui_button_behaviuor(ctx, horizontal_id, horizontal_scrollbar);
|
||||
|
||||
if(ctx->active == horizontal_id)
|
||||
{
|
||||
behaviour = true;
|
||||
f32 relative_pointer = ctx->input.pointer_position.x - horizontal_scrollbar.x - 0.5*relative_w*horizontal_scrollbar.w;
|
||||
relative_pointer /= horizontal_scrollbar.w;
|
||||
relative_x = clamp(0, 1 - relative_w, relative_pointer);
|
||||
displayed.x = r.x - relative_x * area_size.x;
|
||||
}
|
||||
|
||||
// Render
|
||||
r_2d_immediate_rounded_rectangle(horizontal_scrollbar, ctx->style.scrollbar_corner_radius, ctx->style.scrollbar_color);
|
||||
Rect inner_bar_r = {
|
||||
.x = horizontal_scrollbar.x + relative_x * horizontal_scrollbar.w,
|
||||
.y = horizontal_scrollbar.y + (horizontal_scrollbar.h - ctx->style.scrollbar_inner_bar_size) / 2,
|
||||
.w = relative_w * horizontal_scrollbar.w,
|
||||
.h = ctx->style.scrollbar_inner_bar_size,
|
||||
};
|
||||
r_2d_immediate_rounded_rectangle(inner_bar_r, ctx->style.scrollbar_inner_bar_corner_radius, ctx->style.scrollbar_inner_bar_color);
|
||||
}
|
||||
|
||||
if(displayed_r)
|
||||
*displayed_r = displayed;
|
||||
|
||||
gui_clip_start(ctx, Rect{r.x, r.y, displayed.w, displayed.h});
|
||||
return behaviour;
|
||||
}
|
||||
|
||||
bool gui_scrollable_area_start(Rect r, v2 area_size, Rect *displayed_r)
|
||||
{
|
||||
return gui_scrollable_area_start(&global_gui_state.default_context, r, area_size, displayed_r);
|
||||
}
|
||||
|
||||
void gui_scrollable_area_end(Gui_Context *ctx)
|
||||
{
|
||||
gui_clip_end(ctx);
|
||||
gui_id_stack_pop(ctx);
|
||||
}
|
||||
|
||||
void gui_scrollable_area_end()
|
||||
{
|
||||
gui_scrollable_area_end(&global_gui_state.default_context);
|
||||
}
|
||||
|
||||
|
||||
Gui_Window *gui_window_by_id(Gui_Context *ctx, Rect r, Gui_Id id)
|
||||
{
|
||||
Gui_Window *window = NULL;
|
||||
for(u32 i = 0; i < ctx->window_count; i++)
|
||||
for(u32 i = 0; i < ctx->windows.count; i++)
|
||||
{
|
||||
if(ctx->windows[i].id == id)
|
||||
{
|
||||
@@ -737,20 +880,13 @@ Gui_Window *gui_window_by_id(Gui_Context *ctx, Rect r, Gui_Id id)
|
||||
|
||||
if(!window)
|
||||
{
|
||||
if(ctx->window_count >= ctx->window_capacity)
|
||||
{
|
||||
if(ctx->window_capacity == 0)
|
||||
ctx->window_capacity = 1;
|
||||
ctx->window_capacity *= 2;
|
||||
ctx->windows = (Gui_Window*) p_realloc(ctx->windows, sizeof(Gui_Window) * ctx->window_capacity);
|
||||
}
|
||||
|
||||
window = &ctx->windows[ctx->window_count];
|
||||
ctx->window_count++;
|
||||
|
||||
window->id = id;
|
||||
window->r = r;
|
||||
window->framebuffer = r_framebuffer_create(V2S(r.size), 0);
|
||||
Gui_Window w = {
|
||||
.id = id,
|
||||
.r = r,
|
||||
.framebuffer = r_framebuffer_create(V2S(r.size), 0)
|
||||
};
|
||||
ctx->windows.push(w);
|
||||
window = &ctx->windows.last();
|
||||
}
|
||||
|
||||
return window;
|
||||
@@ -771,15 +907,27 @@ bool gui_is_hovered(Gui_Context *ctx, Gui_Id widget_id, Rect r)
|
||||
{
|
||||
if(is_inside(r, ctx->input.pointer_position))
|
||||
{
|
||||
for(u64 i = ctx->clipping.count; i > 0; i--) // Start from the end. The last clipping is usually the smallest and the most likely to fail.
|
||||
{
|
||||
// @Correctness: I have a feeling this is wrong. What happens with a stack where the first clips where not in a window, while the last ones are in a window? We would have different relative pointer positions to consider. The clipping would also be relative to its parent window/framebuffer.
|
||||
if(!is_inside(ctx->clipping[i-1], ctx->input.pointer_position))
|
||||
return false;
|
||||
}
|
||||
|
||||
s32 current_window_index = -1; // We use -1 to indicate we are not in a window. When we iterate over windows we do a +1 and start from 0, aka the first window. If we used 0, we would start from 1 and skip over window index 0.
|
||||
|
||||
// The ctx->windows array is sorted from back to front. If we are inside a window, only the following windows in the array can overlap up. The ones before are covered by the current window.
|
||||
if(ctx->current_window)
|
||||
current_window_index = ctx->current_window - ctx->windows;
|
||||
{
|
||||
current_window_index = ctx->current_window - ctx->windows.data;
|
||||
|
||||
if(!is_inside(ctx->current_window->r, ctx->input.absolute_pointer_position))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Am I a window? If so, we start checking from us. If ctx->current_window is set and widget_id is a window, it means we are a subwindow.
|
||||
// Subwindow are not supported yet though (20 September 2023), so this should be a bug in the user code. Yeah we don't check to prevent this, but anyways.
|
||||
for(s32 i = current_window_index + 1; i < ctx->window_count; i++)
|
||||
for(s32 i = current_window_index + 1; i < ctx->windows.count; i++)
|
||||
{
|
||||
Gui_Id window_id = ctx->windows[i].id;
|
||||
if(widget_id == window_id)
|
||||
@@ -790,7 +938,7 @@ bool gui_is_hovered(Gui_Context *ctx, Gui_Id widget_id, Rect r)
|
||||
}
|
||||
|
||||
// Iterate over windows that cover the current one
|
||||
for(u32 i = current_window_index + 1; i < ctx->window_count; i++)
|
||||
for(u32 i = current_window_index + 1; i < ctx->windows.count; i++)
|
||||
{
|
||||
Gui_Id window_id = ctx->windows[i].id;
|
||||
Rect window_rect = ctx->windows[i].r;
|
||||
@@ -827,7 +975,7 @@ bool gui_button_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r)
|
||||
|
||||
if(ctx->active == widget_id && ctx->input.mouse_released_this_frame)
|
||||
{
|
||||
ctx->active = NULL;
|
||||
ctx->active = 0;
|
||||
ctx->active_status = 0;
|
||||
}
|
||||
|
||||
@@ -858,7 +1006,7 @@ bool gui_text_input_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r)
|
||||
|
||||
if(ctx->active == widget_id && ctx->input.mouse_released_this_frame)
|
||||
{
|
||||
// ctx->active = NULL;
|
||||
// ctx->active = 0;
|
||||
// ctx->active_status = 0;
|
||||
}
|
||||
|
||||
@@ -868,26 +1016,57 @@ bool gui_text_input_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r)
|
||||
Gui_Id gui_id_from_pointer(Gui_Context *ctx, const void* ptr)
|
||||
{
|
||||
u32 seed = 0xFFFFFFFF;
|
||||
if(ctx->id_count)
|
||||
seed = ctx->id_stack[ctx->id_count - 1];
|
||||
if(ctx->id_stack.count)
|
||||
seed = ctx->id_stack.last();
|
||||
return hash_crc32(&ptr, sizeof(void*), seed);
|
||||
}
|
||||
|
||||
void gui_id_stack_push(Gui_Context *ctx, Gui_Id id)
|
||||
{
|
||||
if(ctx->id_capacity <= ctx->id_count)
|
||||
{
|
||||
u32 new_capacity = maximum(ctx->id_count + 1, ctx->id_capacity * 2);
|
||||
ctx->id_stack = (Gui_Id*)p_realloc(ctx->id_stack, sizeof(Gui_Id) * new_capacity);
|
||||
ctx->id_capacity = new_capacity;
|
||||
}
|
||||
|
||||
ctx->id_stack[ctx->id_count] = id;
|
||||
ctx->id_count++;
|
||||
ctx->id_stack.push(id);
|
||||
}
|
||||
|
||||
void gui_id_stack_pop(Gui_Context *ctx)
|
||||
{
|
||||
if(ctx->id_count > 0)
|
||||
ctx->id_count--;
|
||||
ctx->id_stack.pop();
|
||||
}
|
||||
|
||||
// Clipping
|
||||
static void gui_clip_internal(Gui_Context *ctx, Rect r)
|
||||
{
|
||||
f32 height = ctx->current_window ? ctx->current_window->r.h : ctx->height;
|
||||
glScissor(floor(r.x), floor(height - r.y - r.h), ceil(r.w), ceil(r.h)); // Textures are rendered flipped vertically, so we need to start r.y far away from the bottom and end r.h farther.
|
||||
}
|
||||
|
||||
void gui_clip_start(Gui_Context *ctx, Rect r)
|
||||
{
|
||||
ctx->clipping.push(r);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
gui_clip_internal(ctx, r);
|
||||
}
|
||||
|
||||
void gui_clip_end(Gui_Context *ctx)
|
||||
{
|
||||
ctx->clipping.pop();
|
||||
if(ctx->clipping.count)
|
||||
gui_clip_internal(ctx, ctx->clipping.last());
|
||||
else
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
bool gui_is_clipped(Gui_Context *ctx, Rect r)
|
||||
{
|
||||
for(u64 i = 0; i < ctx->clipping.count; i++)
|
||||
{
|
||||
// @Correctness: I have a feeling this is wrong. What happens with a stack where the first clips where not in a window, while the last ones are in a window? We would have different relative pointer positions to consider. The clipping would also be relative to its parent window/framebuffer.
|
||||
if(!is_inside(ctx->clipping[i], r.position) && !is_inside(ctx->clipping[i], r.position + r.size))
|
||||
return true;
|
||||
}
|
||||
if(ctx->current_window)
|
||||
{
|
||||
Rect window_r = {0, 0, ctx->current_window->r.w, ctx->current_window->r.h};
|
||||
if(!is_inside(window_r, r.position) && !is_inside(window_r, r.position + r.size))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../render/2d.h"
|
||||
#include "../lib/text.h"
|
||||
#include "../lib/event.h"
|
||||
#include "../lib/array.h"
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +23,7 @@ struct Gui_Input
|
||||
bool mouse_released_this_frame;
|
||||
s64 text_cursor_move;
|
||||
char text[32];
|
||||
s32 scroll_move;
|
||||
|
||||
v2 absolute_pointer_position_last_frame;
|
||||
};
|
||||
@@ -58,6 +60,13 @@ struct Gui_Style
|
||||
f32 window_corner_radius;
|
||||
v4 window_titlebar_color;
|
||||
v4 window_titlebar_color_inactive;
|
||||
|
||||
f32 scrollbar_size;
|
||||
f32 scrollbar_corner_radius;
|
||||
f32 scrollbar_inner_bar_size;
|
||||
f32 scrollbar_inner_bar_corner_radius;
|
||||
v4 scrollbar_color;
|
||||
v4 scrollbar_inner_bar_color;
|
||||
};
|
||||
|
||||
struct Gui_Window
|
||||
@@ -94,16 +103,15 @@ struct Gui_Context
|
||||
u64 text_length;
|
||||
|
||||
// Windows
|
||||
Gui_Window *windows;
|
||||
u32 window_count;
|
||||
u32 window_capacity;
|
||||
Array<Gui_Window> windows;
|
||||
Gui_Window *current_window;
|
||||
r_framebuffer *old_framebuffer;
|
||||
|
||||
// Clipping
|
||||
Array<Rect> clipping;
|
||||
|
||||
// ID
|
||||
Gui_Id *id_stack;
|
||||
u32 id_count;
|
||||
u32 id_capacity;
|
||||
Array<Gui_Id> id_stack;
|
||||
|
||||
Gui_Input input;
|
||||
Gui_Style style;
|
||||
@@ -150,18 +158,20 @@ v2 gui_text_compute_size(const char *text);
|
||||
// Button
|
||||
bool gui_button(Gui_Context *ctx, Rect r, const char *text);
|
||||
bool gui_button(Rect r, const char *text);
|
||||
// @Feature: Buttons with widgets inside
|
||||
|
||||
// Slider
|
||||
bool gui_slider(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value);
|
||||
bool gui_slider(Rect r, f32 min, f32 max, f32 *value);
|
||||
bool gui_slider_range(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value);
|
||||
bool gui_slider_range(Rect r, f32 min, f32 max, f32 *value);
|
||||
|
||||
bool gui_slider_text(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value, const char *text);
|
||||
bool gui_slider_text(Rect r, f32 min, f32 max, f32 *value, const char *text);
|
||||
|
||||
// Checkbox
|
||||
// Option buttons
|
||||
// Combo box
|
||||
// Tooltips
|
||||
bool gui_slider_text(Gui_Context *ctx, Rect r, f32 *ratio, const char *text); // ratio must be between 0 and 1.
|
||||
bool gui_slider_text(Rect r, f32 *ratio, const char *text);
|
||||
|
||||
// @Feature: Checkbox
|
||||
// @Feature: Option buttons
|
||||
// @Feature: Combo box
|
||||
// @Feature: Tooltips
|
||||
|
||||
// Images
|
||||
bool gui_image(Gui_Context *ctx, Rect r, r_texture *texture);
|
||||
@@ -178,13 +188,38 @@ void gui_panel(Gui_Context *ctx, Rect r);
|
||||
void gui_panel(Rect r);
|
||||
|
||||
// Windows
|
||||
struct Gui_Titlebar_State
|
||||
{
|
||||
v2 move;
|
||||
v2 anchor_point;
|
||||
bool close;
|
||||
};
|
||||
|
||||
struct Gui_Window_Titlebar_State
|
||||
{
|
||||
Rect inner_r;
|
||||
Gui_Titlebar_State titlebar;
|
||||
};
|
||||
|
||||
bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id); // You have to provide some kind of unique id to identify windows
|
||||
bool gui_window_start(Rect r, Gui_Id id);
|
||||
bool gui_window_with_titlebar_start(Gui_Context *ctx, Rect r, const char *title, Gui_Window_Titlebar_State *state);
|
||||
bool gui_window_with_titlebar_start(Rect r, const char *title, Gui_Window_Titlebar_State *state);
|
||||
void gui_window_end(Gui_Context *ctx);
|
||||
void gui_window_end();
|
||||
|
||||
bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, bool *close, v2 *move);
|
||||
bool gui_window_titlebar(Rect r, const char *title, bool *close, v2 *move);
|
||||
// Standalone titlebar for your custom windows
|
||||
bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, Gui_Titlebar_State *state);
|
||||
bool gui_window_titlebar(Rect r, const char *title, Gui_Titlebar_State *state);
|
||||
|
||||
|
||||
// @Feature: Panel
|
||||
// If you want to change which zone is displayed, change position in displayed_r. size will always be computed from r after removing the scrollbar size
|
||||
bool gui_scrollable_area_start(Gui_Context *ctx, Rect r, v2 area_size, Rect *displayed_r);
|
||||
bool gui_scrollable_area_start(Rect r, v2 area_size, Rect *displayed_r);
|
||||
void gui_scrollable_area_end(Gui_Context *ctx);
|
||||
void gui_scrollable_area_end();
|
||||
|
||||
|
||||
Gui_Window *gui_window_by_id(Gui_Context *ctx, Rect r, Gui_Id id); // Rect r might be needed for creation
|
||||
void gui_window_update_rect(Gui_Context *ctx, Gui_Window *window, Rect r);
|
||||
@@ -199,4 +234,9 @@ Gui_Id gui_id_from_pointer(Gui_Context *ctx, const void* ptr);
|
||||
void gui_id_stack_push(Gui_Context *ctx, Gui_Id id);
|
||||
void gui_id_stack_pop(Gui_Context *ctx);
|
||||
|
||||
// Clipping
|
||||
void gui_clip_start(Gui_Context *ctx, Rect r);
|
||||
void gui_clip_end(Gui_Context *ctx);
|
||||
bool gui_is_clipped(Gui_Context *ctx, Rect r); // Returns true only if completely clipped
|
||||
|
||||
#endif
|
||||
|
||||
77
code/lib/array.h
Normal file
77
code/lib/array.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef _PIUMA_LIB_ARRAY_H_
|
||||
#define _PIUMA_LIB_ARRAY_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "math.h"
|
||||
#include <assert.h>
|
||||
|
||||
template<typename T>
|
||||
struct Array
|
||||
{
|
||||
T *data = NULL;
|
||||
u64 count = 0;
|
||||
u64 capacity = 0;
|
||||
|
||||
inline void reserve(u64 new_capacity = 4)
|
||||
{
|
||||
new_capacity = maximum(new_capacity, count);
|
||||
if(new_capacity > capacity)
|
||||
{
|
||||
data = (T*)realloc(data, sizeof(T) * new_capacity);
|
||||
capacity = new_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
free(data);
|
||||
data = NULL;
|
||||
count = 0;
|
||||
capacity = 0;
|
||||
}
|
||||
|
||||
inline void push(T element)
|
||||
{
|
||||
if(count + 1 >= capacity)
|
||||
reserve(maximum(count + 1, capacity * 2));
|
||||
data[count++] = element;
|
||||
}
|
||||
|
||||
inline void push_unchecked(T element)
|
||||
{
|
||||
data[count++] = element;
|
||||
}
|
||||
|
||||
inline bool pop(T *element = NULL)
|
||||
{
|
||||
if(count <= 0)
|
||||
return false;
|
||||
count--;
|
||||
if(element) *element = data[count];
|
||||
return true;
|
||||
}
|
||||
|
||||
inline T pop_unchecked()
|
||||
{
|
||||
return data[--count];
|
||||
}
|
||||
|
||||
inline T &operator[](u64 index)
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
inline T &last()
|
||||
{
|
||||
return data[count - 1];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -37,9 +37,9 @@ struct Box
|
||||
inline bool is_inside(Box b, v3 p)
|
||||
{
|
||||
return
|
||||
(p.x < b.min.x || p.x > b.max.x) ||
|
||||
(p.y < b.min.y || p.y > b.max.y) ||
|
||||
(p.z < b.min.z || p.z > b.max.z);
|
||||
(p.x > b.min.x && p.x < b.max.x) &&
|
||||
(p.y > b.min.y && p.y < b.max.y) &&
|
||||
(p.z > b.min.z && p.z < b.max.z);
|
||||
}
|
||||
|
||||
inline bool overlaps(Box a, Box b)
|
||||
@@ -67,7 +67,7 @@ inline Box box_from_point_cloud(v3 *points, u32 count)
|
||||
Box box;
|
||||
box.min = points[0];
|
||||
box.max = points[0];
|
||||
for(u32 i = 0; i < count; i++)
|
||||
for(u32 i = 1; i < count; i++)
|
||||
{
|
||||
v3 p = points[i];
|
||||
box.min.x = minimum(box.min.x, p.x);
|
||||
@@ -302,6 +302,17 @@ inline m4 scale(f32 factor)
|
||||
}
|
||||
|
||||
|
||||
// Other geometric algebra
|
||||
inline void compute_basis(v3 a, v3 *b, v3 *c)
|
||||
{
|
||||
// from https://box2d.org/posts/2014/02/computing-a-basis/
|
||||
if(abs(a.x) >= 0.57735f)
|
||||
*b = {a.y, -a.x, 0.0f};
|
||||
else
|
||||
*b = {0.0f, a.z, -a.y};
|
||||
*b = normalize(*b);
|
||||
*c = cross(a, *b);
|
||||
}
|
||||
|
||||
|
||||
// Primitives
|
||||
|
||||
109
code/lib/math.h
109
code/lib/math.h
@@ -414,6 +414,11 @@ inline v3 V3(f32 e[3])
|
||||
return v3{e[0], e[1], e[2]};
|
||||
}
|
||||
|
||||
inline v3 V3(v4 v)
|
||||
{
|
||||
return v3{v.x / v.w, v.y / v.w, v.z / v.w};
|
||||
}
|
||||
|
||||
inline v3s V3S(v2s a, s32 z)
|
||||
{
|
||||
return {a.x, a.y, z};
|
||||
@@ -475,6 +480,11 @@ inline m3 M3(m4 m)
|
||||
return m3{ m.row[0].xyz, m.row[1].xyz, m.row[2].xyz };
|
||||
}
|
||||
|
||||
inline m4 M4(m3 m)
|
||||
{
|
||||
return m4{ V4(m.row[0], 0), V4(m.row[1], 0), V4(m.row[2], 0), v4{0,0,0,1} };
|
||||
}
|
||||
|
||||
|
||||
// Operators
|
||||
inline v2 operator+(v2 a)
|
||||
@@ -1079,38 +1089,6 @@ inline v4 & operator/=(v4 &a, f32 b)
|
||||
|
||||
|
||||
// Vector functions
|
||||
inline f32 length(v2 a)
|
||||
{
|
||||
return sqrt(square(a.x) + square(a.y));
|
||||
}
|
||||
|
||||
inline f32 length(v3 a)
|
||||
{
|
||||
return sqrt(square(a.x) + square(a.y) + square(a.z));
|
||||
}
|
||||
|
||||
inline f32 length(v4 a)
|
||||
{
|
||||
return sqrt(square(a.x) + square(a.y) + square(a.z) + square(a.w));
|
||||
}
|
||||
|
||||
|
||||
inline f32 distance(v2 a, v2 b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline f32 distance(v3 a, v3 b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline f32 distance(v4 a, v4 b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
|
||||
inline f32 dot(v2 a, v2 b)
|
||||
{
|
||||
return a.x*b.x + a.y*b.y;
|
||||
@@ -1138,6 +1116,69 @@ inline v3 cross(v3 a, v3 b)
|
||||
}
|
||||
|
||||
|
||||
inline f32 length(v2 a)
|
||||
{
|
||||
return sqrt(dot(a,a));
|
||||
}
|
||||
|
||||
inline f32 length(v3 a)
|
||||
{
|
||||
return sqrt(dot(a,a));
|
||||
}
|
||||
|
||||
inline f32 length(v4 a)
|
||||
{
|
||||
return sqrt(dot(a,a));
|
||||
}
|
||||
|
||||
inline f32 length2(v2 a)
|
||||
{
|
||||
return dot(a,a);
|
||||
}
|
||||
|
||||
inline f32 length2(v3 a)
|
||||
{
|
||||
return dot(a,a);
|
||||
}
|
||||
|
||||
inline f32 length2(v4 a)
|
||||
{
|
||||
return dot(a,a);
|
||||
}
|
||||
|
||||
|
||||
inline f32 distance(v2 a, v2 b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline f32 distance(v3 a, v3 b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
inline f32 distance(v4 a, v4 b)
|
||||
{
|
||||
return length(a - b);
|
||||
}
|
||||
|
||||
|
||||
inline f32 distance2(v2 a, v2 b)
|
||||
{
|
||||
return length2(a - b);
|
||||
}
|
||||
|
||||
inline f32 distance2(v3 a, v3 b)
|
||||
{
|
||||
return length2(a - b);
|
||||
}
|
||||
|
||||
inline f32 distance2(v4 a, v4 b)
|
||||
{
|
||||
return length2(a - b);
|
||||
}
|
||||
|
||||
|
||||
inline v2 normalize(v2 a)
|
||||
{
|
||||
return a / length(a);
|
||||
@@ -1209,10 +1250,6 @@ inline m3 operator*(m3 a, m3 b)
|
||||
result.E[2][1] = dot(a.row[2], c1);
|
||||
result.E[2][2] = dot(a.row[2], c2);
|
||||
|
||||
result.E[3][0] = dot(a.row[3], c0);
|
||||
result.E[3][1] = dot(a.row[3], c1);
|
||||
result.E[3][2] = dot(a.row[3], c2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ void system_info_gui(Gui_Layout_Grid *grid)
|
||||
|
||||
// slider for ram?
|
||||
char ram[128]; snprintf(ram, 128, "%.2f/%.2f GiB", system_info.ram_used / (1024.0*1024.0*1024.0), system_info.ram_total / (1024.0*1024.0*1024.0));
|
||||
f32 ram_value = system_info.ram_used;
|
||||
f32 ram_ratio = (f32)system_info.ram_used / system_info.ram_total;
|
||||
|
||||
gui_text_aligned(layout.cell(layout.max_cells_count.x), processors, GUI_ALIGN_LEFT);
|
||||
|
||||
@@ -416,7 +416,7 @@ void system_info_gui(Gui_Layout_Grid *grid)
|
||||
ram_rect.position.x += ram_text_size.x;
|
||||
ram_rect.size.x -= ram_text_size.x;
|
||||
gui_text_aligned(layout.rect(), "RAM: ", GUI_ALIGN_LEFT);
|
||||
gui_slider_text(ram_rect, 0, system_info.ram_total, &ram_value, ram);
|
||||
gui_slider_text(ram_rect, &ram_ratio, ram);
|
||||
|
||||
gui_text_aligned(layout.cell(layout.max_cells_count.x), load, GUI_ALIGN_LEFT);
|
||||
|
||||
@@ -659,8 +659,8 @@ void fs_gui(Gui_Layout_Grid *grid)
|
||||
layout.row(2);
|
||||
char space[64]; snprintf(space, 64, "%.1f/%.1f GiB", (f32)(fs_info.fs[i].bytes_total - fs_info.fs[i].bytes_available) / (1024*1024*1024), (f32)fs_info.fs[i].bytes_total / (1024*1024*1024));
|
||||
char percentage[64]; snprintf(percentage, 64, "%.1f%%", (f32)(fs_info.fs[i].bytes_total - fs_info.fs[i].bytes_available) / fs_info.fs[i].bytes_total * 100);
|
||||
f32 space_value = fs_info.fs[i].bytes_total - fs_info.fs[i].bytes_available;
|
||||
gui_slider_text(layout.cell(layout.max_cells_count.x), 0, fs_info.fs[i].bytes_total, &space_value, space);
|
||||
f32 space_ratio = (f32)(fs_info.fs[i].bytes_total - fs_info.fs[i].bytes_available) / fs_info.fs[i].bytes_total;
|
||||
gui_slider_text(layout.cell(layout.max_cells_count.x), &space_ratio, space);
|
||||
gui_text_aligned(layout.cell(layout.max_cells_count.x), percentage, GUI_ALIGN_CENTER);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user