225 lines
6.4 KiB
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");
|
|
}
|