diff --git a/.gitignore b/.gitignore index b6c0915..3f91fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ build/** logs .ark logs*.zip -code_compiler_staged +code_flattened diff --git a/code/sectr/app/screen.odin b/code/sectr/app/screen.odin index a7a25c0..b8ef33d 100644 --- a/code/sectr/app/screen.odin +++ b/code/sectr/app/screen.odin @@ -52,12 +52,13 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f { @static theme : UI_Theme @static loaded : b32 = false - if true && ! loaded + if ! loaded { + app_color := app_color_theme() layout := UI_Layout { flags = {}, anchor = range2({},{}), - alignment = {0.5, 0.5}, + // alignment = UI_Align_Presets.text_centered, text_alignment = {0.0, 1.5}, font_size = 12, margins = {0, 0, 0, 0}, @@ -67,34 +68,30 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f size = range2({},{}) } style := UI_Style { - bg_color = Color_ThmDark_BG, - border_color = Color_ThmDark_Border_Default, + bg_color = app_color.bg, + border_color = app_color.border_default, corner_radii = {}, blur_size = 0, font = get_state().default_font, - text_color = Color_ThmDark_Text_Default, + text_color = app_color.text_default, cursor = {}, } - - // loaded = true layout_combo := to_ui_layout_combo(layout) style_combo := to_ui_style_combo(style) - { + Hot: { using layout_combo.hot using style_combo.hot - bg_color = Color_ThmDark_Btn_BG_Hot - text_color = Color_ThmDark_Text_Hot + bg_color = app_color.btn_bg_hot + text_color = app_color.text_hot } - { + Active: { using layout_combo.active using style_combo.active - bg_color = Color_ThmDark_Btn_BG_Active - text_color = Color_ThmDark_Text_Active - } - - theme = UI_Theme { - layout_combo, style_combo + bg_color = app_color.btn_bg_active + text_color = app_color.text_active } + theme = UI_Theme { layout_combo, style_combo } + loaded = true } ui_layout_push(theme.layout) ui_style_push(theme.style) @@ -105,7 +102,8 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f { using screen_ui.menu_bar ui_theme_app_menu_bar_default() - container = ui_hbox( .Left_To_Right, "Menu Bar" ) + container = ui_hbox_begin( .Left_To_Right, "Menu Bar" ) + ui_parent(container) { using container layout.flags = {.Fixed_Position_X, .Fixed_Position_Y, .Fixed_Width, .Fixed_Height, .Origin_At_Anchor_Center} @@ -114,7 +112,7 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f text = str_intern("menu_bar") } - ui_theme_btn_default() + ui_theme_btn() move_box := ui_button("Move Box"); { using move_box @@ -122,7 +120,7 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f pos += input.mouse.delta should_raise = true } - layout.anchor.ratio.x = 0.4 + layout.anchor.ratio.x = 1.0 } spacer := ui_spacer("Menu Bar: Move Spacer") @@ -135,17 +133,18 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f using settings_btn text = str_intern("Settings") layout.flags = { - // .Scale_Width_By_Height_Ratio, - .Fixed_Width + .Scale_Width_By_Height_Ratio, + // .Fixed_Width } - layout.size.min.x = 100 + layout.size.ratio.x = 2.0 if pressed { screen_ui.settings_menu.is_open = true } } - spacer = ui_spacer("Menu Bar: End Spacer") - spacer.layout.anchor.ratio.x = 1.0 + spacer.layout.anchor.ratio.x = 2.0 + + ui_hbox_end(container, compute_layout = false) } return } @@ -156,22 +155,18 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b using state := get_state() using state.screen_ui if ! settings_menu.is_open do return + app_color := app_color_theme() using settings_menu if size.x < min_size.x do size.x = min_size.x if size.y < min_size.y do size.y = min_size.y - ui_theme_transparent() - container = ui_widget("Settings Menu", {}) - { + ui_theme_window_panel() + container = ui_widget("Settings Menu", {}); { using container - layout.flags = { .Fixed_Width, .Fixed_Height, .Fixed_Position_X, .Fixed_Position_Y, .Origin_At_Anchor_Center } - style.bg_color = Color_ThmDark_Translucent_Panel - style.border_color = { 0, 0, 0, 200 } - layout.alignment = {0.0, 0.0} - layout.border_width = 1.0 - layout.pos = pos - layout.size = range2( size, {}) + layout.flags = { .Fixed_Width, .Fixed_Height, .Fixed_Position_X, .Fixed_Position_Y, .Origin_At_Anchor_Center } + layout.pos = pos + layout.size = range2( size, {}) } ui_parent(container) if settings_menu.is_maximized { @@ -179,54 +174,27 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b layout.flags = {.Origin_At_Anchor_Center } layout.pos = {} } - should_raise |= ui_resizable_handles( & container, & pos, & size/*, compute_layout = true*/) - // ui_box_compute_layout(container) + should_raise |= ui_resizable_handles( & container, & pos, & size) vbox := ui_vbox_begin( .Top_To_Bottom, "Settings Menu: VBox", {.Mouse_Clickable}, compute_layout = true) { should_raise |= b32(vbox.active) ui_parent(vbox) - ui_layout( UI_Layout { - // font_size = 16, - // alignment = {0, 1}, - }) - ui_style( UI_Style { - // bg_color = Color_Transparent, - font = default_font, - text_color = Color_White, - }) - ui_style_ref().hot.bg_color = Color_Blue - frame_bar := ui_hbox_begin(.Left_To_Right, "Settings Menu: Frame Bar", { .Mouse_Clickable, .Focusable, .Click_To_Focus }) + ui_theme_window_bar() + frame_bar := ui_hbox_begin(.Left_To_Right, "Settings Menu: Frame Bar", { .Mouse_Clickable }) { - frame_bar.layout.flags = {.Fixed_Height} - frame_bar.layout.size.min.y = 50 ui_parent(frame_bar) - - ui_layout( UI_Layout { - font_size = 18, - }) - title := ui_text("Settings Menu: Title", str_intern("Settings Menu"), {.Disabled}) - { + ui_theme_text() + title := ui_text("Settings Menu: Title", str_intern("Settings Menu"), {.Disabled}); { using title + layout.anchor.ratio.x = 1.0 layout.margins = { 0, 0, 15, 0} - layout.text_alignment = {0 , 0.5} - layout.anchor.ratio.x = 1.0 + layout.font_size = 16 } - - ui_layout( UI_Layout { - font_size = 16, - }) - - ui_style(ui_style_peek()) - style := ui_style_ref() - maximize_btn := ui_button("Settings Menu: Maximize Btn") - { + ui_theme_window_bar_btn() + maximize_btn := ui_button("Settings Menu: Maximize Btn"); { using maximize_btn - layout.flags = {.Fixed_Width} - layout.size.min = {50, 50} - layout.text_alignment = {0.5, 0.5} - layout.anchor.ratio.x = 1.0 if maximize_btn.pressed { settings_menu.is_maximized = ~settings_menu.is_maximized should_raise = true @@ -234,19 +202,12 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b if settings_menu.is_maximized do text = str_intern("min") else do text = str_intern("max") } - close_btn := ui_button("Settings Menu: Close Btn") - { + close_btn := ui_button("Settings Menu: Close Btn"); { using close_btn text = str_intern("close") - layout.flags = {.Fixed_Width} - layout.size.min = {50, 0} - layout.text_alignment = {0.5, 0.5} - layout.anchor.ratio.x = 1.0 - if close_btn.pressed { - settings_menu.is_open = false - } + if close_btn.hot do style.bg_color = app_color.window_btn_close_bg_hot + if close_btn.pressed do settings_menu.is_open = false } - ui_hbox_end(frame_bar, compute_layout = true) } if frame_bar.active { @@ -254,66 +215,109 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b should_raise = true } - // Populate settings with values from config (hardcoded for now) - ui_layout(UI_Layout { - flags = { - // .Origin_At_Anchor_Center, - // .Fixed_Height, - }, - // pos = {0, 50}, - // size = range2({100, 100},{}), - // alignment = {0,0}, - }) - ui_style( UI_Style { - // bg_color = Color_GreyRed - }) - drop_down_bar := ui_hbox_begin(.Left_To_Right, "settings_menu.vbox: config drop_down_bar", {.Mouse_Clickable}) - btn : UI_Widget + @static config_drop_down_open := false + ui_theme_drop_down() + drop_down_bar := ui_hbox_begin(.Left_To_Right, "settings_menu.vbox: config drop_down_bar", {.Mouse_Clickable }) { - drop_down_bar.layout.anchor.ratio.y = 0.1 + ui_parent_push(drop_down_bar) { using drop_down_bar text = str_intern("drop_down_bar") - // style.bg_color = { 55, 55, 55, 100 } - style.font = default_font - style.text_color = Color_White - layout.flags = {.Fixed_Height} - layout.font_size = 12 layout.text_alignment = {1, 0} - layout.size.min.y = 35 + layout.anchor.ratio.y = 1.0 } - ui_parent(drop_down_bar) - - btn = ui_text("pls", str_intern("Lets figure this out...")) - { - using btn - text = str_intern("Config") - style.font = default_font - style.text_color = Color_White - // layout.flags = {.Origin_At_Anchor_Center} + ui_theme_text() + title := ui_text("drop_down_bar.btn", str_intern("drop_down_bar.btn")); { + using title + text = str_intern("App Config") + style.text_color = drop_down_bar.style.text_color layout.alignment = {0.0, 0.0} + layout.text_alignment = {0.0, 0.5} layout.anchor.ratio.x = 1.0 - layout.font_size = 12 - layout.margins = {0,0, 15, 0} - layout.size.min.y = 35 } - um := ui_spacer("um...") - um.layout.anchor.ratio.x = 1.0 + ui_parent_pop() ui_hbox_end(drop_down_bar, compute_layout = true) + if drop_down_bar.pressed do config_drop_down_open = !config_drop_down_open } - // ui_layout(UI_Layout { + if config_drop_down_open + { + { + ui_theme_table_row(is_even = false) + hb := ui_hbox(.Left_To_Right, "settings_menu.engine_refresh_hz.hb"); { using hb + layout.size.min = {0, 30} + layout.flags = {.Fixed_Height} + layout.padding = to_ui_layout_side(4) + } + ui_theme_text(); title := ui_text("settings_menu.engine_refresh_hz.title", str_intern("Engine Refresh Hz")); { using title + layout.anchor.ratio.x = 1.0 + layout.margins.left = 10 + layout.text_alignment = {0, 0.5} - // }) - // ui_style( UI_Style { + } + input_box := ui_widget("settings_menu.engine_refresh.input_box", {.Mouse_Clickable, .Focusable, .Click_To_Focus}); { using input_box + layout.flags = {.Fixed_Width} + layout.margins.left = 5 + layout.padding.right = 10 + layout.size.min.x = 80 + if input_box.active do style.bg_color = app_color.input_box_bg_active + else if input_box.hot do style.bg_color = app_color.input_box_bg_hot + else do style.bg_color = app_color.input_box_bg + style.corner_radii[0] = 0.35 + } + @static value_str : Array(rune) + if value_str.data == nil { + error : AllocatorError + value_str, error = array_init_reserve(rune, persistent_slab_allocator(), Kilo) + ensure(error != AllocatorError.None, "Failed to allocate array for value_str of input_box") + array_append( & value_str, rune('_')) + } + if input_box.active { + array_append( & value_str, input.keyboard_events.chars_pressed ) + array_clear( input.keyboard_events.chars_pressed ) + } + else if input_box.was_active { - // }) - // res_width_hbox := ui_hbox_begin(.Left_To_Right, "settings_menu.vbox: config.resolution_width: hbox", {}) - // ui_parent(res_width_hbox) - - spacer := ui_spacer("Settings Menu: Spacer") - spacer.layout.anchor.ratio.y = 1.0 + } + else { + array_clear( value_str) + array_append( & value_str, to_runes(str_fmt_alloc("%v", config.engine_refresh_hz))) + } + // input_box + { + ui_parent(input_box) + value_txt := ui_text("settings_menu.engine_refresh.refresh_value", to_str_runes_pair(value_str)) + value_txt.layout.text_alignment = vec2(1, 0.5) + } + spacer := ui_spacer("settings_menu.engine_refresh.end_spacer") + spacer.layout.flags = {.Fixed_Width} + spacer.layout.size.min.x = 10 + // input_text := ui_text("settings_menu.engine_refresh", str_fmt_alloc(value_str)) + } + { + ui_theme_table_row(is_even = true) + hb := ui_hbox(.Left_To_Right, "settings_menu.cam_min_zoom.hb"); { using hb + layout.size.min = {0, 30} + layout.flags = {.Fixed_Height} + } + ui_theme_text(); title := ui_text("settings_menu.cam_min_zoom.title", str_intern("Camera: Min Zoom")); { using title + layout.anchor.ratio.x = 1.0 + layout.margins.left = 10 + } + } + { + ui_theme_table_row(is_even = false) + hb := ui_hbox(.Left_To_Right, "settings_menu.cam_max_zoom.hb"); { using hb + layout.size.min = {0, 30} + layout.flags = {.Fixed_Height} + } + ui_theme_text(); title := ui_text("settings_menu.cam_max_zoom.title", str_intern("Camera: Max Zoom")); { using title + layout.anchor.ratio.x = 1.0 + layout.margins.left = 10 + } + } + } ui_vbox_end(vbox, compute_layout = false ) } return diff --git a/code/sectr/app/state.odin b/code/sectr/app/state.odin index 46213e4..c2613e8 100644 --- a/code/sectr/app/state.odin +++ b/code/sectr/app/state.odin @@ -159,6 +159,8 @@ AppConfig :: struct { timing_fps_moving_avg_alpha : f32, ui_resize_border_width : f32, + + color_theme : AppColorTheme, } AppWindow :: struct { @@ -257,4 +259,7 @@ get_state :: #force_inline proc "contextless" () -> ^ State { // return get_state().frametime // } +app_config :: #force_inline proc "contextless" () -> AppConfig { return get_state().config } +app_color_theme :: #force_inline proc "contextless" () -> AppColorTheme { return get_state().config.color_theme } + #endregion("State") diff --git a/code/sectr/app/ui_theme.odin b/code/sectr/app/ui_theme.odin new file mode 100644 index 0000000..1b76587 --- /dev/null +++ b/code/sectr/app/ui_theme.odin @@ -0,0 +1,473 @@ +package sectr + +/* +UI Themes: Comprise of UI_Box's layout & style + +Provides presets for themes and their interface for manipulating the combo stacks in UI_State in pairs +*/ +// TODO(Ed): Eventually this will have a configuration wizard, and we'll save the presets + +@(deferred_none = ui_theme_pop) +ui_theme_btn :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {}, + anchor = range2_zero, + alignment = {0, 0}, + text_alignment = {0.5, 0.5}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 1, + pos = {0, 0}, + size = range2_zero, + } + style := UI_Style { + bg_color = app_color.btn_bg_default, + border_color = app_color.border_default, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Hot: { + using layout_combo.hot + using style_combo.hot + bg_color = app_color.btn_bg_hot + text_color = app_color.text_hot + } + Active: { + using layout_combo.active + using style_combo.active + bg_color = app_color.btn_bg_active + text_color = app_color.text_active + margins = {2, 2, 2, 2} + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_drop_down :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {.Fixed_Height}, + anchor = range2({0, 0},{}), + alignment = {0, 0}, + text_alignment = {0.5, 0.5}, + font_size = 14, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 1, + pos = {0, 0}, + size = range2({0,20},{}) + } + style := UI_Style { + bg_color = app_color.btn_bg_default, + border_color = app_color.border_default, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Hot: { + using layout_combo.hot + using style_combo.hot + bg_color = app_color.btn_bg_hot + text_color = app_color.text_hot + margins = {2, 2, 2, 2} + } + Active: { + using layout_combo.active + using style_combo.active + bg_color = app_color.btn_bg_active + text_color = app_color.text_active + margins = {2, 2, 2, 2} + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_table_row :: proc(is_even : bool) +{ + @static theme : UI_Theme + @static loaded : b32 = false + // if ! loaded + { + app_color := app_color_theme() + table_bg : Color + if is_even { + table_bg = app_color.table_even_bg_color + } + else { + table_bg = app_color.table_odd_bg_color + } + layout := UI_Layout { + flags = {}, + anchor = range2({},{}), + alignment = {0, 0}, + text_alignment = {0.0, 0.0}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 0, + pos = {0, 0}, + size = range2({},{}) + } + style := UI_Style { + bg_color = table_bg, + border_color = Color_Transparent, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color_theme().text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Hot: { + using layout_combo.disabled + using style_combo.disabled + } + Active: { + using layout_combo.hot + using style_combo.hot + } + { + using layout_combo.active + using style_combo.active + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_window_bar :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded || true + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {.Fixed_Height}, + anchor = range2({},{}), + alignment = {0, 0}, + text_alignment = {0.0, 0.0}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 0.0, + pos = {0, 0}, + size = range2({0, 35},{}) + } + style := UI_Style { + bg_color = app_color.window_bar_bg, + border_color = Color_Transparent, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Disabled : { + using layout_combo.disabled + using style_combo.disabled + } + Hot: { + using layout_combo.hot + using style_combo.hot + border_color = app_color.window_bar_border + border_width = 1.0 + } + Active: { + using layout_combo.active + using style_combo.active + border_color = app_color.window_bar_border + border_width = 2.0 + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_window_bar_title :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {}, + anchor = range2({},{}), + alignment = {0, 0}, + text_alignment = {0.0, 0.0}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 0, + pos = {0, 0}, + size = range2({},{}) + } + style := UI_Style { + bg_color = Color_Transparent, + border_color = Color_Transparent, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Disabed: { + using layout_combo.disabled + using style_combo.disabled + } + Hot: { + using layout_combo.hot + using style_combo.hot + } + Active: { + using layout_combo.active + using style_combo.active + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_window_bar_btn :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {.Fixed_Width}, + anchor = range2({1, 0},{}), + alignment = {0, 0}, + text_alignment = {0.5, 0.5}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 1, + pos = {0, 0}, + size = range2({50,0},{}) + } + style := UI_Style { + bg_color = app_color.btn_bg_default, + border_color = app_color.border_default, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Hot: { + using layout_combo.hot + using style_combo.hot + bg_color = app_color.btn_bg_hot + text_color = app_color.text_hot + } + Active: { + using layout_combo.active + using style_combo.active + bg_color = app_color.btn_bg_active + text_color = app_color.text_active + margins = {2, 2, 2, 2} + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_window_panel :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {}, + anchor = range2({},{}), + alignment = {0, 0}, + text_alignment = {0.0, 0.0}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 1, + pos = {0, 0}, + size = range2({},{}) + } + style := UI_Style { + bg_color = app_color.window_panel_bg, + border_color = app_color.window_panel_border, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + Disabled: { + using layout_combo.disabled + using style_combo.disabled + } + Hot: { + using layout_combo.hot + using style_combo.hot + } + Active: { + using layout_combo.active + using style_combo.active + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_transparent :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {}, + anchor = range2({},{}), + alignment = {0, 0}, + text_alignment = {0.0, 0.0}, + font_size = 16, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 0, + pos = {0, 0}, + size = range2({},{}) + } + style := UI_Style { + bg_color = Color_Transparent, + border_color = Color_Transparent, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + { + using layout_combo.disabled + using style_combo.disabled + } + { + using layout_combo.hot + using style_combo.hot + } + { + using layout_combo.active + using style_combo.active + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} + +@(deferred_none = ui_theme_pop) +ui_theme_text :: proc() +{ + @static theme : UI_Theme + @static loaded : b32 = false + if ! loaded + { + app_color := app_color_theme() + layout := UI_Layout { + flags = {}, + anchor = range2({},{}), + alignment = {0, 0}, + text_alignment = {0.0, 0.5}, + font_size = 14, + margins = {0, 0, 0, 0}, + padding = {0, 0, 0, 0}, + border_width = 0, + pos = {0, 0}, + size = range2({},{}) + } + style := UI_Style { + bg_color = Color_Transparent, + border_color = Color_Transparent, + corner_radii = {}, + blur_size = 0, + font = get_state().default_font, + text_color = app_color.text_default, + cursor = {}, + } + layout_combo := to_ui_layout_combo(layout) + style_combo := to_ui_style_combo(style) + { + using layout_combo.disabled + using style_combo.disabled + } + { + using layout_combo.hot + using style_combo.hot + } + { + using layout_combo.active + using style_combo.active + } + theme = UI_Theme { layout_combo, style_combo } + loaded = true + } + ui_layout_push(theme.layout) + ui_style_push(theme.style) +} diff --git a/code/sectr/app/ui_theme_default.odin b/code/sectr/app/ui_theme_default.odin deleted file mode 100644 index 17d1aa1..0000000 --- a/code/sectr/app/ui_theme_default.odin +++ /dev/null @@ -1,114 +0,0 @@ -package sectr - -/* -UI Themes: Comprise of UI_Box's layout & style - -Provides presets for themes and their interface for manipulating the combo stacks in UI_State in pairs -*/ -// TODO(Ed): Eventually this will have a configuration wizard, and we'll save the presets - -@(deferred_none = ui_theme_pop) -ui_theme_btn_default :: proc() -{ - @static theme : UI_Theme - @static loaded : b32 = false - if ! loaded - { - layout := UI_Layout { - flags = {}, - anchor = range2({},{}), - alignment = {0, 0}, - text_alignment = {0.5, 0.5}, - font_size = 16, - margins = {0, 0, 0, 0}, - padding = {0, 0, 0, 0}, - border_width = 1, - pos = {0, 0}, - size = range2({},{}) - } - style := UI_Style { - bg_color = Color_ThmDark_Btn_BG_Default, - border_color = Color_ThmDark_Border_Default, - corner_radii = {}, - blur_size = 0, - font = get_state().default_font, - text_color = Color_ThmDark_Text_Default, - cursor = {}, - } - layout_combo := to_ui_layout_combo(layout) - style_combo := to_ui_style_combo(style) - { - using layout_combo.hot - using style_combo.hot - bg_color = Color_ThmDark_Btn_BG_Hot - text_color = Color_ThmDark_Text_Hot - margins = {2, 2, 2, 2} - } - { - using layout_combo.active - using style_combo.active - bg_color = Color_ThmDark_Btn_BG_Active - text_color = Color_ThmDark_Text_Active - margins = {2, 2, 2, 2} - } - theme = UI_Theme { - layout_combo, style_combo - } - loaded = true - } - ui_layout_push(theme.layout) - ui_style_push(theme.style) -} - -@(deferred_none = ui_theme_pop) -ui_theme_transparent :: proc() -{ - @static theme : UI_Theme - @static loaded : b32 = false - if ! loaded || true - { - layout := UI_Layout { - flags = {}, - anchor = range2({},{}), - alignment = {0, 0}, - text_alignment = {0.0, 0.0}, - font_size = 16, - margins = {0, 0, 0, 0}, - padding = {0, 0, 0, 0}, - border_width = 0, - pos = {0, 0}, - size = range2({},{}) - } - style := UI_Style { - bg_color = Color_Transparent, - border_color = Color_Transparent, - corner_radii = {}, - blur_size = 0, - font = get_state().default_font, - text_color = Color_ThmDark_Text_Default, - cursor = {}, - } - - layout_combo := to_ui_layout_combo(layout) - style_combo := to_ui_style_combo(style) - { - using layout_combo.disabled - using style_combo.disabled - } - { - using layout_combo.hot - using style_combo.hot - } - { - using layout_combo.active - using style_combo.active - } - - theme = UI_Theme { - layout_combo, style_combo - } - loaded = true - } - ui_layout_push(theme.layout) - ui_style_push(theme.style) -} diff --git a/code/sectr/colors.odin b/code/sectr/colors.odin index 3c02445..030ac68 100644 --- a/code/sectr/colors.odin +++ b/code/sectr/colors.odin @@ -31,32 +31,114 @@ Color_Debug_UI_Content_Bounds :: Color { 170, 120, 240, 160 } // TODO(Ed): The entire rendering pass should be post-processed by a tone curve configurable for the user // This is how you properly support any tonality of light or dark themes and not have it be base don the monitors raw output. -// Dark Theme +AppColorTheme :: struct { + light_limit, + dark_limit, -// Brightest value limited to (text is the only exception): -Color_ThmDark_BrightLimit :: Color {230, 230, 230, 255} -// Darkness value limited to (text is the only exception): -Color_ThmDark_DarkLimit :: Color {10, 10, 10, 255} + bg, + border_default, -Color_ThmDark_BG :: Color {33, 33, 33, 255} + btn_bg_default, + btn_bg_hot, + btn_bg_active, -Color_ThmDark_Translucent_Panel :: Color { 0, 0, 0, 60} + input_box_bg, + input_box_bg_hot, + input_box_bg_active, -Color_ThmDark_ResizeHandle_Default :: Color_Transparent -Color_ThmDark_ResizeHandle_Hot :: Color { 72, 72, 72, 90} -Color_ThmDark_ResizeHandle_Active :: Color { 88, 88, 88, 90} + resize_hndl_default, + resize_hndl_hot, + resize_hndl_active, -Color_ThmDark_Border_Default :: Color { 64, 64, 64, 255} + table_even_bg_color, + table_odd_bg_color, -Color_ThmDark_Btn_BG_Default :: Color { 40, 40, 40, 255} -Color_ThmDark_Btn_BG_Hot :: Color { 60, 60, 70, 255} -Color_ThmDark_Btn_BG_Active :: Color { 90, 100, 130, 255} + text_default, + text_hot, + text_active, -Color_ThmDark_Text_Default :: Color {120, 117, 115, 255} -Color_ThmDark_Text_Hot :: Color {180, 180, 180, 255} -Color_ThmDark_Text_Active :: Color {240, 240, 240, 255} + translucent_panel, -// Light Theme + window_bar_border, + window_bar_bg, + window_btn_close_bg_hot, -// LightTheme_BG :: Color { 120, 120, 120, 255 } + window_panel_bg, + window_panel_border \ + : Color +} + +App_Thm_Dusk :: AppColorTheme { + light_limit = Color {125, 125, 125, 255}, + dark_limit = Color { 10, 10, 10, 255}, + + bg = Color {33, 33, 33, 255}, + + border_default = Color { 64, 64, 64, 255}, + + btn_bg_default = Color { 40, 40, 40, 255}, + btn_bg_hot = Color { 60, 60, 70, 255}, + btn_bg_active = Color { 90, 100, 130, 255}, + + input_box_bg = Color { 20, 20, 20, 255}, + input_box_bg_hot = Color { 25, 25, 25, 255}, + input_box_bg_active = Color { 15, 15, 15, 255}, + + resize_hndl_default = Color_Transparent, + resize_hndl_hot = Color { 72, 72, 72, 90}, + resize_hndl_active = Color { 88, 88, 88, 90}, + + table_even_bg_color = Color { 35, 35, 35, 255}, + table_odd_bg_color = Color { 30, 30, 30, 255}, + + text_default = Color {120, 117, 115, 255}, + text_hot = Color {180, 180, 180, 255}, + text_active = Color {240, 240, 240, 255}, + + translucent_panel = Color { 10, 10, 10, 50}, + + window_bar_border = Color { 64, 64, 64, 255}, // border_default + window_bar_bg = Color{35, 35, 35, 255}, + window_btn_close_bg_hot = Color{45, 35, 35, 255}, + + window_panel_bg = Color { 10, 10, 10, 50}, // translucent_panel + window_panel_border = Color{24, 24, 24, 255}, +} + +App_Thm_Light :: AppColorTheme { + light_limit = Color {195, 195, 195, 255}, + dark_limit = Color { 60, 60, 60, 255}, + + bg = Color {135, 135, 135, 255}, + + border_default = Color { 174, 174, 174, 255}, + + btn_bg_default = Color { 160, 160, 160, 255}, + btn_bg_hot = Color { 145, 145, 155, 255}, + btn_bg_active = Color { 124, 124, 136, 255}, + + input_box_bg = Color {115, 115, 115, 255}, + input_box_bg_hot = Color {125, 125, 125, 255}, + input_box_bg_active = Color {105, 105, 105, 255}, + + resize_hndl_default = Color_Transparent, + resize_hndl_hot = Color { 95, 95, 95, 90}, + resize_hndl_active = Color { 80, 80, 80, 90}, + + table_even_bg_color = Color {150, 150, 150, 255}, + table_odd_bg_color = Color {160, 160, 160, 255}, + + text_default = Color { 55, 55, 55, 255}, + text_hot = Color { 85, 85, 85, 255}, + text_active = Color { 45, 45, 49, 255}, + + translucent_panel = Color { 110, 110, 110, 50}, + + window_bar_border = Color{ 174, 174, 174, 255}, // border_default + window_bar_bg = Color{ 155, 155, 155, 255}, + window_btn_close_bg_hot = Color{ 145, 135, 135, 255}, + + window_panel_bg = Color {135, 135, 135, 50}, // translucent_panel + window_panel_border = Color{184, 184, 184, 255}, +} diff --git a/code/sectr/engine/client_api.odin b/code/sectr/engine/client_api.odin index 8d25ae0..2dc6ee0 100644 --- a/code/sectr/engine/client_api.odin +++ b/code/sectr/engine/client_api.odin @@ -60,7 +60,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem Memory_App.state = state using state - // Setup Persistent Slab + // Setup Persistent Slabs & String Cache { alignment := uint(mem.DEFAULT_ALIGNMENT) @@ -93,14 +93,24 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem verify( alloc_error == .None, "Failed to allocate transient slab" ) transient_clear_time = 120 // Seconds, 2 Minutes + + string_cache = str_cache_init() } - string_cache = str_cache_init() - + // Setup input frame poll references input = & input_data[1] input_prev = & input_data[0] + for & input in input_data { + using input + error : AllocatorError + keyboard_events.keys_pressed, error = array_init_reserve(KeyboardKey, persistent_slab_allocator(), Kilo) + ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.keys_pressed array") + keyboard_events.chars_pressed, error = array_init_reserve(rune, persistent_slab_allocator(), Kilo) + ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.chars_pressed array") + } // Configuration Load + // TODO(Ed): Make this actually load from an ini { using config resolution_width = 1000 @@ -119,6 +129,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem timing_fps_moving_avg_alpha = 0.9 ui_resize_border_width = 5 + + color_theme = App_Thm_Dusk } Desired_OS_Scheduler_MS :: 1 @@ -182,6 +194,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem } // Demo project setup + // TODO(Ed): This will eventually have to occur when the user either creates or loads a workspace. I don't know { using project path = str_intern("./") @@ -318,10 +331,12 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 rl.PollInputEvents() - debug.draw_ui_box_bounds_points = true + debug.draw_ui_box_bounds_points = false debug.draw_UI_padding_bounds = false - debug.draw_ui_content_bounds = true + debug.draw_ui_content_bounds = false + config.color_theme = App_Thm_Light + // config.color_theme = App_Thm_Dusk should_close = update( host_delta_time ) render() diff --git a/code/sectr/engine/render_raylib.odin b/code/sectr/engine/render_raylib.odin index 8807d46..67d28c0 100644 --- a/code/sectr/engine/render_raylib.odin +++ b/code/sectr/engine/render_raylib.odin @@ -43,7 +43,7 @@ render :: proc() render_mode_3d() rl.BeginDrawing() - rl.ClearBackground( Color_ThmDark_BG ) + rl.ClearBackground( app_color_theme().bg ) render_mode_2d_workspace() render_mode_screenspace() @@ -252,7 +252,7 @@ render_mode_screenspace :: proc () { // debug_text( "Screen Width : %v", rl.GetScreenWidth () ) // debug_text( "Screen Height: %v", rl.GetScreenHeight() ) - debug_text( "frametime_target_ms : %f ms", frametime_target_ms ) + // debug_text( "frametime_target_ms : %f ms", frametime_target_ms ) debug_text( "frametime : %f ms", frametime_delta_ms ) // debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms ) if replay.mode == ReplayMode.Record { @@ -261,10 +261,9 @@ render_mode_screenspace :: proc () if replay.mode == ReplayMode.Playback { debug_text( "Replaying Input") } + // debug_text("Zoom Target: %v", project.workspace.zoom_target) } - debug_text("Zoom Target: %v", project.workspace.zoom_target) - if debug.mouse_vis { debug_text("Mouse Vertical Wheel: %v", input.mouse.vertical_wheel ) debug_text("Mouse Delta : %v", input.mouse.delta ) @@ -277,34 +276,39 @@ render_mode_screenspace :: proc () ui := & project.workspace.ui - debug_text("Box Count (Workspace): %v", ui.built_box_count ) + if false + { + debug_text("Box Count (Workspace): %v", ui.built_box_count ) - hot_box := ui_box_from_key( ui.curr_cache, ui.hot ) - active_box := ui_box_from_key( ui.curr_cache, ui.active ) - if hot_box != nil { - debug_text("Worksapce Hot Box : %v", hot_box.label.str ) - debug_text("Workspace Hot Range2: %v", hot_box.computed.bounds.pts) - } - if active_box != nil{ - debug_text("Workspace Active Box: %v", active_box.label.str ) + hot_box := ui_box_from_key( ui.curr_cache, ui.hot ) + active_box := ui_box_from_key( ui.curr_cache, ui.active ) + if hot_box != nil { + debug_text("Worksapce Hot Box : %v", hot_box.label.str ) + debug_text("Workspace Hot Range2: %v", hot_box.computed.bounds.pts) + } + if active_box != nil{ + debug_text("Workspace Active Box: %v", active_box.label.str ) + } } ui = & screen_ui - debug_text("Box Count: %v", ui.built_box_count ) + if true + { + debug_text("Box Count: %v", ui.built_box_count ) - hot_box = ui_box_from_key( ui.curr_cache, ui.hot ) - active_box = ui_box_from_key( ui.curr_cache, ui.active ) - if hot_box != nil { - debug_text("Hot Box : %v", hot_box.label.str ) - debug_text("Hot Range2: %v", hot_box.computed.bounds.pts) - } - if active_box != nil{ - debug_text("Active Box: %v", active_box.label.str ) + hot_box := ui_box_from_key( ui.curr_cache, ui.hot ) + active_box := ui_box_from_key( ui.curr_cache, ui.active ) + if hot_box != nil { + debug_text("Hot Box : %v", hot_box.label.str ) + debug_text("Hot Range2: %v", hot_box.computed.bounds.pts) + } + if active_box != nil{ + debug_text("Active Box: %v", active_box.label.str ) + } } view := view_get_bounds() - debug.draw_debug_text_y = 14 // Define the triangle vertices and colors diff --git a/code/sectr/engine/update.odin b/code/sectr/engine/update.odin index 7196558..ae161bb 100644 --- a/code/sectr/engine/update.odin +++ b/code/sectr/engine/update.odin @@ -239,7 +239,7 @@ update :: proc( delta_time : f64 ) -> b32 // test_hover_n_click() // test_draggable() // test_text_box() - test_parenting( & default_layout, & frame_style_default ) + // test_parenting( & default_layout, & frame_style_default ) // test_whitespace_ast( & default_layout, & frame_style_default ) } //endregion Workspace Imgui Tick diff --git a/code/sectr/grime/array.odin b/code/sectr/grime/array.odin index fb63f14..6e1c78f 100644 --- a/code/sectr/grime/array.odin +++ b/code/sectr/grime/array.odin @@ -75,7 +75,7 @@ array_init_reserve :: proc return } -array_append :: proc( self : ^Array( $ Type), value : Type ) -> AllocatorError +array_append_value :: proc( self : ^Array( $ Type), value : Type ) -> AllocatorError { // profile(#procedure) if self.header.num == self.header.capacity @@ -91,11 +91,11 @@ array_append :: proc( self : ^Array( $ Type), value : Type ) -> AllocatorError return AllocatorError.None } -array_append_slice :: proc( using self : ^Array( $ Type ), items : []Type ) -> AllocatorError +array_append_array :: proc( using self: ^Array( $ Type), other : Array(Type)) -> AllocatorError { - if num + len(items) > capacity + if num + other.num > capacity { - grow_result := array_grow( self, capacity ) + grow_result := array_grow( self, num + other.num ) if grow_result != AllocatorError.None { return grow_result } @@ -106,9 +106,31 @@ array_append_slice :: proc( using self : ^Array( $ Type ), items : []Type ) -> A // TODO(Ed) : VERIFY VIA DEBUG THIS COPY IS FINE. target := ptr_offset( data, num ) - copy( slice_ptr(target, capacity - num), items ) + copy( slice_ptr(target, int(capacity - num)), array_to_slice(other) ) - num += len(items) + num += other.num + return AllocatorError.None +} + +array_append_slice :: proc( using self : ^Array( $ Type ), items : []Type ) -> AllocatorError +{ + items_num :=u64(len(items)) + if num + items_num > capacity + { + grow_result := array_grow( self, num + items_num ) + if grow_result != AllocatorError.None { + return grow_result + } + } + + // Note(Ed) : Original code from gencpp + // libc.memcpy( ptr_offset(data, num), raw_data(items), len(items) * size_of(Type) ) + + // TODO(Ed) : VERIFY VIA DEBUG THIS COPY IS FINE. + target := ptr_offset( data, num ) + copy( slice_ptr(target, int(capacity - num)), items ) + + num += items_num return AllocatorError.None } diff --git a/code/sectr/grime/grime.odin b/code/sectr/grime/grime.odin index e9b9cdd..ab19b01 100644 --- a/code/sectr/grime/grime.odin +++ b/code/sectr/grime/grime.odin @@ -107,6 +107,12 @@ add :: proc { add_range2, } +array_append :: proc { + array_append_value, + array_append_array, + array_append_slice, +} + bivec3 :: proc { bivec3_via_f32s, vec3_to_bivec3, diff --git a/code/sectr/input/event.odin b/code/sectr/input/event.odin index 3876406..2432dbc 100644 --- a/code/sectr/input/event.odin +++ b/code/sectr/input/event.odin @@ -1 +1 @@ -package sectr \ No newline at end of file +package sectr diff --git a/code/sectr/input/input.odin b/code/sectr/input/input.odin index bb0f49a..9d72bef 100644 --- a/code/sectr/input/input.odin +++ b/code/sectr/input/input.odin @@ -281,221 +281,13 @@ mouse_world_delta :: #force_inline proc "contextless" () -> Vec2 { InputState :: struct { keyboard : KeyboardState, - mouse : MouseState + mouse : MouseState, + + keyboard_events : KeyboardEvents, } -import "core:os" -import c "core:c/libc" -import rl "vendor:raylib" - -poll_input :: proc( old, new : ^ InputState ) -{ - profile(#procedure) - input_process_digital_btn :: proc( old_state, new_state : ^ DigitalBtn, is_down : b32 ) - { - new_state.ended_down = is_down - had_transition := old_state.ended_down != new_state.ended_down - if had_transition { - new_state.half_transitions += 1 - } - else { - new_state.half_transitions = 0 - } - } - - // Keyboard - { - // profile("Keyboard") - check_range :: proc( old, new : ^ InputState, start, end : i32 ) - { - for id := start; id < end; id += 1 - { - // TODO(Ed) : LOOK OVER THIS... - entry_old := & old.keyboard.keys[id - 1] - entry_new := & new.keyboard.keys[id - 1] - - key_id := cast(KeyboardKey) id - - is_down := cast(b32) rl.IsKeyDown( to_raylib_key(id) ) - input_process_digital_btn( entry_old, entry_new, is_down ) - } - } - - DeadBound_1 :: 0x0A - DeadBound_2 :: 0x2E - DeadBound_3 :: 0x19 - DeadBound_4 :: 0x3F - check_range( old, new, cast(i32) KeyboardKey.enter, DeadBound_1 ) - check_range( old, new, cast(i32) KeyboardKey.caps_lock, DeadBound_2 ) - check_range( old, new, cast(i32) KeyboardKey.escape, DeadBound_3 ) - check_range( old, new, cast(i32) KeyboardKey.backtick, DeadBound_4 ) - check_range( old, new, cast(i32) KeyboardKey.A, cast(i32) KeyboardKey.count ) - } - - // Mouse - { - // profile("Mouse") - // Process Buttons - for id : i32 = 0; id < i32(MouseBtn.count); id += 1 - { - old_btn := & old.mouse.btns[id] - new_btn := & new.mouse.btns[id] - - mouse_id := cast(MouseBtn) id - - is_down := cast(b32) rl.IsMouseButtonDown( to_raylib_mouse_btn(id) ) - input_process_digital_btn( old_btn, new_btn, is_down ) - } - - new.mouse.raw_pos = rl.GetMousePosition() - new.mouse.pos = render_to_screen_pos(new.mouse.raw_pos) - new.mouse.delta = rl.GetMouseDelta() * {1, -1} - new.mouse.vertical_wheel = rl.GetMouseWheelMove() - } -} - -record_input :: proc( replay_file : os.Handle, input : ^ InputState ) { - raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) ) - file_write( replay_file, raw_data ) -} - -play_input :: proc( replay_file : os.Handle, input : ^ InputState ) { - raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) ) - total_read, result_code := file_read( replay_file, raw_data ) - if result_code == os.ERROR_HANDLE_EOF { - file_rewind( replay_file ) - load_snapshot( & Memory_App.snapshot ) - } -} - -to_raylib_key :: proc( key : i32 ) -> rl.KeyboardKey { - @static raylib_key_lookup_table := [?] rl.KeyboardKey { - rl.KeyboardKey.KEY_NULL, - rl.KeyboardKey.ENTER, - rl.KeyboardKey.TAB, - rl.KeyboardKey.SPACE, - rl.KeyboardKey.LEFT_BRACKET, - rl.KeyboardKey.RIGHT_BRACKET, - rl.KeyboardKey.SEMICOLON, - rl.KeyboardKey.APOSTROPHE, - rl.KeyboardKey.COMMA, - rl.KeyboardKey.PERIOD, - cast(rl.KeyboardKey) 0, // 0x0A - cast(rl.KeyboardKey) 0, // 0x0B - cast(rl.KeyboardKey) 0, // 0x0C - cast(rl.KeyboardKey) 0, // 0x0D - cast(rl.KeyboardKey) 0, // 0x0E - cast(rl.KeyboardKey) 0, // 0x0F - rl.KeyboardKey.CAPS_LOCK, - rl.KeyboardKey.SCROLL_LOCK, - rl.KeyboardKey.NUM_LOCK, - rl.KeyboardKey.LEFT_ALT, - rl.KeyboardKey.LEFT_SHIFT, - rl.KeyboardKey.LEFT_CONTROL, - rl.KeyboardKey.RIGHT_ALT, - rl.KeyboardKey.RIGHT_SHIFT, - rl.KeyboardKey.RIGHT_CONTROL, - cast(rl.KeyboardKey) 0, // 0x0F - cast(rl.KeyboardKey) 0, // 0x0F - cast(rl.KeyboardKey) 0, // 0x0F - cast(rl.KeyboardKey) 0, // 0x0F - cast(rl.KeyboardKey) 0, // 0x0F - cast(rl.KeyboardKey) 0, // 0x0F - cast(rl.KeyboardKey) 0, // 0x0F - rl.KeyboardKey.ESCAPE, - rl.KeyboardKey.F1, - rl.KeyboardKey.F2, - rl.KeyboardKey.F3, - rl.KeyboardKey.F4, - rl.KeyboardKey.F5, - rl.KeyboardKey.F7, - rl.KeyboardKey.F8, - rl.KeyboardKey.F9, - rl.KeyboardKey.F10, - rl.KeyboardKey.F11, - rl.KeyboardKey.F12, - rl.KeyboardKey.PRINT_SCREEN, - rl.KeyboardKey.PAUSE, - cast(rl.KeyboardKey) 0, // 0x2E - rl.KeyboardKey.GRAVE, - cast(rl.KeyboardKey) '0', - cast(rl.KeyboardKey) '1', - cast(rl.KeyboardKey) '2', - cast(rl.KeyboardKey) '3', - cast(rl.KeyboardKey) '4', - cast(rl.KeyboardKey) '5', - cast(rl.KeyboardKey) '6', - cast(rl.KeyboardKey) '7', - cast(rl.KeyboardKey) '8', - cast(rl.KeyboardKey) '9', - rl.KeyboardKey.MINUS, - rl.KeyboardKey.EQUAL, - rl.KeyboardKey.BACKSPACE, - rl.KeyboardKey.BACKSLASH, - rl.KeyboardKey.SLASH, - cast(rl.KeyboardKey) 0, // 0x3F - cast(rl.KeyboardKey) 0, // 0x40 - rl.KeyboardKey.A, - rl.KeyboardKey.B, - rl.KeyboardKey.C, - rl.KeyboardKey.D, - rl.KeyboardKey.E, - rl.KeyboardKey.F, - rl.KeyboardKey.G, - rl.KeyboardKey.H, - rl.KeyboardKey.I, - rl.KeyboardKey.J, - rl.KeyboardKey.K, - rl.KeyboardKey.L, - rl.KeyboardKey.M, - rl.KeyboardKey.N, - rl.KeyboardKey.O, - rl.KeyboardKey.P, - rl.KeyboardKey.Q, - rl.KeyboardKey.R, - rl.KeyboardKey.S, - rl.KeyboardKey.T, - rl.KeyboardKey.U, - rl.KeyboardKey.V, - rl.KeyboardKey.W, - rl.KeyboardKey.X, - rl.KeyboardKey.Y, - rl.KeyboardKey.Z, - rl.KeyboardKey.INSERT, - rl.KeyboardKey.DELETE, - rl.KeyboardKey.HOME, - rl.KeyboardKey.END, - rl.KeyboardKey.PAGE_UP, - rl.KeyboardKey.PAGE_DOWN, - rl.KeyboardKey.KP_0, - rl.KeyboardKey.KP_1, - rl.KeyboardKey.KP_2, - rl.KeyboardKey.KP_3, - rl.KeyboardKey.KP_4, - rl.KeyboardKey.KP_5, - rl.KeyboardKey.KP_6, - rl.KeyboardKey.KP_7, - rl.KeyboardKey.KP_8, - rl.KeyboardKey.KP_9, - rl.KeyboardKey.KP_DECIMAL, - rl.KeyboardKey.KP_EQUAL, - rl.KeyboardKey.KP_ADD, - rl.KeyboardKey.KP_SUBTRACT, - rl.KeyboardKey.KP_MULTIPLY, - rl.KeyboardKey.KP_DIVIDE, - rl.KeyboardKey.KP_ENTER } - return raylib_key_lookup_table[ key ] -} - -to_raylib_mouse_btn :: proc( btn : i32 ) -> rl.MouseButton { - @static raylib_mouse_btn_lookup_table := [?] rl.MouseButton { - rl.MouseButton.LEFT, - rl.MouseButton.MIDDLE, - rl.MouseButton.RIGHT, - rl.MouseButton.SIDE, - rl.MouseButton.FORWARD, - rl.MouseButton.BACK, - rl.MouseButton.EXTRA, - } - return raylib_mouse_btn_lookup_table[ btn ] +// TODO(Ed): Whats the lifetime of these events? (So far we're picking per full-frame) +KeyboardEvents :: struct { + keys_pressed : Array(KeyboardKey), + chars_pressed : Array(rune), } diff --git a/code/sectr/input/input_raylib.odin b/code/sectr/input/input_raylib.odin new file mode 100644 index 0000000..c03f093 --- /dev/null +++ b/code/sectr/input/input_raylib.odin @@ -0,0 +1,350 @@ +package sectr + +import "base:runtime" +import "core:os" +import c "core:c/libc" +import rl "vendor:raylib" + +poll_input :: proc( old, new : ^ InputState ) +{ + profile(#procedure) + input_process_digital_btn :: proc( old_state, new_state : ^ DigitalBtn, is_down : b32 ) + { + new_state.ended_down = is_down + had_transition := old_state.ended_down != new_state.ended_down + if had_transition { + new_state.half_transitions += 1 + } + else { + new_state.half_transitions = 0 + } + } + + // Keyboard + { + // profile("Keyboard") + check_range :: proc( old, new : ^ InputState, start, end : i32 ) + { + for id := start; id < end; id += 1 + { + // TODO(Ed) : LOOK OVER THIS... + entry_old := & old.keyboard.keys[id - 1] + entry_new := & new.keyboard.keys[id - 1] + + key_id := cast(KeyboardKey) id + + is_down := cast(b32) rl.IsKeyDown( to_raylib_key(id) ) + input_process_digital_btn( entry_old, entry_new, is_down ) + } + } + + DeadBound_1 :: 0x0A + DeadBound_2 :: 0x2E + DeadBound_3 :: 0x19 + DeadBound_4 :: 0x3F + check_range( old, new, cast(i32) KeyboardKey.enter, DeadBound_1 ) + check_range( old, new, cast(i32) KeyboardKey.caps_lock, DeadBound_2 ) + check_range( old, new, cast(i32) KeyboardKey.escape, DeadBound_3 ) + check_range( old, new, cast(i32) KeyboardKey.backtick, DeadBound_4 ) + check_range( old, new, cast(i32) KeyboardKey.A, cast(i32) KeyboardKey.count ) + + swap( & old.keyboard_events.keys_pressed, & new.keyboard_events.keys_pressed ) + swap( & old.keyboard_events.chars_pressed, & new.keyboard_events.chars_pressed ) + + array_clear( new.keyboard_events.keys_pressed ) + array_clear( new.keyboard_events.chars_pressed ) + + for key_pressed := rl.GetKeyPressed(); key_pressed != rl.KeyboardKey.KEY_NULL; key_pressed = rl.GetKeyPressed() { + array_append( & new.keyboard_events.keys_pressed, to_key_from_raylib(key_pressed)) + } + for char_pressed := rl.GetCharPressed(); char_pressed != cast(rune)0; char_pressed = rl.GetCharPressed() { + array_append( & new.keyboard_events.chars_pressed, char_pressed) + } + } + + // Mouse + { + // profile("Mouse") + // Process Buttons + for id : i32 = 0; id < i32(MouseBtn.count); id += 1 + { + old_btn := & old.mouse.btns[id] + new_btn := & new.mouse.btns[id] + + mouse_id := cast(MouseBtn) id + + is_down := cast(b32) rl.IsMouseButtonDown( to_raylib_mouse_btn(id) ) + input_process_digital_btn( old_btn, new_btn, is_down ) + } + + new.mouse.raw_pos = rl.GetMousePosition() + new.mouse.pos = render_to_screen_pos(new.mouse.raw_pos) + new.mouse.delta = rl.GetMouseDelta() * {1, -1} + new.mouse.vertical_wheel = rl.GetMouseWheelMove() + } +} + +record_input :: proc( replay_file : os.Handle, input : ^ InputState ) { + raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) ) + file_write( replay_file, raw_data ) +} + +play_input :: proc( replay_file : os.Handle, input : ^ InputState ) { + raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) ) + total_read, result_code := file_read( replay_file, raw_data ) + if result_code == os.ERROR_HANDLE_EOF { + file_rewind( replay_file ) + load_snapshot( & Memory_App.snapshot ) + } +} + +to_key_from_raylib :: proc( key : rl.KeyboardKey ) -> KeyboardKey { + @static lookup_table := [?] KeyboardKey { + KeyboardKey.null, + KeyboardKey.enter, + KeyboardKey.tab, + KeyboardKey.space, + KeyboardKey.bracket_open, + KeyboardKey.bracket_close, + KeyboardKey.semicolon, + KeyboardKey.apostrophe, + KeyboardKey.comma, + KeyboardKey.period, + cast(KeyboardKey) 0, // 0x0A + cast(KeyboardKey) 0, // 0x0B + cast(KeyboardKey) 0, // 0x0C + cast(KeyboardKey) 0, // 0x0D + cast(KeyboardKey) 0, // 0x0E + cast(KeyboardKey) 0, // 0x0F + KeyboardKey.caps_lock, + KeyboardKey.scroll_lock, + KeyboardKey.num_lock, + KeyboardKey.left_alt, + KeyboardKey.left_shit, + KeyboardKey.left_control, + KeyboardKey.right_alt, + KeyboardKey.right_shift, + KeyboardKey.right_control, + cast(KeyboardKey) 0, // 0x0F + cast(KeyboardKey) 0, // 0x0F + cast(KeyboardKey) 0, // 0x0F + cast(KeyboardKey) 0, // 0x0F + cast(KeyboardKey) 0, // 0x0F + cast(KeyboardKey) 0, // 0x0F + cast(KeyboardKey) 0, // 0x0F + KeyboardKey.escape, + KeyboardKey.F1, + KeyboardKey.F2, + KeyboardKey.F3, + KeyboardKey.F4, + KeyboardKey.F5, + KeyboardKey.F7, + KeyboardKey.F8, + KeyboardKey.F9, + KeyboardKey.F10, + KeyboardKey.F11, + KeyboardKey.F12, + KeyboardKey.print_screen, + KeyboardKey.pause, + cast(KeyboardKey) 0, // 0x2E + KeyboardKey.backtick, + KeyboardKey.nrow_0, + KeyboardKey.nrow_1, + KeyboardKey.nrow_2, + KeyboardKey.nrow_3, + KeyboardKey.nrow_4, + KeyboardKey.nrow_5, + KeyboardKey.nrow_6, + KeyboardKey.nrow_7, + KeyboardKey.nrow_8, + KeyboardKey.nrow_9, + KeyboardKey.hyphen, + KeyboardKey.equals, + KeyboardKey.backspace, + KeyboardKey.backslash, + KeyboardKey.slash, + cast(KeyboardKey) 0, // 0x3F + cast(KeyboardKey) 0, // 0x40 + KeyboardKey.A, + KeyboardKey.B, + KeyboardKey.C, + KeyboardKey.D, + KeyboardKey.E, + KeyboardKey.F, + KeyboardKey.G, + KeyboardKey.H, + KeyboardKey.I, + KeyboardKey.J, + KeyboardKey.K, + KeyboardKey.L, + KeyboardKey.M, + KeyboardKey.N, + KeyboardKey.O, + KeyboardKey.P, + KeyboardKey.Q, + KeyboardKey.R, + KeyboardKey.S, + KeyboardKey.T, + KeyboardKey.U, + KeyboardKey.V, + KeyboardKey.W, + KeyboardKey.X, + KeyboardKey.Y, + KeyboardKey.Z, + KeyboardKey.insert, + KeyboardKey.delete, + KeyboardKey.home, + KeyboardKey.end, + KeyboardKey.page_up, + KeyboardKey.page_down, + KeyboardKey.npad_0, + KeyboardKey.npad_1, + KeyboardKey.npad_2, + KeyboardKey.npad_3, + KeyboardKey.npad_4, + KeyboardKey.npad_5, + KeyboardKey.npad_6, + KeyboardKey.npad_7, + KeyboardKey.npad_8, + KeyboardKey.npad_9, + KeyboardKey.npad_decimal, + KeyboardKey.npad_equals, + KeyboardKey.npad_plus, + KeyboardKey.npad_minus, + KeyboardKey.npad_multiply, + KeyboardKey.npad_divide, + KeyboardKey.npad_enter, } + return lookup_table[key] +} + +to_raylib_key :: proc( key : i32 ) -> rl.KeyboardKey +{ + @static raylib_key_lookup_table := [?] rl.KeyboardKey { + rl.KeyboardKey.KEY_NULL, + rl.KeyboardKey.ENTER, + rl.KeyboardKey.TAB, + rl.KeyboardKey.SPACE, + rl.KeyboardKey.LEFT_BRACKET, + rl.KeyboardKey.RIGHT_BRACKET, + rl.KeyboardKey.SEMICOLON, + rl.KeyboardKey.APOSTROPHE, + rl.KeyboardKey.COMMA, + rl.KeyboardKey.PERIOD, + cast(rl.KeyboardKey) 0, // 0x0A + cast(rl.KeyboardKey) 0, // 0x0B + cast(rl.KeyboardKey) 0, // 0x0C + cast(rl.KeyboardKey) 0, // 0x0D + cast(rl.KeyboardKey) 0, // 0x0E + cast(rl.KeyboardKey) 0, // 0x0F + rl.KeyboardKey.CAPS_LOCK, + rl.KeyboardKey.SCROLL_LOCK, + rl.KeyboardKey.NUM_LOCK, + rl.KeyboardKey.LEFT_ALT, + rl.KeyboardKey.LEFT_SHIFT, + rl.KeyboardKey.LEFT_CONTROL, + rl.KeyboardKey.RIGHT_ALT, + rl.KeyboardKey.RIGHT_SHIFT, + rl.KeyboardKey.RIGHT_CONTROL, + cast(rl.KeyboardKey) 0, // 0x0F + cast(rl.KeyboardKey) 0, // 0x0F + cast(rl.KeyboardKey) 0, // 0x0F + cast(rl.KeyboardKey) 0, // 0x0F + cast(rl.KeyboardKey) 0, // 0x0F + cast(rl.KeyboardKey) 0, // 0x0F + cast(rl.KeyboardKey) 0, // 0x0F + rl.KeyboardKey.ESCAPE, + rl.KeyboardKey.F1, + rl.KeyboardKey.F2, + rl.KeyboardKey.F3, + rl.KeyboardKey.F4, + rl.KeyboardKey.F5, + rl.KeyboardKey.F7, + rl.KeyboardKey.F8, + rl.KeyboardKey.F9, + rl.KeyboardKey.F10, + rl.KeyboardKey.F11, + rl.KeyboardKey.F12, + rl.KeyboardKey.PRINT_SCREEN, + rl.KeyboardKey.PAUSE, + cast(rl.KeyboardKey) 0, // 0x2E + rl.KeyboardKey.GRAVE, + cast(rl.KeyboardKey) '0', + cast(rl.KeyboardKey) '1', + cast(rl.KeyboardKey) '2', + cast(rl.KeyboardKey) '3', + cast(rl.KeyboardKey) '4', + cast(rl.KeyboardKey) '5', + cast(rl.KeyboardKey) '6', + cast(rl.KeyboardKey) '7', + cast(rl.KeyboardKey) '8', + cast(rl.KeyboardKey) '9', + rl.KeyboardKey.MINUS, + rl.KeyboardKey.EQUAL, + rl.KeyboardKey.BACKSPACE, + rl.KeyboardKey.BACKSLASH, + rl.KeyboardKey.SLASH, + cast(rl.KeyboardKey) 0, // 0x3F + cast(rl.KeyboardKey) 0, // 0x40 + rl.KeyboardKey.A, + rl.KeyboardKey.B, + rl.KeyboardKey.C, + rl.KeyboardKey.D, + rl.KeyboardKey.E, + rl.KeyboardKey.F, + rl.KeyboardKey.G, + rl.KeyboardKey.H, + rl.KeyboardKey.I, + rl.KeyboardKey.J, + rl.KeyboardKey.K, + rl.KeyboardKey.L, + rl.KeyboardKey.M, + rl.KeyboardKey.N, + rl.KeyboardKey.O, + rl.KeyboardKey.P, + rl.KeyboardKey.Q, + rl.KeyboardKey.R, + rl.KeyboardKey.S, + rl.KeyboardKey.T, + rl.KeyboardKey.U, + rl.KeyboardKey.V, + rl.KeyboardKey.W, + rl.KeyboardKey.X, + rl.KeyboardKey.Y, + rl.KeyboardKey.Z, + rl.KeyboardKey.INSERT, + rl.KeyboardKey.DELETE, + rl.KeyboardKey.HOME, + rl.KeyboardKey.END, + rl.KeyboardKey.PAGE_UP, + rl.KeyboardKey.PAGE_DOWN, + rl.KeyboardKey.KP_0, + rl.KeyboardKey.KP_1, + rl.KeyboardKey.KP_2, + rl.KeyboardKey.KP_3, + rl.KeyboardKey.KP_4, + rl.KeyboardKey.KP_5, + rl.KeyboardKey.KP_6, + rl.KeyboardKey.KP_7, + rl.KeyboardKey.KP_8, + rl.KeyboardKey.KP_9, + rl.KeyboardKey.KP_DECIMAL, + rl.KeyboardKey.KP_EQUAL, + rl.KeyboardKey.KP_ADD, + rl.KeyboardKey.KP_SUBTRACT, + rl.KeyboardKey.KP_MULTIPLY, + rl.KeyboardKey.KP_DIVIDE, + rl.KeyboardKey.KP_ENTER, } + return raylib_key_lookup_table[ key ] +} + +to_raylib_mouse_btn :: proc( btn : i32 ) -> rl.MouseButton { + @static raylib_mouse_btn_lookup_table := [?] rl.MouseButton { + rl.MouseButton.LEFT, + rl.MouseButton.MIDDLE, + rl.MouseButton.RIGHT, + rl.MouseButton.SIDE, + rl.MouseButton.FORWARD, + rl.MouseButton.BACK, + rl.MouseButton.EXTRA, } + return raylib_mouse_btn_lookup_table[ btn ] +} diff --git a/code/sectr/math/math.odin b/code/sectr/math/math.odin index 8bc92bf..b4b06e8 100644 --- a/code/sectr/math/math.odin +++ b/code/sectr/math/math.odin @@ -101,6 +101,8 @@ Range2 :: struct #raw_union { UnitRange2 :: distinct Range2 +range2_zero :: Range2 {} + range2 :: #force_inline proc "contextless" ( a, b : Vec2 ) -> Range2 { result := Range2 { pts = { a, b } } return result diff --git a/code/sectr/ui/core/Readme.md b/code/sectr/ui/core/Readme.md new file mode 100644 index 0000000..454e1d0 --- /dev/null +++ b/code/sectr/ui/core/Readme.md @@ -0,0 +1,7 @@ +# Plans + +Eventually want to generalize this core UI as its own library. +This will keep track of here whats needed for it to work wihtout the rest of this codebase. + +* Provide UI input state in its own data stucture at the beginning of `ui_build_graph`: +* \ No newline at end of file diff --git a/code/sectr/ui/core_box.odin b/code/sectr/ui/core/box.odin similarity index 95% rename from code/sectr/ui/core_box.odin rename to code/sectr/ui/core/box.odin index b631ed5..65cbd95 100644 --- a/code/sectr/ui/core_box.odin +++ b/code/sectr/ui/core/box.odin @@ -15,6 +15,10 @@ UI_BoxFlag :: enum u64 UI_BoxFlags :: bit_set[UI_BoxFlag; u64] // UI_BoxFlag_Scroll :: UI_BoxFlags { .Scroll_X, .Scroll_Y } +UI_NavLinks :: struct { + left, right, up, down : ^UI_Box, +} + UI_RenderBoxInfo :: struct { using computed : UI_Computed, using style : UI_Style, @@ -26,12 +30,15 @@ UI_RenderBoxInfo :: struct { UI_Box :: struct { // Cache ID key : UI_Key, - // label : string, label : StrRunesPair, text : StrRunesPair, // Regenerated per frame. + nav : UI_NavLinks, + // signal_callback : #type proc(), + + // first, last : The first and last child of this box // prev, next : The adjacent neighboring boxes who are children of to the same parent using links : DLL_NodeFull( UI_Box ), @@ -129,7 +136,7 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) { // Check to make sure parent is present on the screen, if its not don't bother. is_app_ui := ui_context == & screen_ui - if intersects_range2( view_get_bounds(), box.computed.bounds) + if intersects_range2( ui_view_bounds(), box.computed.bounds) { return box.first } diff --git a/code/sectr/ui/core_layout.odin b/code/sectr/ui/core/layout.odin similarity index 96% rename from code/sectr/ui/core_layout.odin rename to code/sectr/ui/core/layout.odin index bf2b3ce..234da98 100644 --- a/code/sectr/ui/core_layout.odin +++ b/code/sectr/ui/core/layout.odin @@ -16,13 +16,17 @@ LayoutAlign_OriginTL_Bottom :: Vec2{0.5, 1} LayoutAlign_OriginTL_BottomLeft :: Vec2{ 0, 1} LayoutAlign_OriginTL_BottomRight :: Vec2{ 1, 1} -// LayoutAlign_OriginTL_ Layout_OriginCenter_Centered :: Vec2{0.5, 0.5} - - - +UI_Align_Presets_Struct :: struct { + origin_tl_centered : Vec2, + text_centered : Vec2, +} +UI_Align_Presets :: UI_Align_Presets_Struct { + origin_tl_centered = {0.5, 0.5}, + text_centered = {0.5, 0.5}, +} // The UI_Box's actual positioning and sizing diff --git a/code/sectr/ui/core_layout_compute.odin b/code/sectr/ui/core/layout_compute.odin similarity index 86% rename from code/sectr/ui/core_layout_compute.odin rename to code/sectr/ui/core/layout_compute.odin index 8f22de7..6d1dc86 100644 --- a/code/sectr/ui/core_layout_compute.odin +++ b/code/sectr/ui/core/layout_compute.odin @@ -85,9 +85,16 @@ ui_box_compute_layout :: proc( box : ^UI_Box, if .Scale_Width_By_Height_Ratio in layout.flags { adjusted_size.x = adjusted_size.y * layout.size.min.x } + else if .Fixed_Width in layout.flags { + adjusted_size.x = layout.size.min.x + } + if .Scale_Height_By_Width_Ratio in layout.flags { adjusted_size.y = adjusted_size.x * layout.size.min.y } + else if .Fixed_Height in layout.flags { + adjusted_size.y = layout.size.min.y + } if .Size_To_Content in layout.flags { // Preemtively traverse the children of this parent and have them compute their layout. @@ -97,14 +104,6 @@ ui_box_compute_layout :: proc( box : ^UI_Box, //ui_compute_children_bounding_area(box) } - // TODO(Ed): Should this force override all of the previous auto-sizing possible? - if .Fixed_Width in layout.flags { - adjusted_size.x = layout.size.min.x - } - if .Fixed_Height in layout.flags { - adjusted_size.y = layout.size.min.y - } - // 5. Determine relative position origin_center := margined_bounds_origin @@ -186,34 +185,3 @@ ui_box_compute_layout_children :: proc( box : ^UI_Box ) } } -ui_core_compute_layout :: proc( ui : ^UI_State ) -{ - profile(#procedure) - state := get_state() - - root := ui.root - { - computed := & root.computed - style := root.style - layout := & root.layout - if ui == & state.screen_ui { - computed.bounds.min = transmute(Vec2) state.app_window.extent * -1 - computed.bounds.max = transmute(Vec2) state.app_window.extent - } - computed.content = computed.bounds - } - - for current := root.first; current != nil; current = ui_box_tranverse_next( current ) - { - if ! current.computed.fresh { - ui_box_compute_layout( current ) - } - array_append( & ui.render_queue, UI_RenderBoxInfo { - current.computed, - current.style, - current.text, - current.layout.font_size, - current.layout.border_width, - }) - } -} diff --git a/code/sectr/ui/core_signal.odin b/code/sectr/ui/core/signal.odin similarity index 90% rename from code/sectr/ui/core_signal.odin rename to code/sectr/ui/core/signal.odin index 741e930..a216cee 100644 --- a/code/sectr/ui/core_signal.odin +++ b/code/sectr/ui/core/signal.odin @@ -70,13 +70,13 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas mouse_clickable := UI_BoxFlag.Mouse_Clickable in box.flags keyboard_clickable := UI_BoxFlag.Keyboard_Clickable in box.flags + is_focusable := .Focusable in box.flags - was_hot := (box.hot_delta > 0) was_active := (ui.active == box.key) && (box.active_delta > 0) + was_hot := (box.hot_delta > 0) was_disabled := box.disabled_delta > 0 - // if was_hot { - // runtime.debug_trap() - // } + + is_focused_locked := is_focusable ? was_active : false // Check to see if this box is active if mouse_clickable && signal.cursor_over && left_pressed && was_hot @@ -93,14 +93,14 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas // TODO(Ed) : Support double-click detection } - if mouse_clickable && ! signal.cursor_over && left_released + if ! is_focused_locked && was_active && mouse_clickable && ! signal.cursor_over && left_released { box.active_delta = 0 ui.active = UI_Key(0) ui.active_mouse[MouseBtn.Left] = UI_Key(0) - signal.released = true + signal.released = true } if keyboard_clickable @@ -109,6 +109,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas } // TODO(Ed): Should panning and scrolling get supported here? (problably not...) + // We can just report the scroll amount in the signal and have the scrollbox widget handle it // TODO(Ed) : Add scrolling support // if UI_BoxFlag.Scroll_X in box.flags { @@ -123,39 +124,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas // if UI_BoxFlag.Pan_Y in box.flags { // } - is_disabled := UI_BoxFlag.Disabled in box.flags - is_hot := ui.hot == box.key - is_active := ui.active == box.key - - // TODO(Ed): It should be able to enter hot without mouse_clickable - if mouse_clickable && signal.cursor_over && ! is_disabled - { - hot_vacant := ui.hot == UI_Key(0) - active_vacant := ui.active == UI_Key(0) - // (active_vacant is_active) - if signal.cursor_over && active_vacant - { - if ! hot_vacant { - prev := ui_box_from_key( ui.curr_cache, ui.hot ) - prev.hot_delta = 0 - } - // prev_hot := zpl_hmap_get( ui.prev_cache, u64(ui.hot) ) - // prev_hot_label := prev_hot != nil ? prev_hot.label.str : "" - // log( str_fmt_tmp("Detected HOT via CURSOR OVER: %v is_hot: %v is_active: %v prev_hot: %v", box.label.str, is_hot, is_active, prev_hot_label )) - ui.hot = box.key - is_hot = true - - ui.hot_start_style = box.style - } - } - else if ! signal.cursor_over && was_hot - { - ui.hot = UI_Key(0) - is_hot = false - box.hot_delta = 0 - } - - if mouse_clickable && signal.cursor_over && left_released + if ! is_focused_locked && was_active && mouse_clickable && signal.cursor_over && left_released { box.active_delta = 0 @@ -169,6 +138,39 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas ui.last_clicked = box.key } } + + is_disabled := UI_BoxFlag.Disabled in box.flags + is_hot := ui.hot == box.key + is_active := ui.active == box.key + + // TODO(Ed): It should be able to enter hot without mouse_clickable + if mouse_clickable && signal.cursor_over && ! is_disabled + { + hot_vacant := ui.hot == UI_Key(0) + // active_vacant := ui.active == UI_Key(0) + if signal.cursor_over //&& active_vacant + { + if ! hot_vacant { + prev := ui_box_from_key( ui.curr_cache, ui.hot ) + prev.hot_delta = 0 + } + // prev_hot := zpl_hmap_get( ui.prev_cache, u64(ui.hot) ) + // prev_hot_label := prev_hot != nil ? prev_hot.label.str : "" + // log( str_fmt_tmp("Detected HOT via CURSOR OVER: %v is_hot: %v is_active: %v prev_hot: %v", box.label.str, is_hot, is_active, prev_hot_label )) + ui.hot = box.key + is_hot = true + + ui.hot_start_style = box.style + signal.hot = true + } + } + else if ! signal.cursor_over && was_hot + { + ui.hot = UI_Key(0) + is_hot = false + box.hot_delta = 0 + } + // profile_end() // State Deltas update diff --git a/code/sectr/ui/core.odin b/code/sectr/ui/core/state.odin similarity index 85% rename from code/sectr/ui/core.odin rename to code/sectr/ui/core/state.odin index 910fe48..3ce5b85 100644 --- a/code/sectr/ui/core.odin +++ b/code/sectr/ui/core/state.odin @@ -206,14 +206,55 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} ) ui_parent_push(root) } +ui_core_compute_layout :: proc( ui : ^UI_State ) +{ + profile(#procedure) + state := get_state() + + + + +} + + ui_graph_build_end :: proc( ui : ^UI_State ) { profile(#procedure) + state := get_state() ui_parent_pop() // Should be ui_context.root - // Regenerate the computed layout if dirty - ui_compute_layout( ui ) + Post_Build_Graph_Traversal: + { + root := ui.root + { + computed := & root.computed + style := root.style + layout := & root.layout + if ui == & state.screen_ui { + computed.bounds.min = transmute(Vec2) state.app_window.extent * -1 + computed.bounds.max = transmute(Vec2) state.app_window.extent + } + computed.content = computed.bounds + } + for current := root.first; current != nil; current = ui_box_tranverse_next( current ) + { + + + if ! current.computed.fresh { + ui_box_compute_layout( current ) + } + + // Enqueue for rendering + array_append( & ui.render_queue, UI_RenderBoxInfo { + current.computed, + current.style, + current.text, + current.layout.font_size, + current.layout.border_width, + }) + } + } get_state().ui_context = nil } @@ -259,4 +300,14 @@ ui_top_ancestor :: #force_inline proc "contextless" ( box : ^UI_Box ) -> (^UI_Bo return ancestor } +ui_view_bounds :: #force_inline proc "contextless" () -> (range : Range2) { + using state := get_state(); + if ui_context == & screen_ui { + return screen_get_bounds() + } + else { + return view_get_bounds() + } +} + ui_context :: #force_inline proc() -> ^UI_State { return get_state().ui_context } diff --git a/code/sectr/ui/core_style.odin b/code/sectr/ui/core/style.odin similarity index 100% rename from code/sectr/ui/core_style.odin rename to code/sectr/ui/core/style.odin diff --git a/code/sectr/ui/theme.odin b/code/sectr/ui/core/theme.odin similarity index 100% rename from code/sectr/ui/theme.odin rename to code/sectr/ui/core/theme.odin diff --git a/code/sectr/ui/floating.odin b/code/sectr/ui/floating.odin index 21d9b5a..a57e87f 100644 --- a/code/sectr/ui/floating.odin +++ b/code/sectr/ui/floating.odin @@ -117,20 +117,6 @@ ui_floating_build :: proc() } lookup.queued = true dll_full_push_back(floating, lookup, nil ) - // if first == nil { - // first = lookup - // last = lookup - // continue - // } - // if first == last { - // last = lookup - // last.prev = first - // first.next = last - // continue - // } - // last.next = lookup - // lookup.prev = last - // last = lookup } array_clear(build_queue) @@ -140,28 +126,7 @@ ui_floating_build :: proc() if ! entry.queued { ensure(false, "There should be no queue failures yet") - - if entry == first - { - first = entry.next - entry.next = nil - continue - } - if entry == last - { - last = last.prev - last.prev = nil - entry.prev = nil - continue - } - - left := entry.prev - right := entry.next - - left.next = right - right.prev = left - entry.prev = nil - entry.next = nil + dll_full_pop(to_raise, floating) } if entry.builder( entry.captures ) && entry != last && to_raise == nil diff --git a/code/sectr/ui/layout_widget.odin b/code/sectr/ui/layout_widget.odin index 4d5a986..dabc1d2 100644 --- a/code/sectr/ui/layout_widget.odin +++ b/code/sectr/ui/layout_widget.odin @@ -1,5 +1,7 @@ package sectr +import "base:runtime" + /* Widget Layout Ops */ @@ -13,6 +15,7 @@ ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_Lay else { container_width = container.computed.content.max.x - container.computed.content.min.x } + container_height := container.computed.content.max.y - container.computed.content.min.y // do layout calculations for the children total_stretch_ratio : f32 = 0.0 @@ -21,16 +24,13 @@ ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_Lay { using child.layout scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags) + if .Scale_Width_By_Height_Ratio in flags + { + size_req_children += size.min.x * container_height + continue + } if .Fixed_Width in flags { - if scaled_width_by_height { - height := size.max.y != 0 ? size.max.y : container_width - width := height * size.min.x - - size_req_children += width - continue - } - size_req_children += size.min.x continue } @@ -40,32 +40,45 @@ ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_Lay avail_flex_space := container_width - size_req_children - allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space : f32 ) + allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space, container_height : f32 ) -> (space_allocated : f32) { using child.layout - if ! (.Fixed_Width in flags) { - size.min.x = anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space - child.layout.margins.left - child.layout.margins.right + if .Scale_Width_By_Height_Ratio in flags { + size.min.y = container_height + space_allocated = size.min.x * container_height } + else if ! (.Fixed_Width in flags) { + size.min.x = anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space + space_allocated = size.min.x + } + else { + space_allocated = size.min.x + } + space_allocated -= child.layout.margins.left - child.layout.margins.right + size.min.x -= child.layout.margins.left - child.layout.margins.right flags |= {.Fixed_Width} + return } space_used : f32 = 0.0 switch direction{ case .Right_To_Left: for child := container.last; child != nil; child = child.prev { - allocate_space(child, total_stretch_ratio, avail_flex_space) using child.layout - anchor = range2({0, 0}, {0, 0}) - pos.x = space_used - space_used += size.min.x + child.layout.margins.left + child.layout.margins.right + child_width := allocate_space(child, total_stretch_ratio, avail_flex_space, container_height) + anchor = range2({0, 0}, {0, 0}) + width : f32 + pos.x = space_used + space_used += child_width + child.layout.margins.left + child.layout.margins.right } case .Left_To_Right: for child := container.first; child != nil; child = child.next { - allocate_space(child, total_stretch_ratio, avail_flex_space) using child.layout - anchor = range2({0, 0}, {0, 0}) - pos.x = space_used - space_used += size.min.x + child.layout.margins.left + child.layout.margins.right + child_width := allocate_space(child, total_stretch_ratio, avail_flex_space, container_height) + anchor = range2({0, 0}, {0, 0}) + width : f32 + pos.x = space_used + space_used += child_width + child.layout.margins.left + child.layout.margins.right } } } @@ -123,9 +136,8 @@ ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_Layou allocate_space(child, total_stretch_ratio, avail_flex_space) using child.layout anchor = range2({0,0}, {0, 0}) - // alignment = {0, 0} pos.y = -space_used - space_used += size.min.y + space_used += size.min.y + child.layout.margins.top + child.layout.margins.bottom size.min.x = container.computed.content.max.x + container.computed.content.min.x } case .Top_To_Bottom: @@ -133,9 +145,8 @@ ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_Layou allocate_space(child, total_stretch_ratio, avail_flex_space) using child.layout anchor = range2({0, 0}, {0, 0}) - // alignment = {0, 0} pos.y = -space_used - space_used += size.min.y + space_used += size.min.y + child.layout.margins.top + child.layout.margins.bottom size.min.x = container.computed.content.max.x - container.computed.content.min.x } } diff --git a/code/sectr/ui/widgets.odin b/code/sectr/ui/widgets.odin index e093a25..b37967f 100644 --- a/code/sectr/ui/widgets.odin +++ b/code/sectr/ui/widgets.odin @@ -17,7 +17,7 @@ ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget) ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widget) { - btn_flags := UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus } + btn_flags := UI_BoxFlags { .Mouse_Clickable } btn.box = ui_box_make( btn_flags | flags, label ) btn.signal = ui_signal_from_box( btn.box ) return @@ -197,6 +197,7 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, } else { + app_color := app_color_theme() layout := UI_Layout { flags = flags, anchor = range2({},{}), @@ -215,7 +216,7 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, corner_radii = {5, 0, 0, 0}, blur_size = 0, font = get_state().default_font, - text_color = Color_ThmDark_Text_Default, + text_color = app_color.text_default, cursor = {}, } layout_combo = to_ui_layout_combo(layout) @@ -223,12 +224,12 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, { using layout_combo.hot using style_combo.hot - bg_color = Color_ThmDark_ResizeHandle_Hot + bg_color = app_color.resize_hndl_hot } { using layout_combo.active using style_combo.active - bg_color = Color_ThmDark_ResizeHandle_Active + bg_color = app_color.resize_hndl_active } } theme := UI_Theme { @@ -364,7 +365,7 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, } was_dragging = true } - else if released && was_dragging + else if released// && was_dragging { // This needed to be added as for some reason, this was getting called in screen_ui even when we were resizing with a handle in a worksapce if active_context != ui do return false diff --git a/ols.json b/ols.json index b7cf8de..d93e3b7 100644 --- a/ols.json +++ b/ols.json @@ -1,13 +1,21 @@ { "$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json", + "odin_command": "C:/projects/SectrPrototype/toolchain/Odin/odin.exe", "collections": [ + { + "name": "sectr_host", + "path": "C:/projects/SectrPrototype/code/host" + }, + { + "name": "sectr", + "path": "C:/projects/SectrPrototype/code/sectr" + }, { "name": "thirdparty", "path": "C:/projects/SectrPrototype/thirdparty" } ], - "odin_command": "C:/projects/SectrPrototype/toolchain/Odin/odin.exe", - "enable_document_symbols": true, + "enable_document_symbols": false, "enable_fake_methods": true, "enable_format": false, "enable_hover": true, @@ -18,5 +26,6 @@ "enable_inlay_hints": true, "enable_procedure_context": true, "enable_procedure_snippet": false, + "verbose": true, "disable_parser_errors": false } diff --git a/scripts/gen_flattened_codebase.ps1 b/scripts/gen_flattened_codebase.ps1 new file mode 100644 index 0000000..d4fa41c --- /dev/null +++ b/scripts/gen_flattened_codebase.ps1 @@ -0,0 +1,84 @@ +cls +Write-Host "Reverse Build.ps1" + +$ps_misc = join-path $PSScriptRoot 'helpers/misc.ps1' +. $ps_misc + +$path_root = git rev-parse --show-toplevel +$path_code = Join-Path $path_root 'code' +$path_code_flattened = Join-Path $path_root 'code_flattened' + +if (Test-Path $path_code_flattened) { + Remove-Item -Path $path_code_flattened -Recurse -Force -ErrorAction Ignore +} +New-Item -ItemType Directory -Path $path_code_flattened + +# $whitelist_package = 'sectr' + +function get-flattened-package +{ + param( + [string]$pkg_name, + [string]$path_pkg_dir, + [string]$path_flattend_dir + ) + $files = Get-ChildItem -Path $path_pkg_dir -File -Recurse + foreach ($file in $files) + { + if ($file.Name -eq '.ODIN_MONOLITHIC_PACKAGE') { + continue + } + + # Read the file line by line to determine the package name, ignoring comments + $package_name = $null + Get-Content -Path $file.FullName | ForEach-Object { + if ($_ -notmatch '^\s*//') + { + if ($_ -match '^package\s+(\w+)$') { + $package_name = $Matches[1] + return $false + } + } + } + + if ($pacakge_name -ne $pkg_name) { + Write-Host "Warning: The file $($file.FullName) does not contain a valid package declaration." + } + + # Calculate relative path and prepend directory names to the file name + $relative_path = $file.FullName.Substring($path_pkg_dir.Length + 1) + $relative_dir = Split-Path $relative_path -Parent + $relative_dir = $relative_dir.Replace('\', '_').Replace('/', '_') + + if ($relative_dir -ne '') { + $target_file_name = "$relative_dir" + "_" + $file.Name + } else { + $target_file_name = $file.Name + } + + $target_file_path = Join-Path $path_flattend_dir $target_file_name + + if (-not (Test-Path $target_file_path)) + { New-Item -ItemType SymbolicLink -Path $target_file_path -Value $file.FullName } + else + { Write-Host "Warning: The link for $($file.FullName) already exists at $target_file_path. Skipping..." } + } +} + +$path_pkg_gen = join-path $path_code 'gen' +$path_pkg_host = join-path $path_code 'host' +$path_pkg_sectr = join-path $path_code 'sectr' + +$path_flattend_gen = join-path $path_code_flattened 'gen' +$path_flattend_host = join-path $path_code_flattened ' host' +$path_flattend_sectr = join-path $path_code_flattened ' sectr' + +verify-path $path_flattend_gen +verify-path $path_flattend_host +verify-path $path_flattend_sectr + +get-flattened-package 'gen' $path_pkg_gen $path_flattend_gen +get-flattened-package 'host' $path_pkg_host $path_flattend_host +get-flattened-package 'sectr' $path_pkg_sectr $path_flattend_sectr + +Write-Host "Flattened directory structure for packages created successfully." diff --git a/scripts/helpers/misc.ps1 b/scripts/helpers/misc.ps1 new file mode 100644 index 0000000..bba724c --- /dev/null +++ b/scripts/helpers/misc.ps1 @@ -0,0 +1,6 @@ +function verify-path { param( $path ) + if (test-path $path) {return $true} + + new-item -ItemType Directory -Path $path + return $false +} diff --git a/toolchain/Odin b/toolchain/Odin index 67ae530..389e12a 160000 --- a/toolchain/Odin +++ b/toolchain/Odin @@ -1 +1 @@ -Subproject commit 67ae530d22c116f86d476743b5dfc978255391d4 +Subproject commit 389e12a78580362cae32d522ac021d74187d02b5