From: Brendan Hansen Date: Wed, 6 Oct 2021 19:44:27 +0000 (-0500) Subject: refactored general purpose editor X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=ac0913aaaef2ec17b8422f8fe7b6cae87fd300ba;p=onyx-wasm-analyzer.git refactored general purpose editor --- diff --git a/src/app/app.onyx b/src/app/app.onyx index 6ab1cc2..01e9740 100644 --- a/src/app/app.onyx +++ b/src/app/app.onyx @@ -1,13 +1,13 @@ package app #private_file { - events :: package js_events - gl :: package gl - gfx :: package immediate_mode - ui :: package ui - config :: package config - wasm :: package wasm_utils - debug :: package debug + events :: package js_events + gl :: package gl + gfx :: package immediate_mode + ui :: package ui + config :: package config + wasm :: package wasm_utils + debug :: package debug use package core @@ -81,7 +81,7 @@ init :: () { load_fonts(); settings_load(^state.settings); - colorscheme_switch(state.settings.colorscheme); + settings_apply(^state.settings); search_buffer = string.buffer_make(memory.make_slice(u8, 256)); @@ -327,8 +327,8 @@ draw :: () { } defer ui.window_end(); - window.window_state.background_color = config.Colors.background; - window.window_state.border_color = config.Colors.primary_dark; + // window.window_state.background_color = config.Colors.background; + // window.window_state.border_color = config.Colors.primary_dark; window.draw(window.draw_data, window); @@ -423,6 +423,7 @@ open_tool_opener :: macro () { @Relocate // This should be moved to somewhere else when the code base. invoke_feature_function :: macro (name: str, Hook_Type := #type () -> void, call := #code f()) { use type_info; + use package feature { Feature } #persist features : [..] ^type_info.Type_Info_Struct; if features.data == null { @@ -434,9 +435,14 @@ invoke_feature_function :: macro (name: str, Hook_Type := #type () -> void, call if type.kind != .Struct do continue; feature := cast(^Type_Info_Struct) type; - if !string.starts_with(feature.name, "Feature_") do continue; - - features << feature; + + for ^tag: feature.tags { + if tag.type == Feature { + debug_log(.Debug, "Found feature: {}", (cast(^Feature) tag.data).name); + features << feature; + break; + } + } } } diff --git a/src/app/colors.onyx b/src/app/colors.onyx index 4553dc7..6a49d35 100644 --- a/src/app/colors.onyx +++ b/src/app/colors.onyx @@ -107,4 +107,11 @@ update_ui_colors :: () { ui.default_slider_theme.box_border_color = config.Colors.primary; ui.default_slider_theme.bar_hover_color = config.Colors.secondary; ui.default_slider_theme.bar_color = config.Colors.primary_light; + + for window: state.windows_sorted { + window.window_state.border_color = config.Colors.primary_dark; + window.window_state.background_color = config.Colors.background; + } + + invoke_feature_function("colorscheme_change"); } diff --git a/src/app/editor.onyx b/src/app/editor.onyx new file mode 100644 index 0000000..5f6985d --- /dev/null +++ b/src/app/editor.onyx @@ -0,0 +1,179 @@ +package editor + +#private_file { + app :: package app + ui :: package ui + config :: package config + use package core +} + +Rename :: struct { name: str; } +Slider_Int :: struct { min, max: i32; } +Slider_Float :: struct { min, max: f32; } +Ignore_Option :: struct { option: str; } + +modify_window_draw :: (obj: any, win: ^app.Application_Window, site := #callsite) { + __counter = 0; + + rect := win.window_state->get_internal_rectangle(); + + hash := ui.get_site_hash(site); + handle := ui.scrollable_region_start(rect, increment=hash, .{ + minimum_x = 0, maximum_x = 0, + minimum_y = 0, maximum_y = 100000.0f, + }); + defer ui.scrollable_region_stop(); + + win_rect := handle->get_visible_rectangle(); + + // rect = ui.Flow.padding(rect, left=8, right=8); + rect.y1 = 1000000.0f; + + { + use type_info; + + obj_info := get_type_info(obj.type); + if obj_info.kind == .Pointer { + obj.data = *cast(^rawptr) obj.data; + obj.type = (cast(^Type_Info_Pointer) obj_info).to; + } + } + + render_settings(^rect, win_rect, obj); +} + +// +// The nitty-gritty of how everything is rendered. +// + +#private_file { + __counter : i32; + + msg_buffer : [512] u8; + row_rect : ui.Rectangle; + tmp_rect : ui.Rectangle; + + // Needs to be separate because this part is recursive + render_settings :: (rect: ^ui.Rectangle, win_rect: ui.Rectangle, obj: any) { + use type_info; + + type := get_type_info(obj.type); + assert(type.kind == .Struct, "Expected a struct type"); + + settings_type := cast(^Type_Info_Struct) type; + + heading_theme := ui.default_text_theme; + heading_theme.font_size = 1.25f; + + background_color := config.Colors.background; + + for ^member: settings_type.members { + __counter += 1; + member_type := get_type_info(member.type); + + row_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=48); + name_rect, data_rect := ui.Flow.split_vertical(row_rect, right_width=ui.Rectangle.width(win_rect) * .6); + + row_visible := ui.Rectangle.intersects(row_rect, win_rect); + if row_visible { + background_color = config.Colors.background if __counter % 2 == 0 else config.Colors.dark_background; + ui.draw_rect(.{ win_rect.x0, row_rect.y0, win_rect.x1, row_rect.y1 }, background_color); + } + + { + // Draw the name of the variable, handling a "Rename" tag. + name := member.name; + if rename := array.first(member.tags, (x) => x.type == Rename); rename != null { + name = (cast(^Rename) rename.data).name; + } + + if row_visible do ui.draw_text(name_rect, name, theme=^heading_theme); + } + + switch member_type.kind { + case .Enum { + value := cast(^i32) (cast(^u8) obj.data + member.offset); + + enum_type := cast(^Type_Info_Enum) member_type; + for ^enum_member: enum_type.members { + for ^tag: member.tags do if tag.type == Ignore_Option { + if (cast(^Ignore_Option) tag.data).option == enum_member.name { + continue continue; + } + } + + __counter += 1; + tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32); + + if ui.Rectangle.intersects(tmp_rect, win_rect) { + ui.radio(ui.Flow.padding(tmp_rect, left=32), + value, cast(i32) enum_member.value, + enum_member.name, increment=__counter); + } + } + } + + case .Struct { + sub_object := cast(rawptr) (cast(^u8) obj.data + member.offset); + + y0 := row_rect.y1; + + rect.x0 += 32; + render_settings(rect, win_rect, .{ sub_object, member.type }); + rect.x0 -= 32; + + y1 := rect.y0; + + ui.draw_rect(.{ rect.x0, y0, rect.x0 + 24, y1 }, config.Colors.primary_dark); + } + + case .Basic do switch member.type { + case i32 { + value := cast(^i32) (cast(^u8) obj.data + member.offset); + + low, high : i32; + low, high = 0, 10; + + if elem := array.first(member.tags, (x) => x.type == Slider_Int); elem != null { + slider_int := cast(^Slider_Int) elem.data; + low = slider_int.min; + high = slider_int.max; + } + + if row_visible { + ui.slider(data_rect, value, low, high, increment=__counter); + ui.draw_text(data_rect, conv.str_format(msg_buffer, " {}", *value)); + } + } + + case f32 { + value := cast(^f32) (cast(^u8) obj.data + member.offset); + + low, high : f32; + low, high = 0, 10000; + + if elem := array.first(member.tags, (x) => x.type == Slider_Float); elem != null { + slider_int := cast(^Slider_Float) elem.data; + low = slider_int.min; + high = slider_int.max; + } + + if row_visible { + ui.slider(data_rect, value, low, high, increment=__counter); + ui.draw_text(data_rect, conv.str_format(msg_buffer, " {}", *value)); + } + } + + case bool { + value := cast(^bool) (cast(^u8) obj.data + member.offset); + + if row_visible { + ui.checkbox(data_rect, value, "", increment=__counter); + } + } + } + } + } + } +} + diff --git a/src/app/settings.onyx b/src/app/settings.onyx index 3e55858..15bdd3d 100644 --- a/src/app/settings.onyx +++ b/src/app/settings.onyx @@ -1,6 +1,9 @@ package app #private_file { + config :: package config + editor :: package editor + json :: package json __initialize :: (package core.intrinsics.onyx).__initialize init :: (package core.intrinsics.onyx).init @@ -11,12 +14,11 @@ package app } Application_Settings :: struct { + #tag(editor.Ignore_Option.{ "Undefined" }) + #tag(editor.Rename.{ "Color scheme" }) colorscheme := Colorscheme.Undefined; } -Slider_Int :: struct { min, max: i32; } -Slider_Float :: struct { min, max: f32; } - settings_save :: (settings: ^Application_Settings) { use type_info; @@ -47,7 +49,9 @@ settings_load :: (settings: ^Application_Settings) { else do settings.colorscheme = .Dark; } - +settings_apply :: (settings: ^Application_Settings) { + colorscheme_switch(settings.colorscheme); +} // @@ -59,142 +63,30 @@ settings_load :: (settings: ^Application_Settings) { window_name :: "Settings" ui :: package ui - - @Temporary - settings_any: any; } -make_any :: macro (v: $T) => any.{ ^v, T }; - settings_window_show :: () { - settings_any = make_any(state.settings); - - open_window(window_id, window_name, .{ 0, 0 }, settings_window_draw, draw_data=^settings_any); + open_window(window_id, window_name, .{ 0, 0 }, settings_window_draw); focus_window(window_id); move_window_to_top(window_id); - - open_window("settings_dummy", "Settings Dummy", .{ 0, 0 }, (_, win) => { - buffer: [1024] u8; - msg := conv.str_format(buffer, "{*p}", ^state.settings); - ui.draw_text(.{ 0, 0, win.window_state.size.x, win.window_state.size.y }, msg); - }); } -settings_window_draw :: (obj: ^any, win: ^Application_Window) { - // Uniqueness counter - #persist counter: i32; - counter = 0; +#private_file +settings_window_draw :: (_, win) => { + editor.modify_window_draw(^state.settings, win); - rect := ui.Rectangle.{ 0, 0, win.window_state.size.x, win.window_state.size.y }; + button_theme := ui.default_button_theme; + button_theme.border_width = 0; - #persist scrollable_region := ui.Scrollable_Region_State.{}; - handle := ui.scrollable_region_start(rect, state=^scrollable_region, .{ - minimum_x = 0, maximum_x = 0, - minimum_y = 0, maximum_y = 100000.0f, - }); - defer ui.scrollable_region_stop(); + w, h := win.window_state.size.x, win.window_state.size.y; + if ui.button(.{ 0, h - 40, w / 2, h }, "Apply", theme=^button_theme) { + settings_apply(^state.settings); + } - // Rectangle of the visible scroll region, transformed to have the top-left of - // the window at 0, 0 - win_rect := handle->get_visible_rectangle(); + if ui.button(.{ w / 2, h - 40, w, h }, "Save", theme=^button_theme) { + settings_apply(^state.settings); + settings_save(^state.settings); - rect = ui.Flow.padding(rect, left=8, right=8); - rect.y1 = 1000000.0f; - - render_settings(^rect, win_rect, obj); - - // Needs to be separate because this part is recursive - render_settings :: (rect: ^ui.Rectangle, win_rect: ui.Rectangle, obj: ^any) { - use type_info; - - #persist msg_buffer : [512] u8; - #persist tmp_rect : ui.Rectangle; - - type := get_type_info(obj.type); - assert(type.kind == .Struct, "Expected a struct type"); - - settings_type := cast(^Type_Info_Struct) type; - - heading_theme := ui.default_text_theme; - heading_theme.font_size = 1.25f; - - for ^member: settings_type.members { - counter += 1; - member_type := get_type_info(member.type); - - tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=48); - if ui.Rectangle.intersects(tmp_rect, win_rect) { - ui.draw_text(tmp_rect, member.name, theme=^heading_theme); - } - - switch member_type.kind { - case .Enum { - value := cast(^i32) (cast(^u8) obj.data + member.offset); - - enum_type := cast(^Type_Info_Enum) member_type; - for ^enum_member: enum_type.members { - counter += 1; - tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32); - - if ui.Rectangle.intersects(tmp_rect, win_rect) { - ui.radio(ui.Flow.padding(tmp_rect, left=32), - value, cast(i32) enum_member.value, - enum_member.name, increment=counter); - } - } - } - - case .Struct { - struct_type := cast(^Type_Info_Struct) member_type; - sub_object := cast(rawptr) (cast(^u8) obj.data + member.offset); - - rect.x0 += 32; - defer rect.x0 -= 32; - - sub_object_any := any.{ sub_object, member.type }; - render_settings(rect, win_rect, ^sub_object_any); - } - - case .Basic do switch member.type { - case i32 { - value := cast(^i32) (cast(^u8) obj.data + member.offset); - - low, high : i32; - low, high = 0, 10; - - if elem := array.first(member.tags, (x) => x.type == Slider_Int); elem != null { - slider_int := cast(^Slider_Int) elem.data; - low = slider_int.min; - high = slider_int.max; - } - - tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32); - if ui.Rectangle.intersects(tmp_rect, win_rect) { - ui.slider(tmp_rect, value, low, high, increment=counter); - ui.draw_text(tmp_rect, conv.str_format(msg_buffer, " Current: {}", *value)); - } - } - - case f32 { - value := cast(^f32) (cast(^u8) obj.data + member.offset); - - low, high : f32; - low, high = 0, 1; - - if elem := array.first(member.tags, (x) => x.type == Slider_Float); elem != null { - slider_int := cast(^Slider_Float) elem.data; - low = slider_int.min; - high = slider_int.max; - } - - tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32); - if ui.Rectangle.intersects(tmp_rect, win_rect) { - ui.slider(tmp_rect, value, low, high, increment=counter); - ui.draw_text(tmp_rect, conv.str_format(msg_buffer, " Current: {}", *value)); - } - } - } - } - } + close_window(window_id); } } diff --git a/src/build.onyx b/src/build.onyx index dbe2d2d..357a339 100644 --- a/src/build.onyx +++ b/src/build.onyx @@ -23,6 +23,7 @@ #load "src/app/colors" #load "src/app/storage" #load "src/app/settings" + #load "src/app/editor" #load "src/features/load_features" diff --git a/src/features/hex_editor/feature.onyx b/src/features/hex_editor/feature.onyx index d28262d..ea974e6 100644 --- a/src/features/hex_editor/feature.onyx +++ b/src/features/hex_editor/feature.onyx @@ -2,6 +2,12 @@ package feature.hex_viewer #load "./hex_viewer" -Feature_Hex_Viewer :: struct { +use package feature { Feature } + +_ :: struct #tag(Feature.{ "Hex Viewer" }) { setup := setup; } + + + + diff --git a/src/features/hex_editor/hex_viewer.onyx b/src/features/hex_editor/hex_viewer.onyx index b8aacb8..fe2f4c6 100644 --- a/src/features/hex_editor/hex_viewer.onyx +++ b/src/features/hex_editor/hex_viewer.onyx @@ -2,6 +2,7 @@ package feature.hex_viewer #private_file { app :: package app + editor :: package editor ui :: package ui config :: package config } @@ -15,14 +16,20 @@ window_name :: "Hex Viewer" Hex_Viewer_State :: struct { scrollable_region := ui.Scrollable_Region_State.{}; - highlighted_line : i32 = -1; // -1 is uninitialized - highlighted_column : i32 = -1; + #tag(editor.Slider_Int.{ -1, 40 }) + highlighted_line := -1; + + #tag(editor.Slider_Int.{ -1, 16 }) + highlighted_column := -1; } // Should this be global? Or should each viewer window get their own? #private global_viewer_state := Hex_Viewer_State.{}; +hex_window: ^app.Application_Window; + window_draw :: (use viewer_state: ^Hex_Viewer_State, win: ^app.Application_Window) { + hex_window = win; orig_r: ui.Rectangle = .{ 0, 0, win.window_state.size.x, win.window_state.size.y }; r := orig_r; @@ -35,7 +42,6 @@ window_draw :: (use viewer_state: ^Hex_Viewer_State, win: ^app.Application_Windo line_height := em_height * 2; win.window_state.max_size.x = (10 + 1 + 3 * 16 + 2 + 16) * em_height; - win.window_state.background_color = config.Colors.dark_background; if !app.state.has_active_file { ui.draw_text(r, "No loaded file.", theme=^text_theme); @@ -144,13 +150,8 @@ open_window :: () { app.move_window_to_top(window_id); app.focus_window(window_id); - app.open_window("dummy", "DUMMY TESTING", .{ 0, 0 }, (_, win) => { - buffer: [1024] u8; - s := conv.str_format(buffer, "{p*}", ^global_viewer_state); - theme := ui.default_text_theme; - theme.font = config.Fonts.FiraCode.index; - ui.draw_text(.{0,0,0,0}, s, theme=^theme); - }); + app.open_window("dummy", "DUMMY TESTING", .{ 0, 0 }, + (_, win) => { (package editor).modify_window_draw(hex_window, win); }); } setup :: () { diff --git a/src/features/load_features.onyx b/src/features/load_features.onyx index 367ab17..e8eb80e 100644 --- a/src/features/load_features.onyx +++ b/src/features/load_features.onyx @@ -1,6 +1,11 @@ +package feature + +Feature :: struct { + name: str; +} // Add #load statements for the features #load "./wasm/feature" #load "./hex_editor/feature" -#load "./text_editor/feature" \ No newline at end of file +#load "./text_editor/feature" diff --git a/src/features/text_editor/feature.onyx b/src/features/text_editor/feature.onyx index 8bb1fc4..a5ea200 100644 --- a/src/features/text_editor/feature.onyx +++ b/src/features/text_editor/feature.onyx @@ -2,6 +2,8 @@ package feature.text_editor #load "./text_editor" -Feature_Text_Editor :: struct { +use package feature { Feature } + +_ :: struct #tag(Feature.{ "Text Editor" }) { setup := setup; -} \ No newline at end of file +} diff --git a/src/features/wasm/feature.onyx b/src/features/wasm/feature.onyx index cd52edf..3c3cf36 100644 --- a/src/features/wasm/feature.onyx +++ b/src/features/wasm/feature.onyx @@ -2,7 +2,9 @@ package feature.wasm #load "./wasm" -Feature_Wasm_Loader :: struct { +use package feature { Feature } + +_ :: struct #tag(Feature.{ "WASM" }) { setup := setup; file_loaded := file_loaded; } diff --git a/src/ui/window.onyx b/src/ui/window.onyx index 7913ca5..b114baf 100644 --- a/src/ui/window.onyx +++ b/src/ui/window.onyx @@ -2,7 +2,7 @@ package ui #private_file { map :: package core.map - // gfx :: package immediate_mode + editor :: package editor } // This will be stored by the end user's library. @@ -14,10 +14,12 @@ Window_State :: struct { title := "(undefined)"; + #tag(editor.Slider_Float.{ 0, 64 }) border_width := 10.0f; border_color := gfx.Color4.{ 1, 0, 0 }; background_color := gfx.Color4.{ 0.2, 0.2, 0.2 }; + #tag(editor.Slider_Float.{ 0, 64 }) bar_height := 28.0f; draggable := true; @@ -35,6 +37,10 @@ Window_State :: struct { position.x + size.x + bw, position.y + size.y + bw }; } + get_internal_rectangle :: (use w: ^Window_State) -> Rectangle { + return .{ 0, 0, size.x, size.y }; + } + get_transformed_rectangle :: (use w: ^Window_State) -> Rectangle { bw := border_width; if dragging do bw *= 5;