feat: port GUI to boxflow layout engine
This commit is contained in:
parent
727054ed4c
commit
effa34f7d1
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
|||
[submodule "zalgebra"]
|
||||
path = zalgebra
|
||||
url = https://github.com/kooparse/zalgebra.git
|
||||
[submodule "boxflow"]
|
||||
path = boxflow
|
||||
url = https://mzte.de/git/LordMZTE/boxflow
|
||||
|
|
1
boxflow
Submodule
1
boxflow
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 22fc38acd8c4a2b74676e25b4c95c3a763c93cb4
|
|
@ -45,6 +45,11 @@ fn addDeps(exe: *std.build.LibExeObjStep) void {
|
|||
.source = .{ .path = "zalgebra/src/main.zig" },
|
||||
});
|
||||
|
||||
exe.addPackage(.{
|
||||
.name = "boxflow",
|
||||
.source = .{ .path = "boxflow/src/main.zig" },
|
||||
});
|
||||
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("glfw3");
|
||||
exe.linkSystemLibrary("freetype2");
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const std = @import("std");
|
||||
const gl = @import("gl.zig");
|
||||
const zalgebra = @import("zalgebra");
|
||||
|
||||
pub const inital_win_size = zalgebra.Vec2_i32.new(800, 800);
|
||||
|
@ -9,6 +10,8 @@ pub var state: Self = undefined;
|
|||
|
||||
current_screen_size: zalgebra.Vec2_i32 = inital_win_size,
|
||||
|
||||
projection_matrix: zalgebra.Mat4 = undefined,
|
||||
|
||||
vbo: c_uint = 0,
|
||||
vao: c_uint = 0,
|
||||
|
||||
|
@ -28,3 +31,24 @@ pub fn init() !void {
|
|||
pub fn deinit() void {
|
||||
state = undefined;
|
||||
}
|
||||
|
||||
pub fn updateProjectionMatrix(size: zalgebra.Vec2_i32) void {
|
||||
state.projection_matrix = zalgebra.Mat4.orthographic(
|
||||
0.0,
|
||||
@intToFloat(f32, size.x()),
|
||||
0.0,
|
||||
@intToFloat(f32, size.y()),
|
||||
-1.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
for ([_]c_uint{ state.shader_program, state.font_shader_program }) |program| {
|
||||
gl.useProgram(program);
|
||||
gl.uniformMatrix4fv(
|
||||
gl.getUniformLocation(program, "projection"),
|
||||
1,
|
||||
gl.FALSE,
|
||||
@ptrCast([*]const f32, std.mem.asBytes(&state.projection_matrix.data)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
41
src/font.zig
41
src/font.zig
|
@ -32,8 +32,6 @@ pub const CharMap = std.AutoHashMap(u8, Character);
|
|||
// TODO: use fontconfig to find system font?
|
||||
const font_data = @embedFile("iosevka-regular.ttf");
|
||||
|
||||
pub var current_projection_matrix: zalgebra.Mat4 = undefined;
|
||||
|
||||
pub fn load() !FontInfo {
|
||||
std.log.info("loading font", .{});
|
||||
var freetype: c.FT_Library = undefined;
|
||||
|
@ -131,8 +129,6 @@ pub fn initFontGl() !void {
|
|||
|
||||
GlState.state.font_shader_program = program;
|
||||
|
||||
updateProgramProjectionMatrix(GlState.inital_win_size);
|
||||
|
||||
gl.genVertexArrays(1, &GlState.state.font_vao);
|
||||
gl.genBuffers(1, &GlState.state.font_vbo);
|
||||
|
||||
|
@ -143,43 +139,6 @@ pub fn initFontGl() !void {
|
|||
gl.enableVertexAttribArray(0);
|
||||
}
|
||||
|
||||
pub fn updateProgramProjectionMatrix(size: zalgebra.Vec2_i32) void {
|
||||
gl.useProgram(GlState.state.font_shader_program);
|
||||
current_projection_matrix = zalgebra.Mat4.orthographic(
|
||||
0.0,
|
||||
@intToFloat(f32, size.x()),
|
||||
0.0,
|
||||
@intToFloat(f32, size.y()),
|
||||
-1.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
gl.uniformMatrix4fv(
|
||||
gl.getUniformLocation(GlState.state.font_shader_program, "projection"),
|
||||
1,
|
||||
gl.FALSE,
|
||||
// elegant much
|
||||
&[_]f32{
|
||||
current_projection_matrix.data[0][0],
|
||||
current_projection_matrix.data[0][1],
|
||||
current_projection_matrix.data[0][2],
|
||||
current_projection_matrix.data[0][3],
|
||||
current_projection_matrix.data[1][0],
|
||||
current_projection_matrix.data[1][1],
|
||||
current_projection_matrix.data[1][2],
|
||||
current_projection_matrix.data[1][3],
|
||||
current_projection_matrix.data[2][0],
|
||||
current_projection_matrix.data[2][1],
|
||||
current_projection_matrix.data[2][2],
|
||||
current_projection_matrix.data[2][3],
|
||||
current_projection_matrix.data[3][0],
|
||||
current_projection_matrix.data[3][1],
|
||||
current_projection_matrix.data[3][2],
|
||||
current_projection_matrix.data[3][3],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn textVertices(
|
||||
s: []const u8,
|
||||
info: *const FontInfo,
|
||||
|
|
272
src/gui.zig
272
src/gui.zig
|
@ -1,5 +1,6 @@
|
|||
const std = @import("std");
|
||||
const zalgebra = @import("zalgebra");
|
||||
const boxflow = @import("boxflow");
|
||||
const c = @import("ffi.zig").c;
|
||||
const gl = @import("gl.zig");
|
||||
const glutil = @import("glutil.zig");
|
||||
|
@ -7,34 +8,111 @@ const Color = @import("Color.zig");
|
|||
const GlState = @import("GlState.zig");
|
||||
const font = @import("font.zig");
|
||||
|
||||
const BingoBox = struct {
|
||||
checked: bool = false,
|
||||
b: boxflow.boxes.Simple = .{},
|
||||
text: []const u8,
|
||||
|
||||
pub fn render(
|
||||
self: *BingoBox,
|
||||
state: *State,
|
||||
) !void {
|
||||
if (self.checked) {
|
||||
try glutil.rectangle(
|
||||
&state.vertex_buf,
|
||||
@intToFloat(f32, self.b.data.pos.x),
|
||||
@intToFloat(
|
||||
f32,
|
||||
GlState.state.current_screen_size.y() -
|
||||
@intCast(i32, self.b.data.pos.y),
|
||||
),
|
||||
@intToFloat(f32, self.b.data.pos.x + self.b.data.size.width),
|
||||
@intToFloat(
|
||||
f32,
|
||||
GlState.state.current_screen_size.y() -
|
||||
@intCast(i32, self.b.data.pos.y + self.b.data.size.height),
|
||||
),
|
||||
0.0,
|
||||
Color{
|
||||
.r = Color.square_frame.r,
|
||||
.g = Color.square_frame.g,
|
||||
.b = Color.square_frame.b,
|
||||
.a = @sin(
|
||||
@floatCast(f32, c.glfwGetTime()),
|
||||
) / 2.0 + 0.5,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
try glutil.rectangle(
|
||||
&state.vertex_buf,
|
||||
@intToFloat(f32, self.b.data.pos.x + 6),
|
||||
@intToFloat(
|
||||
f32,
|
||||
GlState.state.current_screen_size.y() -
|
||||
@intCast(i32, self.b.data.pos.y) - 6,
|
||||
),
|
||||
@intToFloat(f32, (self.b.data.pos.x + self.b.data.size.width) -| 6),
|
||||
@intToFloat(
|
||||
f32,
|
||||
GlState.state.current_screen_size.y() -
|
||||
@intCast(i32, (self.b.data.pos.y + self.b.data.size.height) -| 6),
|
||||
),
|
||||
0.0,
|
||||
Color.square_background,
|
||||
);
|
||||
|
||||
try font.textVertices(
|
||||
self.text,
|
||||
&state.font_info,
|
||||
&state.font_vertex_buf,
|
||||
@intToFloat(f32, self.b.data.pos.x + 6),
|
||||
@intToFloat(
|
||||
f32,
|
||||
GlState.state.current_screen_size.y() -
|
||||
@intCast(i32, self.b.data.pos.y) - 6,
|
||||
) - 48.0 * 0.4,
|
||||
0.4,
|
||||
@intToFloat(f32, self.b.data.pos.x + self.b.data.size.width),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: make these command line params
|
||||
const width = 4;
|
||||
const height = 4;
|
||||
|
||||
const square_margin = 0.1;
|
||||
|
||||
pub const State = struct {
|
||||
vertex_buf: std.ArrayList(f32),
|
||||
font_vertex_buf: std.ArrayList(f32),
|
||||
squares: [][]const u8,
|
||||
checked: []bool,
|
||||
boxes: []BingoBox,
|
||||
font_info: font.FontInfo,
|
||||
prev_mouse_state: c_int = 0,
|
||||
debug_render: bool = false,
|
||||
|
||||
pub fn init(squares: [][]const u8) !State {
|
||||
const checked = try std.heap.c_allocator.alloc(bool, squares.len);
|
||||
std.mem.set(bool, checked, false);
|
||||
const boxes = try std.heap.c_allocator.alloc(BingoBox, width * height);
|
||||
errdefer std.heap.c_allocator.free(boxes);
|
||||
|
||||
for (boxes) |*b, i| {
|
||||
b.* = BingoBox{
|
||||
.text = squares[i],
|
||||
};
|
||||
}
|
||||
|
||||
return .{
|
||||
.vertex_buf = std.ArrayList(f32).init(std.heap.c_allocator),
|
||||
.font_vertex_buf = std.ArrayList(f32).init(std.heap.c_allocator),
|
||||
.squares = squares,
|
||||
.checked = checked,
|
||||
.boxes = boxes,
|
||||
.font_info = try font.load(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *State) void {
|
||||
self.vertex_buf.deinit();
|
||||
std.heap.c_allocator.free(self.checked);
|
||||
std.heap.c_allocator.free(self.boxes);
|
||||
self.font_info.deinit();
|
||||
|
||||
self.* = undefined;
|
||||
|
@ -59,45 +137,20 @@ pub fn handleInput(win: *c.GLFWwindow, state: *State) !void {
|
|||
// TODO: convert to event-based system
|
||||
const mouse_state = c.glfwGetMouseButton(win, c.GLFW_MOUSE_BUTTON_LEFT);
|
||||
if (state.prev_mouse_state != c.GLFW_PRESS and mouse_state == c.GLFW_PRESS) {
|
||||
var mousex: f64 = 0;
|
||||
var mousey: f64 = 0;
|
||||
var fmousex: f64 = 0;
|
||||
var fmousey: f64 = 0;
|
||||
|
||||
c.glfwGetCursorPos(win, &mousex, &mousey);
|
||||
c.glfwGetCursorPos(win, &fmousex, &fmousey);
|
||||
|
||||
var norm_cursor_pos = font.current_projection_matrix.mulByVec4(
|
||||
zalgebra.Vec4.new(
|
||||
@floatCast(f32, mousex),
|
||||
@floatCast(f32, mousey),
|
||||
0.0,
|
||||
1.0,
|
||||
),
|
||||
);
|
||||
const mousex = @floatToInt(usize, fmousex);
|
||||
const mousey = @floatToInt(usize, fmousey);
|
||||
|
||||
norm_cursor_pos.data[1] = -norm_cursor_pos.y();
|
||||
|
||||
const square_width = 2.0 / @intToFloat(f32, width);
|
||||
const square_height = 2.0 / @intToFloat(f32, height);
|
||||
|
||||
blk: {
|
||||
var x: u32 = 0;
|
||||
while (x < width) : (x += 1) {
|
||||
var y: u32 = 0;
|
||||
while (y < width) : (y += 1) {
|
||||
const square_x1 = ((@intToFloat(f32, x) + square_margin) / 2.0) - 1.0;
|
||||
const square_y1 = ((@intToFloat(f32, y) + square_margin) / 2.0) - 1.0;
|
||||
const square_x2 = square_x1 + square_width - square_margin;
|
||||
const square_y2 = square_y1 + square_height - square_margin;
|
||||
|
||||
if (norm_cursor_pos.x() >= square_x1 and
|
||||
norm_cursor_pos.x() <= square_x2 and
|
||||
norm_cursor_pos.y() >= square_y1 and
|
||||
norm_cursor_pos.y() <= square_y2)
|
||||
for (state.boxes) |*b| {
|
||||
if (mousey > b.b.data.pos.y and mousey < b.b.data.pos.y + b.b.data.size.width and
|
||||
mousex > b.b.data.pos.x and mousex < b.b.data.pos.x + b.b.data.size.height)
|
||||
{
|
||||
state.checked[y * width + x] = !state.checked[y * width + x];
|
||||
|
||||
break :blk;
|
||||
}
|
||||
}
|
||||
b.checked = !b.checked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,9 +160,32 @@ pub fn handleInput(win: *c.GLFWwindow, state: *State) !void {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn layout(state: *State, w: c_int, h: c_int) !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const gridboxes = try alloc.alloc(boxflow.Box, state.boxes.len);
|
||||
|
||||
for (state.boxes) |*b, i| {
|
||||
var padding = try alloc.create(boxflow.boxes.Padding);
|
||||
padding.* = boxflow.boxes.Padding{ .child = b.b.box(), .padding = 4 };
|
||||
|
||||
gridboxes[i] = padding.box();
|
||||
}
|
||||
|
||||
var grid = boxflow.boxes.FixedGrid{ .cols = width, .children = gridboxes };
|
||||
var root = boxflow.Root{ .root_box = grid.box(), .size = .{
|
||||
.width = @intCast(usize, w),
|
||||
.height = @intCast(usize, h),
|
||||
} };
|
||||
_ = try root.layout();
|
||||
}
|
||||
|
||||
pub fn draw(win: *c.GLFWwindow, state: *State) !void {
|
||||
_ = win;
|
||||
state.vertex_buf.clearRetainingCapacity();
|
||||
state.font_vertex_buf.clearRetainingCapacity();
|
||||
|
||||
gl.clearColor(
|
||||
Color.background.r,
|
||||
|
@ -119,118 +195,12 @@ pub fn draw(win: *c.GLFWwindow, state: *State) !void {
|
|||
);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
const square_width = 2.0 / @intToFloat(f32, width);
|
||||
const square_height = 2.0 / @intToFloat(f32, height);
|
||||
|
||||
// square frame
|
||||
{
|
||||
const color =
|
||||
Color{
|
||||
.r = Color.square_frame.r,
|
||||
.g = Color.square_frame.g,
|
||||
.b = Color.square_frame.b,
|
||||
.a = @sin(
|
||||
@floatCast(f32, c.glfwGetTime()),
|
||||
) / 2.0 + 0.5,
|
||||
};
|
||||
|
||||
var x: u32 = 0;
|
||||
while (x < width) : (x += 1) {
|
||||
var y: u32 = 0;
|
||||
while (y < width) : (y += 1) {
|
||||
if (!state.checked[y * width + x])
|
||||
continue;
|
||||
|
||||
const square_x = ((@intToFloat(f32, x) + square_margin - 0.02) / 2.0) -
|
||||
1.0;
|
||||
const square_y = ((@intToFloat(f32, y) + square_margin - 0.02) / 2.0) -
|
||||
1.0;
|
||||
try glutil.rectangle(
|
||||
&state.vertex_buf,
|
||||
square_x,
|
||||
square_y,
|
||||
square_x + square_width - square_margin + 0.02,
|
||||
square_y + square_height - square_margin + 0.02,
|
||||
-1.0,
|
||||
color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// squares
|
||||
{
|
||||
var x: u32 = 0;
|
||||
while (x < width) : (x += 1) {
|
||||
var y: u32 = 0;
|
||||
while (y < width) : (y += 1) {
|
||||
const square_x = ((@intToFloat(f32, x) + square_margin) / 2.0) - 1.0;
|
||||
const square_y = ((@intToFloat(f32, y) + square_margin) / 2.0) - 1.0;
|
||||
try glutil.rectangle(
|
||||
&state.vertex_buf,
|
||||
square_x,
|
||||
square_y,
|
||||
square_x + square_width - square_margin,
|
||||
square_y + square_height - square_margin,
|
||||
0.0,
|
||||
Color.square_background,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (state.boxes) |*b| {
|
||||
try b.render(state);
|
||||
}
|
||||
|
||||
glutil.drawTriangles(state.vertex_buf.items);
|
||||
state.vertex_buf.clearRetainingCapacity();
|
||||
|
||||
// Text
|
||||
{
|
||||
const scale = 0.4;
|
||||
|
||||
var x: u32 = 0;
|
||||
while (x < width) : (x += 1) {
|
||||
var y: u32 = 0;
|
||||
while (y < width) : (y += 1) {
|
||||
const pos = zalgebra.Vec4.new(
|
||||
((@intToFloat(f32, x) + square_margin) / 2.0) - 1.0,
|
||||
((@intToFloat(f32, y) + square_margin) / 2.0) - 1.0,
|
||||
0.0,
|
||||
1.0,
|
||||
).add(
|
||||
zalgebra.Vec4.new(
|
||||
0.0,
|
||||
square_height - square_margin,
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
);
|
||||
|
||||
const inv_proj_mtx = font.current_projection_matrix.inv();
|
||||
const screenpos = inv_proj_mtx.mulByVec4(pos).sub(
|
||||
zalgebra.Vec4.new(0.0, 48.0 * scale, 0.0, 0.0),
|
||||
);
|
||||
|
||||
const square_size = inv_proj_mtx.mulByVec4(
|
||||
pos.add(zalgebra.Vec4.new(
|
||||
square_width - square_margin,
|
||||
square_height - square_margin,
|
||||
0.0,
|
||||
0.0,
|
||||
)),
|
||||
);
|
||||
|
||||
try font.textVertices(
|
||||
state.squares[y * width + x],
|
||||
&state.font_info,
|
||||
&state.vertex_buf,
|
||||
screenpos.x(),
|
||||
screenpos.y(),
|
||||
scale,
|
||||
square_size.x(),
|
||||
);
|
||||
}
|
||||
}
|
||||
font.renderText(state.vertex_buf.items, &state.font_info);
|
||||
}
|
||||
font.renderText(state.font_vertex_buf.items, &state.font_info);
|
||||
|
||||
// debug stuff
|
||||
if (state.debug_render) {
|
||||
|
|
19
src/main.zig
19
src/main.zig
|
@ -123,6 +123,8 @@ pub fn main() !void {
|
|||
|
||||
try font.initFontGl();
|
||||
|
||||
GlState.updateProjectionMatrix(GlState.inital_win_size);
|
||||
|
||||
//gl.enable(gl.CULL_FACE);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
@ -131,6 +133,11 @@ pub fn main() !void {
|
|||
|
||||
var gui_state = try gui.State.init(bingos.bingos);
|
||||
defer gui_state.deinit();
|
||||
try gui.layout(
|
||||
&gui_state,
|
||||
GlState.inital_win_size.x(),
|
||||
GlState.inital_win_size.y(),
|
||||
);
|
||||
c.glfwSetWindowUserPointer(win, &gui_state);
|
||||
while (c.glfwWindowShouldClose(win) == 0) {
|
||||
c.glfwPollEvents();
|
||||
|
@ -172,9 +179,17 @@ fn keyEventCb(
|
|||
}
|
||||
|
||||
fn fbResizeCb(win: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
||||
_ = win;
|
||||
const vec = zalgebra.Vec2_i32.new(width, height);
|
||||
gl.viewport(0, 0, width, height);
|
||||
font.updateProgramProjectionMatrix(vec);
|
||||
GlState.updateProjectionMatrix(vec);
|
||||
GlState.state.current_screen_size = vec;
|
||||
|
||||
const state = @ptrCast(
|
||||
*gui.State,
|
||||
@alignCast(@alignOf(*gui.State), c.glfwGetWindowUserPointer(win)),
|
||||
);
|
||||
|
||||
gui.layout(state, width, height) catch |e| {
|
||||
std.log.err("layout: {}", .{e});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec4 vColor;
|
||||
|
||||
uniform mat4 projection;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.0);
|
||||
gl_Position = projection * vec4(aPos, 1.0);
|
||||
color = vColor;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue