Backlog/engine/papyrus/src/TextEntrySystem.zig

225 lines
6.4 KiB
Zig

// Main text entry engine rooted in papyrus
backingAllocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
ctx: *papyrus.Context,
trg: ?*TextRenderGeometry = null,
cursorResults: ?TextRenderGeometry.HitResults = null,
selected: ?*papyrus.NodeProperty_TextEntry = null,
firstFrame: bool = false,
cursorBlink: bool = true,
cursorBlinkTime: f64 = 0.4,
cursorBlinkResetTime: f64 = 0.4,
insertIndex: u32 = 0,
const std = @import("std");
const papyrus = @import("papyrus.zig");
const NodeHandle = papyrus.NodeHandle;
const Context = papyrus.Context;
const core = @import("core");
const TextRenderGeometry = @import("textRender/textRenderGeometry.zig");
// couple of things we'll need to do...
pub fn create(ctx: *papyrus.Context, backingAllocator: std.mem.Allocator) !*@This() {
const self = try backingAllocator.create(@This());
self.* = .{
.ctx = ctx,
.backingAllocator = backingAllocator,
.arena = std.heap.ArenaAllocator.init(backingAllocator),
};
return self;
}
pub fn sendCodePoint(self: *@This(), codepoint: u32) !void {
const codepoints = @as([4]u8, @bitCast(codepoint));
if (self.selected) |te| {
te.editText.insert(self.insertIndex, codepoints[0]) catch unreachable;
self.insertIndex += 1;
}
}
pub fn tick(self: *@This(), deltaTime: f64) !void {
if (self.firstFrame) {
if (self.trg) |trg| {
self.firstFrame = false;
self.cursorResults = trg.getCurrentEndGeo();
}
}
self.cursorBlinkTime -= deltaTime;
if (self.cursorBlinkTime < 0) {
self.cursorBlinkTime = self.cursorBlinkResetTime + self.cursorBlinkTime;
self.cursorBlink = !self.cursorBlink;
}
if (self.trg) |trg| {
self.cursorResults = trg.getGeometryAtIndex(self.insertIndex);
}
}
pub fn testHits(self: *@This()) !void {
if (self.trg) |trg| {
const ctx = papyrus.getContext();
const cursorPos = ctx.currentCursorPosition;
self.cursorResults = trg.testHit(cursorPos);
if (self.cursorResults) |hr| {
try self.ctx.pushDebugText("text entry hittest found found: line={d} index={d}", .{ hr.line, hr.index });
self.insertIndex = hr.index;
}
}
}
pub fn maybeResetSelection(self: *@This()) void {
if (self.selected) |te| {
te.entryState = .Normal;
}
self.selected = null;
self.trg = null;
self.cursorResults = null;
}
pub fn selectTextForEdit(self: *@This(), node: NodeHandle) void {
self.maybeResetSelection();
self.ctx.getTextEntry(node).entryState = .Pressed;
self.selected = self.ctx.getTextEntry(node);
self.insertIndex = if (self.insertIndex >= self.selected.?.editText.items.len) 0 else self.insertIndex;
self.firstFrame = true;
self.cursorBlink = true;
self.cursorBlinkTime = self.cursorBlinkResetTime;
}
pub fn destroy(self: *@This()) void {
self.arena.deinit();
self.backingAllocator.destroy(self);
}
// ===== key events =====
pub fn sendEscape(self: *@This()) !void {
// core.ui_logs("escape recieved");
self.maybeResetSelection();
}
pub fn sendEnter(self: *@This()) !void {
// core.ui_logs("enter recieved");
if (self.selected) |te| {
if (te.enterSendsNewline) {
te.editText.insert(self.insertIndex, '\n') catch unreachable;
self.insertIndex += 1;
}
}
}
pub fn sendTab(self: *@This()) !void {
_ = self;
// core.ui_logs("tab recieved");
}
pub fn sendBackspace(self: *@This()) !void {
// core.ui_logs("backspace recieved");
if (self.selected) |te| {
if (self.insertIndex == 0) {
return;
} else {
_ = te.editText.orderedRemove(@max(self.insertIndex - 1, 0));
self.insertIndex -= 1;
}
}
}
pub fn sendDelete(self: *@This()) !void {
// core.ui_logs("delete recieved");
if (self.selected) |te| {
if (self.insertIndex >= te.editText.items.len) {
return;
} else {
_ = te.editText.orderedRemove(self.insertIndex);
}
}
}
pub fn sendRight(self: *@This()) !void {
// core.ui_logs("right recieved");
if (self.selected) |te| {
if (self.insertIndex < te.editText.items.len) {
self.insertIndex = @min(self.insertIndex + 1, te.editText.items.len);
}
}
self.cursorBlinkTime = self.cursorBlinkResetTime;
self.resetCursorBlink();
}
pub fn sendLeft(self: *@This()) !void {
// core.ui_logs("left recieved");
if (self.selected) |te| {
_ = te;
if (self.insertIndex > 0) {
self.insertIndex = self.insertIndex - 1;
}
}
self.cursorBlinkTime = self.cursorBlinkResetTime;
self.resetCursorBlink();
}
fn cursorJumpRelative(self: *@This(), offset: core.Vector2f) !void {
if (self.selected) |te| {
_ = te;
if (self.trg) |trg| {
if (self.cursorResults) |cr| {
const jumpTo = cr.characterGeo.pos.add(offset).add(.{ .x = 1.0 });
if (trg.testHit(jumpTo)) |newCr| {
self.cursorResults = newCr;
self.insertIndex = newCr.index;
} else {}
} else {}
} else {}
}
}
pub fn sendUp(self: *@This()) !void {
// core.ui_logs("up recieved");
if (self.cursorResults) |cr| {
_ = cr;
self.cursorJumpRelative(.{ .x = 1.0, .y = self.cursorResults.?.characterGeo.size.y * 1.5 }) catch unreachable;
}
self.resetCursorBlink();
}
pub fn sendDown(self: *@This()) !void {
// core.ui_logs("down recieved");
if (self.cursorResults) |cr| {
_ = cr;
self.cursorJumpRelative(.{ .x = 1.0, .y = -self.cursorResults.?.characterGeo.size.y * 0.5 }) catch unreachable;
}
self.resetCursorBlink();
}
inline fn resetCursorBlink(self: *@This()) void {
self.cursorBlinkTime = self.cursorBlinkResetTime;
self.cursorBlink = true;
}
pub fn sendPageup(self: *@This()) !void {
_ = self;
// core.ui_logs("pageup recieved");
}
pub fn sendPagedown(self: *@This()) !void {
_ = self;
// core.ui_logs("pagedown recieved");
}
pub fn sendHome(self: *@This()) !void {
_ = self;
// core.ui_logs("home recieved");
}
pub fn sendEnd(self: *@This()) !void {
_ = self;
// core.ui_logs("end recieved");
}