Compare commits
No commits in common. "main" and "zig" have entirely different histories.
15 changed files with 528 additions and 3566 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/target
|
|
66
3rd-party/cimgui/build.zig
vendored
Normal file
66
3rd-party/cimgui/build.zig
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const dep_cimgui = b.dependency("cimgui", .{});
|
||||||
|
const dep_imgui = b.dependency("imgui", .{});
|
||||||
|
|
||||||
|
// create file tree for cimgui and imgui
|
||||||
|
const wf = b.addNamedWriteFiles("cimgui");
|
||||||
|
_ = wf.addCopyDirectory(dep_cimgui.path(""), "", .{});
|
||||||
|
_ = wf.addCopyDirectory(dep_imgui.path(""), "imgui", .{});
|
||||||
|
const root = wf.getDirectory();
|
||||||
|
|
||||||
|
// build cimgui as C/C++ library
|
||||||
|
const lib_cimgui = b.addStaticLibrary(.{
|
||||||
|
.name = "cimgui_clib",
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.link_libc = true,
|
||||||
|
});
|
||||||
|
lib_cimgui.linkLibCpp();
|
||||||
|
lib_cimgui.addCSourceFiles(.{
|
||||||
|
.root = root,
|
||||||
|
.files = &.{
|
||||||
|
b.pathJoin(&.{"cimgui.cpp"}),
|
||||||
|
b.pathJoin(&.{ "imgui", "imgui.cpp" }),
|
||||||
|
b.pathJoin(&.{ "imgui", "imgui_widgets.cpp" }),
|
||||||
|
b.pathJoin(&.{ "imgui", "imgui_draw.cpp" }),
|
||||||
|
b.pathJoin(&.{ "imgui", "imgui_tables.cpp" }),
|
||||||
|
b.pathJoin(&.{ "imgui", "imgui_demo.cpp" }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
lib_cimgui.addIncludePath(root);
|
||||||
|
|
||||||
|
// make cimgui available as artifact, this then allows to inject
|
||||||
|
// the Emscripten include path in another build.zig
|
||||||
|
b.installArtifact(lib_cimgui);
|
||||||
|
|
||||||
|
// lib compilation depends on file tree
|
||||||
|
lib_cimgui.step.dependOn(&wf.step);
|
||||||
|
|
||||||
|
// translate-c the cimgui.h file
|
||||||
|
// NOTE: always run this with the host target, that way we don't need to inject
|
||||||
|
// the Emscripten SDK include path into the translate-C step when building for WASM
|
||||||
|
const cimgui_h = dep_cimgui.path("cimgui.h");
|
||||||
|
const translateC = b.addTranslateC(.{
|
||||||
|
.root_source_file = cimgui_h,
|
||||||
|
.target = b.host,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
translateC.defineCMacroRaw("CIMGUI_DEFINE_ENUMS_AND_STRUCTS=\"\"");
|
||||||
|
const entrypoint = translateC.getOutput();
|
||||||
|
|
||||||
|
// build cimgui as a module with the header file as the entrypoint
|
||||||
|
const mod_cimgui = b.addModule("cimgui", .{
|
||||||
|
.root_source_file = entrypoint,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.link_libc = true,
|
||||||
|
.link_libcpp = true,
|
||||||
|
});
|
||||||
|
mod_cimgui.linkLibrary(lib_cimgui);
|
||||||
|
}
|
18
3rd-party/cimgui/build.zig.zon
vendored
Normal file
18
3rd-party/cimgui/build.zig.zon
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
.{
|
||||||
|
.name = "cimgui",
|
||||||
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.dependencies = .{
|
||||||
|
.cimgui = .{
|
||||||
|
.url = "git+https://github.com/cimgui/cimgui.git#1.90.7",
|
||||||
|
.hash = "1220d925a8374fbc3a21a5b46025fb867672d85b4099d2151bed607618e08b0cd71c",
|
||||||
|
},
|
||||||
|
.imgui = .{
|
||||||
|
.url = "git+https://github.com/ocornut/imgui.git#v1.90.7",
|
||||||
|
.hash = "122072b125179c7cbdbbee8fa81d22a1050a950ad61cfeefee8dc0dca5423b5d05b9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
3338
Cargo.lock
generated
3338
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
10
Cargo.toml
10
Cargo.toml
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "doggo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
iced = {version = "0.12.1"}
|
|
||||||
rfd = { version = "0.14.1", default-features = false, features = ["gtk3"] }
|
|
110
build.zig
Normal file
110
build.zig
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Build = std.Build;
|
||||||
|
const OptimizeMode = std.builtin.OptimizeMode;
|
||||||
|
const ResolvedTarget = Build.ResolvedTarget;
|
||||||
|
const Dependency = Build.Dependency;
|
||||||
|
const sokol = @import("sokol");
|
||||||
|
|
||||||
|
pub fn build(b: *Build) !void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// note that the sokol dependency is built with `.with_imgui_sokol = true`
|
||||||
|
const dep_sokol = b.dependency("sokol", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.with_sokol_imgui = true,
|
||||||
|
});
|
||||||
|
const dep_cimgui = b.dependency("cimgui", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
// inject the cimgui header search path into the sokol C library compile step
|
||||||
|
const cimgui_root = dep_cimgui.namedWriteFiles("cimgui").getDirectory();
|
||||||
|
dep_sokol.artifact("sokol_clib").addIncludePath(cimgui_root);
|
||||||
|
|
||||||
|
// from here on different handling for native vs wasm builds
|
||||||
|
if (target.result.isWasm()) {
|
||||||
|
try buildWasm(b, target, optimize, dep_sokol, dep_cimgui);
|
||||||
|
} else {
|
||||||
|
try buildNative(b, target, optimize, dep_sokol, dep_cimgui);
|
||||||
|
}
|
||||||
|
|
||||||
|
try buildTui(b, target, optimize);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buildNative(b: *Build, target: ResolvedTarget, optimize: OptimizeMode, dep_sokol: *Dependency, dep_cimgui: *Dependency) !void {
|
||||||
|
const demo = b.addExecutable(.{
|
||||||
|
.name = "demo",
|
||||||
|
.root_source_file = b.path("src/gui/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
demo.root_module.addImport("sokol", dep_sokol.module("sokol"));
|
||||||
|
demo.root_module.addImport("cimgui", dep_cimgui.module("cimgui"));
|
||||||
|
b.installArtifact(demo);
|
||||||
|
b.step("run-gui", "Run demo").dependOn(&b.addRunArtifact(demo).step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buildWasm(b: *Build, target: ResolvedTarget, optimize: OptimizeMode, dep_sokol: *Dependency, dep_cimgui: *Dependency) !void {
|
||||||
|
// build the main file into a library, this is because the WASM 'exe'
|
||||||
|
// needs to be linked in a separate build step with the Emscripten linker
|
||||||
|
const demo = b.addStaticLibrary(.{
|
||||||
|
.name = "demo",
|
||||||
|
.root_source_file = b.path("src/gui/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
demo.root_module.addImport("sokol", dep_sokol.module("sokol"));
|
||||||
|
demo.root_module.addImport("cimgui", dep_cimgui.module("cimgui"));
|
||||||
|
|
||||||
|
// get the Emscripten SDK dependency from the sokol dependency
|
||||||
|
const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
|
||||||
|
|
||||||
|
// need to inject the Emscripten system header include path into
|
||||||
|
// the cimgui C library otherwise the C/C++ code won't find
|
||||||
|
// C stdlib headers
|
||||||
|
const emsdk_incl_path = dep_emsdk.path("upstream/emscripten/cache/sysroot/include");
|
||||||
|
dep_cimgui.artifact("cimgui_clib").addSystemIncludePath(emsdk_incl_path);
|
||||||
|
|
||||||
|
// all C libraries need to depend on the sokol library, when building for
|
||||||
|
// WASM this makes sure that the Emscripten SDK has been setup before
|
||||||
|
// C compilation is attempted (since the sokol C library depends on the
|
||||||
|
// Emscripten SDK setup step)
|
||||||
|
dep_cimgui.artifact("cimgui_clib").step.dependOn(&dep_sokol.artifact("sokol_clib").step);
|
||||||
|
|
||||||
|
// create a build step which invokes the Emscripten linker
|
||||||
|
const link_step = try sokol.emLinkStep(b, .{
|
||||||
|
.lib_main = demo,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.emsdk = dep_emsdk,
|
||||||
|
.use_webgl2 = true,
|
||||||
|
.use_emmalloc = true,
|
||||||
|
.use_filesystem = false,
|
||||||
|
.shell_file_path = dep_sokol.path("src/sokol/web/shell.html").getPath(b),
|
||||||
|
});
|
||||||
|
// ...and a special run step to start the web build output via 'emrun'
|
||||||
|
const run = sokol.emRunStep(b, .{ .name = "demo", .emsdk = dep_emsdk });
|
||||||
|
run.step.dependOn(&link_step.step);
|
||||||
|
b.step("run-gui", "Run demo").dependOn(&run.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buildTui(b: *Build, target: ResolvedTarget, optimize: OptimizeMode) !void {
|
||||||
|
const tui = b.addExecutable(.{
|
||||||
|
.name = "demo",
|
||||||
|
.root_source_file = b.path("src/tui/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const vaxis_dep = b.dependency("vaxis", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
tui.root_module.addImport("vaxis", vaxis_dep.module("vaxis"));
|
||||||
|
|
||||||
|
b.installArtifact(tui);
|
||||||
|
b.step("run-tui", "Run TUI").dependOn(&b.addRunArtifact(tui).step);
|
||||||
|
}
|
22
build.zig.zon
Normal file
22
build.zig.zon
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.{
|
||||||
|
.name = "doggo",
|
||||||
|
.version = "0.1.0",
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
.dependencies = .{
|
||||||
|
.sokol = .{
|
||||||
|
.url = "git+https://github.com/floooh/sokol-zig.git#d9f3ef983b7021417cc91bd5788cd925ca06aa83",
|
||||||
|
.hash = "12200a472b4f1a3a6db1002f39f10ae0cccf58ada45bf25ddc5fd48384dd187f55ea",
|
||||||
|
},
|
||||||
|
.cimgui = .{
|
||||||
|
.path = "3rd-party/cimgui",
|
||||||
|
},
|
||||||
|
.vaxis = .{
|
||||||
|
.url = "git+https://github.com/rockorager/libvaxis?ref=main#235e0bb27bc89655d8c66fc6378b31d4a98a117a",
|
||||||
|
.hash = "1220fef553676a4c90035db27c13ad609d5823198a94cc971e95ab9c680e3dcd2df0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
46
flake.lock
generated
46
flake.lock
generated
|
@ -1,63 +1,23 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"fenix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1712211755,
|
|
||||||
"narHash": "sha256-KIJA4OvXFDXEeu7wstFDCxqZEfjaPQIowpzNww48TUw=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"rev": "39763c6e23a8423af316b85a74bad0cc5bc88d86",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1712163089,
|
"lastModified": 1720058333,
|
||||||
"narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=",
|
"narHash": "sha256-gM2RCi5XkxmcsZ44pUkKIYBiBMfZ6u7MdcZcykmccrs=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "fd281bd6b7d3e32ddfa399853946f782553163b5",
|
"rev": "6842b061970bf96965d66fcc86a28e1f719aae95",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fenix": "fenix",
|
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"rust-analyzer-src": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1712156296,
|
|
||||||
"narHash": "sha256-St7ZQrkrr5lmQX9wC1ZJAFxL8W7alswnyZk9d1se3Us=",
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"rev": "8e581ac348e223488622f4d3003cb2bd412bf27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"ref": "nightly",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
51
flake.nix
51
flake.nix
|
@ -1,51 +1,30 @@
|
||||||
{
|
{
|
||||||
description = "Doggo flake";
|
description = "Doggo flake";
|
||||||
|
|
||||||
inputs = {
|
outputs = { self, nixpkgs }:
|
||||||
fenix = {
|
|
||||||
url = "github:nix-community/fenix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, fenix, nixpkgs }:
|
|
||||||
let pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
let pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||||
in {
|
in {
|
||||||
devShell.x86_64-linux = with pkgs;
|
devShell.x86_64-linux = with pkgs;
|
||||||
mkShell rec {
|
mkShell {
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
just
|
just
|
||||||
pkg-config
|
zig
|
||||||
protobuf
|
zls
|
||||||
(fenix.packages.x86_64-linux.fromToolchainFile {
|
|
||||||
dir = ./.;
|
|
||||||
sha256 = "sha256-7QfkHty6hSrgNM0fspycYkRcB82eEqYa4CoAJ9qA3tU= ";
|
|
||||||
})
|
|
||||||
fenix.packages.x86_64-linux.rust-analyzer
|
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
fontconfig
|
lldb
|
||||||
|
|
||||||
vulkan-headers
|
# GUI
|
||||||
vulkan-loader
|
alsa-lib
|
||||||
|
xorg.libX11
|
||||||
|
xorg.libXcursor
|
||||||
|
xorg.libXi
|
||||||
|
xorg.libXext
|
||||||
|
xorg.libXrandr
|
||||||
|
xorg.libXinerama
|
||||||
libGL
|
libGL
|
||||||
|
libGLU
|
||||||
libxkbcommon
|
|
||||||
|
|
||||||
wayland
|
|
||||||
|
|
||||||
# rfd
|
|
||||||
gtk3
|
|
||||||
];
|
];
|
||||||
|
|
||||||
env = {
|
|
||||||
# WAYLAND_DISPLAY = ""; # Window has nor decoration on Wayland
|
|
||||||
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath buildInputs;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
BIN
fonts/icons.ttf
BIN
fonts/icons.ttf
Binary file not shown.
16
justfile
16
justfile
|
@ -1,15 +1,9 @@
|
||||||
alias b := build
|
alias b := build
|
||||||
build:
|
build:
|
||||||
cargo build
|
zig build
|
||||||
|
|
||||||
alias r := run
|
gui:
|
||||||
run:
|
zig build run-gui
|
||||||
nixVulkanIntel cargo run
|
|
||||||
|
|
||||||
fmt:
|
tui:
|
||||||
nix fmt flake.nix
|
zig build run-tui
|
||||||
|
|
||||||
# See https://github.com/zed-industries/zed/blob/main/docs/src/developing_zed__building_zed_linux.md
|
|
||||||
alias e := edit
|
|
||||||
edit:
|
|
||||||
WALYAND_DISPLAY= nixVulkanIntel zed .
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[toolchain]
|
|
||||||
channel = "stable"
|
|
||||||
profile = "default"
|
|
92
src/gui/main.zig
Normal file
92
src/gui/main.zig
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
const ig = @import("cimgui");
|
||||||
|
const sokol = @import("sokol");
|
||||||
|
const slog = sokol.log;
|
||||||
|
const sg = sokol.gfx;
|
||||||
|
const sapp = sokol.app;
|
||||||
|
const sglue = sokol.glue;
|
||||||
|
const simgui = sokol.imgui;
|
||||||
|
|
||||||
|
const state = struct {
|
||||||
|
var pass_action: sg.PassAction = .{};
|
||||||
|
};
|
||||||
|
|
||||||
|
export fn init() void {
|
||||||
|
// initialize sokol-gfx
|
||||||
|
sg.setup(.{
|
||||||
|
.environment = sglue.environment(),
|
||||||
|
.logger = .{ .func = slog.func },
|
||||||
|
});
|
||||||
|
// initialize sokol-imgui
|
||||||
|
simgui.setup(.{
|
||||||
|
.logger = .{ .func = slog.func },
|
||||||
|
});
|
||||||
|
|
||||||
|
// initial clear color
|
||||||
|
state.pass_action.colors[0] = .{
|
||||||
|
.load_action = .CLEAR,
|
||||||
|
.clear_value = .{ .r = 0.0, .g = 0.5, .b = 1.0, .a = 1.0 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn frame() void {
|
||||||
|
// call simgui.newFrame() before any ImGui calls
|
||||||
|
simgui.newFrame(.{
|
||||||
|
.width = sapp.width(),
|
||||||
|
.height = sapp.height(),
|
||||||
|
.delta_time = sapp.frameDuration(),
|
||||||
|
.dpi_scale = sapp.dpiScale(),
|
||||||
|
});
|
||||||
|
|
||||||
|
//=== UI CODE STARTS HERE
|
||||||
|
var menu_bar_size: ig.ImVec2 = undefined;
|
||||||
|
if (ig.igBeginMainMenuBar()) {
|
||||||
|
ig.igGetWindowSize(&menu_bar_size);
|
||||||
|
defer ig.igEndMainMenuBar();
|
||||||
|
if (ig.igBeginMenu("File", true)) {
|
||||||
|
defer ig.igEndMenu();
|
||||||
|
if (ig.igMenuItem_Bool("Open", "o", false, true)) {
|
||||||
|
// Do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const io = ig.igGetIO();
|
||||||
|
ig.igSetNextWindowPos(.{ .x = 0, .y = menu_bar_size.y }, ig.ImGuiCond_Always, .{ .x = 0, .y = 0 });
|
||||||
|
var content_size: ig.ImVec2 = io.*.DisplaySize;
|
||||||
|
content_size.y -= menu_bar_size.y;
|
||||||
|
ig.igSetNextWindowSize(content_size, ig.ImGuiCond_Always);
|
||||||
|
_ = ig.igBegin("Hello Dear ImGui!", 0, ig.ImGuiWindowFlags_NoMove | ig.ImGuiWindowFlags_NoResize | ig.ImGuiWindowFlags_NoTitleBar | ig.ImGuiWindowFlags_NoCollapse);
|
||||||
|
_ = ig.igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None);
|
||||||
|
ig.igEnd();
|
||||||
|
//=== UI CODE ENDS HERE
|
||||||
|
|
||||||
|
// call simgui.render() inside a sokol-gfx pass
|
||||||
|
sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() });
|
||||||
|
simgui.render();
|
||||||
|
sg.endPass();
|
||||||
|
sg.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn cleanup() void {
|
||||||
|
simgui.shutdown();
|
||||||
|
sg.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn event(ev: [*c]const sapp.Event) void {
|
||||||
|
// forward input events to sokol-imgui
|
||||||
|
_ = simgui.handleEvent(ev.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() void {
|
||||||
|
sapp.run(.{
|
||||||
|
.init_cb = init,
|
||||||
|
.frame_cb = frame,
|
||||||
|
.cleanup_cb = cleanup,
|
||||||
|
.event_cb = event,
|
||||||
|
.window_title = "Doggo",
|
||||||
|
.width = 800,
|
||||||
|
.height = 600,
|
||||||
|
.icon = .{ .sokol_default = true },
|
||||||
|
.logger = .{ .func = slog.func },
|
||||||
|
});
|
||||||
|
}
|
123
src/main.rs
123
src/main.rs
|
@ -1,123 +0,0 @@
|
||||||
use iced::{
|
|
||||||
alignment,
|
|
||||||
widget::{button, column, container, row, scrollable, text, text_input, Column},
|
|
||||||
Element, Length, Padding, Sandbox, Settings,
|
|
||||||
};
|
|
||||||
use rfd::FileDialog;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
enum Message {
|
|
||||||
InputValue(String),
|
|
||||||
Submitted,
|
|
||||||
DeleteItem(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is your model. It contains all the data needed for your application to work properly.
|
|
||||||
* The model can only be updated with the `update` function.
|
|
||||||
*/
|
|
||||||
struct GroceryList {
|
|
||||||
grocery_items: Vec<String>,
|
|
||||||
input_value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sandbox for GroceryList {
|
|
||||||
type Message = Message;
|
|
||||||
|
|
||||||
/* Initialize your app */
|
|
||||||
fn new() -> GroceryList {
|
|
||||||
Self {
|
|
||||||
grocery_items: vec!["Eggs".to_owned(), "Milk".to_owned(), "Flour".to_owned()],
|
|
||||||
input_value: String::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The title of the window. It will show up on the top of your application window.
|
|
||||||
*/
|
|
||||||
fn title(&self) -> String {
|
|
||||||
String::from("Grocery List App")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, message: Self::Message) {
|
|
||||||
match message {
|
|
||||||
Message::InputValue(value) => self.input_value = value,
|
|
||||||
Message::Submitted => {
|
|
||||||
let input_value = self.input_value.clone();
|
|
||||||
self.input_value = String::default(); // Clear the input value
|
|
||||||
let _file = FileDialog::new()
|
|
||||||
.set_directory("/home/ffreling/Sync")
|
|
||||||
.pick_file();
|
|
||||||
let file_str = _file.and_then(|p| Some(String::from(p.to_str().unwrap())));
|
|
||||||
println!("{:?}", file_str);
|
|
||||||
match file_str {
|
|
||||||
None => (),
|
|
||||||
Some(path_str) => self.grocery_items.push(path_str),
|
|
||||||
}
|
|
||||||
// self.grocery_items.push(input_value);
|
|
||||||
}
|
|
||||||
Message::DeleteItem(item) => {
|
|
||||||
let _files = FileDialog::new()
|
|
||||||
.set_directory("/home/ffreling/Sync")
|
|
||||||
.pick_file();
|
|
||||||
self.grocery_items.remove(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view(&self) -> Element<Self::Message> {
|
|
||||||
container(
|
|
||||||
column!(
|
|
||||||
items_list_view(&self.grocery_items),
|
|
||||||
row!(
|
|
||||||
text_input("Input grocery item", &self.input_value)
|
|
||||||
.on_input(|value| Message::InputValue(value))
|
|
||||||
.on_submit(Message::Submitted),
|
|
||||||
button("Submit").on_press(Message::Submitted)
|
|
||||||
)
|
|
||||||
.spacing(30)
|
|
||||||
.padding(Padding::from(30))
|
|
||||||
)
|
|
||||||
.align_items(iced::Alignment::Center),
|
|
||||||
)
|
|
||||||
.height(Length::Fill)
|
|
||||||
.width(Length::Fill)
|
|
||||||
.align_x(alignment::Horizontal::Center)
|
|
||||||
.align_y(alignment::Vertical::Center)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn theme(&self) -> iced::Theme {
|
|
||||||
iced::Theme::Dark
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn items_list_view(items: &Vec<String>) -> Element<'static, Message> {
|
|
||||||
let mut column = Column::new()
|
|
||||||
.spacing(20)
|
|
||||||
.align_items(iced::Alignment::Center)
|
|
||||||
.width(Length::Fill);
|
|
||||||
|
|
||||||
for (index, value) in items.into_iter().enumerate() {
|
|
||||||
column = column.push(grocery_item(index, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollable(container(column))
|
|
||||||
.height(250.0)
|
|
||||||
.width(300)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grocery_item(index: usize, value: &str) -> Element<'static, Message> {
|
|
||||||
row!(
|
|
||||||
text(value),
|
|
||||||
button("Delete").on_press(Message::DeleteItem(index))
|
|
||||||
)
|
|
||||||
.align_items(iced::Alignment::Center)
|
|
||||||
.spacing(30)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
|
||||||
GroceryList::run(Settings::default())
|
|
||||||
}
|
|
196
src/tui/main.zig
Normal file
196
src/tui/main.zig
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
// const std = @import("std");
|
||||||
|
|
||||||
|
// pub fn main() !void {
|
||||||
|
// const stdout = std.io.getStdOut().writer();
|
||||||
|
// try stdout.print("Can spawn process: {}\n", .{std.process.can_spawn});
|
||||||
|
|
||||||
|
// var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
// const alloc = gpa.allocator();
|
||||||
|
// var lldb_child = std.process.Child.init(&[_][]const u8{"lldb-vscode"}, alloc);
|
||||||
|
// _ = try lldb_child.spawnAndWait();
|
||||||
|
// }
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const vaxis = @import("vaxis");
|
||||||
|
|
||||||
|
/// Set the default panic handler to the vaxis panic_handler. This will clean up the terminal if any
|
||||||
|
/// panics occur
|
||||||
|
pub const panic = vaxis.panic_handler;
|
||||||
|
|
||||||
|
/// Set some scope levels for the vaxis scopes
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_scope_levels = &.{
|
||||||
|
.{ .scope = .vaxis, .level = .warn },
|
||||||
|
.{ .scope = .vaxis_parser, .level = .warn },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Tagged union of all events our application will handle. These can be generated by Vaxis or your
|
||||||
|
/// own custom events
|
||||||
|
const Event = union(enum) {
|
||||||
|
key_press: vaxis.Key,
|
||||||
|
key_release: vaxis.Key,
|
||||||
|
mouse: vaxis.Mouse,
|
||||||
|
focus_in, // window has gained focus
|
||||||
|
focus_out, // window has lost focus
|
||||||
|
paste_start, // bracketed paste start
|
||||||
|
paste_end, // bracketed paste end
|
||||||
|
paste: []const u8, // osc 52 paste, caller must free
|
||||||
|
color_report: vaxis.Color.Report, // osc 4, 10, 11, 12 response
|
||||||
|
color_scheme: vaxis.Color.Scheme, // light / dark OS theme changes
|
||||||
|
winsize: vaxis.Winsize, // the window size has changed. This event is always sent when the loop
|
||||||
|
// is started
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The application state
|
||||||
|
const MyApp = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
// A flag for if we should quit
|
||||||
|
should_quit: bool,
|
||||||
|
/// The tty we are talking to
|
||||||
|
tty: vaxis.Tty,
|
||||||
|
/// The vaxis instance
|
||||||
|
vx: vaxis.Vaxis,
|
||||||
|
/// A mouse event that we will handle in the draw cycle
|
||||||
|
mouse: ?vaxis.Mouse,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) !MyApp {
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.should_quit = false,
|
||||||
|
.tty = try vaxis.Tty.init(),
|
||||||
|
.vx = try vaxis.init(allocator, .{}),
|
||||||
|
.mouse = null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *MyApp) void {
|
||||||
|
// Deinit takes an optional allocator. You can choose to pass an allocator to clean up
|
||||||
|
// memory, or pass null if your application is shutting down and let the OS clean up the
|
||||||
|
// memory
|
||||||
|
self.vx.deinit(self.allocator, self.tty.anyWriter());
|
||||||
|
self.tty.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self: *MyApp) !void {
|
||||||
|
// Initialize our event loop. This particular loop requires intrusive init
|
||||||
|
var loop: vaxis.Loop(Event) = .{
|
||||||
|
.tty = &self.tty,
|
||||||
|
.vaxis = &self.vx,
|
||||||
|
};
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
|
// Start the event loop. Events will now be queued
|
||||||
|
try loop.start();
|
||||||
|
|
||||||
|
try self.vx.enterAltScreen(self.tty.anyWriter());
|
||||||
|
|
||||||
|
// Query the terminal to detect advanced features, such as kitty keyboard protocol, etc.
|
||||||
|
// This will automatically enable the features in the screen you are in, so you will want to
|
||||||
|
// call it after entering the alt screen if you are a full screen application. The second
|
||||||
|
// arg is a timeout for the terminal to send responses. Typically the response will be very
|
||||||
|
// fast, however it could be slow on ssh connections.
|
||||||
|
try self.vx.queryTerminal(self.tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
|
// Enable mouse events
|
||||||
|
try self.vx.setMouseMode(self.tty.anyWriter(), true);
|
||||||
|
|
||||||
|
// This is the main event loop. The basic structure is
|
||||||
|
// 1. Handle events
|
||||||
|
// 2. Draw application
|
||||||
|
// 3. Render
|
||||||
|
while (!self.should_quit) {
|
||||||
|
// pollEvent blocks until we have an event
|
||||||
|
loop.pollEvent();
|
||||||
|
// tryEvent returns events until the queue is empty
|
||||||
|
while (loop.tryEvent()) |event| {
|
||||||
|
try self.update(event);
|
||||||
|
}
|
||||||
|
// Draw our application after handling events
|
||||||
|
self.draw();
|
||||||
|
|
||||||
|
// It's best to use a buffered writer for the render method. TTY provides one, but you
|
||||||
|
// may use your own. The provided bufferedWriter has a buffer size of 4096
|
||||||
|
var buffered = self.tty.bufferedWriter();
|
||||||
|
// Render the application to the screen
|
||||||
|
try self.vx.render(buffered.writer().any());
|
||||||
|
try buffered.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update our application state from an event
|
||||||
|
pub fn update(self: *MyApp, event: Event) !void {
|
||||||
|
switch (event) {
|
||||||
|
.key_press => |key| {
|
||||||
|
// key.matches does some basic matching algorithms. Key matching can be complex in
|
||||||
|
// the presence of kitty keyboard encodings, this will generally be a good approach.
|
||||||
|
// There are other matching functions available for specific purposes, as well
|
||||||
|
if (key.matches('c', .{ .ctrl = true }))
|
||||||
|
self.should_quit = true;
|
||||||
|
},
|
||||||
|
.mouse => |mouse| self.mouse = mouse,
|
||||||
|
.winsize => |ws| try self.vx.resize(self.allocator, self.tty.anyWriter(), ws),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw our current state
|
||||||
|
pub fn draw(self: *MyApp) void {
|
||||||
|
const msg = "Hello, world!";
|
||||||
|
|
||||||
|
// Window is a bounded area with a view to the screen. You cannot draw outside of a windows
|
||||||
|
// bounds. They are light structures, not intended to be stored.
|
||||||
|
const win = self.vx.window();
|
||||||
|
|
||||||
|
// Clearing the window has the effect of setting each cell to it's "default" state. Vaxis
|
||||||
|
// applications typically will be immediate mode, and you will redraw your entire
|
||||||
|
// application during the draw cycle.
|
||||||
|
win.clear();
|
||||||
|
|
||||||
|
// In addition to clearing our window, we want to clear the mouse shape state since we may
|
||||||
|
// be changing that as well
|
||||||
|
self.vx.setMouseShape(.default);
|
||||||
|
|
||||||
|
const child = win.child(.{
|
||||||
|
.x_off = (win.width / 2) - 7,
|
||||||
|
.y_off = win.height / 2 + 1,
|
||||||
|
.width = .{ .limit = msg.len },
|
||||||
|
.height = .{ .limit = 1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
// mouse events are much easier to handle in the draw cycle. Windows have a helper method to
|
||||||
|
// determine if the event occurred in the target window. This method returns null if there
|
||||||
|
// is no mouse event, or if it occurred outside of the window
|
||||||
|
const style: vaxis.Style = if (child.hasMouse(self.mouse)) |_| blk: {
|
||||||
|
// We handled the mouse event, so set it to null
|
||||||
|
self.mouse = null;
|
||||||
|
self.vx.setMouseShape(.pointer);
|
||||||
|
break :blk .{ .reverse = true };
|
||||||
|
} else .{};
|
||||||
|
|
||||||
|
// Print a text segment to the screen. This is a helper function which iterates over the
|
||||||
|
// text field for graphemes. Alternatively, you can implement your own print functions and
|
||||||
|
// use the writeCell API.
|
||||||
|
_ = try child.printSegment(.{ .text = msg, .style = style }, .{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Keep our main function small. Typically handling arg parsing and initialization only
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer {
|
||||||
|
const deinit_status = gpa.deinit();
|
||||||
|
//fail test; can't try in defer as defer is executed after we return
|
||||||
|
if (deinit_status == .leak) {
|
||||||
|
std.log.err("memory leak", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
// Initialize our application
|
||||||
|
var app = try MyApp.init(allocator);
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
// Run the application
|
||||||
|
try app.run();
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue