From 6cf19a6f9bbff19dc06cfc85b1c17425ffa54687 Mon Sep 17 00:00:00 2001 From: Daniel Bross Date: Mon, 30 Dec 2024 01:42:27 +0100 Subject: [PATCH] UI fixes and new layout features --- editor/editor_ui.jai | 259 ++++++++++--------------------------------- metaprogram.jai | 10 +- module.jai | 2 +- ui/ui.jai | 77 +++++++++---- ui/widgets.jai | 24 +++- 5 files changed, 144 insertions(+), 228 deletions(-) diff --git a/editor/editor_ui.jai b/editor/editor_ui.jai index 239c0c9..88a6b02 100644 --- a/editor/editor_ui.jai +++ b/editor/editor_ui.jai @@ -33,15 +33,17 @@ pick_scene_view_at :: (camera: Camera, coordinates: Vector2) { editor_ui :: () { // Scene picking - if key_down(.MOUSE_LEFT) { - if engine.current_scene != null { - coords := engine.input.normalized_viewport_mouse_position; - pick_scene_view_at(engine.editor.camera, .{coords.x, 1.0-coords.y}); + if !ui_mouse_over_window() { + if key_down(.MOUSE_LEFT) { + if engine.current_scene != null { + coords := engine.input.normalized_viewport_mouse_position; + pick_scene_view_at(engine.editor.camera, .{coords.x, 1.0-coords.y}); + } } } { - ui_window_begin("Create", 100, 5, 200, 200); + ui_window_begin("Create", cast(s32)engine.renderer.render_target_width - 200, 5, 200, 200); new_entity := editor_ui_entity_creation(); if new_entity != null { set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); @@ -83,208 +85,63 @@ editor_ui :: () { } ui_window_end(); - ui_full_size_background(); - ui_push_parent(ui_state.last_box, alignment=.LEFT, axis=.VERTICAL); + ui_window_begin("Settings", 0, cast(s32)engine.renderer.render_target_height-80, 300, 80); { - ui_set_next_padding(1); - ui_toolbar(); - ui_push_parent(ui_state.last_box, alignment=.LEFT, axis=.HORIZONTAL); - { - if ui_toolbar_button("File") { + if engine.editor.transform_gizmo.snap_to_grid { + if ui_toolbar_button("Snap to grid enabled", .{0.0, 0.4, 0.0, 1.0}) { + engine.editor.transform_gizmo.snap_to_grid = false; } - - //ui_space(15, 10); - - if ui_toolbar_button("Edit") { - } - - //ui_space(15, 10); - - //if ui_toolbar_button("View") { - //} - - ui_space(20, 0); - - if engine.mode == { - case .PLAYING; { - ui_set_next_background_color(.{0,0.6,0,1}); - if ui_button_with_texture(engine.editor.icons.stop) { - switch_engine_mode(.EDITING); - } - ui_label(tprint("Playing '%'", engine.current_scene.name), .{0,0.7,0,1}); - } - case .EDITING; { - ui_set_next_background_color(.{0.6,0,0,1}); - if ui_button_with_texture(engine.editor.icons.play) { - switch_engine_mode(.PLAYING); - } - ui_label(tprint("Editing '%'", engine.current_scene.name), .{1,1,1,1}); - } + } else { + if ui_toolbar_button("Snap to grid disabled", .{0.4, 0.0, 0.0, 1.0}) { + engine.editor.transform_gizmo.snap_to_grid = true; } } - ui_pop_parent(); - ui_set_next_size_x(.PCT, 1.0); - ui_set_next_size_y(.PCT, 0.965); - ui_set_next_background_color(.{0,0,0,1}); - background := ui_box(.NONE); - - ui_push_parent(ui_state.last_box, alignment=.LEFT, axis=.HORIZONTAL); - { - ui_set_next_size_x(.PCT, 0.1); - ui_set_next_size_y(.PCT, 1.0); - ui_set_next_padding(1); - first_panel := ui_box(.NONE); - ui_push_parent(first_panel, alignment=.LEFT, axis=.VERTICAL); - { - ui_set_next_size_x(.PCT, 1.0); - ui_set_next_size_y(.PCT, 1.0); - ui_set_next_background_color(.{0.04, 0.04, 0.04, 1.0}); - background := ui_box(.DRAW_BACKGROUND|.DRAW_BORDER); - - ui_push_parent(background, alignment=.LEFT, axis=.VERTICAL); - { - ui_set_next_size_x(.PCT, 1.0); - ui_tab_title_bar("ENTITIES"); - - //if engine.current_scene != null { - // for engine.current_scene.entities { - // if it.flags & .DELETED continue; - - // ui_set_next_padding(20); - // clicked := false; - // selected := array_find(engine.editor.selected_entities, it); - // if it.name.count == 0 { - // clicked = ui_clickable_label(tprint("%", it.type), selected, it.id); - // } else { - // clicked = ui_clickable_label(it.name, selected, it.id); - // } - - // if clicked { - // if !key_pressed(.CTRL) { - // engine.editor.selected_entities.count = 0; - // array_add(*engine.editor.selected_entities, it); - // } else { - // if selected { - // array_unordered_remove_by_value(*engine.editor.selected_entities, it); - // } else { - // array_add(*engine.editor.selected_entities, it); - // } - // } - // } - // //ui_space(0, 5); - // } - //} - } - ui_pop_parent(); + if engine.editor.transform_gizmo.space == .LOCAL { + if ui_toolbar_button("LOCAL", .{0.0, 0.3, 0.0, 1.0}) { + engine.editor.transform_gizmo.space = .WORLD; } - ui_pop_parent(); - - // Scene - ui_set_next_size_x(.PCT, 0.7); - ui_set_next_size_y(.PCT, 1.0); - ui_set_next_padding(1); - viewport_layer := ui_box(.NONE); - ui_push_parent(viewport_layer, alignment=.LEFT, axis=.VERTICAL); - { - - ui_set_next_size_x(.PCT, 1.0); - ui_tab_title_bar("SCENE"); - - ui_set_next_size_x(.PCT, 1.0); - ui_set_next_size_y(.PCT, 0.1); - ui_toolbar(); - ui_push_parent(ui_state.last_box, alignment=.LEFT, axis=.HORIZONTAL); - { - if engine.editor.transform_gizmo.snap_to_grid { - if ui_toolbar_button("Snap to grid enabled", .{0.0, 0.4, 0.0, 1.0}) { - engine.editor.transform_gizmo.snap_to_grid = false; - } - } else { - if ui_toolbar_button("Snap to grid disabled", .{0.4, 0.0, 0.0, 1.0}) { - engine.editor.transform_gizmo.snap_to_grid = true; - } - } - - if engine.editor.transform_gizmo.space == .LOCAL { - if ui_toolbar_button("LOCAL", .{0.0, 0.3, 0.0, 1.0}) { - engine.editor.transform_gizmo.space = .WORLD; - } - } else { - if ui_toolbar_button("WORLD", .{0.0, 0.0, 0.3, 1.0}) { - engine.editor.transform_gizmo.space = .LOCAL; - } - } - //ui_space(15, 10) - - ui_space(20, 0); - } - ui_pop_parent(); - - //ui_set_next_size_x(.PCT, 1.0); - //ui_set_next_size_y(.PCT, 0.65); - - //state := ui_interactable_texture(get_texture_from_pass("UI Blend Pass")); - //engine.input.viewport_mouse_position = state.local_mouse_coordinates; - //engine.input.normalized_viewport_mouse_position = state.normalized_local_mouse_coordinates; - - - //engine.editor.mouse_viewport_state = state; - - //ui_set_next_size_x(.PCT, 1.0); - //ui_set_next_size_y(.PCT, 0.25); - //ui_tab_title_bar("Create entities"); - //{ - // new_entity := editor_ui_entity_creation(); - // if new_entity != null { - // set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); - // engine.editor.selected_entities.count = 0; - // array_add(*engine.editor.selected_entities, new_entity); - // } - //} + } else { + if ui_toolbar_button("WORLD", .{0.0, 0.0, 0.3, 1.0}) { + engine.editor.transform_gizmo.space = .LOCAL; } - ui_pop_parent(); - - // Properties - ui_set_next_size_x(.PCT, 0.2); - ui_set_next_size_y(.PCT, 1.0); - ui_set_next_background_color(.{0.04, 0.04, 0.04, 1.0}); - ui_set_next_padding(1); - inspector := ui_box(.DRAW_BACKGROUND | .DRAW_BORDER); - ui_push_parent(inspector, alignment=.LEFT, axis=.VERTICAL); - { - ui_set_next_size_x(.PCT, 1.0); - ui_tab_title_bar("PROPERTIES"); - - if engine.editor.selected_entities.count == 1 { - entity := engine.editor.selected_entities[0]; - //ui_slider(*slider_value, 0.0, 1.0); - - ui_textfield("Name", *entity.name); - - ui_label(tprint("Id: %", entity.id)); - - updated := ui_vector_field("Position", *entity.transform.position); - euler_rotation := quaternion_to_euler_v3(entity.transform.orientation); - euler_rotation *= RADIANS_TO_DEGREES; - updated |= ui_vector_field("Rotation", *euler_rotation); - euler_rotation *= DEGREES_TO_RADIANS; - entity.transform.orientation = euler_to_quaternion(euler_rotation); - updated |= ui_vector_field("Scale", *entity.transform.scale); - - if updated { - update_matrix(*entity.transform); - } - - entity_ui(entity); - } - - } - ui_pop_parent(); } - ui_pop_parent(); + //ui_space(15, 10) + + ui_space(20, 0); + } + ui_window_end(); + + // Properties + { + if engine.editor.selected_entities.count == 1 { + ui_window_begin("Properties", 200, 200, 400, 500); + + if engine.editor.selected_entities.count == 1 { + entity := engine.editor.selected_entities[0]; + //ui_slider(*slider_value, 0.0, 1.0); + + ui_textfield("Name", *entity.name); + + ui_label(tprint("Id: %", entity.id)); + + updated := ui_vector_field("Position", *entity.transform.position); + euler_rotation := quaternion_to_euler_v3(entity.transform.orientation); + euler_rotation *= RADIANS_TO_DEGREES; + updated |= ui_vector_field("Rotation", *euler_rotation); + euler_rotation *= DEGREES_TO_RADIANS; + entity.transform.orientation = euler_to_quaternion(euler_rotation); + updated |= ui_vector_field("Scale", *entity.transform.scale); + + if updated { + update_matrix(*entity.transform); + } + + entity_ui(entity); + } + ui_window_end(); + } } - ui_pop_parent(); } text_fun : string; @@ -310,8 +167,10 @@ base_editor_update :: () { //coordinates := Vector2.{engine.editor.mouse_viewport_state.normalized_local_mouse_coordinates.x, 1.0 - engine.editor.mouse_viewport_state.normalized_local_mouse_coordinates.y}; ray := normalized_screen_to_ray_v2(engine.editor.camera, coordinates); - if update_transform_gizmo(ray, coordinates) { - engine.editor.should_check_entities = false; + if !ui_mouse_over_window() { + if update_transform_gizmo(ray, coordinates) { + engine.editor.should_check_entities = false; + } } } diff --git a/metaprogram.jai b/metaprogram.jai index 0d17191..62d1ad2 100644 --- a/metaprogram.jai +++ b/metaprogram.jai @@ -123,10 +123,12 @@ generate_member_ui :: (type: *Type_Info_Struct, builder: *String_Builder, path: for type.members { if should_make_ui(type, it) && should_serialize(type, it) { new_path : string; - if path.count == 0 { - new_path = it.name; - } else { - new_path = tprint("%1.%2", path, it.name); + if it.name != "entity" { + if path.count == 0 { + new_path = it.name; + } else { + new_path = tprint("%1.%2", path, it.name); + } } type : Type_Info_Tag; diff --git a/module.jai b/module.jai index e05e794..376b7e4 100644 --- a/module.jai +++ b/module.jai @@ -108,7 +108,7 @@ coven_run :: (game_update_proc: (float), game_editor_update_proc: (float), game_ dt = now - time; time = now; engine.time = time; - engine.dt = dt; + engine.dt = min(dt, 0.4); update_fps_counter(dt); diff --git a/ui/ui.jai b/ui/ui.jai index 73c5a7e..3db1b45 100644 --- a/ui/ui.jai +++ b/ui/ui.jai @@ -5,7 +5,7 @@ MAX_WINDOWS :: 128; SCROLL_SPEED :: 5; WINDOW_TITLE_BAR_HEIGHT :: 20.0; -WINDOW_BORDER_WIDTH :: 3.0; +WINDOW_BORDER_WIDTH :: 3; UI_Box_Flags :: enum_flags u32 { NONE :: 0; @@ -55,7 +55,8 @@ UI_Box :: struct { hash: u32; last_used_frame_index: u64; - root_for_window: *UI_Window; + window: *UI_Window; + root_in_window: bool; parent: *UI_Box; first_child: *UI_Box; last_child: *UI_Box; @@ -165,6 +166,7 @@ UI_State :: struct { last_box: *UI_Box; + mouse_over_window: bool; parent_stack: Stack(*UI_Box); allocator: Allocator; @@ -254,7 +256,6 @@ ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box { 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; @@ -264,13 +265,16 @@ ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box { box.num_children = 0; box.flags = flags; box.parent = null; - box.root_for_window = 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_for_window = ui_state.current_window; + box.root_in_window = true; } + box.window = ui_state.current_window; + // Set the links box.parent = parent; if box.parent != null { @@ -345,7 +349,7 @@ ui_init :: () { 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", 18); + 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 @@ -504,8 +508,8 @@ ui_figure_out_sizes :: () { 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); - } 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 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); } @@ -513,8 +517,8 @@ ui_figure_out_sizes :: () { 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_for_window != null { - box.size.y = cast(float)box.root_for_window.size.y * box.semantic_size[0].value - (WINDOW_BORDER_WIDTH); + } 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); } @@ -566,10 +570,10 @@ ui_figure_out_sizes :: () { // Find final positions for *box : ui_state.boxes { if box.parent == null { - 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; + 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; } else { box.rect.x = 0.0; box.rect.y = 0.0; @@ -762,6 +766,8 @@ get_rect_instance_buffer :: () -> *Instanced_Rects_Data { 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; @@ -814,7 +820,7 @@ ui_render :: () { { if window.title.count > 0 { font_handle := ui_state.fonts.regular; - x : float = (xx window.actual_position.x + WINDOW_BORDER_WIDTH + 2); + x : float = cast(float)(xx window.actual_position.x + cast(u32)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); @@ -830,10 +836,10 @@ ui_render :: () { } 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)); + 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(); @@ -900,6 +906,10 @@ ui_render :: () { //} } +ui_mouse_over_window :: () -> bool { + return ui_state.mouse_over_window; +} + #scope_file set_properties_to_defaults :: () { background_color = .{1,1,1,1}; @@ -1103,7 +1113,7 @@ ui_render_text_recursively :: (box: *UI_Box) { inv_h := 1.0 / cast(float)engine.renderer.render_target_height; if box.flags & .DRAW_TEXT { - font_handle := ui_state.fonts.regular; + font_handle := ui_state.fonts.button; if box.text.count > 0 { text_size := get_text_size(engine.renderer, box.text, font_handle); @@ -1150,8 +1160,23 @@ ui_update_input :: () { 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); @@ -1159,12 +1184,14 @@ ui_update_input :: () { 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; @@ -1188,6 +1215,7 @@ ui_update_input :: () { } for *window: ui_state.windows { + if window.last_used_frame_index != ui_state.frame_index continue; window.actual_position = window.position + window.offset; } @@ -1212,7 +1240,14 @@ ui_update_input :: () { box.interaction.left_mouse_pressed = false; } } else { - if is_mouse_inside(mouse_x, mouse_y, box.rect) && key_pressed(.MOUSE_LEFT) { + 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; } diff --git a/ui/widgets.jai b/ui/widgets.jai index ff6392d..2639e25 100644 --- a/ui/widgets.jai +++ b/ui/widgets.jai @@ -98,6 +98,16 @@ ui_label :: (text: string, text_color: Color = .{1,1,1,1}, identifier: s64 = 0, box := ui_box_make(.DRAW_TEXT, get_hash(loc, identifier)); } +ui_label_for_fields :: (text: string, text_color: Color = .{1,1,1,1}, identifier: s64 = 0, loc := #caller_location) { + ui_set_next_text(text); + ui_set_next_background_color(.{0.0,1.0,1.0,0.0}); + ui_set_next_text_color(text_color); + ui_set_next_size_x(.PCT, 0.5); + ui_set_next_size_y(.TEXT_DIM); + ui_set_next_padding(5); + box := ui_box_make(.DRAW_TEXT, get_hash(loc, identifier)); +} + ui_label_fill_parent_x :: (text: string, identifier: s64 = 0, loc := #caller_location) { ui_set_next_text(text); ui_set_next_background_color(.{0.0,1.0,1.0,0.0}); @@ -288,7 +298,6 @@ ui_int_field :: (label: string, value: *int, identifier: s64 = 0, loc := #caller ui_float_field :: (label: string, value: *float, identifier: s64 = 0, loc := #caller_location) -> bool { changed := false; ui_container_layout(identifier=identifier, loc=loc); - ui_set_next_size_x(.CHILDREN_SUM); ui_push_parent(ui_state.last_box, .LEFT, .HORIZONTAL); { ui_label(label); @@ -425,16 +434,27 @@ ui_float_field :: (value: *float, identifier: s64 = 0, loc := #caller_location) ui_vector_field :: (label: string, value: *Vector3, identifier: s64 = 0, loc := #caller_location) -> bool { changed := false; + ui_set_next_size_x(.TEXT_DIM); + ui_set_next_size_y(.TEXT_DIM); ui_container_layout(identifier=identifier, loc=loc); ui_push_parent(ui_state.last_box, .LEFT, .HORIZONTAL); { - ui_label(label); + ui_label_for_fields(label); + + //ui_set_next_size_x(.PCT, 0.5); + //ui_set_next_size_y(.CHILDREN_SUM); + + //container := ui_box_make(0, get_hash(loc, 123)); + //ui_push_parent(ui_state.last_box, .LEFT, .HORIZONTAL); + ui_label("X"); changed |= ui_float_field(*value.x); ui_label("Y"); changed |= ui_float_field(*value.y); ui_label("Z"); changed |= ui_float_field(*value.z); + + //ui_pop_parent(); } ui_pop_parent();