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
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));
}
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);
@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 {
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;
+ }
+ }
}
}
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");
}
--- /dev/null
+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);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
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
}
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;
else do settings.colorscheme = .Dark;
}
-
+settings_apply :: (settings: ^Application_Settings) {
+ colorscheme_switch(settings.colorscheme);
+}
//
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);
}
}
#load "src/app/colors"
#load "src/app/storage"
#load "src/app/settings"
+ #load "src/app/editor"
#load "src/features/load_features"
#load "./hex_viewer"
-Feature_Hex_Viewer :: struct {
+use package feature { Feature }
+
+_ :: struct #tag(Feature.{ "Hex Viewer" }) {
setup := setup;
}
+
+
+
+
#private_file {
app :: package app
+ editor :: package editor
ui :: package ui
config :: package config
}
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;
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);
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 :: () {
+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"
#load "./text_editor"
-Feature_Text_Editor :: struct {
+use package feature { Feature }
+
+_ :: struct #tag(Feature.{ "Text Editor" }) {
setup := setup;
-}
\ No newline at end of file
+}
#load "./wasm"
-Feature_Wasm_Loader :: struct {
+use package feature { Feature }
+
+_ :: struct #tag(Feature.{ "WASM" }) {
setup := setup;
file_loaded := file_loaded;
}
#private_file {
map :: package core.map
- // gfx :: package immediate_mode
+ editor :: package editor
}
// This will be stored by the end user's library.
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;
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;