package sectr import "base:runtime" import "core:math" import "core:math/linalg" import "core:os" import str "core:strings" import rl "vendor:raylib" DebugActions :: struct { load_project : b32, save_project : b32, pause_renderer : b32, load_auto_snapshot : b32, record_replay : b32, play_replay : b32, show_mouse_pos : b32, mouse_select : b32, cam_move_up : b32, cam_move_left : b32, cam_move_down : b32, cam_move_right : b32, cam_mouse_pan : b32, } poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) { // profile(#procedure) using actions using input modifier_active := keyboard.right_alt.ended_down || keyboard.right_control.ended_down || keyboard.right_shift.ended_down || keyboard.left_alt.ended_down || keyboard.left_control.ended_down || keyboard.left_shift.ended_down load_project = keyboard.left_control.ended_down && pressed( keyboard.O ) save_project = keyboard.left_control.ended_down && pressed( keyboard.S ) base_replay_bind := keyboard.right_alt.ended_down && pressed( keyboard.L) record_replay = base_replay_bind && keyboard.right_shift.ended_down play_replay = base_replay_bind && ! keyboard.right_shift.ended_down show_mouse_pos = keyboard.right_alt.ended_down && pressed(keyboard.M) mouse_select = pressed(mouse.left) cam_move_up = keyboard.W.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) cam_move_left = keyboard.A.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) cam_move_down = keyboard.S.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) cam_move_right = keyboard.D.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) cam_mouse_pan = mouse.right.ended_down && ! pressed(mouse.right) } frametime_delta32 :: #force_inline proc "contextless" () -> f32 { return cast(f32) get_state().frametime_delta_seconds } update :: proc( delta_time : f64 ) -> b32 { profile(#procedure) state := get_state(); using state replay := & Memory_App.replay workspace := & project.workspace cam := & workspace.cam if rl.IsWindowResized() { window := & state.app_window window.extent.x = f32(rl.GetScreenWidth()) * 0.5 window.extent.y = f32(rl.GetScreenHeight()) * 0.5 project.workspace.cam.offset = transmute(Vec2) window.extent } state.input, state.input_prev = swap( state.input, state.input_prev ) poll_input( state.input_prev, state.input ) debug_actions : DebugActions = {} poll_debug_actions( & debug_actions, state.input ) // Saving & Loading { if debug_actions.save_project { project_save( & project ) } if debug_actions.load_project { project_load( str_tmp_from_any( project.path, project.name, ".sectr_proj", sep = "" ), & project ) } } //region Input Replay // TODO(Ed) : Implment host memory mapping api when false { if debug_actions.record_replay { #partial switch replay.mode { case ReplayMode.Off : { save_snapshot( & Memory_App.snapshot ) replay_recording_begin( Path_Input_Replay ) } case ReplayMode.Record : { replay_recording_end() } }} if debug_actions.play_replay { switch replay.mode { case ReplayMode.Off : { if ! file_exists( Path_Input_Replay ) { save_snapshot( & Memory_App.snapshot ) replay_recording_begin( Path_Input_Replay ) } else { load_snapshot( & Memory_App.snapshot ) replay_playback_begin( Path_Input_Replay ) } } case ReplayMode.Playback : { replay_playback_end() load_snapshot( & Memory_App.snapshot ) } case ReplayMode.Record : { replay_recording_end() load_snapshot( & Memory_App.snapshot ) replay_playback_begin( Path_Input_Replay ) } }} if replay.mode == ReplayMode.Record { record_input( replay.active_file, input ) } else if replay.mode == ReplayMode.Playback { play_input( replay.active_file, input ) } } //endregion Input Replay if debug_actions.show_mouse_pos { debug.mouse_vis = !debug.mouse_vis } //region 2D Camera Manual Nav { // profile("Camera Manual Nav") digital_move_speed : f32 = 200.0 if workspace.zoom_target == 0.0 { workspace.zoom_target = cam.zoom } switch config.cam_zoom_mode { case .Smooth: zoom_delta := input.mouse.vertical_wheel * config.cam_zoom_sensitivity_smooth workspace.zoom_target *= 1 + zoom_delta * f32(delta_time) workspace.zoom_target = clamp(workspace.zoom_target, config.cam_min_zoom, config.cam_max_zoom) // Linearly interpolate cam.zoom towards zoom_target lerp_factor := config.cam_zoom_smooth_snappiness // Adjust this value to control the interpolation speed cam.zoom += (workspace.zoom_target - cam.zoom) * lerp_factor * f32(delta_time) cam.zoom = clamp(cam.zoom, config.cam_min_zoom, config.cam_max_zoom) // Ensure cam.zoom stays within bounds case .Digital: zoom_delta := input.mouse.vertical_wheel * config.cam_zoom_sensitivity_digital workspace.zoom_target = clamp(workspace.zoom_target + zoom_delta, config.cam_min_zoom, config.cam_max_zoom) cam.zoom = workspace.zoom_target } move_velocity : Vec2 = { - cast(f32) i32(debug_actions.cam_move_left) + cast(f32) i32(debug_actions.cam_move_right), - cast(f32) i32(debug_actions.cam_move_up) + cast(f32) i32(debug_actions.cam_move_down), } move_velocity *= digital_move_speed * f32(delta_time) cam.target += move_velocity if debug_actions.cam_mouse_pan { if is_within_screenspace(input.mouse.pos) { pan_velocity := input.mouse.delta * ( 1 / cam.zoom ) cam.target -= pan_velocity } } } //endregion 2D Camera Manual Nav //region WorkspaceImgui Tick { profile("Workspace Imgui") // Creates the root box node, set its as the first parent. ui_graph_build( & state.project.workspace.ui ) ui := ui_context frame_style_flags : UI_StyleFlags = { .Fixed_Position_X, .Fixed_Position_Y, .Fixed_Width, .Fixed_Height, } default_layout := UI_Layout { anchor = {}, alignment = { 0., 0.0 }, text_alignment = { 0.0, 0.0 }, // corner_radii = { 0.2, 0.2, 0.2, 0.2 }, pos = { 0, 0 }, size = range2( { 1000, 1000 }, {}), // padding = { 20, 20, 20, 20 } } frame_style_default := UI_Style { flags = frame_style_flags, bg_color = Color_BG_TextBox, font = default_font, font_size = 30, text_color = Color_White, layout = default_layout, } frame_theme := UI_StyleTheme { styles = { frame_style_default, frame_style_default, frame_style_default, frame_style_default, }} frame_theme.disabled.bg_color = Color_Frame_Disabled frame_theme.hot.bg_color = Color_Frame_Hover frame_theme.active.bg_color = Color_Frame_Select ui_style_theme( frame_theme ) config.ui_resize_border_width = 2.5 // test_draggable() // test_text_box() // test_parenting( & default_layout, & frame_style_default ) // test_whitespace_ast( & default_layout, & frame_style_default ) /* Prototype app menu This is a menu bar for the app for now inside the same ui as the workspace's UI state Eventually this will get moved out to its own UI state for the app itself. */ if true { fmt :: str_fmt_alloc @static bar_pos := Vec2 {} bar_size := vec2( 400, 40 ) menu_bar : UI_Widget { theme := UI_Style { flags = { }, bg_color = { 0, 0, 0, 30 }, border_color = { 0, 0, 0, 200 }, font = default_font, font_size = 12, text_color = Color_White, layout = UI_Layout { anchor = {}, border_width = 1.0 * (1.0/cam.zoom), pos = screen_to_world(bar_pos), size = range2( bar_size * (1.0/cam.zoom), {}), // padding = { 10, 10, 10, 10 }, }, } ui_theme_via_style(theme) menu_bar = ui_widget("App Menu Bar", UI_BoxFlags {} ) } // Setup Children settings_btn : UI_Widget { ui_parent(menu_bar) style := UI_Style { flags = { // .Origin_At_Anchor_Center .Fixed_Height }, bg_color = Color_Frame_Disabled, font = default_font, font_size = 18 * (1.0/cam.zoom), text_color = Color_White, layout = UI_Layout { anchor = range2( {0, 0}, {0.0, 0} ), alignment = { 0.0, 1.0 }, text_alignment = { 0.5, 0.5 }, pos = { 0, 0 }, size = range2( {25, bar_size.y} * (1.0/cam.zoom), {0, 0}) } } theme := UI_StyleTheme { styles = { style, style, style, style, }} theme.disabled.bg_color = Color_Frame_Disabled theme.hot.bg_color = Color_White theme.active.bg_color = Color_Frame_Select ui_style_theme(theme) move_box : UI_Widget { move_box = ui_button("Move Box") if move_box.dragging { // bar_pos += mouse_world_delta() bar_pos += state.input.mouse.delta } } move_settings_spacer := ui_widget("Move-Settings Spacer", {}) move_settings_spacer.text = str_intern("") move_settings_spacer.style.font_size = 10 * (1.0/cam.zoom) move_settings_spacer.style.bg_color = Color_Transparent // settings_btn : UI_Widget { settings_btn = ui_button("Settings Btn") settings_btn.text = str_intern("Settings") settings_btn.style.flags = { .Scale_Width_By_Height_Ratio, } } // HBox layout calculation? { hb_space_ratio_move_box := 0.1 hb_space_ratio_move_settings_spacer := 0.05 hb_space_ratio_settings_btn := 1.0 style := & move_box.box.style style.anchor.max.x = 0.9 style = & move_settings_spacer.box.style style.anchor.min.x = 0.1 style.anchor.max.x = 0.8 style = & settings_btn.box.style style.anchor.min.x = 0.2 style.anchor.max.x = 0.55 } } @static settings_open := false if settings_btn.left_clicked || settings_open { settings_open = true @static pos := Vec2 {0, 0} settings_menu := ui_widget("Settings Menu", { .Mouse_Clickable, .Focusable, .Click_To_Focus }) settings_menu.style.pos = screen_to_world(pos) settings_menu.style.size = range2( {600, 800} * (1/cam.zoom), {}) settings_menu.style.text_alignment = {0, 0.0} settings_menu.style.alignment = { 0.5, 0.5 } settings_menu.style.bg_color = Color_Transparent settings_menu.style.border_width = 1.0 * (1/cam.zoom) settings_menu.style.border_color = Color_Blue // settings_menu.style.padding = { 10, 10, 10, 10 } settings_menu.text = { fmt("%v", pos), {} } settings_menu.text.runes = to_runes(settings_menu.text.str) settings_menu.style.font_size = 16 * (1/cam.zoom) // pos.x += frametime_delta32() * 100 if settings_menu.dragging { pos += state.input.mouse.delta // pos.x += frametime_delta32() * 1 } ui_parent(settings_menu) frame_bar := ui_widget("Settings Menu: Frame Bar", {}) { using frame_bar // style.bg_color = Color_Red style.flags = {} style.alignment = { 0, 1 } style.size = {} style.anchor = range2( {0, 0.95}, {0, 0} ) // Close button { } } } } } //endregion Workspace Imgui Tick //region App Screenspace Imgui Tick { profile("App Screenspace Imgui") ui_graph_build( & state.app_ui ) ui := ui_context /* Prototype app menu TODO(Ed): Move it to here */ } //endregion App Screenspace Imgui Tick debug.last_mouse_pos = input.mouse.pos should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() return should_shutdown }