Repetition/parameter system, implemented and integrated.

This commit is contained in:
KikooDX 2021-02-25 17:13:12 +01:00
parent 8cc9aeb129
commit a0528de619
9 changed files with 108 additions and 57 deletions

View File

@ -41,6 +41,9 @@ $ kble $HOME/projects/kble/sample.kble
```
# Default keybindings
Repetition/parameter:
* `0``9`: behavior similar to Vi.
Movement:
* `h`: left
* `j`: down
@ -54,7 +57,7 @@ Movement:
Verbs:
* `<space>`: clear selection
* `d`: delete selection
* `r`: replace selection
* `r`: replace selection with parameter
* `+`: increase scale (zoom)
* `-`: decrease scale (dezoom)
* `=`: reset scale

View File

@ -1,11 +0,0 @@
#!/usr/bin/lua
local out = io.open("sample.kble", "wb")
out:write(string.char(
2,
0, 2,
0, 2,
0, 1,
0, 0,
0, 0,
1, 1))
out:close()

View File

@ -6,7 +6,7 @@
const std = @import("std");
const expect = std.testing.expect;
const conf = @import("conf.zig");
const Parameter = @import("parameter.zig");
const movement = @import("movement.zig");
const scaling = @import("scaling.zig");
const verbs = @import("verbs.zig");
@ -16,6 +16,7 @@ const Mode = @import("modes.zig").Mode;
pub const ActionCat = enum {
none, // do nothing
parameter, // add key to Parameter buffer
movement, // move and change selection
verb, // do stuff with selection
scale, // change draw scaling
@ -26,9 +27,10 @@ pub const ActionCat = enum {
pub const Action = struct {
category: ActionCat,
// Only one of these should be set, and only one should be used.
function_move: fn (*Vec2, conf.arg_type) movement.SelectionUpdate = movement.move_left,
function_verb: fn (*Level, conf.arg_type) void = verbs.delete,
function_scale: fn (scaling.scale_type) scaling.scale_type = scaling.scale_reset,
function_parameter: fn (*Parameter, u8) void = Parameter.process_key,
function_move: fn (*Vec2, Parameter.buffer_type) movement.SelectionUpdate = movement.move_left,
function_verb: fn (*Level, Parameter.buffer_type) void = verbs.delete,
function_scale: fn (scaling.scale_type, Parameter.buffer_type) scaling.scale_type = scaling.scale_reset,
function_file: fn (*Level, *std.mem.Allocator, [*:0]const u8) void = Level.action_write,
next_mode: Mode = Mode.normal,
};
@ -37,6 +39,12 @@ pub const ActionsDef = .{
.none = Action{
.category = ActionCat.none,
},
// Parameter/repetition.
.parameter = Action{
.category = ActionCat.parameter,
.function_parameter = Parameter.process_key,
},
// Movement.
// Reset selection.
.move_but_dont = Action{

View File

@ -8,7 +8,6 @@
/// Number passed to commands.
const std = @import("std");
pub const arg_type: type = u16;
pub const mouse_enabled: bool = true;
pub const mouse_left_btn: c_int = 0;
pub const mouse_right_btn: c_int = 1;

View File

@ -16,6 +16,7 @@ const Vec2 = @import("vec2.zig");
const Rect = @import("rect.zig");
const Mouse = @import("mouse.zig");
const draw = @import("draw.zig");
const Parameter = @import("parameter.zig");
const movement = @import("movement.zig");
const verbs = @import("verbs.zig");
const scaling = @import("scaling.zig");
@ -72,9 +73,19 @@ pub fn main() void {
// Set default mode.
var mode: Mode = Mode.normal;
// Parameter buffer.
var parameter: Parameter = Parameter{};
// Create binding "table".
var bindings = [_]*const actions.Action{&actions.ActionsDef.none} ** char_range;
// Set default bindings.
// Parameters/repetition.
{
var i: u8 = 0;
while (i < 10) : (i += 1) {
bindings['0' + i] = &ActionsDef.parameter;
}
}
// Movement.
bindings['h'] = &ActionsDef.move_left;
bindings['j'] = &ActionsDef.move_down;
@ -142,8 +153,8 @@ pub fn main() void {
// Read the buffer backwards. This is placeholder logic.
while (input_cursor > 0) {
input_cursor -= 1;
const key = if (input_buffer[input_cursor] <= char_range)
input_buffer[input_cursor]
const key: u8 = if (input_buffer[input_cursor] <= char_range)
@intCast(u8, input_buffer[input_cursor])
else
0;
@ -153,18 +164,21 @@ pub fn main() void {
switch (action.category) {
.none => std.log.info("No action bound to {}.", .{key}),
.parameter => {
action.function_parameter(&parameter, key);
},
.movement => {
const selection_update: movement.SelectionUpdate =
action.function_move(&cursor, 1);
action.function_move(&cursor, parameter.pop(1));
if (apply_selection and selection_update.active) {
level.apply_selection_update(selection_update);
}
},
.verb => {
action.function_verb(&level, 1);
action.function_verb(&level, parameter.pop(1));
},
.scale => {
scale = action.function_scale(scale);
scale = action.function_scale(scale, parameter.pop(1));
},
.file => {
action.function_file(&level, allocator, level_path);

View File

@ -8,7 +8,7 @@ const std = @import("std");
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
const conf = @import("conf.zig");
const Parameter = @import("parameter.zig");
const Vec2 = @import("vec2.zig");
const Rect = @import("rect.zig");
@ -22,27 +22,27 @@ pub const SelectionUpdate = struct {
};
/// Universal move system, prefer direction wrappers.
fn move(cursor: *Vec2, arg: conf.arg_type, dx: i2, dy: i2) SelectionUpdate {
fn move(cursor: *Vec2, arg: Parameter.buffer_type, dx: i2, dy: i2) SelectionUpdate {
const before: Vec2 = cursor.*;
if (dx > 0) {
var i: conf.arg_type = 0;
var i: Parameter.buffer_type= 0;
while (i != arg and cursor.x < maxIntVec2) : (i += 1) {
cursor.x += 1;
}
} else if (dx < 0) {
var i: conf.arg_type = 0;
var i: Parameter.buffer_type= 0;
while (i != arg and cursor.x > 0) : (i += 1) {
cursor.x -= 1;
}
}
if (dy > 0) {
var i: conf.arg_type = 0;
var i: Parameter.buffer_type= 0;
while (i != arg and cursor.y < maxIntVec2) : (i += 1) {
cursor.y += 1;
}
} else if (dy < 0) {
var i: conf.arg_type = 0;
var i: Parameter.buffer_type= 0;
while (i != arg and cursor.y > 0) : (i += 1) {
cursor.y -= 1;
}
@ -56,47 +56,47 @@ fn move(cursor: *Vec2, arg: conf.arg_type, dx: i2, dy: i2) SelectionUpdate {
}
/// Just don't move.
pub fn move_but_dont(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_but_dont(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, 0, 0);
}
/// Try to move the cursor `n` times left.
pub fn move_left(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_left(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, -1, 0);
}
/// Try to move the cursor `n` times right.
pub fn move_right(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_right(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, 1, 0);
}
/// Try to move the cursor `n` times up.
pub fn move_up(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_up(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, 0, -1);
}
/// Try to move the cursor `n` times down.
pub fn move_down(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_down(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, 0, 1);
}
/// Try to move the cursor `n` times up and left.
pub fn move_up_left(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_up_left(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, -1, -1);
}
/// Try to move the cursor `n` times up and right.
pub fn move_up_right(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_up_right(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, 1, -1);
}
/// Try to move the cursor `n` times down and left.
pub fn move_down_left(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_down_left(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, -1, 1);
}
/// Try to move the cursor `n` times down and right.
pub fn move_down_right(cursor: *Vec2, arg: conf.arg_type) SelectionUpdate {
pub fn move_down_right(cursor: *Vec2, arg: Parameter.buffer_type) SelectionUpdate {
return move(cursor, arg, 1, 1);
}

36
src/parameter.zig Normal file
View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2021 KikooDX
// This file is part of [KBLE](https://sr.ht/~kikoodx/kble), which is
// MIT licensed. The MIT license requires this copyright notice to be
// included in all copies and substantial portions of the software.
const std = @import("std");
const Self = @This();
pub const buffer_type = u16;
pub const buffer_limit = std.math.maxInt(buffer_type) / 10;
buffer: buffer_type = 0, // hold current number
used: bool = false, // keep track of buffer usage
/// Only accept key between '0' and '9' inclusive. Add a number to the buffer.
pub fn process_key(self: *Self, key: u8) void {
if (key < '0' or key > '9') unreachable;
self.used = true;
const number: buffer_type = key - '0';
if (self.buffer < buffer_limit) {
self.buffer *= 10;
self.buffer += number;
std.log.info("Parameter buffer: {}", .{self});
} else {
std.log.warn("Buffer limit reached.", .{});
}
}
/// Return the buffer value if used and reset it. Otherwise return `default`.
pub fn pop(self: *Self, default: buffer_type) buffer_type {
defer self.buffer = 0;
defer self.used = false;
return if (self.used) self.buffer else default;
}

View File

@ -8,38 +8,40 @@ const std = @import("std");
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
const Parameter = @import("parameter.zig");
pub const scale_type = u8;
pub const scale_default: comptime_int = 16;
const scale_min: comptime_int = 3;
const scale_max: comptime_int = maxInt(scale_type);
const scale_min = 3;
const scale_max = maxInt(scale_type);
pub fn scale_reset(current_scale: scale_type) scale_type {
pub fn scale_reset(current_scale: scale_type, arg: Parameter.buffer_type) scale_type {
return scale_default;
}
pub fn scale_up(current_scale: scale_type) scale_type {
if (current_scale < scale_max) {
return current_scale + 1;
} else return current_scale;
pub fn scale_up(current_scale: scale_type, arg: Parameter.buffer_type) scale_type {
if (@intCast(u32, current_scale) + @intCast(u32, arg) < @intCast(u32, scale_max)) {
return current_scale + @intCast(u8, arg);
} else return scale_max;
}
pub fn scale_down(current_scale: scale_type) scale_type {
if (current_scale > scale_min) {
return current_scale - 1;
} else return current_scale;
pub fn scale_down(current_scale: scale_type, arg: Parameter.buffer_type) scale_type {
if (@intCast(i32, current_scale) - @intCast(i32, arg) > @intCast(i32, scale_min)) {
return current_scale - @intCast(u8, arg);
} else return scale_min;
}
test "scale reset" {
expect(scale_reset(42) == scale_default);
expect(scale_reset(42, 1) == scale_default);
}
test "scale up" {
expect(scale_up(42) == 43);
expect(scale_up(scale_max) == scale_max);
expect(scale_up(42, 1) == 43);
expect(scale_up(scale_max - 4, 42) == scale_max);
}
test "scale down" {
expect(scale_down(42) == 41);
expect(scale_down(scale_min) == scale_min);
expect(scale_down(42, 1) == 41);
expect(scale_down(scale_min + 2, 42) == scale_min);
}

View File

@ -4,11 +4,11 @@
// MIT licensed. The MIT license requires this copyright notice to be
// included in all copies and substantial portions of the software.
//! Act on level using selection.
const conf = @import("conf.zig");
const Parameter = @import("parameter.zig");
const Level = @import("level.zig");
/// Clear selection (deselect everything).
pub fn clear_selection(level: *Level, arg: conf.arg_type) void {
pub fn clear_selection(level: *Level, arg: Parameter.buffer_type) void {
var i: u32 = 0;
while (i < level.width * level.height) : (i += 1) {
level.selection[i] = false;
@ -16,7 +16,7 @@ pub fn clear_selection(level: *Level, arg: conf.arg_type) void {
}
/// Delete selected cells (set to 0).
pub fn delete(level: *Level, arg: conf.arg_type) void {
pub fn delete(level: *Level, arg: Parameter.buffer_type) void {
var i: u32 = 0;
while (i < level.width * level.height) : (i += 1) {
if (level.selection[i])
@ -25,7 +25,7 @@ pub fn delete(level: *Level, arg: conf.arg_type) void {
}
/// Replace selected cells with `arg`.
pub fn replace(level: *Level, arg: conf.arg_type) void {
pub fn replace(level: *Level, arg: Parameter.buffer_type) void {
const casted_arg = @intCast(Level.cell_type, arg);
var i: u32 = 0;
while (i < level.width * level.height) : (i += 1) {