1310 lines
43 KiB
Plaintext
1310 lines
43 KiB
Plaintext
MAX_VERT_BUFFERS :: 64;
|
|
MAX_BOXES :: 4096;
|
|
MAX_WINDOWS :: 128;
|
|
|
|
SCROLL_SPEED :: 5;
|
|
DEFAULT_PADDING :: 5;
|
|
|
|
WINDOW_TITLE_BAR_HEIGHT :: 20.0;
|
|
WINDOW_BORDER_WIDTH :: 3;
|
|
|
|
UI_Box_Flags :: enum_flags u32 {
|
|
NONE :: 0;
|
|
CLICKABLE :: 1;
|
|
DRAW_BORDER :: 2;
|
|
DRAW_BACKGROUND :: 4;
|
|
DRAW_TEXT :: 8;
|
|
CLIP :: 16;
|
|
|
|
ANIMATE_ON_HOVER :: 32;
|
|
|
|
PLAY_MODE_FOCUSABLE :: 64;
|
|
}
|
|
|
|
Rect :: struct {
|
|
x: float;
|
|
y: float;
|
|
w: float;
|
|
h: float;
|
|
}
|
|
|
|
Rect_Instance_Data :: struct {
|
|
p0 : Vector2;
|
|
p1 : Vector2;
|
|
colors : [4] Color;
|
|
|
|
corner_radius : float;
|
|
edge_softness : float;
|
|
border_thickness : float;
|
|
}
|
|
|
|
Interaction_State :: struct {
|
|
clicked: bool;
|
|
|
|
left_mouse_down: bool;
|
|
right_mouse_down: bool;
|
|
left_mouse_pressed: bool;
|
|
right_mouse_pressed: bool;
|
|
|
|
editing: bool;
|
|
|
|
local_mouse_coordinates: Vector2; // Coordinates inside the rect in the range [0,1]
|
|
normalized_local_mouse_coordinates: Vector2; // Coordinates inside the rect in the range [0,1]
|
|
}
|
|
|
|
UI_Box :: struct {
|
|
hash: u32;
|
|
last_used_frame_index: u64;
|
|
|
|
window: *UI_Window;
|
|
root_in_window: bool;
|
|
parent: *UI_Box;
|
|
first_child: *UI_Box;
|
|
last_child: *UI_Box;
|
|
num_children: u64;
|
|
|
|
next: *UI_Box;
|
|
prev: *UI_Box;
|
|
|
|
// Specified per-frame
|
|
flags : UI_Box_Flags;
|
|
text: string;
|
|
alignment_flags: UI_Text_Alignment_Flags;
|
|
semantic_size: [2] UI_Size;
|
|
padding_left: float;
|
|
padding_right: float;
|
|
padding_top: float;
|
|
padding_bottom: float;
|
|
|
|
// Computed per-frame
|
|
rect : Rect;
|
|
size: Vector2;
|
|
|
|
// Persistent
|
|
animation_t: float;
|
|
hover_animation_t: float;
|
|
|
|
interaction : Interaction_State;
|
|
|
|
_number_text: Static_Array(u8, 8);
|
|
|
|
style : struct {
|
|
texture: Texture_Handle;
|
|
background_color: Color;
|
|
border_color: Color;
|
|
border_width: float;
|
|
text_color: Color;
|
|
}
|
|
|
|
layout : struct {
|
|
alignment: UI_Child_Alignment;
|
|
axis: UI_Child_Axis;
|
|
}
|
|
}
|
|
|
|
UI_Child_Axis :: enum {
|
|
HORIZONTAL;
|
|
VERTICAL;
|
|
}
|
|
|
|
UI_Child_Alignment :: enum {
|
|
LEFT;
|
|
RIGHT;
|
|
CENTERED_VERTICALLY;
|
|
}
|
|
|
|
UI_Text_Alignment_Flags :: enum_flags u8 {
|
|
LEFT_BOTTOM :: 0;
|
|
CENTER_HORIZONTALLY :: 1;
|
|
CENTER_VERTICALLY :: 2;
|
|
RIGHT_BOTTOM :: 4;
|
|
}
|
|
|
|
UI_Size_Kind :: enum {
|
|
PIXELS;
|
|
TEXT_DIM;
|
|
PCT;
|
|
CHILDREN_SUM;
|
|
}
|
|
|
|
UI_Size :: struct {
|
|
size_kind: UI_Size_Kind;
|
|
value: float;
|
|
strictness: float;
|
|
}
|
|
|
|
Textured_Vert_Buffer :: struct {
|
|
vb : Buffer_Handle;
|
|
verts: [6] Textured_Vert;
|
|
}
|
|
|
|
UI_Window :: struct {
|
|
hash: u32;
|
|
boxes: Static_Array(*UI_Box, 4096);
|
|
|
|
title: string;
|
|
position: Vector2i;
|
|
size: Vector2i;
|
|
|
|
actual_position: Vector2i;
|
|
offset: Vector2i;
|
|
|
|
scroll_offset: Vector2i;
|
|
|
|
last_used_frame_index: u64;
|
|
}
|
|
|
|
UI_State :: struct {
|
|
begun: bool;
|
|
frame_index: u64;
|
|
|
|
root: *UI_Box;
|
|
boxes: Table(u32, UI_Box);
|
|
windows: Table(u32, UI_Window);
|
|
current_window: *UI_Window;
|
|
|
|
currently_moving_window: *UI_Window;
|
|
|
|
last_box: *UI_Box;
|
|
|
|
mouse_over_window: bool;
|
|
parent_stack: Stack(*UI_Box);
|
|
|
|
allocator: Allocator;
|
|
pool: Flat_Pool;
|
|
|
|
// Rendering
|
|
shaders : struct {
|
|
ui: Pipeline_State_Handle;
|
|
ui_rect: Pipeline_State_Handle;
|
|
ui_rect_textured: Pipeline_State_Handle;
|
|
text: Pipeline_State_Handle;
|
|
}
|
|
|
|
fonts : struct {
|
|
regular: Font_Handle;
|
|
button: Font_Handle;
|
|
}
|
|
|
|
rect_vb : Buffer_Handle;
|
|
colored_verts: [..] Colored_Vert;
|
|
max_verts: u32;
|
|
|
|
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 :: (hash: u32) -> *UI_Window {
|
|
window := get_ui_window_or_create_new(hash);
|
|
return window;
|
|
}
|
|
|
|
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);
|
|
|
|
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 {
|
|
return ui_box_make(flags, hash=get_hash(loc, identifier));
|
|
}
|
|
|
|
ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box {
|
|
actual_hash := hash;
|
|
parent := get_current_parent();
|
|
|
|
if parent != null {
|
|
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;
|
|
box.last_child = null;
|
|
box.next = null;
|
|
box.prev = null;
|
|
box.num_children = 0;
|
|
box.flags = flags;
|
|
box.parent = null;
|
|
box.window = null;
|
|
box.root_in_window = false;
|
|
|
|
if ui_state.current_window && parent == null {
|
|
array_add(*ui_state.current_window.boxes, box);
|
|
box.root_in_window = true;
|
|
}
|
|
|
|
box.window = ui_state.current_window;
|
|
|
|
// Set the links
|
|
box.parent = parent;
|
|
if box.parent != null {
|
|
if box.parent.first_child == null {
|
|
box.parent.first_child = box;
|
|
}
|
|
|
|
if box.parent.last_child != null {
|
|
box.parent.last_child.next = box;
|
|
box.prev = box.parent.last_child;
|
|
}
|
|
|
|
box.parent.last_child = box;
|
|
}
|
|
|
|
box.text = current_text;
|
|
box.style.texture = current_texture;
|
|
box.style.background_color = background_color;
|
|
box.style.text_color = text_color;
|
|
box.style.border_color = border_color;
|
|
box.style.border_width = border_width;
|
|
box.semantic_size[0] = current_size_x;
|
|
box.semantic_size[1] = current_size_y;
|
|
box.padding_left = padding_left;
|
|
box.padding_right = padding_right;
|
|
box.padding_top = padding_top;
|
|
box.padding_bottom = padding_bottom;
|
|
box.alignment_flags = current_text_alignment;
|
|
|
|
// Reset everything
|
|
set_properties_to_defaults();
|
|
|
|
if ui_state.root == null {
|
|
ui_state.root = box;
|
|
}
|
|
|
|
ui_state.last_box = box;
|
|
|
|
return box;
|
|
}
|
|
|
|
get_hash :: (loc: Source_Code_Location, identifier: s64) -> u32 {
|
|
hash := cast(u32) loc.fully_pathed_filename.data * cast,no_check(u32)(loc.line_number + 1) * cast(u32)(identifier + 1);
|
|
return hash;
|
|
}
|
|
|
|
get_current_parent :: () -> *UI_Box {
|
|
return stack_peek(*ui_state.parent_stack);
|
|
}
|
|
|
|
ui_push_parent :: (box: *UI_Box, alignment: UI_Child_Alignment = .LEFT, axis: UI_Child_Axis = .HORIZONTAL) {
|
|
box.layout.alignment = alignment;
|
|
box.layout.axis = axis;
|
|
|
|
stack_push(*ui_state.parent_stack, box);
|
|
}
|
|
|
|
ui_pop_parent :: () {
|
|
stack_pop(*ui_state.parent_stack);
|
|
}
|
|
|
|
ui_init :: () {
|
|
a: Allocator;
|
|
a.proc = flat_pool_allocator_proc;
|
|
a.data = *ui_state.pool;
|
|
ui_state.allocator = a;
|
|
|
|
ui_state.colored_verts.allocator = ui_state.allocator;
|
|
ui_state.boxes.allocator = ui_state.allocator;
|
|
|
|
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", 32);
|
|
ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/Inconsolata-Regular.ttf", 14);
|
|
ui_state.fonts.button = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 14);
|
|
|
|
// ui_rect
|
|
{
|
|
vs := create_vertex_shader(engine.renderer, "../assets/shaders/ui_rect.hlsl", "VS");
|
|
ps := create_pixel_shader(engine.renderer, "../assets/shaders/ui_rect.hlsl", "PS");
|
|
|
|
layout : [2] Vertex_Data_Info;
|
|
layout[0] = .{0,.POSITION2D, 0};
|
|
layout[1] = .{0,.COLOR_WITH_ALPHA, 0};
|
|
|
|
params : [2] Shader_Parameter;
|
|
params[0].shader = .PIXEL;
|
|
params[0].type = .SAMPLER;
|
|
params[0].name = "samp";
|
|
params[0].slot = 0;
|
|
params[0].mapping = .CLAMP_SAMPLER;
|
|
|
|
params[1].shader = .PIXEL;
|
|
params[1].type = .TEXTURE;
|
|
params[1].name = "tex";
|
|
params[1].slot = 1;
|
|
|
|
ui_state.shaders.ui_rect = create_pipeline_state(engine.renderer, vs, ps, layout, params, blend_type=.TRANSPARENT);
|
|
}
|
|
|
|
// ui_rect
|
|
{
|
|
vs := create_vertex_shader(engine.renderer, "../assets/shaders/ui.hlsl", "VS_Main");
|
|
ps := create_pixel_shader(engine.renderer, "../assets/shaders/ui.hlsl", "PS_Main");
|
|
|
|
layout : [0] Vertex_Data_Info;
|
|
|
|
params : [2] Shader_Parameter;
|
|
params[0].shader = .PIXEL;
|
|
params[0].type = .SAMPLER;
|
|
params[0].name = "samp";
|
|
params[0].slot = 0;
|
|
params[0].mapping = .CLAMP_SAMPLER;
|
|
|
|
params[1].shader = .PIXEL;
|
|
params[1].type = .TEXTURE;
|
|
params[1].name = "tex";
|
|
params[1].slot = 1;
|
|
|
|
ui_state.shaders.ui = create_pipeline_state(engine.renderer, vs, ps, layout, params, blend_type=.TRANSPARENT);
|
|
}
|
|
|
|
{
|
|
vs := create_vertex_shader(engine.renderer, "../assets/shaders/font.hlsl", "VS");
|
|
ps := create_pixel_shader(engine.renderer, "../assets/shaders/font.hlsl", "PS");
|
|
|
|
layout : [3] Vertex_Data_Info;
|
|
layout[0] = .{0,.POSITION2D, 0};
|
|
layout[1] = .{0,.TEXCOORD0, 0};
|
|
layout[2] = .{0,.COLOR_WITH_ALPHA, 0};
|
|
|
|
params : [2] Shader_Parameter;
|
|
params[0].shader = .PIXEL;
|
|
params[0].type = .SAMPLER;
|
|
params[0].name = "ss";
|
|
params[0].slot = 0;
|
|
params[0].mapping = .CLAMP_SAMPLER;
|
|
|
|
params[1].shader = .PIXEL;
|
|
params[1].type = .TEXTURE;
|
|
params[1].name = "tex";
|
|
params[1].slot = 1;
|
|
|
|
ui_state.shaders.text = create_pipeline_state(engine.renderer, vs, ps, layout, params, blend_type=.TRANSPARENT);
|
|
}
|
|
|
|
// Make into one shader....
|
|
// ui_rect textured
|
|
{
|
|
vs := create_vertex_shader(engine.renderer, "../assets/shaders/ui_rect.hlsl", "VS", string.["USE_TEXTURE"]);
|
|
ps := create_pixel_shader(engine.renderer, "../assets/shaders/ui_rect.hlsl", "PS", string.["USE_TEXTURE"]);
|
|
|
|
layout : [3] Vertex_Data_Info;
|
|
layout[0] = .{0,.POSITION2D, 0};
|
|
layout[1] = .{0,.TEXCOORD0, 0};
|
|
layout[2] = .{0,.COLOR, 0};
|
|
|
|
params : [2] Shader_Parameter;
|
|
params[0].shader = .PIXEL;
|
|
params[0].type = .SAMPLER;
|
|
params[0].name = "samp";
|
|
params[0].slot = 0;
|
|
params[0].mapping = .CLAMP_SAMPLER;
|
|
|
|
params[1].shader = .PIXEL;
|
|
params[1].type = .TEXTURE;
|
|
params[1].name = "tex";
|
|
params[1].slot = 1;
|
|
|
|
ui_state.shaders.ui_rect_textured = create_pipeline_state(engine.renderer, vs, ps, layout, params, blend_type=.TRANSPARENT);
|
|
}
|
|
|
|
ui_state.max_verts = 4096;
|
|
{
|
|
dynamic_buffer_size := size_of(Colored_Vert) * ui_state.max_verts;
|
|
ui_state.rect_vb = create_vertex_buffer(engine.renderer, null, dynamic_buffer_size, stride=size_of(Colored_Vert), mappable=true);
|
|
|
|
for 0..MAX_VERT_BUFFERS-1 {
|
|
{
|
|
buffer : Textured_Vert_Buffer;
|
|
|
|
dynamic_buffer_size := size_of(Textured_Vert) * ui_state.max_verts;
|
|
buffer.vb = create_vertex_buffer(engine.renderer, null, dynamic_buffer_size, stride=size_of(Textured_Vert), mappable=true);
|
|
|
|
ui_state.texture_vert_buffers[it] = buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
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
|
|
ui_figure_out_sizes :: () {
|
|
if ui_state.currently_moving_window {
|
|
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 {
|
|
if window.last_used_frame_index != ui_state.frame_index continue;
|
|
window.actual_position = window.position + window.offset;
|
|
}
|
|
|
|
// SET ALL PIXEL AND TEXT SIZES
|
|
for *box : ui_state.boxes {
|
|
if box.semantic_size[0].size_kind == {
|
|
case .PIXELS; {
|
|
box.size.x = box.semantic_size[0].value + box.padding_left + box.padding_right;
|
|
}
|
|
case .TEXT_DIM; {
|
|
text_size := get_text_size(engine.renderer, box.text, ui_state.fonts.button);
|
|
box.size.x = text_size.x + box.padding_left + box.padding_right;
|
|
}
|
|
}
|
|
|
|
if box.semantic_size[1].size_kind == {
|
|
case .PIXELS; {
|
|
box.size.y = box.semantic_size[1].value + box.padding_top + box.padding_bottom;
|
|
}
|
|
case .TEXT_DIM; {
|
|
text_size := get_text_size(engine.renderer, box.text, ui_state.fonts.button);
|
|
box.size.y = text_size.y + box.padding_top + box.padding_bottom;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Upwards-dependent
|
|
for *box : ui_state.boxes {
|
|
if box.semantic_size[0].size_kind == .PCT {
|
|
if box.parent != null {
|
|
box.size.x = box.parent.size.x * box.semantic_size[0].value - (box.parent.padding_left + box.parent.padding_right);
|
|
//print("Box PCT % and final size %\n", box.semantic_size[0].value, box.size.x);
|
|
} else if box.root_in_window {
|
|
box.size.x = cast(float)box.window.size.x * box.semantic_size[0].value - (cast(float)WINDOW_BORDER_WIDTH);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
if box.semantic_size[1].size_kind == .PCT {
|
|
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_in_window {
|
|
box.size.y = cast(float)box.window.size.y * box.semantic_size[0].value - (cast(float)WINDOW_BORDER_WIDTH);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Downwards-dependent
|
|
for *box : ui_state.boxes {
|
|
if box.semantic_size[0].size_kind == .CHILDREN_SUM {
|
|
//assert(box.num_children != 0);
|
|
|
|
size : float = 0.0;
|
|
child := box.first_child;
|
|
|
|
while child != null {
|
|
defer child = child.next;
|
|
|
|
if box.layout.axis == .HORIZONTAL {
|
|
size += child.size.x;
|
|
} else {
|
|
size = max(child.size.x, size);
|
|
}
|
|
}
|
|
|
|
box.size.x = size + box.padding_left + box.padding_right;
|
|
}
|
|
if box.semantic_size[1].size_kind == .CHILDREN_SUM {
|
|
//assert(box.num_children != 0);
|
|
|
|
size : float = 0.0;
|
|
child := box.first_child;
|
|
|
|
while child != null {
|
|
defer child = child.next;
|
|
|
|
if box.layout.axis == .VERTICAL {
|
|
size += child.size.y;
|
|
} else {
|
|
size = max(child.size.y, size);
|
|
}
|
|
}
|
|
|
|
box.size.y = size + box.padding_top + box.padding_bottom;
|
|
}
|
|
}
|
|
|
|
// Align children in parent
|
|
//for *box: ui_state.boxes {
|
|
// if box.parent != null {
|
|
// if box.parent.rect.h > box.rect.h && box.parent.layout.alignment == .CENTERED {
|
|
// box.rect.y = box.rect.y
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
// Find final positions
|
|
for *box : ui_state.boxes {
|
|
if box.parent == null {
|
|
if box.root_in_window {
|
|
window := box.window;
|
|
box.rect.x = xx (window.actual_position.x + window.scroll_offset.x) + cast(float)WINDOW_BORDER_WIDTH;
|
|
box.rect.y = xx (window.actual_position.y + window.scroll_offset.y) + cast(float)WINDOW_BORDER_WIDTH + WINDOW_TITLE_BAR_HEIGHT;
|
|
box.size.x = xx (window.size.x - WINDOW_BORDER_WIDTH * 2);
|
|
box.size.y = xx (window.size.y - WINDOW_BORDER_WIDTH * 2 - 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);
|
|
}
|
|
}
|
|
|
|
for *box : ui_state.boxes {
|
|
if box.style.texture != 0 {
|
|
w, h := get_texture_size(engine.renderer, box.style.texture);
|
|
aspect := cast(float)w/cast(float)h;
|
|
current_aspect := cast(float)box.rect.w/cast(float)box.rect.h;
|
|
rect := box.rect;
|
|
if rect.w < rect.h {
|
|
box.rect.w = box.rect.h * aspect;
|
|
scale := rect.w / box.rect.w;
|
|
// @Note(Daniel): My brain can't think straight so this extra scaling might be circumvented by doing the calculations differently....
|
|
if scale < 1 {
|
|
box.rect.w *= scale;
|
|
box.rect.h *= scale;
|
|
}
|
|
} else {
|
|
box.rect.h = box.rect.w / aspect;
|
|
scale := rect.h / box.rect.h;
|
|
if scale < 1 {
|
|
box.rect.w *= scale;
|
|
box.rect.h *= scale;
|
|
}
|
|
}
|
|
}
|
|
//if box.semantic_size[0].size_kind == .KEEP_ASPECT {
|
|
// assert(box.style.texture != 0);
|
|
// w, h := get_texture_size(engine.renderer, box.style.texture);
|
|
// aspect := cast(float)w/cast(float)h;
|
|
// box.size.x = box.size.y * aspect;
|
|
//}
|
|
//
|
|
//if box.semantic_size[1].size_kind == .KEEP_ASPECT {
|
|
// assert(box.style.texture != 0);
|
|
// w, h := get_texture_size(engine.renderer, box.style.texture);
|
|
// aspect := cast(float)h/cast(float)w;
|
|
// box.size.y = box.size.x * aspect;
|
|
//}
|
|
}
|
|
|
|
//print("\n");
|
|
for *box : ui_state.boxes {
|
|
//print("RECT % % % % TEXT %\n", box.rect.x, box.rect.y, box.rect.w, box.rect.h, box.text);
|
|
}
|
|
//print("\n");
|
|
|
|
}
|
|
|
|
ui_set_rect_recursively :: (parent: *UI_Box) {
|
|
starting_offset_x := parent.rect.x + parent.padding_left;
|
|
starting_offset_y := parent.rect.y + parent.padding_bottom;
|
|
|
|
child := parent.first_child;
|
|
while child != null {
|
|
defer child = child.next;
|
|
|
|
if parent.layout.alignment == .CENTERED_VERTICALLY {
|
|
child.rect.x = starting_offset_x;
|
|
child.rect.y = parent.rect.y + parent.rect.h / 2 - child.rect.h / 2;
|
|
} else {
|
|
child.rect.x = starting_offset_x;
|
|
child.rect.y = starting_offset_y;
|
|
}
|
|
|
|
child.rect.w = child.size.x;
|
|
child.rect.h = child.size.y;
|
|
|
|
if parent.layout.axis == {
|
|
case .HORIZONTAL; {
|
|
starting_offset_x += child.rect.w;
|
|
}
|
|
case .VERTICAL; {
|
|
starting_offset_y += child.rect.h;
|
|
}
|
|
}
|
|
|
|
if child.num_children > 0 {
|
|
ui_set_rect_recursively(child);
|
|
}
|
|
}
|
|
}
|
|
// # END # LAYOUT ALGORITHM
|
|
|
|
|
|
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 :: () {
|
|
hashes_to_remove : [..] u32;
|
|
hashes_to_remove.allocator = temp;
|
|
for * ui_state.boxes {
|
|
if it.last_used_frame_index != ui_state.frame_index {
|
|
array_add(*hashes_to_remove, it.hash);
|
|
}
|
|
}
|
|
|
|
for hashes_to_remove {
|
|
table_remove(*ui_state.boxes, it);
|
|
}
|
|
|
|
// Do all the layouting work
|
|
ui_figure_out_sizes();
|
|
// Check for any input events
|
|
ui_update_input();
|
|
|
|
assert(ui_state.begun);
|
|
ui_state.begun = false;
|
|
ui_state.parent_stack.values.count = 0;
|
|
}
|
|
|
|
// #### BOX PROPERTY FUNCTIONS
|
|
ui_set_next_background_color :: (color: Color) {
|
|
background_color = color;
|
|
}
|
|
|
|
ui_set_next_text_color :: (color: Color) {
|
|
text_color = color;
|
|
}
|
|
|
|
ui_set_next_border_color :: (color: Color) {
|
|
border_color = color;
|
|
}
|
|
|
|
ui_set_next_border_width :: (width: float) {
|
|
border_width = width;
|
|
}
|
|
|
|
ui_set_next_texture :: (handle: Texture_Handle) {
|
|
current_texture = handle;
|
|
}
|
|
|
|
ui_set_next_padding :: (new_padding: float) {
|
|
padding_left = new_padding;
|
|
padding_right = new_padding;
|
|
padding_top = new_padding;
|
|
padding_bottom = new_padding;
|
|
}
|
|
|
|
ui_set_next_padding_left :: (new_padding: float) {
|
|
padding_left = new_padding;
|
|
}
|
|
|
|
ui_set_next_padding_right :: (new_padding: float) {
|
|
padding_right = new_padding;
|
|
}
|
|
|
|
ui_set_next_padding_top :: (new_padding: float) {
|
|
padding_top = new_padding;
|
|
}
|
|
|
|
ui_set_next_padding_bottom :: (new_padding: float) {
|
|
padding_bottom = new_padding;
|
|
}
|
|
|
|
ui_set_next_text :: (text: string) {
|
|
current_text = copy_temporary_string(text);
|
|
}
|
|
|
|
ui_set_next_text_alignment :: (flags: UI_Text_Alignment_Flags) {
|
|
current_text_alignment = flags;
|
|
}
|
|
|
|
ui_set_next_size_x :: (size: UI_Size_Kind, value: float = 0.0, strictness: float = 0.0) {
|
|
current_size_x = .{size, value, strictness};
|
|
}
|
|
|
|
ui_set_next_size_y :: (size: UI_Size_Kind, value: float = 0.0, strictness: float = 0.0) {
|
|
current_size_y = .{size, value, strictness};
|
|
}
|
|
// # 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 :: () {
|
|
// Draw each window separately
|
|
for window: ui_state.windows {
|
|
if window.last_used_frame_index != ui_state.frame_index continue;
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
{
|
|
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 = cast(float)(xx window.actual_position.x + cast(u32)WINDOW_BORDER_WIDTH + DEFAULT_PADDING);
|
|
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 / 2 + (WINDOW_TITLE_BAR_HEIGHT - WINDOW_BORDER_WIDTH) / 2;
|
|
y = round(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- cast(u32)(WINDOW_BORDER_WIDTH*2),
|
|
xx (window.size.y - WINDOW_TITLE_BAR_HEIGHT - cast(u32)(WINDOW_BORDER_WIDTH*2)),
|
|
xx window.actual_position.x + cast(u32)WINDOW_BORDER_WIDTH,
|
|
xx (window.actual_position.y + WINDOW_TITLE_BAR_HEIGHT + WINDOW_BORDER_WIDTH));
|
|
|
|
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, 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_text_recursively(it);
|
|
// }
|
|
//}
|
|
}
|
|
|
|
ui_mouse_over_window :: () -> bool {
|
|
return ui_state.mouse_over_window;
|
|
}
|
|
|
|
#scope_file
|
|
set_properties_to_defaults :: () {
|
|
background_color = .{1,1,1,1};
|
|
text_color = .{0,0,0,1};
|
|
border_color = .{130.0/255.0,95.0/255.0,38.0/255.0,1};
|
|
border_width = 2.0;
|
|
current_text = "";
|
|
current_texture = 0;
|
|
current_size_x = .{};
|
|
current_size_y = .{};
|
|
padding_left = 0;
|
|
padding_right = 0;
|
|
padding_top = 0;
|
|
padding_bottom = 0;
|
|
current_text_alignment = .LEFT_BOTTOM;
|
|
}
|
|
|
|
get_ui_box_or_create_new :: (hash: u32) -> *UI_Box, bool {
|
|
ptr := table_find_pointer(*ui_state.boxes, hash);
|
|
|
|
if ptr == null {
|
|
assert(ui_state.boxes.count < MAX_BOXES);
|
|
table_add(*ui_state.boxes, hash, .{});
|
|
ptr = table_find_pointer(*ui_state.boxes, hash);
|
|
}
|
|
|
|
ptr.hash = hash;
|
|
|
|
|
|
|
|
return ptr, false;
|
|
}
|
|
|
|
get_ui_window_or_create_new :: (hash: u32) -> *UI_Window, bool {
|
|
ptr := table_find_pointer(*ui_state.windows, hash);
|
|
|
|
if ptr == null {
|
|
assert(ui_state.windows.count < MAX_WINDOWS);
|
|
table_add(*ui_state.windows, hash, .{});
|
|
ptr = table_find_pointer(*ui_state.windows, hash);
|
|
}
|
|
|
|
ptr.hash = hash;
|
|
|
|
return ptr, false;
|
|
}
|
|
// #### RENDERING
|
|
make_vert :: (x: float, y: float, color: Color) -> Colored_Vert {
|
|
vert : Colored_Vert;
|
|
vert.position.x = x;
|
|
vert.position.y = y;
|
|
vert.color = color;
|
|
return vert;
|
|
}
|
|
|
|
make_vert_textured :: (x: float, y: float, uv_x: float, uv_y: float) -> Textured_Vert {
|
|
vert : Textured_Vert;
|
|
vert.position.x = x;
|
|
vert.position.y = y;
|
|
vert.texcoord.x = uv_x;
|
|
vert.texcoord.y = uv_y;
|
|
vert.color = .{1,1,1,1};
|
|
return vert;
|
|
}
|
|
|
|
should_draw_hover_animation :: (box: *UI_Box) -> bool {
|
|
return box.flags & .ANIMATE_ON_HOVER && box.hover_animation_t > 0.0;
|
|
}
|
|
|
|
instance_data_from_rect :: (x: float, y: float, w: float, h: float, corner_radius: float, edge_softness: float, border_thickness: float, color: .. Color) -> Rect_Instance_Data {
|
|
data : Rect_Instance_Data = ---;
|
|
data.p0.x = x;
|
|
data.p0.y = y;
|
|
data.p1.x = x + w;
|
|
data.p1.y = y - h;
|
|
data.corner_radius = corner_radius;
|
|
data.edge_softness = edge_softness;
|
|
data.border_thickness = border_thickness;
|
|
|
|
if color.count == 4 {
|
|
data.colors[0] = color[0];
|
|
data.colors[1] = color[1];
|
|
data.colors[2] = color[2];
|
|
data.colors[3] = color[3];
|
|
} else {
|
|
data.colors[0] = color[0];
|
|
data.colors[1] = color[0];
|
|
data.colors[2] = color[0];
|
|
data.colors[3] = color[0];
|
|
}
|
|
return data;
|
|
}
|
|
|
|
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;
|
|
|
|
if box.flags & .DRAW_BACKGROUND && box.style.texture == 0 {
|
|
x : float = box.rect.x * inv_w * 2.0 - 1.0;
|
|
y : float = (cast(float)engine.renderer.render_target_height - box.rect.y) * inv_h * 2.0 - 1.0;
|
|
w : float = box.rect.w * inv_w * 2.0;
|
|
h : float = box.rect.h * inv_h * 2.0;
|
|
|
|
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(*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));
|
|
//array_add(*ui_state.colored_verts, make_vert(x, y, color));
|
|
|
|
//array_add(*ui_state.colored_verts, make_vert(x + w, y - h, color));
|
|
//array_add(*ui_state.colored_verts, make_vert(x, y, color));
|
|
//array_add(*ui_state.colored_verts, make_vert(x + w, y, color));
|
|
}
|
|
|
|
if box.flags & .DRAW_BORDER {
|
|
x : float = box.rect.x * inv_w * 2.0 - 1.0;
|
|
y : float = (cast(float)engine.renderer.render_target_height - box.rect.y) * inv_h * 2.0 - 1.0;
|
|
w : float = box.rect.w * inv_w * 2.0;
|
|
h : float = box.rect.h * inv_h * 2.0;
|
|
|
|
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(*buffer_data.rects, instance_data);
|
|
}
|
|
|
|
if should_draw_hover_animation(box) {
|
|
x : float = box.rect.x * inv_w * 2.0 - 1.0;
|
|
y : float = (cast(float)engine.renderer.render_target_height - box.rect.y) * inv_h * 2.0 - 1.0;
|
|
w : float = box.rect.w * inv_w * 2.0;
|
|
h : float = box.rect.h * inv_h * 2.0;
|
|
|
|
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(*buffer_data.rects, instance_data);
|
|
}
|
|
|
|
child := box.first_child;
|
|
while child != null {
|
|
defer child = child.next;
|
|
ui_render_background_recursively(child, buffer_data);
|
|
}
|
|
}
|
|
|
|
get_next_available_texture_vert_buffer :: () -> *Textured_Vert_Buffer {
|
|
index := ui_state.next_available_texture_buffer_index;
|
|
assert(index < MAX_VERT_BUFFERS);
|
|
|
|
ui_state.next_available_texture_buffer_index += 1;
|
|
|
|
buffer := *ui_state.texture_vert_buffers[index];
|
|
return buffer;
|
|
}
|
|
|
|
ui_render_texture_background_recursively :: (box: *UI_Box) {
|
|
inv_w := 1.0 / cast(float)engine.renderer.render_target_width;
|
|
inv_h := 1.0 / cast(float)engine.renderer.render_target_height;
|
|
|
|
if box.flags & .DRAW_BACKGROUND && box.style.texture != 0 {
|
|
buffer := get_next_available_texture_vert_buffer();
|
|
rect := box.rect;
|
|
inv_w := 1.0 / cast(float)engine.renderer.render_target_width;
|
|
inv_h := 1.0 / cast(float)engine.renderer.render_target_height;
|
|
|
|
x : float = rect.x * inv_w * 2.0 - 1.0;
|
|
y : float = (cast(float)engine.renderer.render_target_height - rect.y) * inv_h * 2.0 - 1.0;
|
|
w : float = rect.w * inv_w * 2.0;
|
|
h : float = rect.h * inv_h * 2.0;
|
|
|
|
buffer.verts[0] = make_vert_textured(x + w, y - h, 1.0, 1.0);
|
|
buffer.verts[1] = make_vert_textured(x, y - h, 0, 1.0);
|
|
buffer.verts[2] = make_vert_textured(x, y, 0.0, 0.0);
|
|
|
|
buffer.verts[3] = make_vert_textured(x + w, y - h, 1.0, 1.0);
|
|
buffer.verts[4] = make_vert_textured(x, y, 0.0, 0.0);
|
|
buffer.verts[5] = make_vert_textured(x + w, y, 1.0, 0.0);
|
|
|
|
upload_data_to_buffer(engine.renderer, buffer.vb, buffer.verts.data, 6 * size_of(Textured_Vert));
|
|
|
|
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_textured);
|
|
|
|
push_cmd_set_sampler(engine.renderer, 0, ui_state.sampler);
|
|
push_cmd_set_texture(engine.renderer, 0, box.style.texture);
|
|
|
|
push_cmd_set_vertex_buffer(engine.renderer, buffer.vb);
|
|
push_cmd_draw(engine.renderer, 6);
|
|
}
|
|
|
|
child := box.first_child;
|
|
while child != null {
|
|
defer child = child.next;
|
|
ui_render_texture_background_recursively(child);
|
|
}
|
|
}
|
|
|
|
ui_render_text_recursively :: (box: *UI_Box) {
|
|
inv_w := 1.0 / cast(float)engine.renderer.render_target_width;
|
|
inv_h := 1.0 / cast(float)engine.renderer.render_target_height;
|
|
|
|
if box.flags & .DRAW_TEXT {
|
|
font_handle := ui_state.fonts.button;
|
|
if box.text.count > 0 {
|
|
text_size := get_text_size(engine.renderer, box.text, font_handle);
|
|
|
|
x := box.rect.x + box.padding_left;
|
|
|
|
y := cast(float)engine.renderer.render_target_height - box.rect.y - box.rect.h + box.padding_bottom;
|
|
if box.alignment_flags & .CENTER_HORIZONTALLY {
|
|
x += box.rect.w * 0.5 - text_size.x * 0.5;
|
|
}
|
|
|
|
if box.alignment_flags & .CENTER_VERTICALLY {
|
|
y += box.rect.h * 0.5 - text_size.y * 0.5;
|
|
}
|
|
|
|
if box.alignment_flags & .RIGHT_BOTTOM {
|
|
x += box.rect.w - text_size.x;
|
|
}
|
|
|
|
render_data := bake_text(engine.renderer, x, y, box.text, font_handle, box.style.text_color);
|
|
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);
|
|
}
|
|
}
|
|
|
|
child := box.first_child;
|
|
while child != null {
|
|
defer child = child.next;
|
|
ui_render_text_recursively(child);
|
|
}
|
|
}
|
|
|
|
ADJUSTMENT :: 0.1;
|
|
|
|
is_mouse_inside :: (x: float, y: float, rect: Rect) -> bool {
|
|
return x - ADJUSTMENT >= rect.x && y - ADJUSTMENT >= rect.y && x + ADJUSTMENT <= rect.x + rect.w && y + ADJUSTMENT <= rect.y + rect.h;
|
|
}
|
|
|
|
ui_update_input :: () {
|
|
//if engine.mode == .PLAYING return; // @Robustness
|
|
|
|
mouse_x := engine.input.mouse.x;
|
|
mouse_y := engine.input.mouse.y;
|
|
|
|
ui_state.mouse_over_window = false;
|
|
for *window: ui_state.windows {
|
|
if window.last_used_frame_index != ui_state.frame_index continue;
|
|
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.size.y;
|
|
|
|
if is_mouse_inside(mouse_x, mouse_y, rect) {
|
|
ui_state.mouse_over_window = true;
|
|
}
|
|
}
|
|
|
|
// Scrolling
|
|
for *window: ui_state.windows {
|
|
if window.last_used_frame_index != ui_state.frame_index continue;
|
|
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) {
|
|
ui_state.mouse_over_window = true;
|
|
window.scroll_offset.y += cast(s32) engine.input.mouse.wheel * SCROLL_SPEED;
|
|
}
|
|
}
|
|
|
|
if ui_state.currently_moving_window == null {
|
|
for *window: ui_state.windows {
|
|
if window.last_used_frame_index != ui_state.frame_index continue;
|
|
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 {
|
|
|
|
}
|
|
|
|
for *box: ui_state.boxes {
|
|
if engine.mode == .EDITING || box.flags & .PLAY_MODE_FOCUSABLE {
|
|
if box.flags & .CLICKABLE {
|
|
local_mouse: Vector2;
|
|
local_mouse.x = clamp(mouse_x - box.rect.x, 0.0, box.rect.w);
|
|
local_mouse.y = clamp(mouse_y - box.rect.y, 0.0, box.rect.h);
|
|
box.interaction.local_mouse_coordinates = local_mouse;
|
|
box.interaction.normalized_local_mouse_coordinates.x = local_mouse.x / box.rect.w;
|
|
box.interaction.normalized_local_mouse_coordinates.y = local_mouse.y / box.rect.h;
|
|
|
|
if box.interaction.clicked {
|
|
box.interaction.clicked = false;
|
|
}
|
|
|
|
if box.interaction.left_mouse_pressed {
|
|
box.interaction.left_mouse_down = false;
|
|
if !key_pressed(.MOUSE_LEFT) {
|
|
box.interaction.clicked = true;
|
|
box.interaction.left_mouse_pressed = false;
|
|
}
|
|
} else {
|
|
window := box.window;
|
|
rect := box.rect;
|
|
rect.x = clamp(rect.x, cast(float)window.actual_position.x, cast(float)window.actual_position.x + window.size.x);
|
|
rect.w += rect.x - box.rect.x;
|
|
rect.y = clamp(rect.y, cast(float)window.actual_position.y, cast(float)window.actual_position.y + window.size.y);
|
|
rect.h += rect.y - box.rect.y;
|
|
|
|
if is_mouse_inside(mouse_x, mouse_y, rect) && key_pressed(.MOUSE_LEFT) {
|
|
box.interaction.left_mouse_pressed = true;
|
|
box.interaction.left_mouse_down = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if box.flags & .ANIMATE_ON_HOVER {
|
|
if is_mouse_inside(mouse_x, mouse_y, box.rect) {
|
|
box.hover_animation_t += dt * 10;
|
|
} else {
|
|
box.hover_animation_t -= dt * 10;
|
|
}
|
|
box.hover_animation_t = clamp(box.hover_animation_t, 0.0, 1.0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// style
|
|
background_color : Color = .{1,1,1,1};
|
|
text_color : Color = .{0,0,0,1};
|
|
border_color : Color;
|
|
border_width : float;
|
|
current_text : string = "";
|
|
current_texture : Texture_Handle;
|
|
current_size_x : UI_Size;
|
|
current_size_y : UI_Size;
|
|
current_text_alignment : UI_Text_Alignment_Flags;
|
|
padding_left : float;
|
|
padding_right : float;
|
|
padding_top : float;
|
|
padding_bottom : float;
|
|
|
|
#load "../core/stack.jai";
|
|
|