Keys now work the way vim reads them. A key press becomes a Key (with vim notation for writing keys: `<C-w>v`, `<leader>c`, `<<`), and the engine collects counts (`12j`), registers (`"+p`), sequences (`gg`), operators with their targets (`dd`, `dab`, `2d3d` multiplies to 6) and keys that take a character (`ma`, `'A`), then hands over a typed Command. Every command has a name and a help text in one table, and the default keys live in one table per place they work (motions, normal, canvas, visual, move, connect, lists). keys.toml changes keys per section, with the leader configurable and "nothing" to unbind. Each mistake is skipped and reported in words, with a suggestion for mistyped command names, and a key that would hide a longer one is reported too. The which-key popup reads the same tables: it shows what can follow the keys typed so far, in skyshift's look and place, with the leader menu instant and other prefixes after 300 ms. The mouse reader turns button events into presses, double-clicks, drags and scrolls. `Direction` moved from the glyphs to the model, since input needs it too. The canvas example now runs on all of this, with a two-line keys.toml of its own that makes `q` quit. Co-Authored-By: Claude Opus 5.5 <[email protected]>
153 lines
3.8 KiB
Rust
153 lines
3.8 KiB
Rust
//! Places and sizes on the canvas.
|
|
//!
|
|
//! The canvas is a grid of terminal cells (at Normal zoom) with no edges, so
|
|
//! coordinates can be negative. `x` grows to the right and `y` downward, as in
|
|
//! the terminal.
|
|
|
|
use std::ops::{Add, AddAssign, Neg, Sub};
|
|
|
|
/// A point on the canvas.
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
pub struct CanvasPos {
|
|
pub x: i32,
|
|
pub y: i32,
|
|
}
|
|
|
|
impl CanvasPos {
|
|
/// The point at `x`, `y`.
|
|
pub const fn new(x: i32, y: i32) -> CanvasPos {
|
|
CanvasPos { x, y }
|
|
}
|
|
}
|
|
|
|
/// A movement on the canvas: `dx` cells to the right, `dy` cells down.
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
pub struct CanvasOffset {
|
|
pub dx: i32,
|
|
pub dy: i32,
|
|
}
|
|
|
|
impl CanvasOffset {
|
|
/// A movement of `dx` cells to the right and `dy` cells down.
|
|
pub const fn new(dx: i32, dy: i32) -> CanvasOffset {
|
|
CanvasOffset { dx, dy }
|
|
}
|
|
}
|
|
|
|
impl Add<CanvasOffset> for CanvasPos {
|
|
type Output = CanvasPos;
|
|
|
|
fn add(self, offset: CanvasOffset) -> CanvasPos {
|
|
CanvasPos::new(self.x + offset.dx, self.y + offset.dy)
|
|
}
|
|
}
|
|
|
|
impl AddAssign<CanvasOffset> for CanvasPos {
|
|
fn add_assign(&mut self, offset: CanvasOffset) {
|
|
*self = *self + offset;
|
|
}
|
|
}
|
|
|
|
impl Sub for CanvasPos {
|
|
type Output = CanvasOffset;
|
|
|
|
/// The movement that goes from `other` to `self`.
|
|
fn sub(self, other: CanvasPos) -> CanvasOffset {
|
|
CanvasOffset::new(self.x - other.x, self.y - other.y)
|
|
}
|
|
}
|
|
|
|
impl Neg for CanvasOffset {
|
|
type Output = CanvasOffset;
|
|
|
|
fn neg(self) -> CanvasOffset {
|
|
CanvasOffset::new(-self.dx, -self.dy)
|
|
}
|
|
}
|
|
|
|
/// A width and a height on the canvas, in cells.
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
pub struct CanvasSize {
|
|
pub width: u16,
|
|
pub height: u16,
|
|
}
|
|
|
|
impl CanvasSize {
|
|
/// A size of `width` by `height` cells.
|
|
pub const fn new(width: u16, height: u16) -> CanvasSize {
|
|
CanvasSize { width, height }
|
|
}
|
|
}
|
|
|
|
/// A rectangle on the canvas: its top-left corner and its size.
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
pub struct CanvasRect {
|
|
pub pos: CanvasPos,
|
|
pub size: CanvasSize,
|
|
}
|
|
|
|
impl CanvasRect {
|
|
/// The rectangle with its top-left corner at `pos`.
|
|
pub const fn new(pos: CanvasPos, size: CanvasSize) -> CanvasRect {
|
|
CanvasRect { pos, size }
|
|
}
|
|
|
|
/// The first column inside.
|
|
pub fn left(self) -> i32 {
|
|
self.pos.x
|
|
}
|
|
|
|
/// The first row inside.
|
|
pub fn top(self) -> i32 {
|
|
self.pos.y
|
|
}
|
|
|
|
/// The first column past the right edge.
|
|
pub fn right(self) -> i32 {
|
|
self.pos.x + i32::from(self.size.width)
|
|
}
|
|
|
|
/// The first row past the bottom edge.
|
|
pub fn bottom(self) -> i32 {
|
|
self.pos.y + i32::from(self.size.height)
|
|
}
|
|
|
|
/// The middle cell (rounded toward the top left).
|
|
pub fn center(self) -> CanvasPos {
|
|
CanvasPos::new(
|
|
self.pos.x + i32::from(self.size.width) / 2,
|
|
self.pos.y + i32::from(self.size.height) / 2,
|
|
)
|
|
}
|
|
|
|
/// Whether the two rectangles share at least one cell.
|
|
pub fn overlaps(self, other: CanvasRect) -> bool {
|
|
self.left() < other.right()
|
|
&& other.left() < self.right()
|
|
&& self.top() < other.bottom()
|
|
&& other.top() < self.bottom()
|
|
}
|
|
}
|
|
|
|
/// The four directions, on screen and on the canvas. Up is toward smaller
|
|
/// `y`.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum Direction {
|
|
Up,
|
|
Right,
|
|
Down,
|
|
Left,
|
|
}
|
|
|
|
impl Direction {
|
|
/// The direction pointing the other way.
|
|
pub fn opposite(self) -> Direction {
|
|
match self {
|
|
Direction::Up => Direction::Down,
|
|
Direction::Right => Direction::Left,
|
|
Direction::Down => Direction::Up,
|
|
Direction::Left => Direction::Right,
|
|
}
|
|
}
|
|
}
|