UI Windows slowly working

This commit is contained in:
2024-12-30 00:10:50 +01:00
parent 4ac24fa03c
commit 6ed67e8fcb
6 changed files with 424 additions and 123 deletions

311
ui/ui.jai
View File

@@ -2,6 +2,11 @@ MAX_VERT_BUFFERS :: 64;
MAX_BOXES :: 4096;
MAX_WINDOWS :: 128;
SCROLL_SPEED :: 5;
WINDOW_TITLE_BAR_HEIGHT :: 20.0;
WINDOW_BORDER_WIDTH :: 3.0;
UI_Box_Flags :: enum_flags u32 {
NONE :: 0;
CLICKABLE :: 1;
@@ -133,11 +138,16 @@ Textured_Vert_Buffer :: struct {
UI_Window :: struct {
hash: u32;
root: *UI_Box;
boxes: Static_Array(*UI_Box, 4096);
title: string;
position: Vector2;
size: Vector2;
position: Vector2i;
size: Vector2i;
actual_position: Vector2i;
offset: Vector2i;
scroll_offset: Vector2i;
last_used_frame_index: u64;
}
@@ -151,6 +161,8 @@ UI_State :: struct {
windows: Table(u32, UI_Window);
current_window: *UI_Window;
currently_moving_window: *UI_Window;
last_box: *UI_Box;
parent_stack: Stack(*UI_Box);
@@ -178,28 +190,52 @@ UI_State :: struct {
instanced_rects : [..] Rect_Instance_Data;
instance_rect_sb : Buffer_Handle;
window_buffers: [MAX_WINDOWS] Instanced_Rects_Data;
num_used_window_buffers: s64;
texture_vert_buffers : [MAX_VERT_BUFFERS] Textured_Vert_Buffer;
next_available_texture_buffer_index: s64;
sampler: Sampler_Handle;
}
Instanced_Rects_Data :: struct {
rects: Static_Array(Rect_Instance_Data, 4096);
buffer: Buffer_Handle;
}
ui_state : UI_State;
ui_window_make :: (title: string, hash: u32) -> *UI_Window {
ui_window_make :: (hash: u32) -> *UI_Window {
window := get_ui_window_or_create_new(hash);
return window;
}
ui_window_begin :: (title: string, identifier: s64 = 0, loc := #caller_location) {
ui_window_begin :: (title: string, x: s32, y: s32, width: s32, height: s32, identifier: s64 = 0, loc := #caller_location) {
assert(ui_state.current_window == null);
ui_state.current_window = ui_window_make(title, hash=get_hash(loc, identifier));
ui_state.current_window.last_used_frame_index = ui_state.frame_index;
window := ui_window_make(hash=get_hash(loc, identifier));
window.title = copy_temporary_string(title);
window.boxes.count = 0;
//window.boxes.allocator = temp;
window.last_used_frame_index = ui_state.frame_index;
window.position.x = x;
window.position.y = y;
window.size.x = width;
window.size.y = height;
ui_state.current_window = window;
ui_set_next_size_x(.PCT, 1.0);
ui_set_next_size_y(.CHILDREN_SUM);
ui_set_next_background_color(.{0.04, 0.04, 0.04, 1.0});
background := ui_box(.DRAW_BACKGROUND);
ui_push_parent(background, alignment=.LEFT, axis=.VERTICAL);
}
ui_window_end :: () {
ui_state.current_window = null;
ui_pop_parent();
}
ui_box :: (flags: UI_Box_Flags, identifier: s64 = 0, loc := #caller_location) -> *UI_Box {
@@ -214,8 +250,11 @@ ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box {
parent.num_children += 1;
actual_hash += parent.hash * xx parent.num_children;
//actual_hash += xx (parent.num_children*2);
} else if ui_state.current_window != null {
actual_hash += ui_state.current_window.hash + actual_hash;
}
box := get_ui_box_or_create_new(actual_hash);
box.last_used_frame_index = ui_state.frame_index;
box.first_child = null;
@@ -227,14 +266,11 @@ ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box {
box.parent = null;
box.root_for_window = null;
if ui_state.current_window {
if ui_state.current_window.root == null {
ui_state.current_window.root = box;
box.root_for_window = ui_state.current_window;
}
if ui_state.current_window && parent == null {
array_add(*ui_state.current_window.boxes, box);
box.root_for_window = ui_state.current_window;
}
// Set the links
box.parent = parent;
if box.parent != null {
@@ -308,7 +344,8 @@ ui_init :: () {
init(*ui_state.boxes, MAX_BOXES);
ui_state.sampler = create_sampler(engine.renderer);
ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 14);
//ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 32);
ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/Inconsolata-Regular.ttf", 18);
ui_state.fonts.button = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 14);
// ui_rect
@@ -428,6 +465,13 @@ ui_init :: () {
dynamic_buffer_size := size_of(Rect_Instance_Data) * ui_state.max_verts;
ui_state.instance_rect_sb = create_structured_buffer(engine.renderer, null, dynamic_buffer_size, stride=size_of(Rect_Instance_Data), mappable=true);
}
{
for 0..MAX_WINDOWS-1 {
dynamic_buffer_size := size_of(Rect_Instance_Data) * ui_state.max_verts;
ui_state.window_buffers[it].buffer = create_structured_buffer(engine.renderer, null, dynamic_buffer_size, stride=size_of(Rect_Instance_Data), mappable=true);
}
}
}
// # BEGIN # LAYOUT ALGORITHM
@@ -458,12 +502,22 @@ ui_figure_out_sizes :: () {
// Upwards-dependent
for *box : ui_state.boxes {
if box.semantic_size[0].size_kind == .PCT {
assert(box.parent != null);
box.size.x = box.parent.size.x * box.semantic_size[0].value - (box.parent.padding_left + box.parent.padding_right);
if box.parent != null {
box.size.x = box.parent.size.x * box.semantic_size[0].value - (box.parent.padding_left + box.parent.padding_right);
} else if box.root_for_window != null {
box.size.x = cast(float)box.root_for_window.size.x * box.semantic_size[0].value - (WINDOW_BORDER_WIDTH);
} else {
assert(false);
}
}
if box.semantic_size[1].size_kind == .PCT {
assert(box.parent != null);
box.size.y = box.parent.size.y * box.semantic_size[1].value - (box.parent.padding_top + box.parent.padding_bottom);
if box.parent != null {
box.size.y = box.parent.size.y * box.semantic_size[1].value - (box.parent.padding_top + box.parent.padding_bottom);
} else if box.root_for_window != null {
box.size.y = cast(float)box.root_for_window.size.y * box.semantic_size[0].value - (WINDOW_BORDER_WIDTH);
} else {
assert(false);
}
}
}
@@ -512,8 +566,14 @@ ui_figure_out_sizes :: () {
// Find final positions
for *box : ui_state.boxes {
if box.parent == null {
box.rect.x = 0.0;
box.rect.y = 0.0;
if box.root_for_window {
window := box.root_for_window;
box.rect.x = xx (window.actual_position.x + window.scroll_offset.x) + WINDOW_BORDER_WIDTH;
box.rect.y = xx (window.actual_position.y + window.scroll_offset.y) + WINDOW_BORDER_WIDTH + WINDOW_TITLE_BAR_HEIGHT;
} else {
box.rect.x = 0.0;
box.rect.y = 0.0;
}
box.rect.w = box.size.x;
box.rect.h = box.size.y;
ui_set_rect_recursively(box);
@@ -600,9 +660,11 @@ ui_begin :: () {
assert(!ui_state.begun);
ui_state.frame_index += 1;
ui_state.begun = true;
ui_state.current_window = null;
ui_state.colored_verts.count = 0;
ui_state.instanced_rects.count = 0;
ui_state.next_available_texture_buffer_index = 0;
ui_state.num_used_window_buffers = 0;
}
ui_end :: () {
@@ -689,55 +751,153 @@ ui_set_next_size_y :: (size: UI_Size_Kind, value: float = 0.0, strictness: float
}
// # END # BOX PROPERTY FUNCTIONS
get_rect_instance_buffer :: () -> *Instanced_Rects_Data {
//assert(ui_state.num_used_window_buffers <= MAX_WINDOWS);
next := *ui_state.window_buffers[ui_state.num_used_window_buffers];
next.rects.count = 0;
ui_state.num_used_window_buffers += 1;
return next;
}
ui_render :: () {
// Render background
for * ui_state.boxes {
if it.parent == null {
ui_render_background_recursively(it);
// Draw each window separately
for window: ui_state.windows {
// Draw outer rect + border of window
title_bar_buffer_data := get_rect_instance_buffer();
inv_w := 1.0 / cast(float)engine.renderer.render_target_width;
inv_h := 1.0 / cast(float)engine.renderer.render_target_height;
{
// Draw the window
x : float = xx window.actual_position.x * inv_w * 2.0 - 1.0;
y : float = (cast(float)engine.renderer.render_target_height - xx window.actual_position.y) * inv_h * 2.0 - 1.0;
w : float = xx window.size.x * inv_w * 2.0;
h : float = xx window.size.y * inv_h * 2.0;
color : Color = .{0.0,0.1,0.3,1};
instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, color);
array_add(*title_bar_buffer_data.rects, instance_data);
{
border_color : Color = .{0.6,0.6,0.6,1};
instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, WINDOW_BORDER_WIDTH, border_color);
array_add(*title_bar_buffer_data.rects, instance_data);
}
}
}
if ui_state.colored_verts.count > 0 {
//upload_data_to_buffer(engine.renderer, ui_state.rect_vb, ui_state.colored_verts.data, cast(s32)ui_state.colored_verts.count * size_of(Colored_Vert));
// Draw the title bar
{
x : float = (xx window.actual_position.x + WINDOW_BORDER_WIDTH) * inv_w * 2.0 - 1.0;
y : float = (cast(float)engine.renderer.render_target_height - (xx window.actual_position.y + WINDOW_BORDER_WIDTH)) * inv_h * 2.0 - 1.0;
w : float = (xx window.size.x - WINDOW_BORDER_WIDTH * 2.0) * inv_w * 2.0;
h : float = xx WINDOW_TITLE_BAR_HEIGHT * inv_h * 2.0;
//push_cmd_set_draw_mode(engine.renderer, .FILL);
//push_cmd_set_depth_write(engine.renderer, false);
//push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui_rect);
instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, .{0.0, 0.2, 0.4, 1.0});
array_add(*title_bar_buffer_data.rects, instance_data);
}
//push_cmd_set_vertex_buffer(engine.renderer, ui_state.rect_vb);
//push_cmd_draw(engine.renderer, ui_state.colored_verts.count);
}
// NEW DRAW TIME
if ui_state.instanced_rects.count > 0 {
upload_data_to_buffer(engine.renderer, ui_state.instance_rect_sb, ui_state.instanced_rects.data, cast(s32)ui_state.instanced_rects.count * size_of(Rect_Instance_Data));
{
upload_data_to_buffer(engine.renderer, title_bar_buffer_data.buffer, title_bar_buffer_data.rects.data.data, cast(s32)title_bar_buffer_data.rects.count * size_of(Rect_Instance_Data));
push_cmd_set_draw_mode(engine.renderer, .FILL);
push_cmd_set_depth_write(engine.renderer, false);
push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui);
push_cmd_set_constant_buffer(engine.renderer, 0, engine.screen_data_buffer, .PIXEL);
push_cmd_set_structured_buffer(engine.renderer, 0, title_bar_buffer_data.buffer, .VERTEX);
push_cmd_draw_instanced(engine.renderer, 4, title_bar_buffer_data.rects.count, topology=.TRIANGLE_STRIP);
// Title bar
push_cmd_set_pipeline_state(engine.renderer, console.text_pipeline);
push_cmd_set_sampler(engine.renderer, 0, ui_state.sampler);
{
if window.title.count > 0 {
font_handle := ui_state.fonts.regular;
x : float = (xx window.actual_position.x + WINDOW_BORDER_WIDTH + 2);
y : float = (cast(float)engine.renderer.render_target_height - (window.actual_position.y + WINDOW_BORDER_WIDTH + 2.0));
text_size := get_text_size(engine.renderer, window.title, font_handle);
y -= text_size.y;
render_data := bake_text(engine.renderer, x, y, window.title, font_handle, .{1,1,1,1});
font := *engine.renderer.fonts[font_handle - 1];
push_cmd_set_texture(engine.renderer, 0, font.texture);
push_cmd_set_vertex_buffer(engine.renderer, render_data.vb);
push_cmd_draw(engine.renderer, render_data.vert_count);
}
}
}
push_cmd_set_scissor(engine.renderer,
xx window.size.x,
xx (window.size.y - WINDOW_TITLE_BAR_HEIGHT),
xx window.actual_position.x,
xx (window.actual_position.y + WINDOW_TITLE_BAR_HEIGHT));
buffer_data := get_rect_instance_buffer();
for box: window.boxes {
// Render background
ui_render_background_recursively(box, buffer_data);
}
upload_data_to_buffer(engine.renderer, buffer_data.buffer, buffer_data.rects.data.data, cast(s32)buffer_data.rects.count * size_of(Rect_Instance_Data));
push_cmd_set_draw_mode(engine.renderer, .FILL);
push_cmd_set_depth_write(engine.renderer, false);
push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui);
push_cmd_set_constant_buffer(engine.renderer, 0, engine.screen_data_buffer, .PIXEL);
push_cmd_set_structured_buffer(engine.renderer, 0, ui_state.instance_rect_sb, .VERTEX);
push_cmd_draw_instanced(engine.renderer, 4, ui_state.instanced_rects.count, topology=.TRIANGLE_STRIP);
push_cmd_set_structured_buffer(engine.renderer, 0, buffer_data.buffer, .VERTEX);
push_cmd_draw_instanced(engine.renderer, 4, buffer_data.rects.count, topology=.TRIANGLE_STRIP);
// Text
push_cmd_set_pipeline_state(engine.renderer, console.text_pipeline);
push_cmd_set_sampler(engine.renderer, 0, ui_state.sampler);
for box: window.boxes {
ui_render_text_recursively(box);
}
push_cmd_clear_scissor(engine.renderer);
}
// Render background
for * ui_state.boxes {
if it.parent == null {
//ui_render_background_recursively(it);
}
}
// NEW DRAW TIME
if ui_state.instanced_rects.count > 0 {
//upload_data_to_buffer(engine.renderer, ui_state.instance_rect_sb, ui_state.instanced_rects.data, cast(s32)ui_state.instanced_rects.count * size_of(Rect_Instance_Data));
//push_cmd_set_draw_mode(engine.renderer, .FILL);
//push_cmd_set_depth_write(engine.renderer, false);
//push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui);
//push_cmd_set_constant_buffer(engine.renderer, 0, engine.screen_data_buffer, .PIXEL);
//push_cmd_set_structured_buffer(engine.renderer, 0, ui_state.instance_rect_sb, .VERTEX);
//push_cmd_draw_instanced(engine.renderer, 4, ui_state.instanced_rects.count, topology=.TRIANGLE_STRIP);
}
// @Incomplete: this should be part of the same rendering somehow... Although it should happen on a per-texture basis
// Render backgrounds with texture
for * ui_state.boxes {
if it.parent == null {
ui_render_texture_background_recursively(it);
}
}
//for * ui_state.boxes {
// if it.parent == null {
// ui_render_texture_background_recursively(it);
// }
//}
// Render text
push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.text);
for * ui_state.boxes {
if it.parent == null {
ui_render_text_recursively(it);
}
}
//for * ui_state.boxes {
// if it.parent == null {
// ui_render_text_recursively(it);
// }
//}
}
#scope_file
@@ -833,7 +993,7 @@ instance_data_from_rect :: (x: float, y: float, w: float, h: float, corner_radiu
return data;
}
ui_render_background_recursively :: (box: *UI_Box) {
ui_render_background_recursively :: (box: *UI_Box, buffer_data: *Instanced_Rects_Data) {
inv_w := 1.0 / cast(float)engine.renderer.render_target_width;
inv_h := 1.0 / cast(float)engine.renderer.render_target_height;
@@ -845,7 +1005,7 @@ ui_render_background_recursively :: (box: *UI_Box) {
color : Color = box.style.background_color;
instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, color);
array_add(*ui_state.instanced_rects, instance_data);
array_add(*buffer_data.rects, instance_data);
//array_add(*ui_state.colored_verts, make_vert(x + w, y - h, color));
//array_add(*ui_state.colored_verts, make_vert(x, y - h, color));
@@ -864,7 +1024,7 @@ ui_render_background_recursively :: (box: *UI_Box) {
color : Color = box.style.border_color;
instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, box.style.border_width, color);
array_add(*ui_state.instanced_rects, instance_data);
array_add(*buffer_data.rects, instance_data);
}
if should_draw_hover_animation(box) {
@@ -875,13 +1035,13 @@ ui_render_background_recursively :: (box: *UI_Box) {
color := Color.{1,1,1, box.hover_animation_t * 0.3};
instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, color);
array_add(*ui_state.instanced_rects, instance_data);
array_add(*buffer_data.rects, instance_data);
}
child := box.first_child;
while child != null {
defer child = child.next;
ui_render_background_recursively(child);
ui_render_background_recursively(child, buffer_data);
}
}
@@ -990,6 +1150,47 @@ ui_update_input :: () {
mouse_x := engine.input.mouse.x;
mouse_y := engine.input.mouse.y;
// Scrolling
for *window: ui_state.windows {
rect: Rect;
rect.x = xx window.actual_position.x;
rect.y = xx (window.actual_position.y+WINDOW_TITLE_BAR_HEIGHT);
rect.w = xx window.size.x;
rect.h = xx (window.size.y - WINDOW_TITLE_BAR_HEIGHT);
if is_mouse_inside(mouse_x, mouse_y, rect) {
window.scroll_offset.y += cast(s32) engine.input.mouse.wheel * SCROLL_SPEED;
}
}
if ui_state.currently_moving_window == null {
for *window: ui_state.windows {
rect: Rect;
rect.x = xx window.actual_position.x;
rect.y = xx window.actual_position.y;
rect.w = xx window.size.x;
rect.h = xx WINDOW_TITLE_BAR_HEIGHT;
if is_mouse_inside(mouse_x, mouse_y, rect) {
if key_down(.MOUSE_LEFT) {
ui_state.currently_moving_window = window;
break;
}
}
}
} else {
ui_state.currently_moving_window.offset.x += xx engine.input.mouse.delta_x;
ui_state.currently_moving_window.offset.y += xx engine.input.mouse.delta_y;
ui_state.currently_moving_window.offset.x -= xx min(0, ui_state.currently_moving_window.position.x + ui_state.currently_moving_window.offset.x);
ui_state.currently_moving_window.offset.y -= xx min(0, ui_state.currently_moving_window.position.y + ui_state.currently_moving_window.offset.y);
if !key_pressed(.MOUSE_LEFT) ui_state.currently_moving_window = null;
}
for *window: ui_state.windows {
window.actual_position = window.position + window.offset;
}
for *box: ui_state.boxes {
if engine.mode == .EDITING || box.flags & .PLAY_MODE_FOCUSABLE {
if box.flags & .CLICKABLE {