feat(lang): block value requires no trailing ; (Rust-style)
A block's value is now its last statement ONLY when that statement is a trailing expression with no `;`. A trailing `;` discards the value, leaving the block void. This makes value-vs-statement explicit and lets the compiler reject "this block was supposed to produce a value". Compiler: - Parser records `Block.produces_value` (last stmt is a no-`;` trailing expression) + `Block.discarded_semi` (the `;` that discarded a value), via `expectSemicolonAfter`. A trailing expression before `}` may now omit its `;` (previously a parse error). Match-arm and else-arm bodies are built value-producing regardless of the arm `;` (arms are exempt — the `;` is an arm terminator). - Lowering: `lowerBlockValue` / the block-expr path / `inferExprType` respect `produces_value`. A value-position block that discards its value is a hard error (`lowerValueBody` for function bodies; the value-context `.block` path for if/else branches, `catch` bodies, value bindings, match arms). Pure-failable `-> !` bodies (value rides the error channel) and a value-if whose branches are void are handled without false errors. - `defer`/`onfail` cleanup bodies lower as statements (void), so a trailing `;` there is fine. Migration (behavior-preserving — output unchanged): - stdlib + ~210 examples: dropped the trailing `;` on value-position last expressions. `format` now ends with an explicit `#insert "return result;"` (it relied on `#insert`-as-block-value, which `;` discards). - Two `main :: () -> s32` examples that relied on the old silent default-return got an explicit trailing `0`. - Rejection snapshots 0412 / 1013 regenerated (their quoted source lines lost a `;`); the diagnostics themselves are unchanged. Docs/tests: specs.md "Block values" section; examples 0040 (rules) + 0041 (rejection); 3 parser unit tests. Filed issue 0066 (pre-existing match-arm negated-literal phi-width quirk, surfaced not caused here). Gates: zig build, zig build test, run_examples.sh -> 343 passed, cross_compile.sh -> 7 passed (also refreshed its stale example names).
This commit is contained in:
@@ -42,7 +42,7 @@ GPA :: struct {
|
||||
alloc_count: s64;
|
||||
|
||||
init :: () -> GPA {
|
||||
GPA.{ alloc_count = 0 };
|
||||
GPA.{ alloc_count = 0 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ Arena :: struct {
|
||||
init :: (parent_alloc: Allocator, size: s64) -> Arena {
|
||||
self : Arena = .{ first = null, end_index = 0, parent = parent_alloc };
|
||||
self.add_chunk(size);
|
||||
self;
|
||||
self
|
||||
}
|
||||
|
||||
reset :: (a: *Arena) {
|
||||
@@ -140,7 +140,7 @@ impl Allocator for Arena {
|
||||
buf : [*]u8 = xx self.first;
|
||||
ptr := @buf[16 + self.end_index];
|
||||
self.end_index = self.end_index + aligned;
|
||||
ptr;
|
||||
ptr
|
||||
}
|
||||
dealloc :: (self: *Arena, ptr: *void) {}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ BufAlloc :: struct {
|
||||
b.buf = @buf[self_size];
|
||||
b.len = len - self_size;
|
||||
b.pos = 0;
|
||||
b;
|
||||
b
|
||||
}
|
||||
|
||||
reset :: (b: *BufAlloc) {
|
||||
@@ -181,7 +181,7 @@ impl Allocator for BufAlloc {
|
||||
}
|
||||
ptr := @self.buf[self.pos];
|
||||
self.pos = self.pos + aligned;
|
||||
ptr;
|
||||
ptr
|
||||
}
|
||||
dealloc :: (self: *BufAlloc, ptr: *void) {}
|
||||
}
|
||||
@@ -221,11 +221,11 @@ TrackingAllocator :: struct {
|
||||
alloc_count = 0,
|
||||
dealloc_count = 0,
|
||||
total_alloc_bytes = 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
leak_count :: (t: *TrackingAllocator) -> s64 {
|
||||
t.alloc_count - t.dealloc_count;
|
||||
t.alloc_count - t.dealloc_count
|
||||
}
|
||||
|
||||
report :: (t: *TrackingAllocator) {
|
||||
@@ -241,7 +241,7 @@ impl Allocator for TrackingAllocator {
|
||||
self.alloc_count += 1;
|
||||
self.total_alloc_bytes += size;
|
||||
}
|
||||
ptr;
|
||||
ptr
|
||||
}
|
||||
dealloc :: (self: *TrackingAllocator, ptr: *void) {
|
||||
self.parent.dealloc(ptr);
|
||||
|
||||
@@ -69,25 +69,25 @@ SeekFrom :: enum { set; current; end; }
|
||||
File :: struct {
|
||||
fd: s32 = -1;
|
||||
|
||||
is_valid :: (self: *File) -> bool { self.fd >= 0; }
|
||||
is_valid :: (self: *File) -> bool { self.fd >= 0 }
|
||||
|
||||
close :: (self: *File) -> bool {
|
||||
if self.fd < 0 { return false; }
|
||||
rc := close(self.fd);
|
||||
self.fd = -1;
|
||||
rc == 0;
|
||||
rc == 0
|
||||
}
|
||||
|
||||
read :: (self: *File, buf: string) -> s64 {
|
||||
if self.fd < 0 { return -1; }
|
||||
n := read(self.fd, buf.ptr, xx buf.len);
|
||||
cast(s64) n;
|
||||
cast(s64) n
|
||||
}
|
||||
|
||||
write :: (self: *File, data: string) -> s64 {
|
||||
if self.fd < 0 { return -1; }
|
||||
n := write(self.fd, data.ptr, xx data.len);
|
||||
cast(s64) n;
|
||||
cast(s64) n
|
||||
}
|
||||
|
||||
seek :: (self: *File, offset: s64, whence: SeekFrom) -> s64 {
|
||||
@@ -95,7 +95,7 @@ File :: struct {
|
||||
w := SEEK_SET;
|
||||
if whence == .current { w = SEEK_CUR; }
|
||||
if whence == .end { w = SEEK_END; }
|
||||
lseek(self.fd, offset, w);
|
||||
lseek(self.fd, offset, w)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,13 +110,13 @@ mode_to_flags :: (m: OpenMode) -> s32 {
|
||||
if m == .write { return O_WRONLY | O_CREAT | O_TRUNC; }
|
||||
if m == .append { return O_WRONLY | O_CREAT | O_APPEND; }
|
||||
if m == .read_write { return O_RDWR; }
|
||||
O_RDONLY;
|
||||
O_RDONLY
|
||||
}
|
||||
|
||||
open_file :: (path: [:0]u8, mode: OpenMode) -> ?File {
|
||||
fd := open(path, mode_to_flags(mode), 420); // 0o644 = 420
|
||||
if fd < 0 { return null; }
|
||||
File.{ fd = fd };
|
||||
File.{ fd = fd }
|
||||
}
|
||||
|
||||
// One-shot read: opens, slurps the whole file into a fresh buffer,
|
||||
@@ -134,7 +134,7 @@ read_file :: (path: [:0]u8) -> ?string {
|
||||
n := read(fd, buf.ptr, xx size);
|
||||
close(fd);
|
||||
if cast(s64) n != size { return null; }
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
// One-shot write: creates / truncates and writes the whole buffer.
|
||||
@@ -143,7 +143,7 @@ write_file :: (path: [:0]u8, data: string) -> bool {
|
||||
if fd < 0 { return false; }
|
||||
n := write(fd, data.ptr, xx data.len);
|
||||
close(fd);
|
||||
cast(s64) n == cast(s64) data.len;
|
||||
cast(s64) n == cast(s64) data.len
|
||||
}
|
||||
|
||||
append_file :: (path: [:0]u8, data: string) -> bool {
|
||||
@@ -151,33 +151,33 @@ append_file :: (path: [:0]u8, data: string) -> bool {
|
||||
if fd < 0 { return false; }
|
||||
n := write(fd, data.ptr, xx data.len);
|
||||
close(fd);
|
||||
cast(s64) n == cast(s64) data.len;
|
||||
cast(s64) n == cast(s64) data.len
|
||||
}
|
||||
|
||||
// ── Single-syscall ops ───────────────────────────────────────────────
|
||||
|
||||
exists :: (path: [:0]u8) -> bool {
|
||||
access(path, F_OK) == 0;
|
||||
access(path, F_OK) == 0
|
||||
}
|
||||
|
||||
delete_file :: (path: [:0]u8) -> bool {
|
||||
unlink(path) == 0;
|
||||
unlink(path) == 0
|
||||
}
|
||||
|
||||
delete_dir :: (path: [:0]u8) -> bool {
|
||||
rmdir(path) == 0;
|
||||
rmdir(path) == 0
|
||||
}
|
||||
|
||||
create_dir :: (path: [:0]u8) -> bool {
|
||||
mkdir(path, 493) == 0; // 0o755 = 493
|
||||
mkdir(path, 493) == 0 // 0o755 = 493
|
||||
}
|
||||
|
||||
set_mode :: (path: [:0]u8, mode: u32) -> bool {
|
||||
chmod(path, mode) == 0;
|
||||
chmod(path, mode) == 0
|
||||
}
|
||||
|
||||
move :: (oldp: [:0]u8, newp: [:0]u8) -> bool {
|
||||
rename(oldp, newp) == 0;
|
||||
rename(oldp, newp) == 0
|
||||
}
|
||||
|
||||
// Recursive mkdir -p. Walks the path and creates each missing
|
||||
@@ -195,7 +195,7 @@ create_dir_all :: (path: [:0]u8) -> bool {
|
||||
memcpy(parent.ptr, path.ptr, last);
|
||||
if !create_dir_all(parent) { return false; }
|
||||
}
|
||||
create_dir(path);
|
||||
create_dir(path)
|
||||
}
|
||||
|
||||
// Copy a file by streaming through a 64KB buffer. Uses libc directly
|
||||
@@ -224,7 +224,7 @@ copy_file :: (src: [:0]u8, dst: [:0]u8) -> bool {
|
||||
}
|
||||
close(src_fd);
|
||||
close(dst_fd);
|
||||
ok;
|
||||
ok
|
||||
}
|
||||
|
||||
// ── Path helpers ─────────────────────────────────────────────────────
|
||||
@@ -243,7 +243,7 @@ basename :: (p: string) -> string {
|
||||
if p[last - 1] == 47 { return substr(p, last, end - last); }
|
||||
last -= 1;
|
||||
}
|
||||
substr(p, 0, end);
|
||||
substr(p, 0, end)
|
||||
}
|
||||
|
||||
dirname :: (p: string) -> string {
|
||||
@@ -264,5 +264,5 @@ dirname :: (p: string) -> string {
|
||||
last -= 1;
|
||||
}
|
||||
if p[0] == 47 { return "/"; }
|
||||
".";
|
||||
"."
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ impl GPU for Gles3Gpu {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
shutdown :: (self: *Gles3Gpu) {
|
||||
@@ -122,7 +122,7 @@ impl GPU for Gles3Gpu {
|
||||
glViewport(0, 0, self.pixel_w, self.pixel_h);
|
||||
glClearColor(clear.r, clear.g, clear.b, clear.a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// eglSwapBuffers happens in AndroidPlatform.end_frame; nothing for us.
|
||||
@@ -141,7 +141,7 @@ impl GPU for Gles3Gpu {
|
||||
tex_loc = glGetUniformLocation(prog, "uTex".ptr),
|
||||
};
|
||||
self.shaders.append(slot, self.parent_allocator);
|
||||
xx self.shaders.len;
|
||||
xx self.shaders.len
|
||||
}
|
||||
|
||||
create_buffer :: (self: *Gles3Gpu, size_bytes: s64) -> BufferHandle {
|
||||
@@ -152,7 +152,7 @@ impl GPU for Gles3Gpu {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, b);
|
||||
glBufferData(GL_ARRAY_BUFFER, xx size_bytes, null, GL_DYNAMIC_DRAW);
|
||||
self.buffers.append(b, self.parent_allocator);
|
||||
xx self.buffers.len;
|
||||
xx self.buffers.len
|
||||
}
|
||||
|
||||
update_buffer :: (self: *Gles3Gpu, handle: BufferHandle, data: *void, size_bytes: s64) {
|
||||
@@ -206,7 +206,7 @@ impl GPU for Gles3Gpu {
|
||||
|
||||
slot : Gles3TextureSlot = .{ tex = t, bytes_per_pixel = bpp };
|
||||
self.textures.append(slot, self.parent_allocator);
|
||||
xx self.textures.len;
|
||||
xx self.textures.len
|
||||
}
|
||||
|
||||
update_texture_region :: (self: *Gles3Gpu, handle: TextureHandle, x: s32, y: s32, w: s32, h: s32, pixels: *void) {
|
||||
@@ -332,5 +332,5 @@ gles3_lookup_buffer :: (self: *Gles3Gpu, handle: u32) -> u32 {
|
||||
if handle == 0 { return 0; }
|
||||
h64 : s64 = xx handle;
|
||||
if h64 > self.buffers.len { return 0; }
|
||||
self.buffers.items[handle - 1];
|
||||
self.buffers.items[handle - 1]
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ impl GPU for MetalGPU {
|
||||
self.pixel_h = pixel_h;
|
||||
}
|
||||
self.parent_allocator = context.allocator;
|
||||
metal_init_ios(self);
|
||||
metal_init_ios(self)
|
||||
}
|
||||
|
||||
shutdown :: (self: *MetalGPU) {
|
||||
@@ -125,7 +125,7 @@ impl GPU for MetalGPU {
|
||||
|
||||
begin_frame :: (self: *MetalGPU, clear: ClearColor) -> bool {
|
||||
inline if OS != .ios { return false; }
|
||||
metal_begin_frame_ios(self, clear);
|
||||
metal_begin_frame_ios(self, clear)
|
||||
}
|
||||
|
||||
end_frame :: (self: *MetalGPU, target_time: f64) {
|
||||
@@ -141,12 +141,12 @@ impl GPU for MetalGPU {
|
||||
|
||||
create_shader :: (self: *MetalGPU, vsrc: string, fsrc: string) -> ShaderHandle {
|
||||
inline if OS != .ios { return 0; }
|
||||
metal_create_shader_ios(self, vsrc);
|
||||
metal_create_shader_ios(self, vsrc)
|
||||
}
|
||||
|
||||
create_buffer :: (self: *MetalGPU, size_bytes: s64) -> BufferHandle {
|
||||
inline if OS != .ios { return 0; }
|
||||
metal_create_buffer_ios(self, size_bytes);
|
||||
metal_create_buffer_ios(self, size_bytes)
|
||||
}
|
||||
|
||||
update_buffer :: (self: *MetalGPU, buf: BufferHandle, data: *void, size_bytes: s64) {
|
||||
@@ -163,7 +163,7 @@ impl GPU for MetalGPU {
|
||||
|
||||
create_texture :: (self: *MetalGPU, w: s32, h: s32, format: TextureFormat, pixels: *void) -> TextureHandle {
|
||||
inline if OS != .ios { return 0; }
|
||||
metal_create_texture_ios(self, w, h, format, pixels);
|
||||
metal_create_texture_ios(self, w, h, format, pixels)
|
||||
}
|
||||
|
||||
update_texture_region :: (self: *MetalGPU, tex: TextureHandle, x: s32, y: s32, w: s32, h: s32, pixels: *void) {
|
||||
@@ -282,7 +282,7 @@ metal_init_ios :: (self: *MetalGPU) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
metal_resize_ios :: (self: *MetalGPU) {
|
||||
@@ -343,7 +343,7 @@ metal_begin_frame_ios :: (self: *MetalGPU, clear: ClearColor) -> bool {
|
||||
sel_registerName("renderCommandEncoderWithDescriptor:".ptr), pass);
|
||||
if self.encoder == null { self.cmd_buffer = null; self.drawable = null; return false; }
|
||||
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
metal_end_frame_ios :: (self: *MetalGPU, target_time: f64) {
|
||||
@@ -438,7 +438,7 @@ metal_create_shader_ios :: (self: *MetalGPU, src: string) -> u32 {
|
||||
}
|
||||
|
||||
self.shaders.append(state, self.parent_allocator);
|
||||
xx self.shaders.len;
|
||||
xx self.shaders.len
|
||||
}
|
||||
|
||||
// ── Buffers ──────────────────────────────────────────────────────────────
|
||||
@@ -458,7 +458,7 @@ metal_create_buffer_ios :: (self: *MetalGPU, size_bytes: s64) -> u32 {
|
||||
if buf == null { return 0; }
|
||||
|
||||
self.buffers.append(buf, self.parent_allocator);
|
||||
xx self.buffers.len;
|
||||
xx self.buffers.len
|
||||
}
|
||||
|
||||
metal_update_buffer_ios :: (self: *MetalGPU, handle: u32, data: *void, size_bytes: s64) {
|
||||
@@ -498,7 +498,7 @@ metal_lookup_buffer :: (self: *MetalGPU, handle: u32) -> *void {
|
||||
if handle == 0 { return null; }
|
||||
h64 : s64 = xx handle;
|
||||
if h64 > self.buffers.len { return null; }
|
||||
self.buffers.items[handle - 1];
|
||||
self.buffers.items[handle - 1]
|
||||
}
|
||||
|
||||
metal_lookup_shader :: (self: *MetalGPU, handle: u32) -> *void {
|
||||
@@ -506,7 +506,7 @@ metal_lookup_shader :: (self: *MetalGPU, handle: u32) -> *void {
|
||||
if handle == 0 { return null; }
|
||||
h64 : s64 = xx handle;
|
||||
if h64 > self.shaders.len { return null; }
|
||||
self.shaders.items[handle - 1];
|
||||
self.shaders.items[handle - 1]
|
||||
}
|
||||
|
||||
// ── Textures ─────────────────────────────────────────────────────────────
|
||||
@@ -553,7 +553,7 @@ metal_create_texture_ios :: (self: *MetalGPU, w: s32, h: s32, format: TextureFor
|
||||
metal_update_texture_region_ios(self, handle, 0, 0, w, h, pixels);
|
||||
}
|
||||
|
||||
xx self.textures.len;
|
||||
xx self.textures.len
|
||||
}
|
||||
|
||||
metal_update_texture_region_ios :: (self: *MetalGPU, handle: u32, x: s32, y: s32, w: s32, h: s32, pixels: *void) {
|
||||
|
||||
@@ -9,29 +9,29 @@ cos :: (x: $T) -> T #builtin;
|
||||
floor :: (x: $T) -> T #builtin;
|
||||
|
||||
min :: (a: $T, b: T) -> T {
|
||||
if a < b then a else b;
|
||||
if a < b then a else b
|
||||
}
|
||||
|
||||
max :: (a: $T, b: T) -> T {
|
||||
if a > b then a else b;
|
||||
if a > b then a else b
|
||||
}
|
||||
|
||||
clamp :: (val: $T, lo: T, hi: T) -> T {
|
||||
if val < lo then lo
|
||||
else if val > hi then hi
|
||||
else val;
|
||||
else val
|
||||
}
|
||||
|
||||
abs :: (x: $T) -> T {
|
||||
if x < 0 then 0 - x else x;
|
||||
if x < 0 then 0 - x else x
|
||||
}
|
||||
|
||||
lerp :: (a: f32, b: f32, t: f32) -> f32 {
|
||||
a + (b - a) * t;
|
||||
a + (b - a) * t
|
||||
}
|
||||
|
||||
sign :: (x: $T) -> T {
|
||||
if x > 0 then 1
|
||||
else if x < 0 then 0 - 1
|
||||
else 0;
|
||||
else 0
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ Mat4 :: struct {
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
]};
|
||||
]}
|
||||
}
|
||||
|
||||
zero :: () -> Mat4 {
|
||||
@@ -19,7 +19,7 @@ Mat4 :: struct {
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0
|
||||
]};
|
||||
]}
|
||||
}
|
||||
|
||||
mul :: (self: Mat4, b: Mat4) -> Mat4 {
|
||||
@@ -39,7 +39,7 @@ Mat4 :: struct {
|
||||
}
|
||||
col += 1;
|
||||
}
|
||||
r;
|
||||
r
|
||||
}
|
||||
|
||||
translate :: (x: f32, y: f32, z: f32) -> Mat4 {
|
||||
@@ -47,7 +47,7 @@ Mat4 :: struct {
|
||||
m.data[12] = x;
|
||||
m.data[13] = y;
|
||||
m.data[14] = z;
|
||||
m;
|
||||
m
|
||||
}
|
||||
|
||||
scale :: (x: f32, y: f32, z: f32) -> Mat4 {
|
||||
@@ -56,7 +56,7 @@ Mat4 :: struct {
|
||||
m.data[5] = y;
|
||||
m.data[10] = z;
|
||||
m.data[15] = 1.0;
|
||||
m;
|
||||
m
|
||||
}
|
||||
|
||||
rotate_x :: (angle: f32) -> Mat4 {
|
||||
@@ -67,7 +67,7 @@ Mat4 :: struct {
|
||||
m.data[6] = s;
|
||||
m.data[9] = 0.0 - s;
|
||||
m.data[10] = c;
|
||||
m;
|
||||
m
|
||||
}
|
||||
|
||||
rotate_y :: (angle: f32) -> Mat4 {
|
||||
@@ -78,7 +78,7 @@ Mat4 :: struct {
|
||||
m.data[2] = 0.0 - s;
|
||||
m.data[8] = s;
|
||||
m.data[10] = c;
|
||||
m;
|
||||
m
|
||||
}
|
||||
|
||||
rotate_z :: (angle: f32) -> Mat4 {
|
||||
@@ -89,7 +89,7 @@ Mat4 :: struct {
|
||||
m.data[1] = s;
|
||||
m.data[4] = 0.0 - s;
|
||||
m.data[5] = c;
|
||||
m;
|
||||
m
|
||||
}
|
||||
|
||||
ortho :: (left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4 {
|
||||
@@ -101,7 +101,7 @@ Mat4 :: struct {
|
||||
m.data[13] = 0.0 - (top + bottom) / (top - bottom);
|
||||
m.data[14] = 0.0 - (far + near) / (far - near);
|
||||
m.data[15] = 1.0;
|
||||
m;
|
||||
m
|
||||
}
|
||||
|
||||
perspective :: (fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
|
||||
@@ -112,6 +112,6 @@ Mat4 :: struct {
|
||||
m.data[10] = 0.0 - (far + near) / (far - near);
|
||||
m.data[11] = 0.0 - 1.0;
|
||||
m.data[14] = 0.0 - 2.0 * far * near / (far - near);
|
||||
m;
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
Vec2 :: struct {
|
||||
x, y: f32;
|
||||
|
||||
zero :: () -> Vec2 { Vec2.{ x = 0.0, y = 0.0 }; }
|
||||
zero :: () -> Vec2 { Vec2.{ x = 0.0, y = 0.0 } }
|
||||
|
||||
add :: (self: Vec2, b: Vec2) -> Vec2 {
|
||||
Vec2.{ x = self.x + b.x, y = self.y + b.y };
|
||||
Vec2.{ x = self.x + b.x, y = self.y + b.y }
|
||||
}
|
||||
|
||||
sub :: (self: Vec2, b: Vec2) -> Vec2 {
|
||||
Vec2.{ x = self.x - b.x, y = self.y - b.y };
|
||||
Vec2.{ x = self.x - b.x, y = self.y - b.y }
|
||||
}
|
||||
|
||||
scale :: (self: Vec2, s: f32) -> Vec2 {
|
||||
Vec2.{ x = self.x * s, y = self.y * s };
|
||||
Vec2.{ x = self.x * s, y = self.y * s }
|
||||
}
|
||||
|
||||
dot :: (self: Vec2, b: Vec2) -> f32 {
|
||||
self.x * b.x + self.y * b.y;
|
||||
self.x * b.x + self.y * b.y
|
||||
}
|
||||
|
||||
length :: (self: Vec2) -> f32 {
|
||||
sqrt(self.x * self.x + self.y * self.y);
|
||||
sqrt(self.x * self.x + self.y * self.y)
|
||||
}
|
||||
|
||||
normalize :: (self: Vec2) -> Vec2 {
|
||||
@@ -28,22 +28,22 @@ Vec2 :: struct {
|
||||
if len > 0.0 {
|
||||
return Vec2.{ x = self.x / len, y = self.y / len };
|
||||
}
|
||||
Vec2.zero();
|
||||
Vec2.zero()
|
||||
}
|
||||
|
||||
lerp :: (self: Vec2, b: Vec2, t: f32) -> Vec2 {
|
||||
Vec2.{ x = self.x + (b.x - self.x) * t, y = self.y + (b.y - self.y) * t };
|
||||
Vec2.{ x = self.x + (b.x - self.x) * t, y = self.y + (b.y - self.y) * t }
|
||||
}
|
||||
|
||||
distance :: (self: Vec2, b: Vec2) -> f32 {
|
||||
self.sub(b).length();
|
||||
self.sub(b).length()
|
||||
}
|
||||
|
||||
negate :: (self: Vec2) -> Vec2 {
|
||||
Vec2.{ x = 0.0 - self.x, y = 0.0 - self.y };
|
||||
Vec2.{ x = 0.0 - self.x, y = 0.0 - self.y }
|
||||
}
|
||||
|
||||
equals :: (self: Vec2, b: Vec2) -> bool {
|
||||
self.x == b.x and self.y == b.y;
|
||||
self.x == b.x and self.y == b.y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ sx_android_render_thread_entry :: (arg: *void) -> *void callconv(.c) {
|
||||
fn := plat.user_main_fn;
|
||||
fn();
|
||||
}
|
||||
null;
|
||||
null
|
||||
}
|
||||
|
||||
// Bring up EGL on `plat.app_window`. Sets the egl_* fields and makes
|
||||
@@ -313,7 +313,7 @@ sx_android_egl_init :: (plat: *AndroidPlatform) -> bool {
|
||||
plat.pixel_w = ANativeWindow_getWidth(plat.app_window);
|
||||
plat.pixel_h = ANativeWindow_getHeight(plat.app_window);
|
||||
sx_android_recompute_scale(plat);
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// Recompute `dpi_scale` from `pixel_w / logical_w`. Called on viewport
|
||||
@@ -378,7 +378,7 @@ impl Platform for AndroidPlatform {
|
||||
self.width = w;
|
||||
self.height = h;
|
||||
sx_android_recompute_scale(self);
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// NOTE: method order must match the `Platform` protocol declaration
|
||||
@@ -405,7 +405,7 @@ impl Platform for AndroidPlatform {
|
||||
result : []Event = ---;
|
||||
result.ptr = self.events.items;
|
||||
result.len = self.events.len;
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
begin_frame :: (self: *AndroidPlatform) -> FrameContext {
|
||||
@@ -421,7 +421,7 @@ impl Platform for AndroidPlatform {
|
||||
dpi_scale = self.dpi_scale,
|
||||
delta_time = 0.016,
|
||||
target_present_time = 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
end_frame :: (self: *AndroidPlatform) {
|
||||
@@ -431,11 +431,11 @@ impl Platform for AndroidPlatform {
|
||||
}
|
||||
|
||||
safe_insets :: (self: *AndroidPlatform) -> EdgeInsets {
|
||||
EdgeInsets.{};
|
||||
EdgeInsets.{}
|
||||
}
|
||||
|
||||
keyboard :: (self: *AndroidPlatform) -> KeyboardState {
|
||||
KeyboardState.zero();
|
||||
KeyboardState.zero()
|
||||
}
|
||||
show_keyboard :: (self: *AndroidPlatform) { }
|
||||
hide_keyboard :: (self: *AndroidPlatform) { }
|
||||
|
||||
@@ -195,7 +195,7 @@ bundle_main :: () -> bool {
|
||||
out("bundled: ");
|
||||
out(bundle);
|
||||
out("\n");
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────
|
||||
@@ -206,7 +206,7 @@ bundle_main :: () -> bool {
|
||||
str_to_cstr :: (s: string) -> [:0]u8 {
|
||||
buf := cstring(s.len);
|
||||
memcpy(buf.ptr, s.ptr, s.len);
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
// Minimum iOS version baked into the Info.plist — matches what the
|
||||
@@ -293,7 +293,7 @@ PLIST, xml_escape(bundle_id), xml_escape(exe_name), xml_escape(exe_name), IOS_MI
|
||||
<string>0.1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST, xml_escape(bundle_id), xml_escape(exe_name), xml_escape(exe_name));
|
||||
PLIST, xml_escape(bundle_id), xml_escape(exe_name), xml_escape(exe_name))
|
||||
}
|
||||
|
||||
// Read a `.mobileprovision` and write it to
|
||||
@@ -313,7 +313,7 @@ embed_provisioning_profile :: (profile: string, bundle: string) -> bool {
|
||||
out("error: bundle: cannot read provisioning profile: ");
|
||||
out(profile);
|
||||
out("\n");
|
||||
false;
|
||||
false
|
||||
}
|
||||
|
||||
// Recursive-copy `<src_dir>` (relative to the build CWD) into
|
||||
@@ -359,7 +359,7 @@ copy_asset_dir :: (src: string, dest: string, bundle: string) -> bool {
|
||||
return true;
|
||||
}
|
||||
out("error: cp -R spawn failed\n");
|
||||
false;
|
||||
false
|
||||
}
|
||||
|
||||
// Recursive-copy `<name>.framework` from one of the user's `-F` search
|
||||
@@ -402,7 +402,7 @@ embed_framework :: (opts: BuildOptions, name: string, dest_dir: string) -> bool
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
|
||||
// Extract entitlements XML from a `.mobileprovision` and resolve the
|
||||
@@ -505,7 +505,7 @@ extract_entitlements :: (profile: string, bundle_id: string) -> ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
ent_path;
|
||||
ent_path
|
||||
}
|
||||
|
||||
// Codesign the bundle. Empty `ent_path` means no `--entitlements`
|
||||
@@ -532,7 +532,7 @@ codesign :: (bundle: string, identity: string, ent_path: string) -> bool {
|
||||
return true;
|
||||
}
|
||||
out("error: codesign spawn failed\n");
|
||||
false;
|
||||
false
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
@@ -572,7 +572,7 @@ absolutify :: (path: string) -> string {
|
||||
if cwd.len == 0 { return path; }
|
||||
return path_join(cwd, path);
|
||||
}
|
||||
path;
|
||||
path
|
||||
}
|
||||
|
||||
android_bundle_main :: (opts: BuildOptions, binary: string, apk_path: string, bundle_id: string) -> bool {
|
||||
@@ -771,7 +771,7 @@ android_bundle_main :: (opts: BuildOptions, binary: string, apk_path: string, bu
|
||||
out("apk: ");
|
||||
out(apk_path);
|
||||
out("\n");
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// ── Android helpers ──────────────────────────────────────────────────
|
||||
@@ -795,7 +795,7 @@ run_in_dir :: (dir: string, cmd: string) -> bool {
|
||||
return true;
|
||||
}
|
||||
out("error: shell spawn failed\n");
|
||||
false;
|
||||
false
|
||||
}
|
||||
|
||||
// Discover the Android SDK root. Honors $ANDROID_HOME /
|
||||
@@ -808,7 +808,7 @@ discover_android_sdk :: () -> string {
|
||||
candidate := path_join(home, "Library/Android/sdk");
|
||||
if exists(str_to_cstr(candidate)) { return candidate; }
|
||||
}
|
||||
"";
|
||||
""
|
||||
}
|
||||
|
||||
// Pick the lexicographically-highest subdir of `parent`. Equivalent to
|
||||
@@ -831,7 +831,7 @@ find_highest_subdir :: (parent: string) -> string {
|
||||
if name.len == 0 { return ""; }
|
||||
return path_join(parent, name);
|
||||
}
|
||||
"";
|
||||
""
|
||||
}
|
||||
|
||||
// `libfoo.so` → `foo`. Android's `android.app.lib_name` meta-data
|
||||
@@ -858,7 +858,7 @@ lib_name_from_so_basename :: (basename: string) -> string {
|
||||
}
|
||||
}
|
||||
}
|
||||
name;
|
||||
name
|
||||
}
|
||||
|
||||
// AndroidManifest.xml synthesizer. When the program declares a
|
||||
@@ -922,7 +922,7 @@ MANIFEST, pkg_esc, lib_esc, cls_esc, lib_esc);
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
MANIFEST, pkg_esc, lib_esc, lib_esc, lib_esc);
|
||||
MANIFEST, pkg_esc, lib_esc, lib_esc, lib_esc)
|
||||
}
|
||||
|
||||
// `co/swipelab/sxchess/SxApp` → `co.swipelab.sxchess.SxApp`.
|
||||
@@ -934,7 +934,7 @@ slash_to_dot :: (path: string) -> string {
|
||||
buf[i] = if c == 47 then 46 else c; // 47 = '/', 46 = '.'
|
||||
i += 1;
|
||||
}
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
// Last `/`-separated component of a forward-slash path (used to split
|
||||
@@ -947,7 +947,7 @@ last_slash_component :: (path: string) -> string {
|
||||
if path[i - 1] == 47 { return substr(path, i, path.len - i); }
|
||||
i -= 1;
|
||||
}
|
||||
path;
|
||||
path
|
||||
}
|
||||
|
||||
dir_part :: (path: string) -> string {
|
||||
@@ -956,7 +956,7 @@ dir_part :: (path: string) -> string {
|
||||
if path[i - 1] == 47 { return substr(path, 0, i - 1); }
|
||||
i -= 1;
|
||||
}
|
||||
"";
|
||||
""
|
||||
}
|
||||
|
||||
// Write each `#jni_main` decl's `.java` source, then compile to
|
||||
@@ -1054,7 +1054,7 @@ compile_jni_main_sources :: (opts: BuildOptions, stage: string, android_jar: str
|
||||
out("error: d8 spawn failed\n");
|
||||
return false;
|
||||
}
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// Locate `javac`. Honors `$JAVA_HOME/bin/javac` first (Android
|
||||
@@ -1066,7 +1066,7 @@ discover_javac :: () -> string {
|
||||
if exists(str_to_cstr(cand)) { return cand; }
|
||||
}
|
||||
if path := find_executable("javac") { return path; }
|
||||
"";
|
||||
""
|
||||
}
|
||||
|
||||
// Zip the contents of `<src>` into the APK at `<dest>/`. Uses a
|
||||
@@ -1109,7 +1109,7 @@ zip_asset_dir :: (src: string, dest: string, apk: string) -> bool {
|
||||
zip_cmd = concat(zip_cmd, dest);
|
||||
zip_cmd = concat(zip_cmd, "\"");
|
||||
if !run_in_dir(".sx-tmp/apk-assets", zip_cmd) { return false; }
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// Generate the Android debug keystore on first use. The defaults
|
||||
@@ -1133,5 +1133,5 @@ ensure_debug_keystore :: (keystore_path: string) -> bool {
|
||||
return true;
|
||||
}
|
||||
out("error: keytool spawn failed\n");
|
||||
false;
|
||||
false
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ impl Platform for SdlPlatform {
|
||||
|
||||
g_sdl_plat = self;
|
||||
self.last_perf = SDL_GetPerformanceCounter();
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
run_frame_loop :: (self: *SdlPlatform, frame_fn: Closure()) {
|
||||
@@ -164,7 +164,7 @@ impl Platform for SdlPlatform {
|
||||
result : []Event = ---;
|
||||
result.ptr = self.events.items;
|
||||
result.len = self.events.len;
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
begin_frame :: (self: *SdlPlatform) -> FrameContext {
|
||||
@@ -195,7 +195,7 @@ impl Platform for SdlPlatform {
|
||||
pixel_h = self.pixel_h,
|
||||
dpi_scale = self.dpi_scale,
|
||||
delta_time = self.delta_time,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
end_frame :: (self: *SdlPlatform) {
|
||||
@@ -203,11 +203,11 @@ impl Platform for SdlPlatform {
|
||||
}
|
||||
|
||||
safe_insets :: (self: *SdlPlatform) -> EdgeInsets {
|
||||
EdgeInsets.zero();
|
||||
EdgeInsets.zero()
|
||||
}
|
||||
|
||||
keyboard :: (self: *SdlPlatform) -> KeyboardState {
|
||||
KeyboardState.zero();
|
||||
KeyboardState.zero()
|
||||
}
|
||||
|
||||
show_keyboard :: (self: *SdlPlatform) { }
|
||||
@@ -242,7 +242,7 @@ sdl_event_watch :: (userdata: *void, event: *SDL_Event) -> bool callconv(.c) {
|
||||
}
|
||||
}
|
||||
}
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
sdl_wasm_tick :: () callconv(.c) {
|
||||
|
||||
@@ -357,7 +357,7 @@ impl Platform for UIKitPlatform {
|
||||
self.read_screen_scale();
|
||||
}
|
||||
}
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
run_frame_loop :: (self: *UIKitPlatform, frame_fn: Closure()) {
|
||||
@@ -380,7 +380,7 @@ impl Platform for UIKitPlatform {
|
||||
result.ptr = self.events.items;
|
||||
result.len = self.events.len;
|
||||
self.events.len = 0;
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
begin_frame :: (self: *UIKitPlatform) -> FrameContext {
|
||||
@@ -395,7 +395,7 @@ impl Platform for UIKitPlatform {
|
||||
dpi_scale = self.dpi_scale,
|
||||
delta_time = self.delta_time,
|
||||
target_present_time = self.last_target_ts,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
end_frame :: (self: *UIKitPlatform) {
|
||||
@@ -417,14 +417,14 @@ impl Platform for UIKitPlatform {
|
||||
left = self.safe_left,
|
||||
bottom = bottom,
|
||||
right = self.safe_right,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
keyboard :: (self: *UIKitPlatform) -> KeyboardState {
|
||||
KeyboardState.{
|
||||
visible = self.keyboard_visible,
|
||||
height = self.keyboard_height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
show_keyboard :: (self: *UIKitPlatform) {
|
||||
@@ -793,7 +793,7 @@ impl Platform for UIKitPlatform {
|
||||
// callconv(.c) so this is callable from `load_gl`'s C-conv proc-loader slot.
|
||||
ios_gl_proc :: (name: [*]u8) -> *void callconv(.c) {
|
||||
rtld_default : *void = xx (0 - 2);
|
||||
dlsym(rtld_default, name);
|
||||
dlsym(rtld_default, name)
|
||||
}
|
||||
|
||||
// Read a `extern NSString * const k...` global from the loaded image. The
|
||||
@@ -804,7 +804,7 @@ uikit_extern_nsstring :: (name: [*]u8) -> *void {
|
||||
p := dlsym(rtld_default, name);
|
||||
if p == null { return null; }
|
||||
pp : **void = xx p;
|
||||
pp.*;
|
||||
pp.*
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
@@ -869,12 +869,12 @@ SxGLView :: #objc_class("SxGLView") {
|
||||
|
||||
uikit_touch_location :: (touch: *void, view: *void) -> Point {
|
||||
p := #objc_call(CGPoint)(touch, "locationInView:", view);
|
||||
Point.{ x = xx p.x, y = xx p.y };
|
||||
Point.{ x = xx p.x, y = xx p.y }
|
||||
}
|
||||
|
||||
uikit_first_touch :: (touches: *void) -> *void {
|
||||
touches_set : *NSSet = xx touches;
|
||||
touches_set.anyObject();
|
||||
touches_set.anyObject()
|
||||
}
|
||||
|
||||
// uikit_register_gl_view_class — deleted (M3.3). SxGLView is now
|
||||
|
||||
@@ -78,7 +78,7 @@ run :: (cmd: [:0]u8) -> ?ProcessResult {
|
||||
exit_code = 128 + (raw_status & 0x7F);
|
||||
}
|
||||
}
|
||||
ProcessResult.{ exit_code = exit_code, stdout = out };
|
||||
ProcessResult.{ exit_code = exit_code, stdout = out }
|
||||
}
|
||||
|
||||
// Read an environment variable. Returns null if unset; an empty
|
||||
@@ -91,7 +91,7 @@ env :: (name: [:0]u8) -> ?string {
|
||||
if n == 0 { return ""; }
|
||||
buf := cstring(cast(s64) n);
|
||||
memcpy(buf.ptr, xx p, cast(s64) n);
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
// Locate an executable by walking `$PATH`. Returns the absolute path
|
||||
@@ -115,7 +115,7 @@ find_executable :: (name: [:0]u8) -> ?string {
|
||||
if out.len == 0 { return null; }
|
||||
return out;
|
||||
}
|
||||
null;
|
||||
null
|
||||
}
|
||||
|
||||
// ── Process termination (ERR step E4.1) ───────────────────────────────
|
||||
@@ -139,7 +139,7 @@ exit :: (code: u8, loc: Source_Location = #caller_location) -> noreturn {
|
||||
print("\nprocess.exit({}) called from {} at {}:{}\n", code, loc.func, loc.file, loc.line);
|
||||
trace.print_interpreter_frames();
|
||||
}
|
||||
clib_exit(xx code);
|
||||
clib_exit(xx code)
|
||||
}
|
||||
|
||||
// Abort with a message when `cond` is false. Prints `ASSERTION FAILED at
|
||||
|
||||
@@ -29,5 +29,5 @@ SockAddr :: struct {
|
||||
}
|
||||
|
||||
htons :: (port: s64) -> u16 {
|
||||
cast(u16) (((port & 0xFF) << 8) | ((port >> 8) & 0xFF));
|
||||
cast(u16) (((port & 0xFF) << 8) | ((port >> 8) & 0xFF))
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ cstring :: (size: s64) -> string {
|
||||
s : string = ---;
|
||||
s.ptr = xx raw;
|
||||
s.len = size;
|
||||
s;
|
||||
s
|
||||
}
|
||||
|
||||
alloc_slice :: ($T: Type, count: s64) -> []T {
|
||||
@@ -61,7 +61,7 @@ alloc_slice :: ($T: Type, count: s64) -> []T {
|
||||
s : []T = ---;
|
||||
s.ptr = xx raw;
|
||||
s.len = count;
|
||||
s;
|
||||
s
|
||||
}
|
||||
|
||||
int_to_string :: (n: s64) -> string {
|
||||
@@ -77,11 +77,11 @@ int_to_string :: (n: s64) -> string {
|
||||
i -= 1;
|
||||
}
|
||||
if neg { tmp[i] = 45; i -= 1; }
|
||||
substr(tmp, i + 1, 20 - i - 1);
|
||||
substr(tmp, i + 1, 20 - i - 1)
|
||||
}
|
||||
|
||||
bool_to_string :: (b: bool) -> string {
|
||||
if b then "true" else "false";
|
||||
if b then "true" else "false"
|
||||
}
|
||||
|
||||
float_to_string :: (f: f64) -> string {
|
||||
@@ -107,7 +107,7 @@ float_to_string :: (f: f64) -> string {
|
||||
memset(@buf[pos], 48, pad);
|
||||
pos = pos + pad;
|
||||
memcpy(@buf[pos], fstr.ptr, fl);
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
hex_group :: (buf: string, offset: s64, val: s64) {
|
||||
@@ -149,7 +149,7 @@ int_to_hex_string :: (n: s64) -> string {
|
||||
if buf[start] != 48 { break; }
|
||||
start += 1;
|
||||
}
|
||||
substr(buf, start, 16 - start);
|
||||
substr(buf, start, 16 - start)
|
||||
}
|
||||
|
||||
concat :: (a: string, b: string) -> string {
|
||||
@@ -158,13 +158,13 @@ concat :: (a: string, b: string) -> string {
|
||||
buf := cstring(al + bl);
|
||||
memcpy(buf.ptr, a.ptr, al);
|
||||
memcpy(@buf[al], b.ptr, bl);
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
substr :: (s: string, start: s64, len: s64) -> string {
|
||||
buf := cstring(len);
|
||||
memcpy(buf.ptr, @s[start], len);
|
||||
buf;
|
||||
buf
|
||||
}
|
||||
|
||||
// Replace XML special characters with their entity references. Used
|
||||
@@ -195,7 +195,7 @@ xml_escape :: (s: string) -> string {
|
||||
if seg_start < s.len {
|
||||
result = concat(result, substr(s, seg_start, s.len - seg_start));
|
||||
}
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
// Join path components with the POSIX separator ('/'). Skips empty
|
||||
@@ -225,7 +225,7 @@ path_join :: (..parts: []string) -> string {
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
struct_to_string :: (s: $T) -> string {
|
||||
@@ -238,7 +238,7 @@ struct_to_string :: (s: $T) -> string {
|
||||
result = concat(result, any_to_string(field_value(s, i)));
|
||||
i += 1;
|
||||
}
|
||||
concat(result, "}");
|
||||
concat(result, "}")
|
||||
}
|
||||
|
||||
vector_to_string :: (v: $T) -> string {
|
||||
@@ -249,7 +249,7 @@ vector_to_string :: (v: $T) -> string {
|
||||
result = concat(result, any_to_string(field_value(v, i)));
|
||||
i += 1;
|
||||
}
|
||||
concat(result, "]");
|
||||
concat(result, "]")
|
||||
}
|
||||
|
||||
array_to_string :: (a: $T) -> string {
|
||||
@@ -260,7 +260,7 @@ array_to_string :: (a: $T) -> string {
|
||||
result = concat(result, any_to_string(field_value(a, i)));
|
||||
i += 1;
|
||||
}
|
||||
concat(result, "]");
|
||||
concat(result, "]")
|
||||
}
|
||||
|
||||
slice_to_string :: (items: []$T) -> string {
|
||||
@@ -271,13 +271,13 @@ slice_to_string :: (items: []$T) -> string {
|
||||
result = concat(result, any_to_string(field_value(items, i)));
|
||||
i += 1;
|
||||
}
|
||||
concat(result, "]");
|
||||
concat(result, "]")
|
||||
}
|
||||
|
||||
pointer_to_string :: (p: $T) -> string {
|
||||
addr : s64 = xx p;
|
||||
if addr == 0 { "null"; } else {
|
||||
concat(type_name(T), concat("@0x", int_to_hex_string(addr)));
|
||||
if addr == 0 { "null" } else {
|
||||
concat(type_name(T), concat("@0x", int_to_hex_string(addr)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ flags_to_string :: (val: $T) -> string {
|
||||
i += 1;
|
||||
}
|
||||
if result.len == 0 { result = "0"; }
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
enum_to_string :: (u: $T) -> string {
|
||||
@@ -306,7 +306,7 @@ enum_to_string :: (u: $T) -> string {
|
||||
if pstr.len > 0 {
|
||||
result = concat(result, concat("(", concat(pstr, ")")));
|
||||
}
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
optional_to_string :: (o: $T) -> string {
|
||||
@@ -333,7 +333,7 @@ any_to_string :: (val: Any) -> string {
|
||||
case optional: result = optional_to_string(cast(type) val);
|
||||
case type: result = type_name(val);
|
||||
}
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
build_format :: (fmt: string) -> string {
|
||||
@@ -399,12 +399,12 @@ build_format :: (fmt: string) -> string {
|
||||
code = concat(code, int_to_string(fmt.len - seg_start));
|
||||
code = concat(code, ")); ");
|
||||
}
|
||||
code;
|
||||
code
|
||||
}
|
||||
|
||||
format :: ($fmt: string, ..$args) -> string {
|
||||
#insert build_format(fmt);
|
||||
#insert "result;";
|
||||
#insert "return result;";
|
||||
}
|
||||
|
||||
print :: ($fmt: string, ..$args) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file lives in modules/testpkg/ and imports std relative to its directory.
|
||||
#import "../std.sx";
|
||||
|
||||
cwd_greet :: () -> string { format("cwd-import-ok"); }
|
||||
cwd_greet :: () -> string { format("cwd-import-ok") }
|
||||
|
||||
@@ -1 +1 @@
|
||||
hello :: () -> string { "hello from testpkg"; }
|
||||
hello :: () -> string { "hello from testpkg" }
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
add :: (a: s32, b: s32) -> s32 { a + b; }
|
||||
mul :: (a: s32, b: s32) -> s32 { a * b; }
|
||||
add :: (a: s32, b: s32) -> s32 { a + b }
|
||||
mul :: (a: s32, b: s32) -> s32 { a * b }
|
||||
|
||||
@@ -38,7 +38,7 @@ spaces :: (n: s32) -> string {
|
||||
s = concat(s, " ");
|
||||
i = i + 1;
|
||||
}
|
||||
s;
|
||||
s
|
||||
}
|
||||
|
||||
// The error-trace buffer C API (library/vendors/sx_trace_runtime/sx_trace.c),
|
||||
@@ -73,7 +73,7 @@ to_string :: () -> string {
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
result;
|
||||
result
|
||||
}
|
||||
|
||||
// Write the current trace to stderr (fd 2). No-op when the buffer is empty.
|
||||
|
||||
@@ -9,14 +9,14 @@ Lerpable :: protocol #inline {
|
||||
|
||||
// --- Easing Functions ---
|
||||
|
||||
ease_linear :: (t: f32) -> f32 { t; }
|
||||
ease_in_quad :: (t: f32) -> f32 { t * t; }
|
||||
ease_out_quad :: (t: f32) -> f32 { t * (2.0 - t); }
|
||||
ease_linear :: (t: f32) -> f32 { t }
|
||||
ease_in_quad :: (t: f32) -> f32 { t * t }
|
||||
ease_out_quad :: (t: f32) -> f32 { t * (2.0 - t) }
|
||||
ease_in_out_quad :: (t: f32) -> f32 {
|
||||
if t < 0.5 then 2.0 * t * t
|
||||
else -1.0 + (4.0 - 2.0 * t) * t;
|
||||
else -1.0 + (4.0 - 2.0 * t) * t
|
||||
}
|
||||
ease_out_cubic :: (t: f32) -> f32 { u := t - 1.0; u * u * u + 1.0; }
|
||||
ease_out_cubic :: (t: f32) -> f32 { u := t - 1.0; u * u * u + 1.0 }
|
||||
|
||||
// --- AnimatedFloat — duration-based ---
|
||||
|
||||
@@ -38,7 +38,7 @@ AnimatedFloat :: struct {
|
||||
duration = 0.0,
|
||||
easing = null,
|
||||
active = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
animate_to :: (self: *AnimatedFloat, target: f32, dur: f32, ease: Closure(f32) -> f32) {
|
||||
@@ -54,7 +54,7 @@ AnimatedFloat :: struct {
|
||||
if !self.active { return; }
|
||||
self.elapsed += dt;
|
||||
t := clamp(self.elapsed / self.duration, 0.0, 1.0);
|
||||
eased := if ease := self.easing { ease(t); } else { t; };
|
||||
eased := if ease := self.easing { ease(t) } else { t };
|
||||
self.current = self.from + (self.to - self.from) * eased;
|
||||
if t >= 1.0 {
|
||||
self.current = self.to;
|
||||
@@ -83,7 +83,7 @@ SpringFloat :: struct {
|
||||
damping = 20.0,
|
||||
mass = 1.0,
|
||||
threshold = 0.01
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
snappy :: (value: f32) -> SpringFloat {
|
||||
@@ -95,7 +95,7 @@ SpringFloat :: struct {
|
||||
damping = 25.0,
|
||||
mass = 1.0,
|
||||
threshold = 0.01
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
tick :: (self: *SpringFloat, dt: f32) {
|
||||
@@ -109,7 +109,7 @@ SpringFloat :: struct {
|
||||
|
||||
is_settled :: (self: *SpringFloat) -> bool {
|
||||
abs(self.current - self.target) < self.threshold
|
||||
and abs(self.velocity) < self.threshold;
|
||||
and abs(self.velocity) < self.threshold
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ Animated :: struct ($T: Lerpable) {
|
||||
elapsed = 0.0,
|
||||
duration = 0.0,
|
||||
active = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Jump immediately to value (no animation). Used to avoid animating from zero on first layout.
|
||||
@@ -163,5 +163,5 @@ Animated :: struct ($T: Lerpable) {
|
||||
}
|
||||
}
|
||||
|
||||
is_animating :: (self: *Animated(T)) -> bool { self.active; }
|
||||
is_animating :: (self: *Animated(T)) -> bool { self.active }
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ ButtonStyle :: struct {
|
||||
pressed_bg = Color.rgb(0, 80, 180),
|
||||
corner_radius = 6.0,
|
||||
padding = EdgeInsets.symmetric(16.0, 8.0)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl View for Button {
|
||||
Size.{
|
||||
width = text_size.width + self.style.padding.horizontal(),
|
||||
height = text_size.height + self.style.padding.vertical()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
layout :: (self: *Button, bounds: Frame) {}
|
||||
@@ -86,6 +86,6 @@ impl View for Button {
|
||||
}
|
||||
}
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ dock_zone_to_alignment :: (zone: DockZone) -> ?Alignment {
|
||||
}
|
||||
|
||||
dock_zone_should_fill :: (zone: DockZone) -> bool {
|
||||
zone == .fill;
|
||||
zone == .fill
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -169,7 +169,7 @@ DockInteraction :: struct {
|
||||
|
||||
get_animated_size :: (self: *DockInteraction, index: s64) -> Size {
|
||||
if index >= self.child_count { return Size.zero(); }
|
||||
(@self.anim_sizes.items[index]).current;
|
||||
(@self.anim_sizes.items[index]).current
|
||||
}
|
||||
|
||||
tick_animations :: (self: *DockInteraction, dt: f32) {
|
||||
@@ -184,7 +184,7 @@ DockInteraction :: struct {
|
||||
get_hovered_dock_zone :: (self: *DockInteraction) -> ?DockZone {
|
||||
if self.hovered_zone < 0 { return null; }
|
||||
// Map ordinal back to DockZone
|
||||
cast(DockZone) self.hovered_zone;
|
||||
cast(DockZone) self.hovered_zone
|
||||
}
|
||||
|
||||
set_hovered_dock_zone :: (self: *DockInteraction, zone: ?DockZone) {
|
||||
@@ -242,7 +242,7 @@ find_hovered_zone :: (bounds: Frame, pos: Point, hint_size: f32, enable_corners:
|
||||
if expanded.contains(pos) { return zone; }
|
||||
i += 1;
|
||||
}
|
||||
null;
|
||||
null
|
||||
}
|
||||
|
||||
calculate_origin :: (bounds: Frame, child_size: Size, alignment: Alignment) -> Point {
|
||||
@@ -262,7 +262,7 @@ calculate_origin :: (bounds: Frame, child_size: Size, alignment: Alignment) -> P
|
||||
y = bounds.origin.y + bounds.size.height - child_size.height;
|
||||
}
|
||||
|
||||
Point.{ x = x, y = y };
|
||||
Point.{ x = x, y = y }
|
||||
}
|
||||
|
||||
get_size_proposal_for_alignment :: (alignment: Alignment, bounds_size: Size, is_fill: bool) -> ProposedSize {
|
||||
@@ -280,7 +280,7 @@ get_size_proposal_for_alignment :: (alignment: Alignment, bounds_size: Size, is_
|
||||
}
|
||||
}
|
||||
// Center or corners: natural size
|
||||
ProposedSize.flexible();
|
||||
ProposedSize.flexible()
|
||||
}
|
||||
|
||||
get_final_size_for_alignment :: (alignment: Alignment, child_size: Size, bounds_size: Size, is_fill: bool) -> Size {
|
||||
@@ -299,7 +299,7 @@ get_final_size_for_alignment :: (alignment: Alignment, child_size: Size, bounds_
|
||||
}
|
||||
}
|
||||
// Center or corners: natural size
|
||||
child_size;
|
||||
child_size
|
||||
}
|
||||
|
||||
draw_zone_indicator :: (ctx: *RenderContext, frame: Frame, zone: DockZone, color: Color) {
|
||||
@@ -385,15 +385,15 @@ DockPanel :: struct {
|
||||
header_height = DockPanel.DEFAULT_HEADER_H,
|
||||
dock_interaction = null, // set by Dock.add_panel
|
||||
panel_index = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View for DockPanel {
|
||||
size_that_fits :: (self: *DockPanel, proposal: ProposedSize) -> Size {
|
||||
content_size := self.child.view.size_that_fits(ProposedSize.{ width = proposal.width, height = null });
|
||||
w := if pw := proposal.width { min(content_size.width, pw); } else { content_size.width; };
|
||||
Size.{ width = w, height = content_size.height + self.header_height };
|
||||
w := if pw := proposal.width { min(content_size.width, pw) } else { content_size.width };
|
||||
Size.{ width = w, height = content_size.height + self.header_height }
|
||||
}
|
||||
|
||||
layout :: (self: *DockPanel, bounds: Frame) {
|
||||
@@ -446,7 +446,7 @@ impl View for DockPanel {
|
||||
}
|
||||
|
||||
// Forward to child content
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ Dock :: struct {
|
||||
d.preview_color = Color.rgba(77, 153, 255, 64);
|
||||
d.enable_corners = true;
|
||||
d.on_dock = null;
|
||||
d;
|
||||
d
|
||||
}
|
||||
|
||||
add_panel :: (self: *Dock, panel: DockPanel) {
|
||||
@@ -508,7 +508,7 @@ impl View for Dock {
|
||||
Size.{
|
||||
width = proposal.width ?? 800.0,
|
||||
height = proposal.height ?? 600.0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
layout :: (self: *Dock, bounds: Frame) {
|
||||
@@ -687,6 +687,6 @@ impl View for Dock {
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ event_position :: (e: *Event) -> ?Point {
|
||||
case .mouse_moved: (d) { return d.position; }
|
||||
case .mouse_wheel: (d) { return d.position; }
|
||||
}
|
||||
null;
|
||||
null
|
||||
}
|
||||
|
||||
// Map a platform (SDL) keycode to the neutral `Keycode`. Only the keys the app
|
||||
@@ -72,7 +72,7 @@ keycode_from_sdl :: (k: SDL_Keycode) -> Keycode {
|
||||
case .up: return .up;
|
||||
case .down: return .down;
|
||||
}
|
||||
.unknown;
|
||||
.unknown
|
||||
}
|
||||
|
||||
// Translate SDL_Event → our Event type
|
||||
@@ -129,5 +129,5 @@ translate_sdl_event :: (sdl: *SDL_Event) -> Event {
|
||||
});
|
||||
}
|
||||
}
|
||||
.none;
|
||||
.none
|
||||
}
|
||||
|
||||
@@ -16,5 +16,5 @@ measure_text :: (text: string, font_size: f32) -> Size {
|
||||
scale := font_size / 16.0;
|
||||
return Size.{ width = xx text.len * 8.0 * scale, height = font_size };
|
||||
}
|
||||
g_font.measure_text(text, font_size);
|
||||
g_font.measure_text(text, font_size)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ TapGesture :: struct {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ DragGesture :: struct {
|
||||
location = self.current_location,
|
||||
start_location = self.start_location,
|
||||
translation = self.current_location.sub(self.start_location)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
handle_event :: (self: *DragGesture, event: *Event, frame: Frame) -> bool {
|
||||
@@ -123,6 +123,6 @@ DragGesture :: struct {
|
||||
self.phase = .possible;
|
||||
}
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +28,16 @@ GlyphEntry :: struct {
|
||||
// Quantize font size to half-point increments to limit cache entries.
|
||||
// e.g., 13.0 -> 26, 13.5 -> 27, 14.0 -> 28
|
||||
quantize_size :: (font_size: f32) -> u16 {
|
||||
xx (font_size * 2.0 + 0.5);
|
||||
xx (font_size * 2.0 + 0.5)
|
||||
}
|
||||
|
||||
dequantize_size :: (q: u16) -> f32 {
|
||||
xx q / 2.0;
|
||||
xx q / 2.0
|
||||
}
|
||||
|
||||
// Pack (glyph_index, size_quantized) into a single u32 for fast comparison
|
||||
make_glyph_key :: (glyph_index: u16, size_quantized: u16) -> u32 {
|
||||
(xx glyph_index << 16) | xx size_quantized;
|
||||
(xx glyph_index << 16) | xx size_quantized
|
||||
}
|
||||
|
||||
// Shaped glyph — output of text shaping (positioned glyph with index)
|
||||
@@ -54,7 +54,7 @@ is_ascii :: (text: string) -> bool {
|
||||
if text[i] >= 128 { return false; }
|
||||
i += 1;
|
||||
}
|
||||
true;
|
||||
true
|
||||
}
|
||||
|
||||
// kbts constants (C enum values)
|
||||
@@ -480,7 +480,7 @@ GlyphCache :: struct {
|
||||
}
|
||||
|
||||
// No space
|
||||
PackResult.{ x = 0 - 1, y = 0 - 1 };
|
||||
PackResult.{ x = 0 - 1, y = 0 - 1 }
|
||||
}
|
||||
|
||||
// Grow the atlas by doubling dimensions
|
||||
@@ -547,18 +547,18 @@ GlyphCache :: struct {
|
||||
|
||||
// Get the scale factor for a logical font size
|
||||
scale_for_size :: (self: *GlyphCache, font_size: f32) -> f32 {
|
||||
stbtt_ScaleForPixelHeight(self.font_info, font_size);
|
||||
stbtt_ScaleForPixelHeight(self.font_info, font_size)
|
||||
}
|
||||
|
||||
// Get scaled ascent for a logical font size
|
||||
get_ascent :: (self: *GlyphCache, font_size: f32) -> f32 {
|
||||
self.ascent * self.scale_for_size(font_size);
|
||||
self.ascent * self.scale_for_size(font_size)
|
||||
}
|
||||
|
||||
// Get scaled line height for a logical font size
|
||||
get_line_height :: (self: *GlyphCache, font_size: f32) -> f32 {
|
||||
s := self.scale_for_size(font_size);
|
||||
(self.ascent - self.descent + self.line_gap) * s;
|
||||
(self.ascent - self.descent + self.line_gap) * s
|
||||
}
|
||||
|
||||
// Shape text into positioned glyphs.
|
||||
@@ -654,6 +654,6 @@ GlyphCache :: struct {
|
||||
width += self.shaped_buf.items[i].advance;
|
||||
i += 1;
|
||||
}
|
||||
Size.{ width = width, height = self.get_line_height(font_size) };
|
||||
Size.{ width = width, height = self.get_line_height(font_size) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ impl View for ImageView {
|
||||
// Maintain aspect ratio: fit within proposal
|
||||
aspect := self.width / self.height;
|
||||
if pw / ph > aspect {
|
||||
Size.{ width = ph * aspect, height = ph };
|
||||
Size.{ width = ph * aspect, height = ph }
|
||||
} else {
|
||||
Size.{ width = pw, height = pw / aspect };
|
||||
Size.{ width = pw, height = pw / aspect }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ impl View for ImageView {
|
||||
}
|
||||
|
||||
handle_event :: (self: *ImageView, event: *Event, frame: Frame) -> bool {
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ Label :: struct {
|
||||
text = text,
|
||||
font_size = 14.0,
|
||||
color = COLOR_WHITE
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl View for Label {
|
||||
size_that_fits :: (self: *Label, proposal: ProposedSize) -> Size {
|
||||
measure_text(self.text, self.font_size);
|
||||
measure_text(self.text, self.font_size)
|
||||
}
|
||||
|
||||
layout :: (self: *Label, bounds: Frame) {
|
||||
@@ -33,6 +33,6 @@ impl View for Label {
|
||||
}
|
||||
|
||||
handle_event :: (self: *Label, event: *Event, frame: Frame) -> bool {
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ measure_vstack :: (children: *List(ViewChild), proposal: ProposedSize, spacing:
|
||||
total_height = total_height + spacing * xx (n - 1);
|
||||
|
||||
result_width := min(proposal.width ?? max_width, max_width);
|
||||
Size.{ width = result_width, height = total_height };
|
||||
Size.{ width = result_width, height = total_height }
|
||||
}
|
||||
|
||||
measure_hstack :: (children: *List(ViewChild), proposal: ProposedSize, spacing: f32) -> Size {
|
||||
@@ -129,7 +129,7 @@ measure_hstack :: (children: *List(ViewChild), proposal: ProposedSize, spacing:
|
||||
total_width = total_width + spacing * xx (n - 1);
|
||||
|
||||
result_height := min(proposal.height ?? max_height, max_height);
|
||||
Size.{ width = total_width, height = result_height };
|
||||
Size.{ width = total_width, height = result_height }
|
||||
}
|
||||
|
||||
measure_zstack :: (children: *List(ViewChild), proposal: ProposedSize) -> Size {
|
||||
@@ -148,5 +148,5 @@ measure_zstack :: (children: *List(ViewChild), proposal: ProposedSize) -> Size {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Size.{ width = max_width, height = max_height };
|
||||
Size.{ width = max_width, height = max_height }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ impl View for PaddingModifier {
|
||||
Size.{
|
||||
width = child_size.width + self.insets.horizontal(),
|
||||
height = child_size.height + self.insets.vertical()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
layout :: (self: *PaddingModifier, bounds: Frame) {
|
||||
@@ -37,7 +37,7 @@ impl View for PaddingModifier {
|
||||
}
|
||||
|
||||
handle_event :: (self: *PaddingModifier, event: *Event, frame: Frame) -> bool {
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ impl View for FrameModifier {
|
||||
Size.{
|
||||
width = self.width ?? child_size.width,
|
||||
height = self.height ?? child_size.height
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
layout :: (self: *FrameModifier, bounds: Frame) {
|
||||
@@ -78,7 +78,7 @@ impl View for FrameModifier {
|
||||
}
|
||||
|
||||
handle_event :: (self: *FrameModifier, event: *Event, frame: Frame) -> bool {
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ BackgroundModifier :: struct {
|
||||
|
||||
impl View for BackgroundModifier {
|
||||
size_that_fits :: (self: *BackgroundModifier, proposal: ProposedSize) -> Size {
|
||||
self.child.view.size_that_fits(proposal);
|
||||
self.child.view.size_that_fits(proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *BackgroundModifier, bounds: Frame) {
|
||||
@@ -110,7 +110,7 @@ impl View for BackgroundModifier {
|
||||
}
|
||||
|
||||
handle_event :: (self: *BackgroundModifier, event: *Event, frame: Frame) -> bool {
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ OpacityModifier :: struct {
|
||||
|
||||
impl View for OpacityModifier {
|
||||
size_that_fits :: (self: *OpacityModifier, proposal: ProposedSize) -> Size {
|
||||
self.child.view.size_that_fits(proposal);
|
||||
self.child.view.size_that_fits(proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *OpacityModifier, bounds: Frame) {
|
||||
@@ -139,7 +139,7 @@ impl View for OpacityModifier {
|
||||
}
|
||||
|
||||
handle_event :: (self: *OpacityModifier, event: *Event, frame: Frame) -> bool {
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ ClipModifier :: struct {
|
||||
|
||||
impl View for ClipModifier {
|
||||
size_that_fits :: (self: *ClipModifier, proposal: ProposedSize) -> Size {
|
||||
self.child.view.size_that_fits(proposal);
|
||||
self.child.view.size_that_fits(proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *ClipModifier, bounds: Frame) {
|
||||
@@ -167,7 +167,7 @@ impl View for ClipModifier {
|
||||
}
|
||||
|
||||
handle_event :: (self: *ClipModifier, event: *Event, frame: Frame) -> bool {
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ HiddenModifier :: struct {
|
||||
impl View for HiddenModifier {
|
||||
size_that_fits :: (self: *HiddenModifier, proposal: ProposedSize) -> Size {
|
||||
if self.is_hidden { return Size.zero(); }
|
||||
self.child.view.size_that_fits(proposal);
|
||||
self.child.view.size_that_fits(proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *HiddenModifier, bounds: Frame) {
|
||||
@@ -197,7 +197,7 @@ impl View for HiddenModifier {
|
||||
|
||||
handle_event :: (self: *HiddenModifier, event: *Event, frame: Frame) -> bool {
|
||||
if self.is_hidden { return false; }
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ TapGestureModifier :: struct {
|
||||
|
||||
impl View for TapGestureModifier {
|
||||
size_that_fits :: (self: *TapGestureModifier, proposal: ProposedSize) -> Size {
|
||||
self.child.view.size_that_fits(proposal);
|
||||
self.child.view.size_that_fits(proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *TapGestureModifier, bounds: Frame) {
|
||||
@@ -224,7 +224,7 @@ impl View for TapGestureModifier {
|
||||
|
||||
handle_event :: (self: *TapGestureModifier, event: *Event, frame: Frame) -> bool {
|
||||
if self.gesture.handle_event(event, frame) { return true; }
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ DragGestureModifier :: struct {
|
||||
|
||||
impl View for DragGestureModifier {
|
||||
size_that_fits :: (self: *DragGestureModifier, proposal: ProposedSize) -> Size {
|
||||
self.child.view.size_that_fits(proposal);
|
||||
self.child.view.size_that_fits(proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *DragGestureModifier, bounds: Frame) {
|
||||
@@ -251,46 +251,46 @@ impl View for DragGestureModifier {
|
||||
|
||||
handle_event :: (self: *DragGestureModifier, event: *Event, frame: Frame) -> bool {
|
||||
if self.gesture.handle_event(event, frame) { return true; }
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Convenience functions ---
|
||||
|
||||
padding :: (view: View, insets: EdgeInsets) -> PaddingModifier {
|
||||
PaddingModifier.{ child = .{ view = view }, insets = insets };
|
||||
PaddingModifier.{ child = .{ view = view }, insets = insets }
|
||||
}
|
||||
|
||||
fixed_frame :: (view: View, width: ?f32, height: ?f32) -> FrameModifier {
|
||||
FrameModifier.{ child = .{ view = view }, width = width, height = height };
|
||||
FrameModifier.{ child = .{ view = view }, width = width, height = height }
|
||||
}
|
||||
|
||||
background :: (view: View, color: Color, corner_radius: f32) -> BackgroundModifier {
|
||||
BackgroundModifier.{ child = .{ view = view }, color = color, corner_radius = corner_radius };
|
||||
BackgroundModifier.{ child = .{ view = view }, color = color, corner_radius = corner_radius }
|
||||
}
|
||||
|
||||
with_opacity :: (view: View, alpha: f32) -> OpacityModifier {
|
||||
OpacityModifier.{ child = .{ view = view }, alpha = alpha };
|
||||
OpacityModifier.{ child = .{ view = view }, alpha = alpha }
|
||||
}
|
||||
|
||||
clip :: (view: View, corner_radius: f32) -> ClipModifier {
|
||||
ClipModifier.{ child = .{ view = view }, corner_radius = corner_radius };
|
||||
ClipModifier.{ child = .{ view = view }, corner_radius = corner_radius }
|
||||
}
|
||||
|
||||
hidden :: (view: View, is_hidden: bool) -> HiddenModifier {
|
||||
HiddenModifier.{ child = .{ view = view }, is_hidden = is_hidden };
|
||||
HiddenModifier.{ child = .{ view = view }, is_hidden = is_hidden }
|
||||
}
|
||||
|
||||
on_tap :: (view: View, handler: Closure()) -> TapGestureModifier {
|
||||
TapGestureModifier.{
|
||||
child = .{ view = view },
|
||||
gesture = TapGesture.{ count = 1, on_tap = handler }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
on_drag :: (view: View, on_changed: ?Closure(DragValue), on_ended: ?Closure(DragValue)) -> DragGestureModifier {
|
||||
DragGestureModifier.{
|
||||
child = .{ view = view },
|
||||
gesture = DragGesture.{ min_distance = 10.0, on_changed = on_changed, on_ended = on_ended }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ UIPipeline :: struct {
|
||||
// Process a single event through the view tree
|
||||
dispatch_event :: (self: *UIPipeline, event: *Event) -> bool {
|
||||
if self.has_root == false { return false; }
|
||||
self.root.view.handle_event(event, self.root.computed_frame);
|
||||
self.root.view.handle_event(event, self.root.computed_frame)
|
||||
}
|
||||
|
||||
// Run one frame: layout → render → commit
|
||||
|
||||
@@ -43,7 +43,7 @@ RenderTree :: struct {
|
||||
generation: s64;
|
||||
|
||||
init :: () -> RenderTree {
|
||||
RenderTree.{ generation = 0 };
|
||||
RenderTree.{ generation = 0 }
|
||||
}
|
||||
|
||||
clear :: (self: *RenderTree) {
|
||||
@@ -54,7 +54,7 @@ RenderTree :: struct {
|
||||
add :: (self: *RenderTree, node: RenderNode) -> s64 {
|
||||
idx := self.nodes.len;
|
||||
self.nodes.append(node);
|
||||
idx;
|
||||
idx
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ RenderContext :: struct {
|
||||
clip_depth = 0,
|
||||
opacity = 1.0,
|
||||
depth = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
add_rect :: (self: *RenderContext, frame: Frame, fill: Color) {
|
||||
|
||||
@@ -413,7 +413,7 @@ create_white_texture :: () -> u32 {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, xx GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, @pixel);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, xx GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, xx GL_NEAREST);
|
||||
tex;
|
||||
tex
|
||||
}
|
||||
|
||||
// --- UI Shaders ---
|
||||
|
||||
@@ -51,7 +51,7 @@ impl View for ScrollView {
|
||||
Size.{
|
||||
width = proposal.width ?? 200.0,
|
||||
height = proposal.height ?? 200.0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
layout :: (self: *ScrollView, bounds: Frame) {
|
||||
@@ -144,6 +144,6 @@ impl View for ScrollView {
|
||||
return self.child.view.handle_event(event, self.child.computed_frame);
|
||||
}
|
||||
}
|
||||
self.child.view.handle_event(event, self.child.computed_frame);
|
||||
self.child.view.handle_event(event, self.child.computed_frame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ VStack :: struct {
|
||||
|
||||
impl View for VStack {
|
||||
size_that_fits :: (self: *VStack, proposal: ProposedSize) -> Size {
|
||||
measure_vstack(@self.children, proposal, self.spacing);
|
||||
measure_vstack(@self.children, proposal, self.spacing)
|
||||
}
|
||||
|
||||
layout :: (self: *VStack, bounds: Frame) {
|
||||
@@ -44,7 +44,7 @@ impl View for VStack {
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ HStack :: struct {
|
||||
|
||||
impl View for HStack {
|
||||
size_that_fits :: (self: *HStack, proposal: ProposedSize) -> Size {
|
||||
measure_hstack(@self.children, proposal, self.spacing);
|
||||
measure_hstack(@self.children, proposal, self.spacing)
|
||||
}
|
||||
|
||||
layout :: (self: *HStack, bounds: Frame) {
|
||||
@@ -85,7 +85,7 @@ impl View for HStack {
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ ZStack :: struct {
|
||||
|
||||
impl View for ZStack {
|
||||
size_that_fits :: (self: *ZStack, proposal: ProposedSize) -> Size {
|
||||
measure_zstack(@self.children, proposal);
|
||||
measure_zstack(@self.children, proposal)
|
||||
}
|
||||
|
||||
layout :: (self: *ZStack, bounds: Frame) {
|
||||
@@ -127,7 +127,7 @@ impl View for ZStack {
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,12 +142,12 @@ impl View for Spacer {
|
||||
size_that_fits :: (self: *Spacer, proposal: ProposedSize) -> Size {
|
||||
w := proposal.width ?? self.min_length;
|
||||
h := proposal.height ?? self.min_length;
|
||||
Size.{ width = max(w, self.min_length), height = max(h, self.min_length) };
|
||||
Size.{ width = max(w, self.min_length), height = max(h, self.min_length) }
|
||||
}
|
||||
|
||||
layout :: (self: *Spacer, bounds: Frame) {}
|
||||
render :: (self: *Spacer, ctx: *RenderContext, frame: Frame) {}
|
||||
handle_event :: (self: *Spacer, event: *Event, frame: Frame) -> bool { false; }
|
||||
handle_event :: (self: *Spacer, event: *Event, frame: Frame) -> bool { false }
|
||||
}
|
||||
|
||||
// Rect — simple colored rectangle view
|
||||
@@ -164,7 +164,7 @@ impl View for RectView {
|
||||
size_that_fits :: (self: *RectView, proposal: ProposedSize) -> Size {
|
||||
w := proposal.width ?? self.preferred_width;
|
||||
h := proposal.height ?? self.preferred_height;
|
||||
Size.{ width = w, height = h };
|
||||
Size.{ width = w, height = h }
|
||||
}
|
||||
|
||||
layout :: (self: *RectView, bounds: Frame) {}
|
||||
@@ -177,5 +177,5 @@ impl View for RectView {
|
||||
}
|
||||
}
|
||||
|
||||
handle_event :: (self: *RectView, event: *Event, frame: Frame) -> bool { false; }
|
||||
handle_event :: (self: *RectView, event: *Event, frame: Frame) -> bool { false }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
State :: struct ($T: Type) {
|
||||
ptr: *T;
|
||||
|
||||
get :: (self: State(T)) -> T { self.ptr.*; }
|
||||
get :: (self: State(T)) -> T { self.ptr.* }
|
||||
|
||||
set :: (self: State(T), val: T) { self.ptr.* = val; }
|
||||
}
|
||||
@@ -52,7 +52,7 @@ StateStore :: struct {
|
||||
size = size_of(T),
|
||||
generation = self.current_generation
|
||||
}, self.parent_allocator);
|
||||
State(T).{ ptr = xx data };
|
||||
State(T).{ ptr = xx data }
|
||||
}
|
||||
|
||||
next_frame :: (self: *StateStore) {
|
||||
|
||||
@@ -24,7 +24,7 @@ impl View for StatsPanel {
|
||||
fps_size := measure_text("FPS: 0000", StatsPanel.VALUE_SIZE);
|
||||
w := max(title_size.width, fps_size.width) + StatsPanel.PADDING * 2.0;
|
||||
h := title_size.height + StatsPanel.LINE_SPACING + fps_size.height + StatsPanel.PADDING * 2.0;
|
||||
Size.{ width = w, height = h };
|
||||
Size.{ width = w, height = h }
|
||||
}
|
||||
|
||||
layout :: (self: *StatsPanel, bounds: Frame) {}
|
||||
@@ -58,6 +58,6 @@ impl View for StatsPanel {
|
||||
}
|
||||
|
||||
handle_event :: (self: *StatsPanel, event: *Event, frame: Frame) -> bool {
|
||||
false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,21 @@ Point :: struct {
|
||||
zero :: () -> Point => .{ x = 0.0, y = 0.0 };
|
||||
|
||||
add :: (self: Point, b: Point) -> Point {
|
||||
Point.{ x = self.x + b.x, y = self.y + b.y };
|
||||
Point.{ x = self.x + b.x, y = self.y + b.y }
|
||||
}
|
||||
|
||||
sub :: (self: Point, b: Point) -> Point {
|
||||
Point.{ x = self.x - b.x, y = self.y - b.y };
|
||||
Point.{ x = self.x - b.x, y = self.y - b.y }
|
||||
}
|
||||
|
||||
scale :: (self: Point, s: f32) -> Point {
|
||||
Point.{ x = self.x * s, y = self.y * s };
|
||||
Point.{ x = self.x * s, y = self.y * s }
|
||||
}
|
||||
|
||||
distance :: (self: Point, b: Point) -> f32 {
|
||||
dx := self.x - b.x;
|
||||
dy := self.y - b.y;
|
||||
sqrt(dx * dx + dy * dy);
|
||||
sqrt(dx * dx + dy * dy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ Size :: struct {
|
||||
zero :: () -> Size => .{ width = 0.0, height = 0.0 };
|
||||
|
||||
contains :: (self: Size, point: Point) -> bool {
|
||||
point.x >= 0.0 and point.x <= self.width and point.y >= 0.0 and point.y <= self.height;
|
||||
point.x >= 0.0 and point.x <= self.width and point.y >= 0.0 and point.y <= self.height
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,20 +39,20 @@ Frame :: struct {
|
||||
origin: Point;
|
||||
size: Size;
|
||||
|
||||
zero :: () -> Frame { Frame.{ origin = Point.zero(), size = Size.zero() }; }
|
||||
zero :: () -> Frame { Frame.{ origin = Point.zero(), size = Size.zero() } }
|
||||
|
||||
make :: (x: f32, y: f32, w: f32, h: f32) -> Frame {
|
||||
Frame.{ origin = Point.{ x = x, y = y }, size = Size.{ width = w, height = h } };
|
||||
Frame.{ origin = Point.{ x = x, y = y }, size = Size.{ width = w, height = h } }
|
||||
}
|
||||
|
||||
max_x :: (self: Frame) -> f32 { self.origin.x + self.size.width; }
|
||||
max_y :: (self: Frame) -> f32 { self.origin.y + self.size.height; }
|
||||
mid_x :: (self: Frame) -> f32 { self.origin.x + self.size.width * 0.5; }
|
||||
mid_y :: (self: Frame) -> f32 { self.origin.y + self.size.height * 0.5; }
|
||||
max_x :: (self: Frame) -> f32 { self.origin.x + self.size.width }
|
||||
max_y :: (self: Frame) -> f32 { self.origin.y + self.size.height }
|
||||
mid_x :: (self: Frame) -> f32 { self.origin.x + self.size.width * 0.5 }
|
||||
mid_y :: (self: Frame) -> f32 { self.origin.y + self.size.height * 0.5 }
|
||||
|
||||
contains :: (self: Frame, point: Point) -> bool {
|
||||
point.x >= self.origin.x and point.x <= self.max_x()
|
||||
and point.y >= self.origin.y and point.y <= self.max_y();
|
||||
and point.y >= self.origin.y and point.y <= self.max_y()
|
||||
}
|
||||
|
||||
intersection :: (self: Frame, other: Frame) -> Frame {
|
||||
@@ -62,7 +62,7 @@ Frame :: struct {
|
||||
y2 := min(self.max_y(), other.max_y());
|
||||
if x2 <= x1 or y2 <= y1
|
||||
then .zero()
|
||||
else .make(x1, y1, x2 - x1, y2 - y1);
|
||||
else .make(x1, y1, x2 - x1, y2 - y1)
|
||||
}
|
||||
|
||||
inset :: (self: Frame, insets: EdgeInsets) -> Frame {
|
||||
@@ -71,7 +71,7 @@ Frame :: struct {
|
||||
self.origin.y + insets.top,
|
||||
self.size.width - insets.left - insets.right,
|
||||
self.size.height - insets.top - insets.bottom
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
expand :: (self: Frame, amount: f32) -> Frame {
|
||||
@@ -80,45 +80,45 @@ Frame :: struct {
|
||||
self.origin.y - amount,
|
||||
self.size.width + amount * 2.0,
|
||||
self.size.height + amount * 2.0
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsets :: struct {
|
||||
top, left, bottom, right: f32;
|
||||
|
||||
zero :: () -> EdgeInsets { EdgeInsets.{ top = 0.0, left = 0.0, bottom = 0.0, right = 0.0 }; }
|
||||
zero :: () -> EdgeInsets { EdgeInsets.{ top = 0.0, left = 0.0, bottom = 0.0, right = 0.0 } }
|
||||
|
||||
all :: (v: f32) -> EdgeInsets {
|
||||
EdgeInsets.{ top = v, left = v, bottom = v, right = v };
|
||||
EdgeInsets.{ top = v, left = v, bottom = v, right = v }
|
||||
}
|
||||
|
||||
symmetric :: (h: f32, v: f32) -> EdgeInsets {
|
||||
EdgeInsets.{ top = v, left = h, bottom = v, right = h };
|
||||
EdgeInsets.{ top = v, left = h, bottom = v, right = h }
|
||||
}
|
||||
|
||||
horizontal :: (self: EdgeInsets) -> f32 { self.left + self.right; }
|
||||
vertical :: (self: EdgeInsets) -> f32 { self.top + self.bottom; }
|
||||
horizontal :: (self: EdgeInsets) -> f32 { self.left + self.right }
|
||||
vertical :: (self: EdgeInsets) -> f32 { self.top + self.bottom }
|
||||
}
|
||||
|
||||
Color :: struct {
|
||||
r, g, b, a: u8;
|
||||
|
||||
rgba :: (r: u8, g: u8, b: u8, a: u8) -> Color {
|
||||
Color.{ r = r, g = g, b = b, a = a };
|
||||
Color.{ r = r, g = g, b = b, a = a }
|
||||
}
|
||||
|
||||
rgb :: (r: u8, g: u8, b: u8) -> Color {
|
||||
Color.{ r = r, g = g, b = b, a = 255 };
|
||||
Color.{ r = r, g = g, b = b, a = 255 }
|
||||
}
|
||||
|
||||
rf :: (self: Color) -> f32 { xx self.r / 255.0; }
|
||||
gf :: (self: Color) -> f32 { xx self.g / 255.0; }
|
||||
bf :: (self: Color) -> f32 { xx self.b / 255.0; }
|
||||
af :: (self: Color) -> f32 { xx self.a / 255.0; }
|
||||
rf :: (self: Color) -> f32 { xx self.r / 255.0 }
|
||||
gf :: (self: Color) -> f32 { xx self.g / 255.0 }
|
||||
bf :: (self: Color) -> f32 { xx self.b / 255.0 }
|
||||
af :: (self: Color) -> f32 { xx self.a / 255.0 }
|
||||
|
||||
with_alpha :: (self: Color, a: u8) -> Color {
|
||||
Color.{ r = self.r, g = self.g, b = self.b, a = a };
|
||||
Color.{ r = self.r, g = self.g, b = self.b, a = a }
|
||||
}
|
||||
|
||||
lerp :: (self: Color, b: Color, t: f32) -> Color {
|
||||
@@ -127,7 +127,7 @@ Color :: struct {
|
||||
g = xx (self.g + (b.g - self.g) * t),
|
||||
b = xx (self.b + (b.b - self.b) * t),
|
||||
a = xx (self.a + (b.a - self.a) * t),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,11 +150,11 @@ ProposedSize :: struct {
|
||||
height: ?f32;
|
||||
|
||||
fixed :: (w: f32, h: f32) -> ProposedSize {
|
||||
ProposedSize.{ width = w, height = h };
|
||||
ProposedSize.{ width = w, height = h }
|
||||
}
|
||||
|
||||
flexible :: () -> ProposedSize {
|
||||
ProposedSize.{ width = null, height = null };
|
||||
ProposedSize.{ width = null, height = null }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ ALIGN_BOTTOM_TRAILING :: Alignment.{ h = .trailing, v = .bottom };
|
||||
align_h :: (alignment: HAlignment, child_width: f32, container_width: f32) -> f32 {
|
||||
if alignment == {
|
||||
case .leading: 0.0;
|
||||
case .center: { (container_width - child_width) * 0.5; }
|
||||
case .center: { (container_width - child_width) * 0.5 }
|
||||
case .trailing: container_width - child_width;
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,7 @@ align_h :: (alignment: HAlignment, child_width: f32, container_width: f32) -> f3
|
||||
align_v :: (alignment: VAlignment, child_height: f32, container_height: f32) -> f32 {
|
||||
if alignment == {
|
||||
case .top: 0.0;
|
||||
case .center: { (container_height - child_height) * 0.5; }
|
||||
case .center: { (container_height - child_height) * 0.5 }
|
||||
case .bottom: container_height - child_height;
|
||||
}
|
||||
}
|
||||
@@ -209,12 +209,12 @@ align_v :: (alignment: VAlignment, child_height: f32, container_height: f32) ->
|
||||
|
||||
impl Lerpable for Point {
|
||||
lerp :: (self: Point, b: Point, t: f32) -> Point {
|
||||
Point.{ x = self.x + (b.x - self.x) * t, y = self.y + (b.y - self.y) * t };
|
||||
Point.{ x = self.x + (b.x - self.x) * t, y = self.y + (b.y - self.y) * t }
|
||||
}
|
||||
}
|
||||
|
||||
impl Lerpable for Size {
|
||||
lerp :: (self: Size, b: Size, t: f32) -> Size {
|
||||
Size.{ width = self.width + (b.width - self.width) * t, height = self.height + (b.height - self.height) * t };
|
||||
Size.{ width = self.width + (b.width - self.width) * t, height = self.height + (b.height - self.height) * t }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user