Compare commits
No commits in common. "zig" and "main" have entirely different histories.
15 changed files with 3567 additions and 529 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
66
3rd-party/cimgui/build.zig
vendored
66
3rd-party/cimgui/build.zig
vendored
|
@ -1,66 +0,0 @@
|
||||||
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
18
3rd-party/cimgui/build.zig.zon
vendored
|
@ -1,18 +0,0 @@
|
||||||
.{
|
|
||||||
.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
Normal file
3338
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[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
110
build.zig
|
@ -1,110 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
.{
|
|
||||||
.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,23 +1,63 @@
|
||||||
{
|
{
|
||||||
"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": 1720058333,
|
"lastModified": 1712163089,
|
||||||
"narHash": "sha256-gM2RCi5XkxmcsZ44pUkKIYBiBMfZ6u7MdcZcykmccrs=",
|
"narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6842b061970bf96965d66fcc86a28e1f719aae95",
|
"rev": "fd281bd6b7d3e32ddfa399853946f782553163b5",
|
||||||
"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",
|
||||||
|
|
53
flake.nix
53
flake.nix
|
@ -1,30 +1,51 @@
|
||||||
{
|
{
|
||||||
description = "Doggo flake";
|
description = "Doggo flake";
|
||||||
|
|
||||||
outputs = { self, nixpkgs }:
|
inputs = {
|
||||||
|
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 {
|
mkShell rec {
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
just
|
just
|
||||||
zig
|
pkg-config
|
||||||
zls
|
protobuf
|
||||||
|
(fenix.packages.x86_64-linux.fromToolchainFile {
|
||||||
|
dir = ./.;
|
||||||
|
sha256 = "sha256-7QfkHty6hSrgNM0fspycYkRcB82eEqYa4CoAJ9qA3tU= ";
|
||||||
|
})
|
||||||
|
fenix.packages.x86_64-linux.rust-analyzer
|
||||||
];
|
];
|
||||||
buildInputs = [
|
|
||||||
lldb
|
|
||||||
|
|
||||||
# GUI
|
buildInputs = [
|
||||||
alsa-lib
|
fontconfig
|
||||||
xorg.libX11
|
|
||||||
xorg.libXcursor
|
vulkan-headers
|
||||||
xorg.libXi
|
vulkan-loader
|
||||||
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
Normal file
BIN
fonts/icons.ttf
Normal file
Binary file not shown.
16
justfile
16
justfile
|
@ -1,9 +1,15 @@
|
||||||
alias b := build
|
alias b := build
|
||||||
build:
|
build:
|
||||||
zig build
|
cargo build
|
||||||
|
|
||||||
gui:
|
alias r := run
|
||||||
zig build run-gui
|
run:
|
||||||
|
nixVulkanIntel cargo run
|
||||||
|
|
||||||
tui:
|
fmt:
|
||||||
zig build run-tui
|
nix fmt flake.nix
|
||||||
|
|
||||||
|
# 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 .
|
||||||
|
|
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
profile = "default"
|
|
@ -1,92 +0,0 @@
|
||||||
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
Normal file
123
src/main.rs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
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
196
src/tui/main.zig
|
@ -1,196 +0,0 @@
|
||||||
// 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