Zig bindings for orca (still WIP) #140
			
				
			
		
		
		
	|  | @ -29,15 +29,17 @@ oc_ui_sig oc_ui_label(const char* label); | |||
| oc_ui_sig oc_ui_label_str8(oc_str8 label); | ||||
| oc_ui_sig oc_ui_button(const char* label); | ||||
| oc_ui_sig oc_ui_checkbox(const char* name, bool* checked); | ||||
| oc_ui_box* oc_ui_slider(const char* label, f32 thumbRatio, f32* scrollValue); | ||||
| oc_ui_box* oc_ui_slider(const char* name, f32* value); | ||||
| oc_ui_box* oc_ui_scrollbar(const char* name, f32 thumbRatio, f32* scrollValue); | ||||
| oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 text); | ||||
| oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_info* info); | ||||
| oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_info* info); | ||||
| 
 | ||||
| void oc_ui_panel_begin(const char* name, oc_ui_flags flags); | ||||
| void oc_ui_panel_end(void); | ||||
| #define oc_ui_panel(s, f) | ||||
| 
 | ||||
| void oc_ui_menu_bar_begin(const char* label); | ||||
| void oc_ui_menu_bar_begin(const char* name); | ||||
| void oc_ui_menu_bar_end(void); | ||||
| #define oc_ui_menu_bar(name) | ||||
| 
 | ||||
|  | @ -45,11 +47,11 @@ void oc_ui_menu_begin(const char* label); | |||
| void oc_ui_menu_end(void); | ||||
| #define oc_ui_menu(name) | ||||
| 
 | ||||
| oc_ui_sig oc_ui_menu_button(const char* name); | ||||
| oc_ui_sig oc_ui_menu_button(const char* label); | ||||
| 
 | ||||
| oc_ui_sig oc_ui_tooltip_begin(const char* name); | ||||
| oc_ui_sig oc_ui_tooltip_begin(const char* label); | ||||
| void oc_ui_tooltip_end(void); | ||||
| #define oc_ui_tooltip(name) | ||||
| #define oc_ui_tooltip(label) | ||||
| 
 | ||||
| //-------------------------------------------------------------------------------------
 | ||||
| // Styling
 | ||||
|  |  | |||
|  | @ -73,6 +73,12 @@ ORCA_EXPORT void oc_on_raw_event(oc_event* event) | |||
|     oc_ui_process_event(event); | ||||
| } | ||||
| 
 | ||||
| ORCA_EXPORT void oc_on_resize(u32 width, u32 height) | ||||
| { | ||||
|     frameSize.x = width; | ||||
|     frameSize.y = height; | ||||
| } | ||||
| 
 | ||||
| void log_push(const char* line) | ||||
| { | ||||
|     oc_str8_list_push(&logArena, &logLines, (oc_str8)OC_STR8(line)); | ||||
|  | @ -158,12 +164,6 @@ void labeled_slider(const char* label, f32* value) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| ORCA_EXPORT void oc_on_resize(u32 width, u32 height) | ||||
| { | ||||
|     frameSize.x = width; | ||||
|     frameSize.y = height; | ||||
| } | ||||
| 
 | ||||
| void reset_next_radio_group_to_dark_theme(oc_arena* arena); | ||||
| 
 | ||||
| ORCA_EXPORT void oc_on_frame_refresh(void) | ||||
|  | @ -330,7 +330,7 @@ ORCA_EXPORT void oc_on_frame_refresh(void) | |||
|                             radioSelected = result.selectedIndex; | ||||
|                             if(result.changed) | ||||
|                             { | ||||
|                                 log_pushf("Selected Radio %i", result.selectedIndex + 1); | ||||
|                                 log_pushf("Selected %s", options[result.selectedIndex].ptr); | ||||
|                             } | ||||
| 
 | ||||
|                             //-----------------------------------------------------------------------------
 | ||||
|  | @ -359,13 +359,13 @@ ORCA_EXPORT void oc_on_frame_refresh(void) | |||
|                                                      .size.height = { OC_UI_SIZE_TEXT } }, | ||||
|                                      OC_UI_STYLE_SIZE); | ||||
|                     static oc_str8 text = OC_STR8_LIT("Text box"); | ||||
|                     oc_ui_text_box_result res = oc_ui_text_box("text", scratch.arena, text); | ||||
|                     if(res.changed) | ||||
|                     oc_ui_text_box_result result = oc_ui_text_box("text", scratch.arena, text); | ||||
|                     if(result.changed) | ||||
|                     { | ||||
|                         oc_arena_clear(&textArena); | ||||
|                         text = oc_str8_push_copy(&textArena, res.text); | ||||
|                         text = oc_str8_push_copy(&textArena, result.text); | ||||
|                     } | ||||
|                     if(res.accepted) | ||||
|                     if(result.accepted) | ||||
|                     { | ||||
|                         log_pushf("Entered text \"%s\"", text.ptr); | ||||
|                     } | ||||
|  | @ -465,7 +465,7 @@ ORCA_EXPORT void oc_on_frame_refresh(void) | |||
| 
 | ||||
|                     oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 }, | ||||
|                                                      .size.height = { OC_UI_SIZE_PIXELS, 152 }, | ||||
|                                                      .layout.margin.x = 320, | ||||
|                                                      .layout.margin.x = 310, | ||||
|                                                      .layout.margin.y = 16, | ||||
|                                                      .bgColor = OC_UI_DARK_THEME.bg0, | ||||
|                                                      .roundness = OC_UI_DARK_THEME.roundnessSmall }, | ||||
|  | @ -527,8 +527,8 @@ ORCA_EXPORT void oc_on_frame_refresh(void) | |||
|                                                     | OC_UI_STYLE_BG_COLOR | ||||
|                                                     | OC_UI_STYLE_ROUNDNESS); | ||||
| 
 | ||||
|                         oc_ui_pattern labelPattern = { 0 }; | ||||
|                         oc_ui_tag labelTag = oc_ui_tag_make("label"); | ||||
|                         oc_ui_pattern labelPattern = { 0 }; | ||||
|                         oc_ui_pattern_push(scratch.arena, &labelPattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = labelTag }); | ||||
|                         oc_ui_style_match_after(labelPattern, | ||||
|                                                 &(oc_ui_style){ .color = labelFontColor, | ||||
|  | @ -818,32 +818,32 @@ ORCA_EXPORT void oc_on_frame_refresh(void) | |||
| // You won't need it in a real program as long as your colors come from ui.theme or ui.theme->palette
 | ||||
| void reset_next_radio_group_to_dark_theme(oc_arena* arena) | ||||
| { | ||||
|     oc_ui_tag defaultTag = oc_ui_tag_make("radio"); | ||||
|     oc_ui_pattern defaultPattern = { 0 }; | ||||
|     oc_ui_pattern_push(arena, &defaultPattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = defaultTag }); | ||||
|     oc_ui_style defaultStyle = { .borderColor = OC_UI_DARK_THEME.text3, | ||||
|     oc_ui_tag unselectedTag = oc_ui_tag_make("radio"); | ||||
|     oc_ui_pattern unselectedPattern = { 0 }; | ||||
|     oc_ui_pattern_push(arena, &unselectedPattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = unselectedTag }); | ||||
|     oc_ui_style unselectedStyle = { .borderColor = OC_UI_DARK_THEME.text3, | ||||
|                                     .borderSize = 1 }; | ||||
|     oc_ui_style_mask defaultMask = OC_UI_STYLE_BORDER_COLOR | ||||
|     oc_ui_style_mask unselectedMask = OC_UI_STYLE_BORDER_COLOR | ||||
|                                     | OC_UI_STYLE_BORDER_SIZE; | ||||
|     oc_ui_style_match_after(defaultPattern, &defaultStyle, defaultMask); | ||||
|     oc_ui_style_match_after(unselectedPattern, &unselectedStyle, unselectedMask); | ||||
| 
 | ||||
|     oc_ui_pattern hoverPattern = { 0 }; | ||||
|     oc_ui_pattern_push(arena, &hoverPattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = defaultTag }); | ||||
|     oc_ui_pattern_push(arena, &hoverPattern, (oc_ui_selector){ .op = OC_UI_SEL_AND, .kind = OC_UI_SEL_STATUS, .status = OC_UI_HOVER }); | ||||
|     oc_ui_pattern unselectedHoverPattern = { 0 }; | ||||
|     oc_ui_pattern_push(arena, &unselectedHoverPattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = unselectedTag }); | ||||
|     oc_ui_pattern_push(arena, &unselectedHoverPattern, (oc_ui_selector){ .op = OC_UI_SEL_AND, .kind = OC_UI_SEL_STATUS, .status = OC_UI_HOVER }); | ||||
|     oc_ui_style hoverStyle = { .bgColor = OC_UI_DARK_THEME.fill0, | ||||
|                                .borderColor = OC_UI_DARK_THEME.primary }; | ||||
|     oc_ui_style_mask hoverMask = OC_UI_STYLE_BG_COLOR | ||||
|                                | OC_UI_STYLE_BORDER_COLOR; | ||||
|     oc_ui_style_match_after(hoverPattern, &hoverStyle, hoverMask); | ||||
|     oc_ui_style_match_after(unselectedHoverPattern, &hoverStyle, hoverMask); | ||||
| 
 | ||||
|     oc_ui_pattern activePattern = { 0 }; | ||||
|     oc_ui_pattern_push(arena, &activePattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = defaultTag }); | ||||
|     oc_ui_pattern_push(arena, &activePattern, (oc_ui_selector){ .op = OC_UI_SEL_AND, .kind = OC_UI_SEL_STATUS, .status = OC_UI_ACTIVE }); | ||||
|     oc_ui_pattern unselectedActivePattern = { 0 }; | ||||
|     oc_ui_pattern_push(arena, &unselectedActivePattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = unselectedTag }); | ||||
|     oc_ui_pattern_push(arena, &unselectedActivePattern, (oc_ui_selector){ .op = OC_UI_SEL_AND, .kind = OC_UI_SEL_STATUS, .status = OC_UI_ACTIVE }); | ||||
|     oc_ui_style activeStyle = { .bgColor = OC_UI_DARK_THEME.fill1, | ||||
|                                 .borderColor = OC_UI_DARK_THEME.primary }; | ||||
|     oc_ui_style_mask activeMask = OC_UI_STYLE_BG_COLOR | ||||
|                                 | OC_UI_STYLE_BORDER_COLOR; | ||||
|     oc_ui_style_match_after(activePattern, &activeStyle, activeMask); | ||||
|     oc_ui_style_match_after(unselectedActivePattern, &activeStyle, activeMask); | ||||
| 
 | ||||
|     oc_ui_tag selectedTag = oc_ui_tag_make("radio_selected"); | ||||
|     oc_ui_pattern selectedPattern = { 0 }; | ||||
|  |  | |||
|  | @ -0,0 +1,6 @@ | |||
| Sample | ||||
| zig-out | ||||
| zig-cache | ||||
| liborca.a | ||||
| profile.dtrace | ||||
| profile.spall | ||||
|  | @ -0,0 +1,13 @@ | |||
| ### Build and run | ||||
| Zig version `0.11.0` or greater is required for this sample. To build and run: | ||||
| ``` | ||||
| orca dev build-runtime | ||||
| zig build run | ||||
| ``` | ||||
| 
 | ||||
| These two commands build the runtime - the native host executable - and the sample as a loadable wasm library, then runs it. To only build the sample without running it, use `zig build bundle`. | ||||
| 
 | ||||
| ### Warning | ||||
| Zig bindings for Orca are in-progress and experimental. You may encounter bugs since not all the bound APIs have been tested extensively - this sample is currently the only code doing so! Additionally, not all APIs have zig coverage yet, notably: | ||||
| * `gles` | ||||
| As more APIs get tested, there is a possibility of breaking changes. Please report any bugs you find on the Handmade discord in the #orca channel. | ||||
|  | @ -0,0 +1,107 @@ | |||
| const std = @import("std"); | ||||
| const builtin = @import("builtin"); | ||||
| 
 | ||||
| fn addSourceString(str: []const u8, strings: *std.ArrayList(u8), sources: *std.ArrayList([]const u8)) !void { | ||||
|     var begin = strings.items.len; | ||||
|     try strings.appendSlice(str); | ||||
|     var realstring = strings.items[begin..]; | ||||
|     try sources.append(realstring); | ||||
| } | ||||
| 
 | ||||
| pub fn build(b: *std.Build) !void { | ||||
|     const optimize = b.standardOptimizeOption(.{}); | ||||
|     var wasm_target = std.zig.CrossTarget{ | ||||
|         .cpu_arch = .wasm32, | ||||
|         .os_tag = .freestanding, | ||||
|     }; | ||||
|     wasm_target.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory)); | ||||
| 
 | ||||
|     var orca_source_strings = try std.ArrayList(u8).initCapacity(b.allocator, 1024 * 4); | ||||
|     var orca_sources = try std.ArrayList([]const u8).initCapacity(b.allocator, 128); | ||||
|     defer orca_source_strings.deinit(); | ||||
|     defer orca_sources.deinit(); | ||||
| 
 | ||||
|     { | ||||
|         try addSourceString("../../src/orca.c", &orca_source_strings, &orca_sources); | ||||
| 
 | ||||
|         var libc_shim_dir = try std.fs.cwd().openIterableDir("../../src/libc-shim/src", .{}); | ||||
|         var walker = try libc_shim_dir.walk(b.allocator); | ||||
|         defer walker.deinit(); | ||||
| 
 | ||||
|         while (try walker.next()) |entry| { | ||||
|             const extension = std.fs.path.extension(entry.path); | ||||
|             if (std.mem.eql(u8, extension, ".c")) { | ||||
|                 var path_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | ||||
|                 var abs_path = try libc_shim_dir.dir.realpath(entry.path, &path_buffer); | ||||
|                 try addSourceString(abs_path, &orca_source_strings, &orca_sources); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const orca_compile_opts = [_][]const u8{ | ||||
|         "-D__ORCA__", | ||||
|         "--no-standard-libraries", | ||||
|         "-fno-builtin", | ||||
|         "-g", | ||||
|         "-O2", | ||||
|         "-mexec-model=reactor", | ||||
|         "-fno-sanitize=undefined", | ||||
|         "-isystem ../../src/libc-shim/include", | ||||
|         "-I../../src", | ||||
|         "-I../../src/ext", | ||||
|         "-Wl,--export-dynamic", | ||||
|     }; | ||||
| 
 | ||||
|     var orca_lib = b.addStaticLibrary(.{ | ||||
|         .name = "orca", | ||||
|         .target = wasm_target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
|     orca_lib.rdynamic = true; | ||||
|     orca_lib.addIncludePath(.{ .path = "../../src" }); | ||||
|     orca_lib.addIncludePath(.{ .path = "../../src/libc-shim/include" }); | ||||
|     orca_lib.addIncludePath(.{ .path = "../../src/ext" }); | ||||
|     orca_lib.addCSourceFiles(orca_sources.items, &orca_compile_opts); | ||||
| 
 | ||||
|     // builds the wasm module out of the orca C sources and main.zig | ||||
|     const orca_module: *std.Build.Module = b.createModule(.{ | ||||
|         .source_file = .{ .path = "../../src/orca.zig" }, | ||||
|     }); | ||||
|     const wasm_lib = b.addSharedLibrary(.{ | ||||
|         .name = "module", | ||||
|         .root_source_file = .{ .path = "src/main.zig" }, | ||||
|         .target = wasm_target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
|     wasm_lib.rdynamic = true; | ||||
|     wasm_lib.addIncludePath(.{ .path = "../../src" }); | ||||
|     wasm_lib.addIncludePath(.{ .path = "../../src/libc-shim/include" }); | ||||
|     wasm_lib.addIncludePath(.{ .path = "../../ext" }); | ||||
|     wasm_lib.addModule("orca", orca_module); | ||||
|     wasm_lib.linkLibrary(orca_lib); | ||||
| 
 | ||||
|     // copies the wasm module into zig-out/wasm_lib | ||||
|     b.installArtifact(wasm_lib); | ||||
| 
 | ||||
|     // Runs the orca build command | ||||
|     const bundle_cmd_str = [_][]const u8{ "orca", "bundle", "--orca-dir", "../..", "--name", "Sample", "--icon", "icon.png", "--resource-dir", "data", "zig-out/lib/module.wasm" }; | ||||
|     var bundle_cmd = b.addSystemCommand(&bundle_cmd_str); | ||||
|     bundle_cmd.step.dependOn(b.getInstallStep()); | ||||
| 
 | ||||
|     const bundle_step = b.step("bundle", "Runs the orca toolchain to bundle the wasm module into an orca app."); | ||||
|     bundle_step.dependOn(&bundle_cmd.step); | ||||
| 
 | ||||
|     // Runs the app | ||||
|     const run_cmd_windows = [_][]const u8{"Sample/bin/Sample.exe"}; | ||||
|     const run_cmd_macos = [_][]const u8{ "open", "Sample.app" }; | ||||
|     const run_cmd_str: []const []const u8 = switch (builtin.os.tag) { | ||||
|         .windows => &run_cmd_windows, | ||||
|         .macos => &run_cmd_macos, | ||||
|         else => @compileError("unsupported platform"), | ||||
|     }; | ||||
|     var run_cmd = b.addSystemCommand(run_cmd_str); | ||||
|     run_cmd.step.dependOn(&bundle_cmd.step); | ||||
| 
 | ||||
|     const run_step = b.step("run", "Runs the bundled app using the Orca runtime."); | ||||
|     run_step.dependOn(&run_cmd.step); | ||||
| } | ||||
|  | @ -0,0 +1,29 @@ | |||
| T E P I D   M O N K E Y   F O N T S | ||||
| freeware fonts for a freeware world | ||||
| 
 | ||||
| Site:   http://www.tepidmonkey.com/ | ||||
| E-mail: brandon@tepidmonkey.com | ||||
| 
 | ||||
| Thanks for your interest in my fonts! | ||||
| 
 | ||||
| For help on how to unzip, unstuff or install one of my  | ||||
| fonts, please visit my site at  | ||||
| www.tepidmonkey.com and go to the Help section. | ||||
| If you have any comments or questions, you can e-mail  | ||||
| me at brandon@tepidmonkey.com and I'll try to reply as  | ||||
| soon as possible. | ||||
| 
 | ||||
| Every week, I present a brand new original font for  | ||||
| your downloading pleasure, so be sure to visit my web  | ||||
| site every Sunday. | ||||
| 
 | ||||
| You may use this font(s) for non-commercial and  | ||||
| commercial purposes. You are not allowed to sell this  | ||||
| font for any fee at all. You are allowed to  | ||||
| redistribute it as long as you don't charge ANYTHING  | ||||
| for it (at all) and if you include this unaltered  | ||||
| Read Me file. You may not change any aspect of the font  | ||||
| file or this file. | ||||
| For the full set of terms of use (which override what  | ||||
| is listed here), go to www.tepidmonkey.com  | ||||
| and visit the Terms section. | ||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 153 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.7 KiB | 
|  | @ -0,0 +1,289 @@ | |||
| const std = @import("std"); | ||||
| const oc = @import("orca"); | ||||
| 
 | ||||
| const lerp = std.math.lerp; | ||||
| 
 | ||||
| const Vec2 = oc.Vec2; | ||||
| const Mat2x3 = oc.Mat2x3; | ||||
| const Str8 = oc.Str8; | ||||
| 
 | ||||
| var surface: oc.Surface = undefined; | ||||
| var canvas: oc.Canvas = undefined; | ||||
| var font: oc.Font = undefined; | ||||
| var orca_image: oc.Image = undefined; | ||||
| var gradient_image: oc.Image = undefined; | ||||
| 
 | ||||
| var counter: u32 = 0; | ||||
| var last_seconds: f64 = 0; | ||||
| var frame_size: Vec2 = .{ .x = 0, .y = 0 }; | ||||
| 
 | ||||
| var rotation_demo: f32 = 0; | ||||
| 
 | ||||
| export fn oc_on_init() void { | ||||
|     oc.windowSetTitle("zig sample"); | ||||
|     oc.windowSetSize(Vec2{ .x = 480, .y = 640 }); | ||||
| 
 | ||||
|     oc.log.info("current platform: {}", .{oc.getHostPlatform()}, @src()); | ||||
| 
 | ||||
|     surface = oc.Surface.canvas(); | ||||
|     canvas = oc.Canvas.create(); | ||||
| 
 | ||||
|     oc.assert(oc.Canvas.nil().isNil() == true, "nil canvas should be nil", .{}, @src()); | ||||
|     oc.assert(canvas.isNil() == false, "created canvas should not be nil", .{}, @src()); | ||||
| 
 | ||||
|     const ranges = oc.UnicodeRange.range(&[_]oc.UnicodeRange.Enum{ | ||||
|         .BasicLatin, | ||||
|         .C1ControlsAndLatin1Supplement, | ||||
|         .LatinExtendedA, | ||||
|         .LatinExtendedB, | ||||
|         .Specials, | ||||
|     }); | ||||
|     font = oc.Font.createFromPath("/zig.ttf", &ranges); | ||||
|     oc.assert(oc.Font.nil().isNil() == true, "nil font should be nil", .{}, @src()); | ||||
|     oc.assert(font.isNil() == false, "created font should not be nil", .{}, @src()); | ||||
| 
 | ||||
|     orca_image = oc.Image.createFromPath(surface, Str8.fromSlice("/orca_jumping.jpg"), false); | ||||
|     oc.assert(oc.Image.nil().isNil() == true, "nil image should be nil", .{}, @src()); | ||||
|     oc.assert(orca_image.isNil() == false, "created image should not be nil", .{}, @src()); | ||||
| 
 | ||||
|     // generate a gradient and upload it to an image | ||||
|     { | ||||
|         const width = 256; | ||||
|         const height = 128; | ||||
| 
 | ||||
|         const tl = oc.Color{ .r = 70.0 / 255.0, .g = 13.0 / 255.0, .b = 108.0 / 255.0 }; | ||||
|         const bl = oc.Color{ .r = 251.0 / 255.0, .g = 167.0 / 255.0, .b = 87.0 / 255.0 }; | ||||
|         const tr = oc.Color{ .r = 48.0 / 255.0, .g = 164.0 / 255.0, .b = 219.0 / 255.0 }; | ||||
|         const br = oc.Color{ .r = 151.0 / 255.0, .g = 222.0 / 255.0, .b = 150.0 / 255.0 }; | ||||
| 
 | ||||
|         var pixels: [width * height]u32 = undefined; | ||||
|         for (0..height) |y| { | ||||
|             for (0..width) |x| { | ||||
|                 const h: f32 = @floatFromInt(height - 1); | ||||
|                 const w: f32 = @floatFromInt(width - 1); | ||||
|                 const y_norm: f32 = @as(f32, @floatFromInt(y)) / h; | ||||
|                 const x_norm: f32 = @as(f32, @floatFromInt(x)) / w; | ||||
| 
 | ||||
|                 const tl_weight = (1 - x_norm) * (1 - y_norm); | ||||
|                 const bl_weight = (1 - x_norm) * y_norm; | ||||
|                 const tr_weight = x_norm * (1 - y_norm); | ||||
|                 const br_weight = x_norm * y_norm; | ||||
| 
 | ||||
|                 const r: f32 = tl_weight * tl.r + bl_weight * bl.r + tr_weight * tr.r + br_weight * br.r; | ||||
|                 const g: f32 = tl_weight * tl.g + bl_weight * bl.g + tr_weight * tr.g + br_weight * br.g; | ||||
|                 const b: f32 = tl_weight * tl.b + bl_weight * bl.b + tr_weight * tr.b + br_weight * br.b; | ||||
|                 const color = oc.Color{ .r = r, .g = g, .b = b, .a = 1.0 }; | ||||
|                 pixels[y * width + x] = color.toRgba8(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         gradient_image = oc.Image.create(surface, width, height); | ||||
|         gradient_image.uploadRegionRgba8(oc.Rect.xywh(0, 0, width, height), @ptrCast((&pixels).ptr)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_resize(width: u32, height: u32) void { | ||||
|     frame_size = Vec2{ .x = @floatFromInt(width), .y = @floatFromInt(height) }; | ||||
|     oc.log.info("frame resize: {d:.2}, {d:.2}", .{ frame_size.x, frame_size.y }, @src()); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_mouse_down(button: oc.MouseButton) void { | ||||
|     oc.log.info("mouse down! {}", .{button}, @src()); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_mouse_up(button: oc.MouseButton) void { | ||||
|     oc.log.info("mouse up! {}", .{button}, @src()); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_mouse_wheel(dx: f32, dy: f32) void { | ||||
|     oc.log.info("mouse wheel! dx: {d:.2}, dy: {d:.2}", .{ dx, dy }, @src()); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_key_down(scan: oc.ScanCode, key: oc.KeyCode) void { | ||||
|     oc.log.info("key down: {} {}", .{ scan, key }, @src()); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_key_up(scan: oc.ScanCode, key: oc.KeyCode) void { | ||||
|     oc.log.info("key up: {} {}", .{ scan, key }, @src()); | ||||
| 
 | ||||
|     switch (key) { | ||||
|         oc.KeyCode.Escape => oc.requestQuit(), | ||||
|         oc.KeyCode.B => oc.abort("aborting", .{}, @src()), | ||||
|         oc.KeyCode.A => oc.assert(false, "test assert failed", .{}, @src()), | ||||
|         oc.KeyCode.W => oc.log.warn("logging a test warning", .{}, @src()), | ||||
|         oc.KeyCode.E => oc.log.err("logging a test error", .{}, @src()), | ||||
|         else => {}, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_frame_refresh() void { | ||||
|     counter += 1; | ||||
| 
 | ||||
|     const secs: f64 = oc.clock.time(.Date); | ||||
| 
 | ||||
|     if (last_seconds != @floor(secs)) { | ||||
|         last_seconds = @floor(secs); | ||||
|         oc.log.info("seconds since Jan 1, 1970: {d:.0}", .{secs}, @src()); | ||||
|     } | ||||
| 
 | ||||
|     _ = canvas.select(); | ||||
|     oc.Canvas.setColorRgba(0.05, 0.05, 0.05, 1.0); | ||||
|     oc.Canvas.clear(); | ||||
| 
 | ||||
|     oc.Canvas.setColorRgba(1.0, 0.05, 0.05, 1.0); | ||||
| 
 | ||||
|     { | ||||
|         const translation: Mat2x3 = .{ .m = [_]f32{ 1, 0, 50, 0, 1, 50 } }; | ||||
|         Mat2x3.push(translation); | ||||
|         defer Mat2x3.pop(); | ||||
| 
 | ||||
|         oc.assert(std.meta.eql(Mat2x3.top(), translation), "top of matrix stack should be what we pushed", .{}, @src()); | ||||
|         oc.Canvas.setWidth(1); | ||||
|         oc.Canvas.rectangleFill(50, 0, 10, 10); | ||||
|         oc.Canvas.rectangleStroke(70, 0, 10, 10); | ||||
|         oc.Canvas.roundedRectangleFill(90, 0, 10, 10, 3); | ||||
|         oc.Canvas.roundedRectangleStroke(110, 0, 10, 10, 3); | ||||
| 
 | ||||
|         const green = oc.Color{ .r = 0.05, .g = 1, .b = 0.05, .a = 1 }; | ||||
|         oc.Canvas.setColor(green); | ||||
|         oc.assert(std.meta.eql(oc.Canvas.getColor(), green), "color should be green", .{}, @src()); | ||||
| 
 | ||||
|         oc.Canvas.setTolerance(1); | ||||
|         oc.Canvas.setJoint(.Bevel); | ||||
|         oc.Canvas.ellipseFill(140, 5, 10, 5); | ||||
|         oc.Canvas.ellipseStroke(170, 5, 10, 5); | ||||
|         oc.Canvas.circleFill(195, 5, 5); | ||||
|         oc.Canvas.circleStroke(215, 5, 5); | ||||
| 
 | ||||
|         oc.Canvas.arc(230, 5, 5, 0, std.math.pi); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         rotation_demo += 0.03; | ||||
| 
 | ||||
|         const rot = Mat2x3.rotate(rotation_demo); | ||||
|         const trans = Mat2x3.translate(285, 55); | ||||
|         Mat2x3.push(Mat2x3.mul_m(trans, rot)); | ||||
|         defer Mat2x3.pop(); | ||||
| 
 | ||||
|         oc.Canvas.rectangleFill(-5, -5, 10, 10); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         var scratch_scope = oc.Arena.scratchBegin(); | ||||
|         defer scratch_scope.end(); | ||||
| 
 | ||||
|         var scratch: *oc.Arena = scratch_scope.arena; | ||||
| 
 | ||||
|         var str1: Str8 = Str8.collate(scratch, &[_][]const u8{ "Hello", "from", "Zig!" }, ">> ", " ", " <<") catch |e| fatal(e, @src()); | ||||
| 
 | ||||
|         var str2_list = oc.Str8List.init(); | ||||
|         str2_list.push(scratch, Str8.fromSlice("All")) catch |e| fatal(e, @src()); | ||||
|         str2_list.pushf(scratch, "your", .{}) catch |e| fatal(e, @src()); | ||||
|         str2_list.pushSlice(scratch, "base!!") catch |e| fatal(e, @src()); | ||||
| 
 | ||||
|         oc.assert(str2_list.containsSlice("All"), "str2_list should have the string we just pushed", .{}, @src()); | ||||
| 
 | ||||
|         { | ||||
|             var elt_first = str2_list.list.first; | ||||
|             var elt_last = str2_list.list.last; | ||||
|             oc.assert(elt_first != null, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_last != null, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_first != elt_last, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_first.?.next != null, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_first.?.prev == null, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_last.?.next == null, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_last.?.prev != null, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_first.?.next != elt_last, "list checks", .{}, @src()); | ||||
|             oc.assert(elt_last.?.prev != elt_first, "list checks", .{}, @src()); | ||||
|         } | ||||
| 
 | ||||
|         var str2: Str8 = str2_list.collate(scratch, Str8.fromSlice("<< "), Str8.fromSlice("-"), Str8.fromSlice(" >>")) catch |e| fatal(e, @src()); | ||||
| 
 | ||||
|         const font_size = 18; | ||||
|         const text_metrics = font.textMetrics(font_size, str1); | ||||
|         const text_rect = text_metrics.ink; | ||||
| 
 | ||||
|         const center_x = frame_size.x / 2; | ||||
|         const text_begin_x = center_x - text_rect.w / 2; | ||||
| 
 | ||||
|         Mat2x3.push(Mat2x3.translate(text_begin_x, 100)); | ||||
|         defer Mat2x3.pop(); | ||||
| 
 | ||||
|         oc.Canvas.setColorRgba(1.0, 0.05, 0.05, 1.0); | ||||
|         oc.Canvas.setFont(font); | ||||
|         oc.Canvas.setFontSize(font_size); | ||||
|         oc.Canvas.moveTo(0, 0); | ||||
|         oc.Canvas.textOutlines(str1); | ||||
|         oc.Canvas.moveTo(0, 35); | ||||
|         oc.Canvas.textOutlines(str2); | ||||
|         oc.Canvas.fill(); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         var scratch_scope = oc.Arena.scratchBegin(); | ||||
|         defer scratch_scope.end(); | ||||
| 
 | ||||
|         var scratch: *oc.Arena = scratch_scope.arena; | ||||
| 
 | ||||
|         var separators = oc.Str8List.init(); | ||||
|         separators.pushSlice(scratch, " ") catch |e| fatal(e, @src()); | ||||
|         separators.pushSlice(scratch, "|") catch |e| fatal(e, @src()); | ||||
|         separators.pushSlice(scratch, "-") catch |e| fatal(e, @src()); | ||||
| 
 | ||||
|         const big_string = Str8.fromSlice("This is |a one-word string that  |  has no      spaces in it"); | ||||
|         var strings: oc.Str8List = big_string.split(scratch, separators) catch |e| fatal(e, @src()); | ||||
|         var collated = strings.join(scratch) catch |e| fatal(e, @src()); | ||||
| 
 | ||||
|         oc.Canvas.setFontSize(12); | ||||
|         oc.Canvas.moveTo(0, 170); | ||||
|         oc.Canvas.textOutlines(collated); | ||||
|         oc.Canvas.fill(); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         const orca_size = orca_image.size(); | ||||
| 
 | ||||
|         { | ||||
|             const trans = Mat2x3.translate(0, 200); | ||||
|             const scale = Mat2x3.scaleUniform(0.25); | ||||
|             Mat2x3.push(Mat2x3.mul_m(trans, scale)); | ||||
|             defer Mat2x3.pop(); | ||||
| 
 | ||||
|             orca_image.draw(oc.Rect.xywh(0, 0, orca_size.x, orca_size.y)); | ||||
| 
 | ||||
|             var half_size = orca_size; | ||||
|             half_size.x /= 2; | ||||
|             orca_image.drawRegion(oc.Rect.xywh(0, 0, half_size.x, half_size.y), oc.Rect.xywh(orca_size.x + 10, 0, half_size.x, half_size.y)); | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             const x_offset = orca_size.x * 0.25 + orca_size.x * 0.25 * 0.5 + 5; | ||||
|             const gradient_size = gradient_image.size(); | ||||
| 
 | ||||
|             const trans = Mat2x3.translate(x_offset, 200); | ||||
|             const scale = Mat2x3.scaleUniform((orca_size.y * 0.25) / gradient_size.y); | ||||
|             Mat2x3.push(Mat2x3.mul_m(trans, scale)); | ||||
|             defer Mat2x3.pop(); | ||||
| 
 | ||||
|             gradient_image.draw(oc.Rect.xywh(0, 0, gradient_size.x, gradient_size.y)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     surface.select(); | ||||
|     canvas.render(); | ||||
|     surface.present(); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_terminate() void { | ||||
|     oc.log.info("byebye {}", .{counter}, @src()); | ||||
| } | ||||
| 
 | ||||
| fn fatal(err: anyerror, source: std.builtin.SourceLocation) noreturn { | ||||
|     oc.abort("Caught fatal {}", .{err}, source); | ||||
|     unreachable; | ||||
| } | ||||
| 
 | ||||
| fn oneMinusLerp(a: anytype, b: anytype, t: anytype) @TypeOf(a, b, t) { | ||||
|     return 1.0 - lerp(a, b, t); | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| UI | ||||
| zig-out | ||||
| zig-cache | ||||
| liborca.a | ||||
| profile.dtrace | ||||
| profile.spall | ||||
|  | @ -0,0 +1,13 @@ | |||
| ### Build and run | ||||
| Zig version `0.11.0` or greater is required for this sample. To build and run: | ||||
| ``` | ||||
| orca dev build-runtime | ||||
| zig build run | ||||
| ``` | ||||
| 
 | ||||
| These two commands build the runtime - the native host executable - and the sample as a loadable wasm library, then runs it. To only build the sample without running it, use `zig build bundle`. | ||||
| 
 | ||||
| ### Warning | ||||
| Zig bindings for Orca are in-progress and experimental. You may encounter bugs since not all the bound APIs have been tested extensively - this sample is currently the only code doing so! Additionally, not all APIs have zig coverage yet, notably: | ||||
| * `gles` | ||||
| As more APIs get tested, there is a possibility of breaking changes. Please report any bugs you find on the Handmade discord in the #orca channel. | ||||
|  | @ -0,0 +1,107 @@ | |||
| const std = @import("std"); | ||||
| const builtin = @import("builtin"); | ||||
| 
 | ||||
| fn addSourceString(str: []const u8, strings: *std.ArrayList(u8), sources: *std.ArrayList([]const u8)) !void { | ||||
|     var begin = strings.items.len; | ||||
|     try strings.appendSlice(str); | ||||
|     var realstring = strings.items[begin..]; | ||||
|     try sources.append(realstring); | ||||
| } | ||||
| 
 | ||||
| pub fn build(b: *std.Build) !void { | ||||
|     const optimize = b.standardOptimizeOption(.{}); | ||||
|     var wasm_target = std.zig.CrossTarget{ | ||||
|         .cpu_arch = .wasm32, | ||||
|         .os_tag = .freestanding, | ||||
|     }; | ||||
|     wasm_target.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory)); | ||||
| 
 | ||||
|     var orca_source_strings = try std.ArrayList(u8).initCapacity(b.allocator, 1024 * 4); | ||||
|     var orca_sources = try std.ArrayList([]const u8).initCapacity(b.allocator, 128); | ||||
|     defer orca_source_strings.deinit(); | ||||
|     defer orca_sources.deinit(); | ||||
| 
 | ||||
|     { | ||||
|         try addSourceString("../../src/orca.c", &orca_source_strings, &orca_sources); | ||||
| 
 | ||||
|         var libc_shim_dir = try std.fs.cwd().openIterableDir("../../src/libc-shim/src", .{}); | ||||
|         var walker = try libc_shim_dir.walk(b.allocator); | ||||
|         defer walker.deinit(); | ||||
| 
 | ||||
|         while (try walker.next()) |entry| { | ||||
|             const extension = std.fs.path.extension(entry.path); | ||||
|             if (std.mem.eql(u8, extension, ".c")) { | ||||
|                 var path_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | ||||
|                 var abs_path = try libc_shim_dir.dir.realpath(entry.path, &path_buffer); | ||||
|                 try addSourceString(abs_path, &orca_source_strings, &orca_sources); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const orca_compile_opts = [_][]const u8{ | ||||
|         "-D__ORCA__", | ||||
|         "--no-standard-libraries", | ||||
|         "-fno-builtin", | ||||
|         "-g", | ||||
|         "-O2", | ||||
|         "-mexec-model=reactor", | ||||
|         "-fno-sanitize=undefined", | ||||
|         "-isystem ../../src/libc-shim/include", | ||||
|         "-I../../src", | ||||
|         "-I../../src/ext", | ||||
|         "-Wl,--export-dynamic", | ||||
|     }; | ||||
| 
 | ||||
|     var orca_lib = b.addStaticLibrary(.{ | ||||
|         .name = "orca", | ||||
|         .target = wasm_target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
|     orca_lib.rdynamic = true; | ||||
|     orca_lib.addIncludePath(.{ .path = "../../src" }); | ||||
|     orca_lib.addIncludePath(.{ .path = "../../src/libc-shim/include" }); | ||||
|     orca_lib.addIncludePath(.{ .path = "../../src/ext" }); | ||||
|     orca_lib.addCSourceFiles(orca_sources.items, &orca_compile_opts); | ||||
| 
 | ||||
|     // builds the wasm module out of the orca C sources and main.zig | ||||
|     const orca_module: *std.Build.Module = b.createModule(.{ | ||||
|         .source_file = .{ .path = "../../src/orca.zig" }, | ||||
|     }); | ||||
|     const wasm_lib = b.addSharedLibrary(.{ | ||||
|         .name = "module", | ||||
|         .root_source_file = .{ .path = "src/main.zig" }, | ||||
|         .target = wasm_target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
|     wasm_lib.rdynamic = true; | ||||
|     wasm_lib.addIncludePath(.{ .path = "../../src" }); | ||||
|     wasm_lib.addIncludePath(.{ .path = "../../src/libc-shim/include" }); | ||||
|     wasm_lib.addIncludePath(.{ .path = "../../ext" }); | ||||
|     wasm_lib.addModule("orca", orca_module); | ||||
|     wasm_lib.linkLibrary(orca_lib); | ||||
| 
 | ||||
|     // copies the wasm module into zig-out/wasm_lib | ||||
|     b.installArtifact(wasm_lib); | ||||
| 
 | ||||
|     // Runs the orca build command | ||||
|     const bundle_cmd_str = [_][]const u8{ "orca", "bundle", "--orca-dir", "../..", "--name", "UI", "--resource-dir", "data", "zig-out/lib/module.wasm" }; | ||||
|     var bundle_cmd = b.addSystemCommand(&bundle_cmd_str); | ||||
|     bundle_cmd.step.dependOn(b.getInstallStep()); | ||||
| 
 | ||||
|     const bundle_step = b.step("bundle", "Runs the orca toolchain to bundle the wasm module into an orca app."); | ||||
|     bundle_step.dependOn(&bundle_cmd.step); | ||||
| 
 | ||||
|     // Runs the app | ||||
|     const run_cmd_windows = [_][]const u8{"UI/bin/UI.exe"}; | ||||
|     const run_cmd_macos = [_][]const u8{ "open", "UI.app" }; | ||||
|     const run_cmd_str: []const []const u8 = switch (builtin.os.tag) { | ||||
|         .windows => &run_cmd_windows, | ||||
|         .macos => &run_cmd_macos, | ||||
|         else => @compileError("unsupported platform"), | ||||
|     }; | ||||
|     var run_cmd = b.addSystemCommand(run_cmd_str); | ||||
|     run_cmd.step.dependOn(&bundle_cmd.step); | ||||
| 
 | ||||
|     const run_step = b.step("run", "Runs the bundled app using the Orca runtime."); | ||||
|     run_step.dependOn(&run_cmd.step); | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,871 @@ | |||
| const std = @import("std"); | ||||
| const oc = @import("orca"); | ||||
| const ui = oc.ui; | ||||
| 
 | ||||
| var frame_size: oc.Vec2 = .{ .x = 1200, .y = 838 }; | ||||
| 
 | ||||
| var surface: oc.Surface = undefined; | ||||
| var canvas: oc.Canvas = undefined; | ||||
| var font_regular: oc.Font = undefined; | ||||
| var font_bold: oc.Font = undefined; | ||||
| var ui_ctx: ui.Context = undefined; | ||||
| var text_arena: oc.Arena = undefined; | ||||
| var log_arena: oc.Arena = undefined; | ||||
| var log_lines: oc.Str8List = undefined; | ||||
| 
 | ||||
| const Cmd = enum { | ||||
|     None, | ||||
|     SetDarkTheme, | ||||
|     SetLightTheme, | ||||
| }; | ||||
| var cmd: Cmd = .None; | ||||
| 
 | ||||
| export fn oc_on_init() void { | ||||
|     oc.windowSetTitle("Orca Zig UI Demo"); | ||||
|     oc.windowSetSize(frame_size); | ||||
| 
 | ||||
|     surface = oc.Surface.canvas(); | ||||
|     canvas = oc.Canvas.create(); | ||||
|     ui.init(&ui_ctx); | ||||
| 
 | ||||
|     var fonts = [_]*oc.Font{ &font_regular, &font_bold }; | ||||
|     var font_names = [_][]const u8{ "/OpenSans-Regular.ttf", "/OpenSans-Bold.ttf" }; | ||||
|     for (fonts, font_names) |font, name| { | ||||
|         var scratch = oc.Arena.scratchBegin(); | ||||
|         defer scratch.end(); | ||||
| 
 | ||||
|         var file = oc.File.open(oc.Str8.fromSlice(name), .{ .read = true }, .{}); | ||||
|         if (file.lastError() != oc.io.Error.Ok) { | ||||
|             oc.log.err("Couldn't open file {s}", .{name}, @src()); | ||||
|         } | ||||
| 
 | ||||
|         var size = file.getSize(); | ||||
|         var buffer = scratch.arena.push(size) catch { | ||||
|             oc.log.err("Out of memory", .{}, @src()); | ||||
|             return; | ||||
|         }; | ||||
|         _ = file.read(size, buffer); | ||||
|         file.close(); | ||||
| 
 | ||||
|         var ranges = oc.UnicodeRange.range(&[_]oc.UnicodeRange.Enum{ | ||||
|             .BasicLatin, | ||||
|             .C1ControlsAndLatin1Supplement, | ||||
|             .LatinExtendedA, | ||||
|             .LatinExtendedB, | ||||
|             .Specials, | ||||
|         }); | ||||
| 
 | ||||
|         font.* = oc.Font.createFromMemory(buffer[0..size], &ranges); | ||||
|     } | ||||
| 
 | ||||
|     text_arena = oc.Arena.init(); | ||||
|     log_arena = oc.Arena.init(); | ||||
|     log_lines = oc.Str8List.init(); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_raw_event(event: *oc.CEvent) void { | ||||
|     ui.processCEvent(event); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_resize(width: u32, height: u32) void { | ||||
|     frame_size.x = @floatFromInt(width); | ||||
|     frame_size.y = @floatFromInt(height); | ||||
| } | ||||
| 
 | ||||
| export fn oc_on_frame_refresh() void { | ||||
|     var scratch = oc.Arena.scratchBegin(); | ||||
|     defer scratch.end(); | ||||
| 
 | ||||
|     switch (cmd) { | ||||
|         .SetDarkTheme => ui.setTheme(ui.dark_theme), | ||||
|         .SetLightTheme => ui.setTheme(ui.light_theme), | ||||
|         .None => {}, | ||||
|     } | ||||
|     cmd = .None; | ||||
| 
 | ||||
|     var default_style = ui.Style{ .font = font_regular }; | ||||
|     { | ||||
|         ui.beginFrame(frame_size, &default_style); | ||||
|         defer ui.endFrame(); | ||||
| 
 | ||||
|         //-------------------------------------------------------------------------------------------- | ||||
|         // Menu bar | ||||
|         //-------------------------------------------------------------------------------------------- | ||||
|         { | ||||
|             ui.menuBarBegin("menu_bar"); | ||||
|             defer ui.menuBarEnd(); | ||||
| 
 | ||||
|             { | ||||
|                 ui.menuBegin("File"); | ||||
|                 defer ui.menuEnd(); | ||||
| 
 | ||||
|                 if (ui.menuButton("Quit").pressed) { | ||||
|                     oc.requestQuit(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.menuBegin("Theme"); | ||||
|                 defer ui.menuEnd(); | ||||
| 
 | ||||
|                 if (ui.menuButton("Dark theme").pressed) { | ||||
|                     cmd = .SetDarkTheme; | ||||
|                 } | ||||
|                 if (ui.menuButton("Light theme").pressed) { | ||||
|                     cmd = .SetLightTheme; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             ui.panelBegin("main panel", .{}); | ||||
|             defer ui.panelEnd(); | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ | ||||
|                     .size = .{ | ||||
|                         .width = .fill_parent, | ||||
|                         .height = .{ .custom = .{ .kind = .Parent, .value = 1, .relax = 1 } }, | ||||
|                     }, | ||||
|                     .layout = .{ | ||||
|                         .axis = .X, | ||||
|                         .margin = .{ .x = 16, .y = 16 }, | ||||
|                         .spacing = 16, | ||||
|                     }, | ||||
|                 }); | ||||
|                 _ = ui.boxBegin("Background", .{ .draw_background = true }); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 widgets(scratch.arena); | ||||
| 
 | ||||
|                 styling(scratch.arena); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _ = canvas.select(); | ||||
|     surface.select(); | ||||
| 
 | ||||
|     oc.Canvas.setColor(ui_ctx.theme.bg0); | ||||
|     oc.Canvas.clear(); | ||||
| 
 | ||||
|     ui.draw(); | ||||
|     canvas.render(); | ||||
|     surface.present(); | ||||
| } | ||||
| 
 | ||||
| var checkbox_checked: bool = false; | ||||
| var v_slider_value: f32 = 0; | ||||
| var v_slider_logged_value: f32 = 0; | ||||
| var v_slider_log_time: f64 = 0; | ||||
| var radio_selected: usize = 0; | ||||
| var h_slider_value: f32 = 0; | ||||
| var h_slider_logged_value: f32 = 0; | ||||
| var h_slider_log_time: f64 = 0; | ||||
| var text: []const u8 = "Text box"; | ||||
| var selected: ?usize = null; | ||||
| 
 | ||||
| fn widgets(arena: *oc.Arena) void { | ||||
|     columnBegin("Widgets", 1.0 / 3.0); | ||||
|     defer columnEnd(); | ||||
| 
 | ||||
|     { | ||||
|         ui.styleNext(.{ | ||||
|             .size = .{ | ||||
|                 .width = .fill_parent, | ||||
|             }, | ||||
|             .layout = .{ | ||||
|                 .axis = .X, | ||||
|                 .spacing = 32, | ||||
|             }, | ||||
|         }); | ||||
|         _ = ui.boxBegin("top", .{}); | ||||
|         defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|         { | ||||
|             ui.styleNext(.{ | ||||
|                 .layout = .{ | ||||
|                     .axis = .Y, | ||||
|                     .spacing = 24, | ||||
|                 }, | ||||
|             }); | ||||
|             _ = ui.boxBegin("top_left", .{}); | ||||
|             defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|             //----------------------------------------------------------------------------- | ||||
|             // Label | ||||
|             //----------------------------------------------------------------------------- | ||||
|             _ = ui.makeLabel("Label"); | ||||
| 
 | ||||
|             //----------------------------------------------------------------------------- | ||||
|             // Button | ||||
|             //----------------------------------------------------------------------------- | ||||
|             if (ui.button("Button").clicked) { | ||||
|                 logPush("Button clicked"); | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ | ||||
|                     .layout = .{ | ||||
|                         .axis = .X, | ||||
|                         .alignment = .{ .y = .Center }, | ||||
|                         .spacing = 8, | ||||
|                     }, | ||||
|                 }); | ||||
|                 _ = ui.boxBegin("checkbox", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 //------------------------------------------------------------------------- | ||||
|                 // Checkbox | ||||
|                 //------------------------------------------------------------------------- | ||||
|                 if (ui.checkbox("checkbox", &checkbox_checked).clicked) { | ||||
|                     if (checkbox_checked) { | ||||
|                         logPush("Checkbox checked"); | ||||
|                     } else { | ||||
|                         logPush("Checkbox unhecked"); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 _ = ui.makeLabel("Checkbox"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //--------------------------------------------------------------------------------- | ||||
|         // Vertical slider | ||||
|         //--------------------------------------------------------------------------------- | ||||
|         ui.styleNext(.{ .size = .{ .height = .{ .pixels = 130 } } }); | ||||
|         _ = ui.slider("v_slider", &v_slider_value); | ||||
| 
 | ||||
|         var now = oc.clock.time(.Monotonic); | ||||
|         if ((now - v_slider_log_time) >= 0.2 and v_slider_value != v_slider_logged_value) { | ||||
|             logPushf("Vertical slider moved to {d:.3}", .{v_slider_value}); | ||||
|             v_slider_logged_value = v_slider_value; | ||||
|             v_slider_log_time = now; | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             ui.styleNext(.{ | ||||
|                 .layout = .{ | ||||
|                     .axis = .Y, | ||||
|                     .spacing = 24, | ||||
|                 }, | ||||
|             }); | ||||
|             _ = ui.boxBegin("top right", .{}); | ||||
|             defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|             //----------------------------------------------------------------------------- | ||||
|             // Tooltip | ||||
|             //----------------------------------------------------------------------------- | ||||
|             if (ui.makeLabel("Tooltip").hovering) { | ||||
|                 ui.tooltip("Hi"); | ||||
|             } | ||||
| 
 | ||||
|             //----------------------------------------------------------------------------- | ||||
|             // Radio group | ||||
|             //----------------------------------------------------------------------------- | ||||
|             var options = [_][]const u8{ | ||||
|                 "Radio 1", | ||||
|                 "Radio 2", | ||||
|             }; | ||||
|             var radio_group_info = ui.RadioGroupInfo{ | ||||
|                 .selected_index = radio_selected, | ||||
|                 .options = &options, | ||||
|             }; | ||||
|             var result = ui.radioGroup("radio_group", &radio_group_info); | ||||
|             radio_selected = result.selected_index.?; | ||||
|             if (result.changed) { | ||||
|                 logPushf("Selected {s}", .{options[radio_selected]}); | ||||
|             } | ||||
| 
 | ||||
|             //----------------------------------------------------------------------------- | ||||
|             // Horizontal slider | ||||
|             //----------------------------------------------------------------------------- | ||||
|             ui.styleNext(.{ .size = .{ .width = .{ .pixels = 130 } } }); | ||||
|             _ = ui.slider("h_slider", &h_slider_value); | ||||
| 
 | ||||
|             if ((now - h_slider_log_time) >= 0.2 and h_slider_value != h_slider_logged_value) { | ||||
|                 logPushf("Slider moved to {d:.3}", .{h_slider_value}); | ||||
|                 h_slider_logged_value = h_slider_value; | ||||
|                 h_slider_log_time = now; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //------------------------------------------------------------------------------------- | ||||
|     // Text box | ||||
|     //------------------------------------------------------------------------------------- | ||||
|     ui.styleNext(.{ | ||||
|         .size = .{ | ||||
|             .width = .{ .pixels = 305 }, | ||||
|             .height = .text, | ||||
|         }, | ||||
|     }); | ||||
|     var textResult = ui.textBox("text", arena, text); | ||||
|     if (textResult.changed) { | ||||
|         text_arena.clear(); | ||||
|         text = text_arena.pushStr(textResult.text) catch { | ||||
|             oc.log.err("Out of memory", .{}, @src()); | ||||
|             oc.requestQuit(); | ||||
|             return; | ||||
|         }; | ||||
|     } | ||||
|     if (textResult.accepted) { | ||||
|         logPushf("Entered text {s}", .{text}); | ||||
|     } | ||||
| 
 | ||||
|     //------------------------------------------------------------------------------------- | ||||
|     // Select | ||||
|     //------------------------------------------------------------------------------------- | ||||
|     var options = [_][]const u8{ | ||||
|         "Option 1", | ||||
|         "Option 2", | ||||
|     }; | ||||
|     var select_popup_info = ui.SelectPopupInfo{ | ||||
|         .selected_index = selected, | ||||
|         .options = &options, | ||||
|         .placeholder = "Select", | ||||
|     }; | ||||
|     var selectResult = ui.selectPopup("select", &select_popup_info); | ||||
|     if (selectResult.selected_index != selected) { | ||||
|         logPushf("Selected {s}", .{options[selectResult.selected_index.?]}); | ||||
|     } | ||||
|     selected = selectResult.selected_index; | ||||
| 
 | ||||
|     //------------------------------------------------------------------------------------- | ||||
|     // Scrollable panel | ||||
|     //------------------------------------------------------------------------------------- | ||||
|     { | ||||
|         ui.styleNext(.{ | ||||
|             .size = .{ | ||||
|                 .width = .fill_parent, | ||||
|                 .height = .{ | ||||
|                     .custom = .{ .kind = .Parent, .value = 1, .relax = 1, .min_size = 200 }, | ||||
|                 }, | ||||
|             }, | ||||
|             .bg_color = ui_ctx.theme.bg2, | ||||
|             .border_color = ui_ctx.theme.border, | ||||
|             .border_size = 1, | ||||
|             .roundness = ui_ctx.theme.roundness_small, | ||||
|         }); | ||||
|         _ = ui.panelBegin("log", .{ .draw_background = true, .draw_border = true }); | ||||
|         defer ui.panelEnd(); | ||||
| 
 | ||||
|         { | ||||
|             ui.styleNext(.{ | ||||
|                 .layout = .{ | ||||
|                     .margin = .{ .x = 16, .y = 16 }, | ||||
|                 }, | ||||
|             }); | ||||
|             _ = ui.boxBegin("contents", .{}); | ||||
|             defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|             if (log_lines.list.empty()) { | ||||
|                 ui.styleNext(.{ .color = ui_ctx.theme.text2 }); | ||||
|                 _ = ui.makeLabel("Log"); | ||||
|             } | ||||
| 
 | ||||
|             var i: i32 = 0; | ||||
|             var log_lines_iter = log_lines.iter(); | ||||
|             while (log_lines_iter.next()) |log_line| { | ||||
|                 var buf: [15]u8 = undefined; | ||||
|                 var id = std.fmt.bufPrint(&buf, "{d}", .{i}) catch unreachable; | ||||
|                 _ = ui.boxBegin(id, .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 _ = ui.makeLabel(log_line.string.slice()); | ||||
| 
 | ||||
|                 i += 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| var styling_selected_radio: ?usize = 0; | ||||
| var unselected_width: f32 = 16; | ||||
| var unselected_height: f32 = 16; | ||||
| var unselected_roundness: f32 = 8; | ||||
| var unselected_bg_color: oc.Color = oc.Color.rgba(0.086, 0.086, 0.102, 1); | ||||
| var unselected_border_color: oc.Color = oc.Color.rgba(0.976, 0.976, 0.976, 0.35); | ||||
| var unselected_border_size: f32 = 1; | ||||
| var unselected_when_status: ui.Status = .{}; | ||||
| var unselected_status_index: ?usize = 0; | ||||
| var selected_width: f32 = 16; | ||||
| var selected_height: f32 = 16; | ||||
| var selected_roundness: f32 = 8; | ||||
| var selected_center_color: oc.Color = oc.Color.rgba(1, 1, 1, 1); | ||||
| var selected_bg_color: oc.Color = oc.Color.rgba(0.33, 0.66, 1, 1); | ||||
| var selected_when_status: ui.Status = .{}; | ||||
| var selected_status_index: ?usize = 0; | ||||
| var label_font_color: oc.Color = oc.Color.rgba(0.976, 0.976, 0.976, 1); | ||||
| var label_font_color_selected: ?usize = 0; | ||||
| var label_font: *oc.Font = &font_regular; | ||||
| var label_font_selected: ?usize = 0; | ||||
| var label_font_size: f32 = 14; | ||||
| 
 | ||||
| fn styling(arena: *oc.Arena) void { | ||||
|     //----------------------------------------------------------------------------------------- | ||||
|     // Styling | ||||
|     //----------------------------------------------------------------------------------------- | ||||
|     // Initial values here are hardcoded from the dark theme and everything is overridden all | ||||
|     // the time. In a real program you'd only override what you need and supply the values from | ||||
|     // ui_ctx.theme or ui_ctx.theme.palette. | ||||
|     // | ||||
|     // Rule-based styling is described at | ||||
|     // https://www.forkingpaths.dev/posts/23-03-10/rule_based_styling_imgui_ctx.html | ||||
|     columnBegin("Styling", 2.0 / 3.0); | ||||
|     defer columnEnd(); | ||||
| 
 | ||||
|     { | ||||
|         ui.styleNext(.{ | ||||
|             .size = .{ | ||||
|                 .width = .fill_parent, | ||||
|                 .height = .{ .pixels = 152 }, | ||||
|             }, | ||||
|             .layout = .{ | ||||
|                 .margin = .{ .x = 310, .y = 16 }, | ||||
|             }, | ||||
|             .bg_color = ui.dark_theme.bg0, | ||||
|             .roundness = ui.dark_theme.roundness_small, | ||||
|         }); | ||||
|         _ = ui.boxBegin("styled_radios", .{ .draw_background = true, .draw_border = true }); | ||||
|         defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|         resetNextRadioGroupToDarkTheme(arena); | ||||
| 
 | ||||
|         var unselected_tag = ui.Tag.make("radio"); | ||||
|         var unselected_pattern = ui.Pattern.init(); | ||||
|         unselected_pattern.push(arena, .{ .sel = .{ .tag = unselected_tag } }); | ||||
|         if (!unselected_when_status.empty()) { | ||||
|             unselected_pattern.push(arena, .{ .op = .And, .sel = .{ .status = unselected_when_status } }); | ||||
|         } | ||||
|         ui.styleMatchAfter(unselected_pattern, .{ | ||||
|             .size = .{ | ||||
|                 .width = .{ .pixels = unselected_width }, | ||||
|                 .height = .{ .pixels = unselected_height }, | ||||
|             }, | ||||
|             .bg_color = unselected_bg_color, | ||||
|             .border_color = unselected_border_color, | ||||
|             .border_size = unselected_border_size, | ||||
|             .roundness = unselected_roundness, | ||||
|         }); | ||||
| 
 | ||||
|         var selected_tag = ui.Tag.make("radio_selected"); | ||||
|         var selected_pattern = ui.Pattern.init(); | ||||
|         selected_pattern.push(arena, .{ .sel = .{ .tag = selected_tag } }); | ||||
|         if (!selected_when_status.empty()) { | ||||
|             selected_pattern.push(arena, .{ .op = .And, .sel = .{ .status = selected_when_status } }); | ||||
|         } | ||||
|         ui.styleMatchAfter(selected_pattern, .{ | ||||
|             .size = .{ | ||||
|                 .width = .{ .pixels = selected_width }, | ||||
|                 .height = .{ .pixels = selected_height }, | ||||
|             }, | ||||
|             .color = selected_center_color, | ||||
|             .bg_color = selected_bg_color, | ||||
|             .roundness = selected_roundness, | ||||
|         }); | ||||
| 
 | ||||
|         var label_tag = ui.Tag.make("label"); | ||||
|         var label_pattern = ui.Pattern.init(); | ||||
|         label_pattern.push(arena, .{ .sel = .{ .tag = label_tag } }); | ||||
|         ui.styleMatchAfter(label_pattern, .{ | ||||
|             .color = label_font_color, | ||||
|             .font = label_font.*, | ||||
|             .font_size = label_font_size, | ||||
|         }); | ||||
| 
 | ||||
|         var options = [_][]const u8{ | ||||
|             "I", | ||||
|             "Am", | ||||
|             "Stylish", | ||||
|         }; | ||||
|         var radio_group_info = ui.RadioGroupInfo{ | ||||
|             .selected_index = styling_selected_radio, | ||||
|             .options = &options, | ||||
|         }; | ||||
|         var result = ui.radioGroup("radio_group", &radio_group_info); | ||||
|         styling_selected_radio = result.selected_index; | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         ui.styleNext(.{ .layout = .{ .axis = .X, .spacing = 32 } }); | ||||
|         _ = ui.boxBegin("controls", .{}); | ||||
|         defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|         { | ||||
|             ui.styleNext(.{ .layout = .{ .axis = .Y, .spacing = 16 } }); | ||||
|             _ = ui.boxBegin("unselected", .{}); | ||||
|             defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|             ui.styleNext(.{ .font_size = 16 }); | ||||
|             _ = ui.makeLabel("Radio style"); | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 4 } }); | ||||
|                 _ = ui.boxBegin("size", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 var width_slider = (unselected_width - 8) / 16; | ||||
|                 labeledSlider("Width", &width_slider); | ||||
|                 unselected_width = 8 + width_slider * 16; | ||||
| 
 | ||||
|                 var height_slider = (unselected_height - 8) / 16; | ||||
|                 labeledSlider("Height", &height_slider); | ||||
|                 unselected_height = 8 + height_slider * 16; | ||||
| 
 | ||||
|                 var roundness_slider = (unselected_roundness - 4) / 8; | ||||
|                 labeledSlider("Roundness", &roundness_slider); | ||||
|                 unselected_roundness = 4 + roundness_slider * 8; | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 4 } }); | ||||
|                 _ = ui.boxBegin("background", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 labeledSlider("Background R", &unselected_bg_color.r); | ||||
|                 labeledSlider("Background G", &unselected_bg_color.g); | ||||
|                 labeledSlider("Background B", &unselected_bg_color.b); | ||||
|                 labeledSlider("Background A", &unselected_bg_color.a); | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 4 } }); | ||||
|                 _ = ui.boxBegin("border", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 labeledSlider("Border R", &unselected_border_color.r); | ||||
|                 labeledSlider("Border G", &unselected_border_color.g); | ||||
|                 labeledSlider("Border B", &unselected_border_color.b); | ||||
|                 labeledSlider("Border A", &unselected_border_color.a); | ||||
|             } | ||||
| 
 | ||||
|             var border_size_slider = unselected_border_size / 5; | ||||
|             labeledSlider("Border size", &border_size_slider); | ||||
|             unselected_border_size = border_size_slider * 5; | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 10 } }); | ||||
|                 _ = ui.boxBegin("status_override", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 _ = ui.makeLabel("Override"); | ||||
| 
 | ||||
|                 var status_options = [_][]const u8{ | ||||
|                     "Always", | ||||
|                     "When hovering", | ||||
|                     "When active", | ||||
|                 }; | ||||
|                 var status_info = ui.RadioGroupInfo{ | ||||
|                     .selected_index = unselected_status_index, | ||||
|                     .options = &status_options, | ||||
|                 }; | ||||
|                 var status_result = ui.radioGroup("status", &status_info); | ||||
|                 unselected_status_index = status_result.selected_index; | ||||
|                 unselected_when_status = switch (unselected_status_index.?) { | ||||
|                     0 => .{}, | ||||
|                     1 => .{ .hover = true }, | ||||
|                     2 => .{ .active = true }, | ||||
|                     else => unreachable, | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             ui.styleNext(.{ .layout = .{ .axis = .Y, .spacing = 16 } }); | ||||
|             _ = ui.boxBegin("selected", .{}); | ||||
|             defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|             ui.styleNext(.{ .font_size = 16 }); | ||||
|             _ = ui.makeLabel("Radio selected style"); | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 4 } }); | ||||
|                 _ = ui.boxBegin("size", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 var width_slider = (selected_width - 8) / 16; | ||||
|                 labeledSlider("Width", &width_slider); | ||||
|                 selected_width = 8 + width_slider * 16; | ||||
| 
 | ||||
|                 var height_slider = (selected_height - 8) / 16; | ||||
|                 labeledSlider("Height", &height_slider); | ||||
|                 selected_height = 8 + height_slider * 16; | ||||
| 
 | ||||
|                 var roundness_slider = (selected_roundness - 4) / 8; | ||||
|                 labeledSlider("Roundness", &roundness_slider); | ||||
|                 selected_roundness = 4 + roundness_slider * 8; | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 4 } }); | ||||
|                 _ = ui.boxBegin("color", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 labeledSlider("Center R", &selected_center_color.r); | ||||
|                 labeledSlider("Center G", &selected_center_color.g); | ||||
|                 labeledSlider("Center B", &selected_center_color.b); | ||||
|                 labeledSlider("Center A", &selected_center_color.a); | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 4 } }); | ||||
|                 _ = ui.boxBegin("background", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 labeledSlider("Background R", &selected_bg_color.r); | ||||
|                 labeledSlider("Background G", &selected_bg_color.g); | ||||
|                 labeledSlider("Background B", &selected_bg_color.b); | ||||
|                 labeledSlider("Background A", &selected_bg_color.a); | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .spacing = 10 } }); | ||||
|                 _ = ui.boxBegin("status_override", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 ui.styleNext(.{ .size = .{ .height = .{ .pixels = 30 } } }); | ||||
|                 _ = ui.boxMake("spacer", .{}); | ||||
| 
 | ||||
|                 _ = ui.makeLabel("Override"); | ||||
| 
 | ||||
|                 var status_options = [_][]const u8{ | ||||
|                     "Always", | ||||
|                     "When hovering", | ||||
|                     "When active", | ||||
|                 }; | ||||
|                 var status_info = ui.RadioGroupInfo{ | ||||
|                     .selected_index = selected_status_index, | ||||
|                     .options = &status_options, | ||||
|                 }; | ||||
|                 var status_result = ui.radioGroup("status", &status_info); | ||||
|                 selected_status_index = status_result.selected_index; | ||||
|                 selected_when_status = switch (selected_status_index.?) { | ||||
|                     0 => .{}, | ||||
|                     1 => .{ .hover = true }, | ||||
|                     2 => .{ .active = true }, | ||||
|                     else => unreachable, | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             ui.styleNext(.{ .layout = .{ .axis = .Y, .spacing = 10 } }); | ||||
|             _ = ui.boxBegin("label", .{}); | ||||
|             defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|             ui.styleNext(.{ .font_size = 16 }); | ||||
|             _ = ui.makeLabel("Label style"); | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .axis = .X, .spacing = 8 } }); | ||||
|                 _ = ui.boxBegin("font_color", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 ui.styleMatchAfter(ui.Pattern.owner(), .{ | ||||
|                     .size = .{ .width = .{ .pixels = 100 } }, | ||||
|                 }); | ||||
|                 _ = ui.makeLabel("Font color"); | ||||
| 
 | ||||
|                 var color_names = [_][]const u8{ | ||||
|                     "Default", | ||||
|                     "Red", | ||||
|                     "Orange", | ||||
|                     "Amber", | ||||
|                     "Yellow", | ||||
|                     "Lime", | ||||
|                     "Light green", | ||||
|                     "Green", | ||||
|                 }; | ||||
|                 var colors = [_]oc.Color{ | ||||
|                     ui.dark_theme.text0, | ||||
|                     ui.dark_theme.palette.red5, | ||||
|                     ui.dark_theme.palette.orange5, | ||||
|                     ui.dark_theme.palette.amber5, | ||||
|                     ui.dark_theme.palette.yellow5, | ||||
|                     ui.dark_theme.palette.lime5, | ||||
|                     ui.dark_theme.palette.light_green5, | ||||
|                     ui.dark_theme.palette.green5, | ||||
|                 }; | ||||
|                 var color_info = ui.SelectPopupInfo{ | ||||
|                     .selected_index = label_font_color_selected, | ||||
|                     .options = &color_names, | ||||
|                 }; | ||||
|                 var color_result = ui.selectPopup("color", &color_info); | ||||
|                 label_font_color_selected = color_result.selected_index; | ||||
|                 label_font_color = colors[label_font_color_selected.?]; | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 ui.styleNext(.{ .layout = .{ .axis = .X, .spacing = 8 } }); | ||||
|                 _ = ui.boxBegin("font", .{}); | ||||
|                 defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|                 ui.styleMatchAfter(ui.Pattern.owner(), .{ | ||||
|                     .size = .{ .width = .{ .pixels = 100 } }, | ||||
|                 }); | ||||
|                 _ = ui.makeLabel("Font"); | ||||
| 
 | ||||
|                 var font_names = [_][]const u8{ | ||||
|                     "Regular", | ||||
|                     "Bold", | ||||
|                 }; | ||||
|                 var fonts = [_]*oc.Font{ | ||||
|                     &font_regular, | ||||
|                     &font_bold, | ||||
|                 }; | ||||
|                 var font_info = ui.SelectPopupInfo{ | ||||
|                     .selected_index = label_font_selected, | ||||
|                     .options = &font_names, | ||||
|                 }; | ||||
|                 var font_result = ui.selectPopup("font_style", &font_info); | ||||
|                 label_font_selected = font_result.selected_index; | ||||
|                 label_font = fonts[label_font_selected.?]; | ||||
|             } | ||||
| 
 | ||||
|             var font_size_slider = (label_font_size - 8) / 16; | ||||
|             labeledSlider("Font size", &font_size_slider); | ||||
|             label_font_size = 8 + font_size_slider * 16; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn columnBegin(header: []const u8, widthFraction: f32) void { | ||||
|     ui.styleNext(.{ | ||||
|         .size = .{ | ||||
|             .width = .{ | ||||
|                 .custom = .{ .kind = .Parent, .value = widthFraction, .relax = 1 }, | ||||
|             }, | ||||
|             .height = .fill_parent, | ||||
|         }, | ||||
|         .layout = .{ | ||||
|             .axis = .Y, | ||||
|             .margin = .{ .y = 8 }, | ||||
|             .spacing = 24, | ||||
|         }, | ||||
|         .bg_color = ui_ctx.theme.bg1, | ||||
|         .border_color = ui_ctx.theme.border, | ||||
|         .border_size = 1, | ||||
|         .roundness = ui_ctx.theme.roundness_small, | ||||
|     }); | ||||
|     _ = ui.boxBegin(header, .{ .draw_background = true, .draw_border = true }); | ||||
| 
 | ||||
|     { | ||||
|         ui.styleNext(.{ | ||||
|             .size = .{ .width = .fill_parent }, | ||||
|             .layout = .{ .alignment = .{ .x = .Center } }, | ||||
|         }); | ||||
|         _ = ui.boxBegin("header", .{}); | ||||
|         defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|         ui.styleNext(.{ .font_size = 18 }); | ||||
|         _ = ui.makeLabel(header); | ||||
|     } | ||||
| 
 | ||||
|     ui.styleNext(.{ | ||||
|         .size = .{ | ||||
|             .width = .fill_parent, | ||||
|             .height = .{ | ||||
|                 .custom = .{ .kind = .Parent, .value = 1, .relax = 1 }, | ||||
|             }, | ||||
|         }, | ||||
|         .layout = .{ | ||||
|             .alignment = .{ .x = .Start }, | ||||
|             .margin = .{ .x = 16 }, | ||||
|             .spacing = 24, | ||||
|         }, | ||||
|     }); | ||||
|     _ = ui.boxBegin("contents", .{}); | ||||
| } | ||||
| 
 | ||||
| fn columnEnd() void { | ||||
|     _ = ui.boxEnd(); // contents | ||||
|     _ = ui.boxEnd(); // column | ||||
| } | ||||
| 
 | ||||
| fn labeledSlider(label: []const u8, value: *f32) void { | ||||
|     ui.styleNext(.{ .layout = .{ .axis = .X, .spacing = 8 } }); | ||||
|     _ = ui.boxBegin(label, .{}); | ||||
|     defer _ = ui.boxEnd(); | ||||
| 
 | ||||
|     ui.styleMatchAfter(ui.Pattern.owner(), .{ | ||||
|         .size = .{ .width = .{ .pixels = 100 } }, | ||||
|     }); | ||||
|     _ = ui.makeLabel(label); | ||||
| 
 | ||||
|     ui.styleNext(.{ | ||||
|         .size = .{ .width = .{ .pixels = 100 } }, | ||||
|     }); | ||||
|     _ = ui.slider("slider", value); | ||||
| } | ||||
| 
 | ||||
| fn logPush(line: []const u8) void { | ||||
|     log_lines.push(&log_arena, oc.Str8.fromSlice(line)) catch { | ||||
|         oc.log.err("Out of memory", .{}, @src()); | ||||
|         oc.requestQuit(); | ||||
|         return; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| fn logPushf(comptime fmt: []const u8, args: anytype) void { | ||||
|     var str = oc.Str8.pushf(&log_arena, fmt, args) catch { | ||||
|         oc.log.err("Out of memory", .{}, @src()); | ||||
|         oc.requestQuit(); | ||||
|         return; | ||||
|     }; | ||||
|     log_lines.push(&log_arena, str) catch { | ||||
|         oc.log.err("Out of memory", .{}, @src()); | ||||
|         oc.requestQuit(); | ||||
|         return; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// This makes sure the light theme doesn't break the styling overrides | ||||
| /// You won't need it in a real program as long as your colors come from ui_ctx.theme or ui_ctx.theme.palette | ||||
| fn resetNextRadioGroupToDarkTheme(arena: *oc.Arena) void { | ||||
|     var unselected_tag = ui.Tag.make("radio"); | ||||
|     var unselected_pattern = ui.Pattern.init(); | ||||
|     unselected_pattern.push(arena, .{ .sel = .{ .tag = unselected_tag } }); | ||||
|     ui.styleMatchAfter(unselected_pattern, .{ | ||||
|         .border_color = ui.dark_theme.text3, | ||||
|         .border_size = 1, | ||||
|     }); | ||||
| 
 | ||||
|     var unselected_hover_pattern = ui.Pattern.init(); | ||||
|     unselected_hover_pattern.push(arena, .{ .sel = .{ .tag = unselected_tag } }); | ||||
|     unselected_hover_pattern.push(arena, .{ .op = .And, .sel = .{ .status = .{ .hover = true } } }); | ||||
|     ui.styleMatchAfter(unselected_hover_pattern, .{ | ||||
|         .bg_color = ui.dark_theme.fill0, | ||||
|         .border_color = ui.dark_theme.primary, | ||||
|     }); | ||||
| 
 | ||||
|     var unselected_active_pattern = ui.Pattern.init(); | ||||
|     unselected_active_pattern.push(arena, .{ .sel = .{ .tag = unselected_tag } }); | ||||
|     unselected_active_pattern.push(arena, .{ .op = .And, .sel = .{ .status = .{ .active = true } } }); | ||||
|     ui.styleMatchAfter(unselected_active_pattern, .{ | ||||
|         .bg_color = ui.dark_theme.fill1, | ||||
|         .border_color = ui.dark_theme.primary, | ||||
|     }); | ||||
| 
 | ||||
|     var selected_tag = ui.Tag.make("radio_selected"); | ||||
|     var selected_pattern = ui.Pattern.init(); | ||||
|     selected_pattern.push(arena, .{ .sel = .{ .tag = selected_tag } }); | ||||
|     ui.styleMatchAfter(selected_pattern, .{ | ||||
|         .color = ui.dark_theme.palette.white, | ||||
|         .bg_color = ui.dark_theme.primary, | ||||
|     }); | ||||
| 
 | ||||
|     var selected_hover_pattern = ui.Pattern.init(); | ||||
|     selected_hover_pattern.push(arena, .{ .sel = .{ .tag = selected_tag } }); | ||||
|     selected_hover_pattern.push(arena, .{ .op = .And, .sel = .{ .status = .{ .hover = true } } }); | ||||
|     ui.styleMatchAfter(selected_hover_pattern, .{ | ||||
|         .bg_color = ui.dark_theme.primary_hover, | ||||
|     }); | ||||
| 
 | ||||
|     var selected_active_pattern = ui.Pattern.init(); | ||||
|     selected_active_pattern.push(arena, .{ .sel = .{ .tag = selected_tag } }); | ||||
|     selected_active_pattern.push(arena, .{ .op = .And, .sel = .{ .status = .{ .active = true } } }); | ||||
|     ui.styleMatchAfter(selected_active_pattern, .{ | ||||
|         .bg_color = ui.dark_theme.primary_active, | ||||
|     }); | ||||
| } | ||||
|  | @ -188,7 +188,7 @@ oc_key_code oc_scancode_to_keycode(oc_scan_code scanCode) | |||
|     return (oc_appData.keyMap[scanCode]); | ||||
| } | ||||
| 
 | ||||
| #define OC_DEFAULT_KEYMAP_ENTRY(sc, sv, ...) [(int) sc] = (oc_key_code)sc, | ||||
| #define OC_DEFAULT_KEYMAP_ENTRY(sc, sv, kc, ...) [(int) sc] = OC_VA_NOPT(sv, ##__VA_ARGS__) __VA_ARGS__, | ||||
| 
 | ||||
| oc_key_code oc_defaultKeyMap[OC_SCANCODE_COUNT] = { | ||||
|     OC_KEY_TABLE(OC_DEFAULT_KEYMAP_ENTRY) | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -8,11 +8,10 @@ | |||
| #include "platform.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| 	oc_host_platform oc_get_host_platform() | ||||
| oc_host_platform oc_get_host_platform(void) | ||||
| { | ||||
|     return OC_HOST_PLATFORM_MACOS; | ||||
| } | ||||
|  |  | |||
|  | @ -121,7 +121,7 @@ typedef enum | |||
|     OC_HOST_PLATFORM_WINDOWS, | ||||
| } oc_host_platform; | ||||
| 
 | ||||
| ORCA_API oc_host_platform oc_get_host_platform(); | ||||
| ORCA_API oc_host_platform oc_get_host_platform(void); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } // extern "C"
 | ||||
|  |  | |||
|  | @ -8,11 +8,10 @@ | |||
| #include "platform.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| { | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| 	oc_host_platform oc_get_host_platform() | ||||
| oc_host_platform oc_get_host_platform(void) | ||||
| { | ||||
|     return OC_HOST_PLATFORM_WINDOWS; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										107
									
								
								src/ui/ui.c
								
								
								
								
							
							
						
						
									
										107
									
								
								src/ui/ui.c
								
								
								
								
							|  | @ -1778,7 +1778,7 @@ oc_ui_sig oc_ui_button_behavior(oc_ui_box* box) | |||
|     { | ||||
|         oc_ui_box_set_hot(box, false); | ||||
|     } | ||||
|     if(!sig.hovering && !sig.dragging) | ||||
|     if(!sig.hovering || !sig.dragging) | ||||
|     { | ||||
|         oc_ui_box_deactivate(box); | ||||
|     } | ||||
|  | @ -1873,7 +1873,7 @@ void oc_ui_checkbox_draw(oc_ui_box* box, void* data) | |||
|     oc_matrix_pop(); | ||||
| } | ||||
| 
 | ||||
| oc_ui_sig oc_ui_checkbox(const char* name, bool* checked) | ||||
| oc_ui_sig oc_ui_checkbox_str8(oc_str8 name, bool* checked) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
|  | @ -1918,7 +1918,7 @@ oc_ui_sig oc_ui_checkbox(const char* name, bool* checked) | |||
|                           | OC_UI_FLAG_HOT_ANIMATION | ||||
|                           | OC_UI_FLAG_ACTIVE_ANIMATION; | ||||
| 
 | ||||
|         box = oc_ui_box_make(name, flags); | ||||
|         box = oc_ui_box_make_str8(name, flags); | ||||
|         oc_ui_tag_box(box, "checkbox"); | ||||
| 
 | ||||
|         oc_ui_box_set_draw_proc(box, oc_ui_checkbox_draw, 0); | ||||
|  | @ -1968,7 +1968,7 @@ oc_ui_sig oc_ui_checkbox(const char* name, bool* checked) | |||
|                           | OC_UI_FLAG_HOT_ANIMATION | ||||
|                           | OC_UI_FLAG_ACTIVE_ANIMATION; | ||||
| 
 | ||||
|         box = oc_ui_box_make(name, flags); | ||||
|         box = oc_ui_box_make_str8(name, flags); | ||||
|         oc_ui_tag_box(box, "checkbox"); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1981,16 +1981,21 @@ oc_ui_sig oc_ui_checkbox(const char* name, bool* checked) | |||
|     return (sig); | ||||
| } | ||||
| 
 | ||||
| oc_ui_sig oc_ui_checkbox(const char* name, bool* checked) | ||||
| { | ||||
|     return oc_ui_checkbox_str8(OC_STR8(name), checked); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // slider / scrollbar
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| oc_ui_box* oc_ui_slider(const char* label, f32* value) | ||||
| oc_ui_box* oc_ui_slider_str8(oc_str8 name, f32* value) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
| 
 | ||||
|     oc_ui_style_match_before(oc_ui_pattern_all(), &(oc_ui_style){ 0 }, OC_UI_STYLE_LAYOUT); | ||||
|     oc_ui_box* frame = oc_ui_box_begin(label, 0); | ||||
|     oc_ui_box* frame = oc_ui_box_begin_str8(name, 0); | ||||
|     { | ||||
|         oc_ui_axis trackAxis = (frame->rect.w > frame->rect.h) ? OC_UI_AXIS_X : OC_UI_AXIS_Y; | ||||
|         oc_ui_axis secondAxis = (trackAxis == OC_UI_AXIS_Y) ? OC_UI_AXIS_X : OC_UI_AXIS_Y; | ||||
|  | @ -2161,12 +2166,17 @@ oc_ui_box* oc_ui_slider(const char* label, f32* value) | |||
|     return (frame); | ||||
| } | ||||
| 
 | ||||
| oc_ui_box* oc_ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) | ||||
| oc_ui_box* oc_ui_slider(const char* name, f32* value) | ||||
| { | ||||
|     return oc_ui_slider_str8(OC_STR8(name), value); | ||||
| } | ||||
| 
 | ||||
| oc_ui_box* oc_ui_scrollbar_str8(oc_str8 name, f32 thumbRatio, f32* scrollValue) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
|     oc_ui_style_match_before(oc_ui_pattern_all(), &(oc_ui_style){ 0 }, OC_UI_STYLE_LAYOUT); | ||||
|     oc_ui_box* frame = oc_ui_box_begin(label, 0); | ||||
|     oc_ui_box* frame = oc_ui_box_begin_str8(name, 0); | ||||
|     { | ||||
|         f32 minThumbRatio = 17. / oc_max(frame->rect.w, frame->rect.h); | ||||
|         thumbRatio = oc_min(oc_max(thumbRatio, minThumbRatio), 1.); | ||||
|  | @ -2292,10 +2302,15 @@ oc_ui_box* oc_ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) | |||
|     return (frame); | ||||
| } | ||||
| 
 | ||||
| oc_ui_box* oc_ui_scrollbar(const char* name, f32 thumbRatio, f32* scrollValue) | ||||
| { | ||||
|     return oc_ui_scrollbar_str8(OC_STR8(name), thumbRatio, scrollValue); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // panels
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| void oc_ui_panel_begin(const char* str, oc_ui_flags flags) | ||||
| void oc_ui_panel_begin_str8(oc_str8 str, oc_ui_flags flags) | ||||
| { | ||||
|     flags = flags | ||||
|           | OC_UI_FLAG_CLIP | ||||
|  | @ -2311,7 +2326,7 @@ void oc_ui_panel_begin(const char* str, oc_ui_flags flags) | |||
|                                      .layout.margin.y = 0 }, | ||||
|                      OC_UI_STYLE_SIZE | ||||
|                          | OC_UI_STYLE_LAYOUT_MARGINS); | ||||
|     oc_ui_box_begin(str, flags); | ||||
|     oc_ui_box_begin_str8(str, flags); | ||||
| 
 | ||||
|     oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 }, | ||||
|                                      .size.height = { OC_UI_SIZE_PARENT, 1 } }, | ||||
|  | @ -2320,6 +2335,11 @@ void oc_ui_panel_begin(const char* str, oc_ui_flags flags) | |||
|     oc_ui_box_begin("contents", 0); | ||||
| } | ||||
| 
 | ||||
| void oc_ui_panel_begin(const char* str, oc_ui_flags flags) | ||||
| { | ||||
|     oc_ui_panel_begin_str8(OC_STR8(str), flags); | ||||
| } | ||||
| 
 | ||||
| void oc_ui_panel_end(void) | ||||
| { | ||||
|     oc_ui_box_end(); // contents
 | ||||
|  | @ -2413,7 +2433,7 @@ void oc_ui_tooltip_arrow_draw(oc_ui_box* box, void* data) | |||
|     oc_matrix_pop(); | ||||
| } | ||||
| 
 | ||||
| void oc_ui_tooltip(const char* label) | ||||
| void oc_ui_tooltip_str8(oc_str8 label) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
|  | @ -2424,7 +2444,7 @@ void oc_ui_tooltip(const char* label) | |||
|                                    .floatTarget.x = p.x, | ||||
|                                    .floatTarget.y = p.y }; | ||||
|     oc_ui_style_next(&containerStyle, OC_UI_STYLE_FLOAT); | ||||
|     oc_ui_container(label, OC_UI_FLAG_OVERLAY) | ||||
|     oc_ui_container_str8(label, OC_UI_FLAG_OVERLAY) | ||||
|     { | ||||
|         oc_ui_style arrowStyle = { .size.width = { OC_UI_SIZE_PIXELS, 24 }, | ||||
|                                    .size.height = { OC_UI_SIZE_PIXELS, 24 }, | ||||
|  | @ -2460,17 +2480,22 @@ void oc_ui_tooltip(const char* label) | |||
| 
 | ||||
|         oc_ui_box* contents = oc_ui_box_begin("contents", OC_UI_FLAG_DRAW_BACKGROUND); | ||||
| 
 | ||||
|         oc_ui_label(label); | ||||
|         oc_ui_label_str8(label); | ||||
| 
 | ||||
|         oc_ui_box_end(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void oc_ui_tooltip(const char* label) | ||||
| { | ||||
|     oc_ui_tooltip_str8(OC_STR8(label)); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // Menus
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| 
 | ||||
| void oc_ui_menu_bar_begin(const char* name) | ||||
| void oc_ui_menu_bar_begin_str8(oc_str8 name) | ||||
| { | ||||
|     oc_ui_style style = { | ||||
|         .size.width = { OC_UI_SIZE_PARENT, 1, 0 }, | ||||
|  | @ -2481,7 +2506,7 @@ void oc_ui_menu_bar_begin(const char* name) | |||
|                           | OC_UI_STYLE_LAYOUT_AXIS; | ||||
| 
 | ||||
|     oc_ui_style_next(&style, mask); | ||||
|     oc_ui_box* bar = oc_ui_box_begin(name, OC_UI_FLAG_DRAW_BACKGROUND); | ||||
|     oc_ui_box* bar = oc_ui_box_begin_str8(name, OC_UI_FLAG_DRAW_BACKGROUND); | ||||
| 
 | ||||
|     oc_ui_sig sig = oc_ui_box_sig(bar); | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|  | @ -2491,16 +2516,21 @@ void oc_ui_menu_bar_begin(const char* name) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void oc_ui_menu_bar_begin(const char* name) | ||||
| { | ||||
|     oc_ui_menu_bar_begin_str8(OC_STR8(name)); | ||||
| } | ||||
| 
 | ||||
| void oc_ui_menu_bar_end(void) | ||||
| { | ||||
|     oc_ui_box_end(); // menu bar
 | ||||
| } | ||||
| 
 | ||||
| void oc_ui_menu_begin(const char* label) | ||||
| void oc_ui_menu_begin_str8(oc_str8 label) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
|     oc_ui_box* container = oc_ui_box_make(label, 0); | ||||
|     oc_ui_box* container = oc_ui_box_make_str8(label, 0); | ||||
|     oc_ui_box_push(container); | ||||
| 
 | ||||
|     oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_CHILDREN }, | ||||
|  | @ -2525,7 +2555,7 @@ void oc_ui_menu_begin(const char* label) | |||
|     oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_TEXT }, | ||||
|                                      .size.height = { OC_UI_SIZE_TEXT } }, | ||||
|                      OC_UI_STYLE_SIZE); | ||||
|     oc_ui_box* buttonLabel = oc_ui_box_make(label, OC_UI_FLAG_DRAW_TEXT); | ||||
|     oc_ui_box* buttonLabel = oc_ui_box_make_str8(label, OC_UI_FLAG_DRAW_TEXT); | ||||
| 
 | ||||
|     oc_ui_box_end(); // button
 | ||||
| 
 | ||||
|  | @ -2592,13 +2622,18 @@ void oc_ui_menu_begin(const char* label) | |||
|     oc_ui_box_push(menu); | ||||
| } | ||||
| 
 | ||||
| void oc_ui_menu_begin(const char* label) | ||||
| { | ||||
|     oc_ui_menu_begin_str8(OC_STR8(label)); | ||||
| } | ||||
| 
 | ||||
| void oc_ui_menu_end(void) | ||||
| { | ||||
|     oc_ui_box_pop(); // menu
 | ||||
|     oc_ui_box_pop(); // container
 | ||||
| } | ||||
| 
 | ||||
| oc_ui_sig oc_ui_menu_button(const char* name) | ||||
| oc_ui_sig oc_ui_menu_button_str8(oc_str8 label) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
|  | @ -2627,11 +2662,16 @@ oc_ui_sig oc_ui_menu_button(const char* name) | |||
|                       | OC_UI_FLAG_DRAW_TEXT | ||||
|                       | OC_UI_FLAG_DRAW_BACKGROUND; | ||||
| 
 | ||||
|     oc_ui_box* box = oc_ui_box_make(name, flags); | ||||
|     oc_ui_box* box = oc_ui_box_make_str8(label, flags); | ||||
|     oc_ui_sig sig = oc_ui_box_sig(box); | ||||
|     return (sig); | ||||
| } | ||||
| 
 | ||||
| oc_ui_sig oc_ui_menu_button(const char* label) | ||||
| { | ||||
|     return oc_ui_menu_button_str8(OC_STR8(label)); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // Select
 | ||||
| //------------------------------------------------------------------------------
 | ||||
|  | @ -2689,14 +2729,14 @@ void oc_ui_select_popup_draw_checkmark(oc_ui_box* box, void* data) | |||
|     oc_matrix_pop(); | ||||
| } | ||||
| 
 | ||||
| oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_info* info) | ||||
| oc_ui_select_popup_info oc_ui_select_popup_str8(oc_str8 name, oc_ui_select_popup_info* info) | ||||
| { | ||||
|     oc_ui_select_popup_info result = *info; | ||||
| 
 | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
| 
 | ||||
|     oc_ui_container(name, 0) | ||||
|     oc_ui_container_str8(name, 0) | ||||
|     { | ||||
|         oc_ui_pattern hoverPattern = { 0 }; | ||||
|         oc_ui_pattern_push(&ui->frameArena, &hoverPattern, (oc_ui_selector){ .kind = OC_UI_SEL_STATUS, .status = OC_UI_HOVER }); | ||||
|  | @ -2908,6 +2948,11 @@ oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_ | |||
|     return (result); | ||||
| } | ||||
| 
 | ||||
| oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_info* info) | ||||
| { | ||||
|     return oc_ui_select_popup_str8(OC_STR8(name), info); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // Radio group
 | ||||
| //------------------------------------------------------------------------------
 | ||||
|  | @ -2926,7 +2971,7 @@ void oc_ui_radio_indicator_draw(oc_ui_box* box, void* data) | |||
|     oc_matrix_pop(); | ||||
| } | ||||
| 
 | ||||
| oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_info* info) | ||||
| oc_ui_radio_group_info oc_ui_radio_group_str8(oc_str8 name, oc_ui_radio_group_info* info) | ||||
| { | ||||
|     oc_ui_radio_group_info result = *info; | ||||
| 
 | ||||
|  | @ -2937,7 +2982,7 @@ oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_inf | |||
|                                      .layout.spacing = 12 }, | ||||
|                      OC_UI_STYLE_LAYOUT_AXIS | ||||
|                          | OC_UI_STYLE_LAYOUT_SPACING); | ||||
|     oc_ui_container(name, 0) | ||||
|     oc_ui_container_str8(name, 0) | ||||
|     { | ||||
|         for(int i = 0; i < info->optionCount; i++) | ||||
|         { | ||||
|  | @ -3036,6 +3081,11 @@ oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_inf | |||
|     return (result); | ||||
| } | ||||
| 
 | ||||
| oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_info* info) | ||||
| { | ||||
|     return oc_ui_radio_group_str8(OC_STR8(name), info); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // text box
 | ||||
| //------------------------------------------------------------------------------
 | ||||
|  | @ -3817,7 +3867,7 @@ void oc_ui_text_box_render(oc_ui_box* box, void* data) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 text) | ||||
| oc_ui_text_box_result oc_ui_text_box_str8(oc_str8 name, oc_arena* arena, oc_str8 text) | ||||
| { | ||||
|     oc_ui_context* ui = oc_ui_get_context(); | ||||
|     oc_ui_theme* theme = ui->theme; | ||||
|  | @ -3853,7 +3903,7 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 | |||
|     oc_ui_flags frameFlags = OC_UI_FLAG_CLICKABLE | ||||
|                            | OC_UI_FLAG_DRAW_BACKGROUND | ||||
|                            | OC_UI_FLAG_DRAW_BORDER; | ||||
|     oc_ui_box* frame = oc_ui_box_begin(name, frameFlags); | ||||
|     oc_ui_box* frame = oc_ui_box_begin_str8(name, frameFlags); | ||||
|     oc_ui_tag_box(frame, "frame"); | ||||
|     oc_font font = frame->style.font; | ||||
|     f32 fontSize = frame->style.fontSize; | ||||
|  | @ -4136,6 +4186,11 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 | |||
|     return (result); | ||||
| } | ||||
| 
 | ||||
| oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 text) | ||||
| { | ||||
|     return oc_ui_text_box_str8(OC_STR8(name), arena, text); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------------------------------
 | ||||
| // Themes
 | ||||
| // doc/UIColors.md has them visualized
 | ||||
|  |  | |||
							
								
								
									
										24
									
								
								src/ui/ui.h
								
								
								
								
							
							
						
						
									
										24
									
								
								src/ui/ui.h
								
								
								
								
							|  | @ -518,7 +518,6 @@ struct oc_ui_box | |||
|     oc_list beforeRules; | ||||
|     oc_list afterRules; | ||||
| 
 | ||||
|     //oc_ui_style_tag tag;
 | ||||
|     oc_ui_style* targetStyle; | ||||
|     oc_ui_style style; | ||||
|     u32 z; | ||||
|  | @ -632,8 +631,6 @@ typedef struct oc_ui_context | |||
|     i32 editWordSelectionInitialCursor; | ||||
|     i32 editWordSelectionInitialMark; | ||||
| 
 | ||||
|     bool clipboardRegistered; | ||||
| 
 | ||||
|     oc_ui_theme* theme; | ||||
| } oc_ui_context; | ||||
| 
 | ||||
|  | @ -731,39 +728,28 @@ ORCA_API void oc_ui_style_match_after(oc_ui_pattern pattern, oc_ui_style* style, | |||
| //-------------------------------------------------------------------------
 | ||||
| // Basic widget helpers
 | ||||
| //-------------------------------------------------------------------------
 | ||||
| enum | ||||
| { | ||||
|     OC_UI_STYLE_TAG_USER_MAX = 1 << 16, | ||||
|     OC_UI_STYLE_TAG_LABEL, | ||||
|     OC_UI_STYLE_TAG_BUTTON, | ||||
|     OC_UI_STYLE_TAG_SCROLLBAR, | ||||
|     OC_UI_STYLE_TAG_PANEL, | ||||
|     OC_UI_STYLE_TAG_TOOLTIP, | ||||
|     OC_UI_STYLE_TAG_MENU | ||||
| }; | ||||
| 
 | ||||
| ORCA_API oc_ui_sig oc_ui_label(const char* label); | ||||
| ORCA_API oc_ui_sig oc_ui_label_str8(oc_str8 label); | ||||
| 
 | ||||
| ORCA_API oc_ui_sig oc_ui_button(const char* label); | ||||
| ORCA_API oc_ui_sig oc_ui_checkbox(const char* name, bool* checked); | ||||
| ORCA_API oc_ui_box* oc_ui_slider(const char* label, f32* value); | ||||
| ORCA_API oc_ui_box* oc_ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue); | ||||
| ORCA_API oc_ui_box* oc_ui_slider(const char* name, f32* value); | ||||
| ORCA_API oc_ui_box* oc_ui_scrollbar(const char* name, f32 thumbRatio, f32* scrollValue); | ||||
| ORCA_API void oc_ui_tooltip(const char* label); | ||||
| 
 | ||||
| ORCA_API void oc_ui_panel_begin(const char* name, oc_ui_flags flags); | ||||
| ORCA_API void oc_ui_panel_end(void); | ||||
| #define oc_ui_panel(s, f) oc_defer_loop(oc_ui_panel_begin(s, f), oc_ui_panel_end()) | ||||
| 
 | ||||
| ORCA_API void oc_ui_menu_bar_begin(const char* label); | ||||
| ORCA_API void oc_ui_menu_bar_begin(const char* name); | ||||
| ORCA_API void oc_ui_menu_bar_end(void); | ||||
| #define oc_ui_menu_bar(name) oc_defer_loop(oc_ui_menu_bar_begin(name), oc_ui_menu_bar_end()) | ||||
| 
 | ||||
| ORCA_API void oc_ui_menu_begin(const char* label); | ||||
| ORCA_API void oc_ui_menu_end(void); | ||||
| #define oc_ui_menu(name) oc_defer_loop(oc_ui_menu_begin(name), oc_ui_menu_end()) | ||||
| #define oc_ui_menu(label) oc_defer_loop(oc_ui_menu_begin(label), oc_ui_menu_end()) | ||||
| 
 | ||||
| ORCA_API oc_ui_sig oc_ui_menu_button(const char* name); | ||||
| ORCA_API oc_ui_sig oc_ui_menu_button(const char* label); | ||||
| 
 | ||||
| typedef struct oc_ui_text_box_result | ||||
| { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue