started work on automatic settings window
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 5 Oct 2021 21:22:21 +0000 (16:22 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 5 Oct 2021 21:22:21 +0000 (16:22 -0500)
src/app/app.onyx
src/app/debug_log.onyx
src/app/settings.onyx
src/ui/menubar.onyx
src/ui/window.onyx

index d1acebb6d5a60eb37fb19862c9f8eac60ee5fafe..6ab1cc2cefad0a09df56100934c6d1d0ba34151b 100644 (file)
@@ -352,7 +352,7 @@ draw :: () {
         gfx.identity();
         ui.menubar(menu_bar, ^search_buffer, ui.Menu_Bar_Option.[
             .{ label = "File" },
-            .{ label = "Test" },
+            .{ label = "Settings", action = settings_window_show },
         ]);
     }
 
index 47cfacf85733db1ab7fe0b5ea8de5114eab2e06d..d743fd194daad0544d069ec1ebc0479cd7d81505 100644 (file)
@@ -53,7 +53,7 @@ debug_log_clear :: () {
 debug_log :: (severity: Severity, format: str, args: ..any) {
     if severity < minimum_severity do return;
 
-    buffer1: [4096] u8;
+    buffer1, buffer2: [4096] u8;
     s := conv.str_format_va(buffer1, format, args);
     lines := string.split(s, #char "\n", context.temp_allocator);
 
@@ -61,7 +61,6 @@ debug_log :: (severity: Severity, format: str, args: ..any) {
     for l: lines {
         if l.count == 0 do continue;
 
-        buffer2: [4096] u8;
         s = conv.str_format(buffer2, "[{}] {}\n", severity, l);
         line := string.alloc_copy(s, line_alloc);
 
@@ -72,32 +71,39 @@ debug_log :: (severity: Severity, format: str, args: ..any) {
 draw_debug_log :: (window_rectangle: ui.Rectangle, site := #callsite) {
     if debug_log_y_offset == 0.0f do return;
 
+    Font_Size :: 0.75f;
+
+    ui.use_font(config.Fonts.FiraCode.index);
+    line_height := ui.get_text_height("M", size=Font_Size);
+
     r, _ := ui.Flow.split_horizontal(window_rectangle, top_percent=.5);
     height := ui.Rectangle.height(r);
     r.y0 -= height * (1 - debug_log_y_offset);
     r.y1 -= height * (1 - debug_log_y_offset);
 
     sh := ui.scrollable_region_start(r, state=^scrollable_region, .{
-        minimum_x = 0, maximum_x = 0,
-        minimum_y = 32.0f * ~~(log_buffer.lines.count - ~~(height / 32)),
+        minimum_x = 0, maximum_x = ui.Rectangle.width(window_rectangle),
+        minimum_y = line_height * ~~(log_buffer.lines.count - ~~(height / line_height)),
         maximum_y = 0,
     });
     defer ui.scrollable_region_stop();
 
-    r.y0 -= 32.0f * ~~log_buffer.lines.count;
+    r.y0 -= line_height * ~~log_buffer.lines.count;
     visible_rectangle := sh->get_visible_rectangle();
 
-    background_color := config.Colors.background;
-    background_color.a = 0.8;
+    // background_color := config.Colors.background;
+    // background_color.a = 0.8;
+    background_color := gfx.Color4.{ 0, 0, 0, 0.8 };
     ui.draw_rect(visible_rectangle, color=background_color);
 
     text_theme := ui.default_text_theme;
     text_theme.font = config.Fonts.FiraCode.index;
-    text_theme.text_color = .{ 0.2, 0.5, 0.2 };
+    text_theme.font_size = Font_Size;
+    text_theme.text_color = .{ 0.2, 0.8, 0.2 };
 
     text_rect: ui.Rectangle;
     while i := cast(i32) log_buffer.lines.count - 1; i >= 0 {
-        r, text_rect = ui.Flow.split_horizontal(r, bottom_height=32);
+        r, text_rect = ui.Flow.split_horizontal(r, bottom_height=line_height);
 
         if ui.Rectangle.intersects(text_rect, visible_rectangle) {
             ui.draw_text(text_rect, log_buffer.lines[i], theme=^text_theme);
@@ -107,6 +113,7 @@ draw_debug_log :: (window_rectangle: ui.Rectangle, site := #callsite) {
     }
 
     r = visible_rectangle;
+    text_theme.font_size = 1;
     clear_button_rect := ui.Rectangle.{
         x0 = r.x1 - 200, x1 = r.x1,
         y0 = r.y0,       y1 = r.y0 + 50,
index b9b9d34615716f37c818d92de64e838ce9d3572e..04ed206c1f8dfc75177011d3fcbade9352808640 100644 (file)
@@ -12,6 +12,24 @@ package app
 
 Application_Settings :: struct {
     colorscheme := Colorscheme.Undefined;
+
+    coolness : Slider_Option(i32) = .{ 5, 0, 5 };
+    badness  : Slider_Option(f32) = .{ 0, 0, 10 };
+
+    test  : Test = .{ 5, 5 };
+    test2 : Test = .{ 0, 0 };
+    wrapped : Wrapper = .{ value = 40 };
+
+    Test :: struct { x: i32; y: f32; }
+    Wrapper :: struct {
+        test := Test.{ 10, 10 };
+        value: i32;
+    }
+}
+
+Slider_Option :: struct (T: type_expr) {
+    value: T;
+    min, max: T;
 }
 
 settings_save :: (settings: ^Application_Settings) {
@@ -43,3 +61,155 @@ settings_load :: (settings: ^Application_Settings) {
     if colorscheme != .Undefined do settings.colorscheme = colorscheme;
     else                         do settings.colorscheme = .Dark;
 }
+
+
+
+
+//
+// Settings Window
+//
+
+#private_file {
+    window_id   :: "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);
+    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;
+
+    rect := ui.Rectangle.{ 0, 0, win.window_state.size.x, win.window_state.size.y }; 
+
+    #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();
+
+    // Rectangle of the visible scroll region, transformed to have the top-left of
+    // the window at 0, 0
+    win_rect := handle->get_visible_rectangle();
+
+    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);
+
+                    switch member.type {
+                        case #type Slider_Option(i32) do render_slider_option(i32, sub_object);
+                        case #type Slider_Option(f32) do render_slider_option(f32, sub_object);
+
+                        case #default {
+                            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);
+
+                        tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32);
+                        if ui.Rectangle.intersects(tmp_rect, win_rect) {
+                            ui.slider(tmp_rect, value, -10, 10, 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);
+
+                        tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32);
+                        if ui.Rectangle.intersects(tmp_rect, win_rect) {
+                            ui.slider(tmp_rect, value, 0, 100, increment=counter);
+                            ui.draw_text(tmp_rect, conv.str_format(msg_buffer, "   Current: {}", *value));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    render_slider_option :: macro ($T: type_expr, so: rawptr) {
+        so_ := cast(^Slider_Option(T)) so;
+
+        tmp_rect, *rect = ui.Flow.split_horizontal(*rect, top_height=32);
+
+        if ui.Rectangle.intersects(tmp_rect, win_rect) {
+            ui.slider(tmp_rect, ^so_.value, so_.min, so_.max, increment=counter);
+            ui.draw_text(tmp_rect, conv.str_format(msg_buffer, "   Current: {}", so_.value));
+        }
+    }
+}
index c3fbcc951b7528d3f17799c2ecdc947b9488d02d..efa0cd5cd604d47d742f89928d0d530a0e24ca28 100644 (file)
@@ -32,7 +32,9 @@ menubar :: (r: Rectangle, @Temporary search_buffer: ^String_Buffer, options: []
 
     for option: options |> iter.from_array() |> iter.enumerate() {
         button_rect, menu_rect = Flow.split_vertical(menu_rect, left_width=100);
-        button(button_rect, option.value.label, theme=^menu_button_theme, increment=option.index);
+        if button(button_rect, option.value.label, theme=^menu_button_theme, increment=option.index) {
+            option.value.action();
+        }
     }
 
     _, search_rect := Flow.split_vertical(menu_rect, right_width=300);
index 5b11afe11f6188a7ca75f5207e3bf579b9bb6c11..7913ca5f5a6f23b81e0db5340c3bbd018c323633 100644 (file)
@@ -136,6 +136,8 @@ window_start :: (use state: ^Window_State, site := #callsite, increment := 0) ->
 
     close_theme := default_button_theme;
     close_theme.border_width = 0;
+    close_theme.background_color = .{ 0.5, 0.2, 0.2 };
+    close_theme.hover_color = .{ 1.0, 0.4, 0.4 };
     if button(.{ x + w - 48, y - bar_height - border_width, x + w + border_width, y }, "X", theme=^close_theme, increment=increment + hash) {
         state.should_close = true;
     }