Split up in files and start implementing docview

This commit is contained in:
Sijmen 2020-05-22 23:06:48 +02:00
parent 7c708b4a31
commit 895ba6bf57
Signed by: vijfhoek
GPG key ID: DAF7821E067D9C48
6 changed files with 483 additions and 163 deletions

25
src/color_theme.zig Normal file
View file

@ -0,0 +1,25 @@
const zig_sdl = @import("zig-sdl");
const sdl = zig_sdl.sdl;
pub const ColorTheme = struct {
bg: sdl.Color,
fg: sdl.Color,
treeBg: sdl.Color,
treeFileFg: sdl.Color,
treeFolderFg: sdl.Color,
treeEntryHighlightBg: sdl.Color,
fn init() ColorTheme {
var fg = sdl.Color{ .r = 56, .g = 58, .b = 68 };
return ColorTheme{
.bg = sdl.Color{ .r = 250, .g = 250, .b = 250 },
.fg = fg,
.treeBg = sdl.Color{ .r = 234, .g = 234, .b = 235 },
.treeFileFg = fg,
.treeFolderFg = sdl.Color{ .r = 64, .g = 120, .b = 242 },
.treeEntryHighlightBg = sdl.Color{ .r = 229, .g = 229, .b = 229 },
};
}
};
pub const colorTheme = ColorTheme.init();

51
src/cursor_manager.zig Normal file
View file

@ -0,0 +1,51 @@
const std = @import("std");
const zig_sdl = @import("zig-sdl");
const sdl = zig_sdl.sdl;
pub const CursorManager = struct {
currentCursor: sdl.SystemCursor,
activeCursor: ?sdl.SystemCursor,
cursorArrow: sdl.Cursor,
cursorWait: sdl.Cursor,
cursorHand: sdl.Cursor,
pub fn init() !CursorManager {
return CursorManager{
.currentCursor = .Arrow,
.activeCursor = null,
.cursorArrow = try sdl.Cursor.initFromSystemCursor(.Arrow),
.cursorWait = try sdl.Cursor.initFromSystemCursor(.Wait),
.cursorHand = try sdl.Cursor.initFromSystemCursor(.Hand),
};
}
pub fn deinit(self: *CursorManager) void {
self.cursorArrow.deinit();
self.cursorWait.deinit();
self.cursorHand.deinit();
}
pub fn set(self: *CursorManager, comptime cursor: sdl.SystemCursor) void {
self.currentCursor = switch (cursor) {
.Arrow, .Wait, .Hand => cursor,
else => @compileError("Invalid cursor: " ++ cursor),
};
}
pub fn update(self: *CursorManager) !void {
if (self.activeCursor == null or self.activeCursor.? != self.currentCursor) {
var cursor = switch (self.currentCursor) {
.Arrow => self.cursorArrow,
.Wait => self.cursorWait,
.Hand => self.cursorHand,
else => return error.InvalidCursor,
};
cursor.activate();
self.activeCursor = self.currentCursor;
}
self.currentCursor = .Arrow;
}
};

100
src/docview.zig Normal file
View file

@ -0,0 +1,100 @@
const std = @import("std");
const zig_sdl = @import("zig-sdl");
const sdl = zig_sdl.sdl;
const Ui = @import("ui.zig").Ui;
const TreeView = @import("treeview.zig").TreeView;
const colorTheme = @import("color_theme.zig").colorTheme;
pub const DocView = struct {
pub const Line = struct {
const height: i32 = 20;
string: []const u8,
texture: ?sdl.Texture = null,
y: i32 = 0,
textWidth: i32 = 0,
textHeight: i32 = 0,
fn prerender(self: *Line, ui: *Ui) !void {
if (self.texture) |texture| {
texture.deinit();
}
var color = colorTheme.fg;
var line = try std.mem.dupeZ(std.heap.c_allocator, u8, self.string);
var surface = try ui.codeFont.renderBlended(line, color);
defer surface.deinit();
self.textWidth = surface.width();
self.textHeight = surface.height();
std.debug.warn("surface size: {}x{}\n", .{ self.textWidth, self.textHeight });
self.texture = try ui.renderer.createTextureFromSurface(surface);
}
pub fn render(self: *Line, ui: *Ui) !void {
if (self.string.len == 0) {
return;
}
if (self.texture == null) {
try self.prerender(ui);
}
var dst = sdl.Rect{
.x = ui.scale(i32, DocView.x + 10),
.y = ui.scale(i32, self.y),
.w = self.textWidth,
.h = self.textHeight,
};
if (self.texture) |texture| {
try ui.renderer.copy(texture, null, dst);
}
}
};
pub const x = TreeView.width + 10;
path: ?[]const u8 = null,
// TODO Do allocator stuff
count: u64 = 0,
lines: [4096]Line = undefined,
pub fn init() DocView {
return DocView{};
}
pub fn render(self: *DocView, ui: *Ui) !void {
for (self.lines[0..self.count]) |*line| {
try line.render(ui);
}
}
pub fn open(self: *DocView, path: []const u8) !void {
self.path = path;
const file = std.fs.cwd().openFile(self.path.?, .{}) catch |err| {
std.debug.warn("unable to open file: {}\n", .{err});
return;
};
defer file.close();
var line_buf: [1024 * 1024]u8 = undefined;
var len = file.readAll(&line_buf) catch unreachable;
var iterator = std.mem.split(line_buf[0..len], "\n");
var index: u32 = 0;
while (iterator.next()) |line| {
std.debug.warn("line {}: '{}'\n", .{ index, line });
self.lines[index] = Line{
.string = line,
.y = @bitCast(i32, index) * Line.height + 10,
};
index += 1;
}
self.count = index;
}
};

View file

@ -1,174 +1,33 @@
const std = @import("std"); const std = @import("std");
const sdl = @import("sdl.zig"); const zig_sdl = @import("zig-sdl");
const ttf = @import("ttf.zig"); const sdl = zig_sdl.sdl;
const dirent = @cImport(@cInclude("dirent.h")); const ttf = zig_sdl.ttf;
const Ui = struct { const TreeView = @import("treeview.zig").TreeView;
window: sdl.Window, const Ui = @import("ui.zig").Ui;
renderer: sdl.Renderer,
uiFont: ttf.Font,
codeFont: ttf.Font,
treeWidget: TreeView,
pub fn init() !Ui {
try sdl.init();
var window = try sdl.Window.init("Zite", 1280, 720, sdl.Window.Shown);
var renderer = try window.createRenderer(null, 0);
try ttf.init();
var uiFont = try ttf.Font.init("/usr/share/fonts/ubuntu/Ubuntu-R.ttf", 16);
var codeFont = try ttf.Font.init("/usr/share/fonts/TTF/Cascadia.ttf", 16);
var treeWidget = TreeView.init();
return Ui{
.window = window,
.renderer = renderer,
.uiFont = uiFont,
.codeFont = codeFont,
.treeWidget = treeWidget,
};
}
pub fn deinit(self: *Ui) void {
self.treeWidget.deinit();
self.uiFont.deinit();
self.codeFont.deinit();
ttf.quit();
self.renderer.deinit();
self.window.deinit();
sdl.quit();
}
pub fn update(self: *Ui) !void {
while (sdl.Event.poll()) |event| {
var type_ = event.type_();
// std.debug.warn("event: {}\n", .{@tagName(type_)});
switch (type_) {
.Quit => return error.Exiting,
.MouseMotion => {
var motion = event.mouseMotion().?;
self.treeWidget.mouseMotion(motion);
},
else => {},
}
}
}
pub fn render(self: *Ui) !void {
try self.renderer.setDrawColor(.{ .r = 30, .g = 30, .b = 30 });
try self.renderer.clear();
try self.treeWidget.render(self.*);
self.renderer.present();
}
};
const TreeView = struct {
const Entry = struct {
name: []const u8,
texture: ?sdl.Texture = null,
y: i32,
width: i32 = 0,
height: i32 = 0,
highlighted: bool = false,
fn prerender(self: *Entry, ui: Ui) !void {
var surface = try ui.uiFont.renderBlended(self.name, .{ .r = 255, .g = 255, .b = 255 });
self.texture = try ui.renderer.createTextureFromSurface(surface);
self.width = surface.width();
self.height = surface.height();
// We don't need the surface anymore.
surface.deinit();
}
pub fn render(self: *Entry, ui: Ui) !void {
if (self.texture == null) {
try self.prerender(ui);
}
if (self.highlighted) {
try ui.renderer.setDrawColor(.{ .r = 42, .g = 45, .b = 46 });
try ui.renderer.fillRect(sdl.Rect{ .x = 0, .y = self.y, .w = 200, .h = 25 });
}
var y = @divTrunc(25 - self.height, 2);
var dst = sdl.Rect{ .x = 10, .y = self.y + y, .w = self.width, .h = self.height };
try ui.renderer.copy(self.texture.?, null, dst);
}
pub fn mouseMotion(self: *Entry, event: sdl.MouseMotionEvent) void {
self.highlighted = event.y > self.y and event.y < self.y + 20;
}
};
cursorHand: *sdl.c.SDL_Cursor,
count: u64 = 0,
// Temporary, std.heap.c_allocator was broken, so this for now
entries: [256]Entry = undefined,
pub fn init() TreeView {
var cursorHand = sdl.c.SDL_CreateSystemCursor(@intToEnum(sdl.c.SDL_SystemCursor, sdl.c.SDL_SYSTEM_CURSOR_HAND)).?;
sdl.c.SDL_SetCursor(cursorHand);
var self = TreeView{ .cursorHand = cursorHand };
self.refresh() catch unreachable;
return self;
}
pub fn deinit(self: TreeView) void {
sdl.c.SDL_FreeCursor(self.cursorHand);
for (self.entries[0..self.count]) |entry| {
if (entry.texture) |texture| {
texture.deinit();
}
}
}
pub fn render(self: *TreeView, ui: Ui) !void {
try ui.renderer.setDrawColor(.{ .r = 38, .g = 38, .b = 38 });
try ui.renderer.fillRect(sdl.Rect{ .x = 0, .y = 0, .w = 200, .h = 720 });
for (self.entries[0..self.count]) |*entry, i| {
try entry.render(ui);
}
}
pub fn refresh(self: *TreeView) !void {
var dir = try std.fs.cwd().openDir("./", .{ .iterate = true });
var iterator = dir.iterate();
var index: u31 = 0;
while (try iterator.next()) |file| {
self.entries[index] = Entry{ .name = file.name, .y = index * 25 + 10 };
index += 1;
}
self.count = index;
}
pub fn mouseMotion(self: *TreeView, event: sdl.MouseMotionEvent) void {
for (self.entries[0..self.count]) |*entry, i| {
entry.mouseMotion(event);
}
}
};
pub fn main() !void { pub fn main() !void {
var ui = try Ui.init(); var ui = try Ui.init(false);
defer ui.deinit(); defer ui.deinit();
const windowSize = 512;
var times = [_]f64{0} ** windowSize;
var index: u32 = 0;
while (true) { while (true) {
var perf = sdl.PerformanceCounter.now();
try ui.update(); try ui.update();
try ui.render(); try ui.render();
times[index] = perf.elapsed().ms();
index += 1;
if (index == windowSize) {
var sum: f64 = 0.0;
for (times) |ms| {
sum += ms;
}
std.debug.warn("{d:.2} ms\n", .{sum / windowSize});
index = 0;
}
} }
} }

139
src/treeview.zig Normal file
View file

@ -0,0 +1,139 @@
const std = @import("std");
const zig_sdl = @import("zig-sdl");
const sdl = zig_sdl.sdl;
const Ui = @import("ui.zig").Ui;
const colorTheme = @import("color_theme.zig").colorTheme;
pub const TreeView = struct {
const Entry = struct {
pub const height: i32 = 25;
name: []const u8 = "",
kind: std.fs.Dir.Entry.Kind,
texture: ?sdl.Texture = null,
y: i32 = 0,
textWidth: i32 = 0,
textHeight: i32 = 0,
highlighted: bool = false,
fn prerender(self: *Entry, ui: *Ui) !void {
if (self.texture) |texture| {
texture.deinit();
}
var color = if (self.kind == .Directory) colorTheme.treeFolderFg else colorTheme.treeFileFg;
var name = try std.mem.dupeZ(std.heap.c_allocator, u8, self.name);
var surface = try ui.uiFont.renderBlended(name, color);
defer surface.deinit();
self.texture = try ui.renderer.createTextureFromSurface(surface);
self.textWidth = surface.width();
self.textHeight = surface.height();
}
pub fn render(self: *Entry, ui: *Ui) !void {
if (self.texture == null) {
try self.prerender(ui);
}
if (self.highlighted) {
try ui.renderer.setDrawColor(colorTheme.treeEntryHighlightBg);
try ui.renderer.fillRect(sdl.Rect{
.x = 0,
.y = ui.scale(i32, self.y),
.w = ui.scale(i32, TreeView.width),
.h = ui.scale(i32, Entry.height),
});
ui.cursorManager.set(.Hand);
}
var y = @divTrunc(ui.scale(i32, Entry.height) - self.textHeight, 2);
var dst = sdl.Rect{
.x = ui.scale(i32, 10),
.y = ui.scale(i32, self.y) + y,
.w = self.textWidth,
.h = self.textHeight,
};
if (self.texture) |texture| {
try ui.renderer.copy(texture, null, dst);
}
}
pub fn mouseMotion(self: *Entry, event: sdl.MouseMotionEvent) void {
self.highlighted = event.y > self.y and event.y <= self.y + Entry.height and event.x <= TreeView.width;
}
pub fn mouseButton(self: *Entry, ui: *Ui, event: sdl.MouseButtonEvent) void {
if (event.state == .Released and self.highlighted) {
ui.openFile(self.name);
}
}
};
pub const width = 200;
height: i32 = 0,
// Temporary, std.heap.c_allocator was broken, so this for now
count: u64 = 0,
entries: [256]Entry = undefined,
pub fn init() TreeView {
return TreeView{};
}
pub fn deinit(self: TreeView) void {
for (self.entries[0..self.count]) |entry| {
if (entry.texture) |texture| {
texture.deinit();
}
}
}
pub fn render(self: *TreeView, ui: *Ui) !void {
try ui.renderer.setDrawColor(colorTheme.treeBg);
try ui.renderer.fillRect(sdl.Rect{
.x = 0,
.y = 0,
.w = ui.scale(i32, 200),
.h = ui.scale(i32, ui.height),
});
for (self.entries[0..self.count]) |*entry, i| {
try entry.render(ui);
}
}
pub fn refresh(self: *TreeView) !void {
var dir = try std.fs.cwd().openDir("./", .{ .iterate = true });
var iterator = dir.iterate();
var index: u32 = 0;
while (try iterator.next()) |file| {
self.entries[index] = Entry{
.name = file.name,
.kind = file.kind,
.y = @bitCast(i32, index) * Entry.height + 10,
};
index += 1;
}
self.count = index;
}
pub fn mouseMotion(self: *TreeView, event: sdl.MouseMotionEvent) void {
for (self.entries[0..self.count]) |*entry, i| {
entry.mouseMotion(event);
}
}
pub fn mouseButton(self: *TreeView, ui: *Ui, event: sdl.MouseButtonEvent) void {
for (self.entries[0..self.count]) |*entry, i| {
entry.mouseButton(ui, event);
}
}
};

146
src/ui.zig Normal file
View file

@ -0,0 +1,146 @@
const std = @import("std");
const zig_sdl = @import("zig-sdl");
const sdl = zig_sdl.sdl;
const ttf = zig_sdl.ttf;
const CursorManager = @import("cursor_manager.zig").CursorManager;
const TreeView = @import("treeview.zig").TreeView;
const DocView = @import("docview.zig").DocView;
const colorTheme = @import("color_theme.zig").colorTheme;
pub const Ui = struct {
window: sdl.Window,
renderer: sdl.Renderer,
uiFont: ttf.Font,
codeFont: ttf.Font,
cursorManager: CursorManager,
treeView: TreeView,
docView: DocView,
width: i32,
height: i32,
scaleFactor: f64,
pub fn init(vsync: bool) !Ui {
try sdl.init();
var window = try sdl.Window.init(
"Zite",
1280,
720,
sdl.WindowFlags.Resizable | sdl.WindowFlags.AllowHighDpi,
);
var flags = sdl.RendererFlags.Accelerated;
flags |= sdl.RendererFlags.PresentVSync;
var renderer = try window.createRenderer(null, flags);
try ttf.init();
var uiFont = try ttf.Font.init("/usr/share/fonts/ubuntu/Ubuntu-R.ttf", 12);
var codeFont = try ttf.Font.init("/usr/share/fonts/TTF/Cascadia.ttf", 12);
var cursorManager = try CursorManager.init();
var treeView = TreeView.init();
try treeView.refresh();
var docView = DocView.init();
return Ui{
.window = window,
.renderer = renderer,
.uiFont = uiFont,
.codeFont = codeFont,
.cursorManager = cursorManager,
.treeView = treeView,
.docView = docView,
.width = 0,
.height = 0,
.scaleFactor = 1.0,
};
}
pub fn deinit(self: *Ui) void {
self.treeView.deinit();
self.cursorManager.deinit();
self.uiFont.deinit();
self.codeFont.deinit();
ttf.quit();
self.renderer.deinit();
self.window.deinit();
sdl.quit();
}
pub fn reloadFonts(self: *Ui) !void {
var size = @floatToInt(i32, 16.0 * self.scaleFactor);
self.uiFont = try ttf.Font.init("/usr/share/fonts/ubuntu/Ubuntu-R.ttf", size);
self.codeFont = try ttf.Font.init("/usr/share/fonts/TTF/Cascadia.ttf", size);
}
pub fn scale(self: Ui, comptime T: type, input: T) T {
var float = @intToFloat(f64, input);
return @floatToInt(T, float * self.scaleFactor);
}
pub fn update(self: *Ui) !void {
while (sdl.Event.poll()) |event| {
var kind = event.kind();
switch (kind) {
.Quit => std.os.exit(0),
.MouseMotion => {
var motion = event.mouseMotion().?;
self.treeView.mouseMotion(motion);
},
.WindowEvent => {
var window = event.window().?;
switch (window.event) {
.Resized => {
self.width = window.data1;
self.height = window.data2;
var size = self.window.glDrawableSize();
var scaleFactor = @intToFloat(f64, size.h) / @intToFloat(f64, window.data2);
if (scaleFactor != self.scaleFactor) {
self.scaleFactor = scaleFactor;
try self.reloadFonts();
}
},
else => {},
}
},
.MouseButtonDown, .MouseButtonUp => {
var button = event.button().?;
self.treeView.mouseButton(self, button);
},
else => {
std.debug.warn("event: {}\n", .{@tagName(kind)});
},
}
}
}
pub fn render(self: *Ui) !void {
try self.renderer.setDrawColor(colorTheme.bg);
try self.renderer.clear();
try self.treeView.render(self);
try self.docView.render(self);
try self.cursorManager.update();
self.renderer.present();
}
pub fn openFile(self: *Ui, name: []const u8) void {
std.debug.warn("opening file {}\n", .{name});
self.docView.open(name) catch unreachable;
}
};