From 6065695af97dcd40618764e2bee17bc76c67b65b Mon Sep 17 00:00:00 2001
From: Jeeves <guydoodlesdev@gmail.com>
Date: Mon, 8 Apr 2024 12:15:54 -0600
Subject: [PATCH] colors with transparency (to fix later)

---
 src/color.zig | 64 +++++++++++++++++++++++++++------
 src/main.zig  | 98 +++++++++++++++++++++++----------------------------
 2 files changed, 98 insertions(+), 64 deletions(-)

diff --git a/src/color.zig b/src/color.zig
index 62fad2e..ce666c1 100644
--- a/src/color.zig
+++ b/src/color.zig
@@ -18,16 +18,45 @@ pub const RGB = struct {
     }
 };
 
-// pub const Default256 = [_]RGB{
-//     RGB{ .r = 0, .g = 0, .b = 0 },
-//     RGB{ .r = 204, .g = 4, .b = 3 },
-//     RGB{ .r = 25, .g = 203, .b = 0 },
-//     RGB{ .r = 206, .g = 203, .b = 0 },
-//     RGB{ .r = 13, .g = 115, .b = 204 },
-//     RGB{ .r = 203, .g = 30, .b = 209 },
-//     RGB{ .r = 13, .g = 205, .b = 205 },
-//     RGB{ .r = 221, .g = 221, .b = 221 },
-// };
+pub const RGBA = struct {
+    r: f32,
+    g: f32,
+    b: f32,
+    /// Alpha channel between 0.0 - 1.0
+    a: f32 = 1.0,
+
+    /// Create from string at comptime
+    pub fn init(comptime string: []const u8) !RGBA {
+        std.debug.assert(string.len == 8 or string.len == 9);
+        comptime var index: usize = 0;
+        if (string[0] == '#') index += 1;
+        const r = try fmt.parseInt(u8, string[index .. index + 2], 16);
+        const g = try fmt.parseInt(u8, string[index + 2 .. index + 4], 16);
+        const b = try fmt.parseInt(u8, string[index + 4 .. index + 6], 16);
+        const a = try fmt.parseInt(u8, string[index + 6 .. index + 8], 16);
+        return .{
+            .r = @as(f32, @floatFromInt(r)) / 0xff,
+            .g = @as(f32, @floatFromInt(g)) / 0xff,
+            .b = @as(f32, @floatFromInt(b)) / 0xff,
+            .a = @as(f32, @floatFromInt(a)) / 0xff,
+        };
+    }
+
+    pub fn blend(base: *const RGBA, add: *const RGBA) RGBA {
+        var r: RGBA = undefined;
+        r.a = 1 - (1 - add.a) * (1 - base.a);
+        if (r.a < std.math.floatEps(f32)) {
+            r.r = 0;
+            r.g = 0;
+            r.b = 0;
+            return r;
+        }
+        r.r = (add.r * add.a / r.a + base.r * base.a * (1 - add.a)) / r.a;
+        r.g = (add.g * add.a / r.a + base.g * base.a * (1 - add.a)) / r.a;
+        r.b = (add.b * add.a / r.a + base.b * base.a * (1 - add.a)) / r.a;
+        return r;
+    }
+};
 
 test "RGB init from string" {
     const red = try RGB.init("#ff0000");
@@ -47,3 +76,18 @@ test "RGB init from string" {
 
     try std.testing.expectError(fmt.ParseIntError.InvalidCharacter, RGB.init("xyzxyz"));
 }
+
+test "RGBA blend" {
+    const red = RGBA{ .r = 1.0, .g = 0, .b = 0, .a = 0.0 };
+    const blue = RGBA{ .r = 0, .g = 0, .b = 1.0, .a = 0.0 };
+    const result = red.blend(&blue);
+    std.debug.print("\n{any}\n", .{result});
+    const reeb = RGBA{ .r = 1.0, .g = 0, .b = 0, .a = 1.0 };
+    const blueb = RGBA{ .r = 0, .g = 0, .b = 1.0, .a = 0.0 };
+    const resultt = reeb.blend(&blueb);
+    std.debug.print("{any}\n", .{resultt});
+    const roob = RGBA{ .r = 1.0, .g = 0, .b = 0, .a = 0.5 };
+    const bloob = RGBA{ .r = 0, .g = 0, .b = 1.0, .a = 0.5 };
+    const resulttt = roob.blend(&bloob);
+    std.debug.print("{any}\n", .{resulttt});
+}
diff --git a/src/main.zig b/src/main.zig
index 1f92b1f..2f8ccdc 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -18,28 +18,27 @@ pub fn main() !void {
 
     var box1 = Terminal.Box.init(allocator);
     defer box1.deinit();
-    box1.border_fg = try Terminal.Color.init("#ff8855");
-    box1.top = 1;
-    box1.bottom = 1;
-    box1.left = 1;
-    box1.right = 1;
+    box1.border_bg = try Terminal.Color.init("#3ace37ff");
+    box1.top = 0;
+    box1.bottom = 0;
+    box1.left = 0;
+    box1.right = 0;
 
     var box2 = Terminal.Box.init(allocator);
     defer box2.deinit();
-    box2.border_fg = try Terminal.Color.init("#aaff55");
+    box2.border_bg = try Terminal.Color.init("#000000c0");
     box2.top = 1;
-    box2.bottom = 1;
-    box2.left = 1;
-    box2.right = 1;
+    box2.bottom = 3;
+    box2.left = 20;
+    box2.right = 2;
 
     var box3 = Terminal.Box.init(allocator);
     defer box3.deinit();
-    box3.border_bg = try Terminal.Color.init("#aa33ff");
-    box3.border_type = .bg;
-    box3.top = 1;
+    box3.border_bg = try Terminal.Color.init("#48d5eaa0");
+    box3.top = 2;
     box3.bottom = 1;
-    box3.left = 1;
-    box3.right = 1;
+    box3.left = 20;
+    box3.right = 20;
 
     try term.box.addChild(&box1);
     try box1.addChild(&box2);
@@ -48,38 +47,9 @@ pub fn main() !void {
     while (true) {
         const events = try term.getEvents();
         _ = events;
-        // for (events) |ev| if (ev.system == .winch) std.debug.print("hii\n", .{});
         try term.draw();
         std.time.sleep(1000);
     }
-
-    // var box = Terminal.Box{
-    //     .x = 0,
-    //     .y = 0,
-    //     .width = 13,
-    //     .height = 1,
-
-    //     .content = "hello world",
-    //     .border = true,
-    // };
-    // try box.draw(&term);
-
-    // try term.clearScreen();
-    // try term.print("fooboo", .{});
-    // try term.cursorLeft();
-    // try term.cursorLeft();
-    // try term.print("ar", .{});
-    // // try term.cursorSet(12, 3);
-    // try term.blinkOn();
-    // try term.boldOn();
-    // try term.underlineOn();
-    // try term.italicsOn();
-    // try term.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ", .{});
-    // try term.blinkOff();
-    // try term.boldOff();
-    // try term.underlineOff();
-    // try term.italicsOff();
-    // try term.print("eeheeveehee\n", .{});
 }
 
 pub const Terminal = struct {
@@ -289,15 +259,23 @@ pub const Terminal = struct {
     // }
 
     pub fn setFg(self: *Terminal, color: Color) !void {
-        try self.info.writeString(.set_rgb_foreground, self.tty.writer(), &[_]u32{ @intCast(color.r), @intCast(color.g), @intCast(color.b) });
+        try self.info.writeString(.set_rgb_foreground, self.tty.writer(), &[_]u32{
+            @intFromFloat(color.r * 0xff),
+            @intFromFloat(color.g * 0xff),
+            @intFromFloat(color.b * 0xff),
+        });
     }
 
     pub fn setBg(self: *Terminal, color: Color) !void {
-        try self.info.writeString(.set_rgb_background, self.tty.writer(), &[_]u32{ @intCast(color.r), @intCast(color.g), @intCast(color.b) });
+        try self.info.writeString(.set_rgb_background, self.tty.writer(), &[_]u32{
+            @intFromFloat(color.r * 0xff),
+            @intFromFloat(color.g * 0xff),
+            @intFromFloat(color.b * 0xff),
+        });
     }
 
     pub const Info = @import("terminfo.zig");
-    pub const Color = @import("color.zig").RGB;
+    pub const Color = @import("color.zig").RGBA;
 
     pub const Box = struct {
         parent: ?*Box,
@@ -311,10 +289,10 @@ pub const Terminal = struct {
         top: u32,
         bottom: u32,
 
-        border_type: enum { line, bg } = .line,
+        border_type: enum { line, bg } = .bg,
         border_char: u8 = ' ',
-        border_bg: Color = Color{ .r = 0, .g = 0, .b = 0 },
-        border_fg: Color = Color{ .r = 0xff, .g = 0xff, .b = 0xff },
+        border_bg: Color = Color{ .r = 0.0, .g = 0.0, .b = 0.0 },
+        border_fg: Color = Color{ .r = 1.0, .g = 1.0, .b = 1.0 },
 
         content: []const u8 = "",
 
@@ -369,13 +347,11 @@ pub const Terminal = struct {
 
         pub fn draw(self: *Box, term: *Terminal) !void {
             if (self.dirty) {
-                // if (self != &term.box) self.calcRect();
                 const rect = self.getRect();
-                // std.debug.print("{any}\r\n", .{rect});
                 switch (self.border_type) {
                     .line => {
-                        try term.setFg(self.border_fg);
-                        try term.setBg(self.border_bg);
+                        try term.setFg(self.getBorderFgColor());
+                        try term.setBg(self.getBorderBgColor());
                         var x: u32 = 0;
                         var y: u32 = 0;
                         try term.cursorSet(rect.x, rect.y);
@@ -396,7 +372,7 @@ pub const Terminal = struct {
                         try term.print("┘", .{});
                     },
                     .bg => {
-                        try term.setBg(self.border_bg);
+                        try term.setBg(self.getBorderBgColor());
                         var y: u32 = 0;
                         try term.cursorSet(rect.x, rect.y);
                         while (y < rect.h) : (y += 1) {
@@ -423,5 +399,19 @@ pub const Terminal = struct {
             return rect;
         }
         const Rect = struct { x: u32, y: u32, w: u32, h: u32 };
+
+        fn getBorderFgColor(self: *Box) Color {
+            if (self.parent) |parent| {
+                const parent_color = parent.getBorderFgColor();
+                return parent_color.blend(&self.border_fg);
+            } else return self.border_fg;
+        }
+
+        fn getBorderBgColor(self: *Box) Color {
+            if (self.parent) |parent| {
+                const parent_color = parent.getBorderBgColor();
+                return parent_color.blend(&self.border_bg);
+            } else return self.border_bg;
+        }
     };
 };