Move keyboard definitions into their own file

This commit is contained in:
Leonard Hecker 2025-08-12 16:58:10 +02:00
parent 8e48ae3d60
commit 873fd03c1d
9 changed files with 401 additions and 339 deletions

View file

@ -6,7 +6,7 @@ use std::num::ParseIntError;
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::icu;
use edit::input::{kbmod, vk};
use edit::kbd::*;
use edit::tui::*;
use crate::localization::*;
@ -68,7 +68,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
ctx.attr_background_rgba(ctx.indexed(IndexedColor::White));
ctx.attr_foreground_rgba(ctx.indexed(IndexedColor::Black));
{
if ctx.contains_focus() && ctx.consume_shortcut(vk::ESCAPE) {
if ctx.contains_focus() && ctx.consume_shortcut(VK_ESCAPE) {
state.wants_search.kind = StateSearchKind::Hidden;
}
@ -90,7 +90,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
if focus == StateSearchKind::Search {
ctx.steal_focus();
}
if ctx.is_focused() && ctx.consume_shortcut(vk::RETURN) {
if ctx.is_focused() && ctx.consume_shortcut(VK_RETURN) {
action = Some(SearchAction::Search);
}
}
@ -105,9 +105,9 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
ctx.steal_focus();
}
if ctx.is_focused() {
if ctx.consume_shortcut(vk::RETURN) {
if ctx.consume_shortcut(VK_RETURN) {
action = Some(SearchAction::Replace);
} else if ctx.consume_shortcut(kbmod::CTRL_ALT | vk::RETURN) {
} else if ctx.consume_shortcut(MOD_CTRL | MOD_ALT | VK_RETURN) {
action = Some(SearchAction::ReplaceAll);
}
}
@ -271,9 +271,9 @@ pub fn draw_handle_wants_close(ctx: &mut Context, state: &mut State) {
// Handle accelerator shortcuts
if contains_focus {
if ctx.consume_shortcut(vk::S) {
if ctx.consume_shortcut(VK_S) {
action = Action::Save;
} else if ctx.consume_shortcut(vk::N) {
} else if ctx.consume_shortcut(VK_N) {
action = Action::Discard;
}
}
@ -319,7 +319,7 @@ pub fn draw_goto_menu(ctx: &mut Context, state: &mut State) {
ctx.attr_intrinsic_size(Size { width: 24, height: 1 });
ctx.steal_focus();
if ctx.consume_shortcut(vk::RETURN) {
if ctx.consume_shortcut(VK_RETURN) {
match validate_goto_point(&state.goto_target) {
Ok(point) => {
let mut buf = doc.buffer.borrow_mut();

View file

@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
use edit::arena::scratch_arena;
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::input::{kbmod, vk};
use edit::kbd::*;
use edit::tui::*;
use edit::{icu, path};
@ -75,9 +75,9 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
if !state.file_picker_autocomplete.is_empty() {
let bg = ctx.indexed_alpha(IndexedColor::Background, 3, 4);
let fg = ctx.contrasted(bg);
let focus_list_beg = ctx.is_focused() && ctx.consume_shortcut(vk::DOWN);
let focus_list_end = ctx.is_focused() && ctx.consume_shortcut(vk::UP);
let mut autocomplete_done = ctx.consume_shortcut(vk::ESCAPE);
let focus_list_beg = ctx.is_focused() && ctx.consume_shortcut(VK_DOWN);
let focus_list_end = ctx.is_focused() && ctx.consume_shortcut(VK_UP);
let mut autocomplete_done = ctx.consume_shortcut(VK_ESCAPE);
ctx.list_begin("suggestions");
ctx.attr_float(FloatSpec {
@ -105,8 +105,8 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
if (is_first && focus_list_beg) || (is_last && focus_list_end) {
ctx.list_item_steal_focus();
} else if ctx.is_focused()
&& ((is_first && ctx.consume_shortcut(vk::UP))
|| (is_last && ctx.consume_shortcut(vk::DOWN)))
&& ((is_first && ctx.consume_shortcut(VK_UP))
|| (is_last && ctx.consume_shortcut(VK_DOWN)))
{
ctx.toss_focus_up();
}
@ -126,7 +126,7 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
}
}
if ctx.is_focused() && ctx.consume_shortcut(vk::RETURN) {
if ctx.is_focused() && ctx.consume_shortcut(VK_RETURN) {
activated = true;
}
}
@ -170,7 +170,7 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
ctx.scrollarea_end();
if contains_focus
&& (ctx.consume_shortcut(vk::BACK) || ctx.consume_shortcut(kbmod::ALT | vk::UP))
&& (ctx.consume_shortcut(VK_BACK) || ctx.consume_shortcut(MOD_ALT | VK_UP))
{
state.file_picker_pending_name = "..".into();
activated = true;
@ -224,8 +224,8 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
ctx.table_end();
if contains_focus {
save |= ctx.consume_shortcut(vk::Y);
if ctx.consume_shortcut(vk::N) {
save |= ctx.consume_shortcut(VK_Y);
if ctx.consume_shortcut(VK_N) {
state.file_picker_overwrite_warning = None;
}
}

View file

@ -3,7 +3,7 @@
use edit::arena_format;
use edit::helpers::*;
use edit::input::{kbmod, vk};
use edit::kbd::*;
use edit::tui::*;
use crate::localization::*;
@ -19,7 +19,7 @@ pub fn draw_menubar(ctx: &mut Context, state: &mut State) {
if ctx.menubar_menu_begin(loc(LocId::File), 'F') {
draw_menu_file(ctx, state);
}
if !contains_focus && ctx.consume_shortcut(vk::F10) {
if !contains_focus && ctx.consume_shortcut(VK_F10) {
ctx.steal_focus();
}
if state.documents.active().is_some() {
@ -38,24 +38,24 @@ pub fn draw_menubar(ctx: &mut Context, state: &mut State) {
}
fn draw_menu_file(ctx: &mut Context, state: &mut State) {
if ctx.menubar_menu_button(loc(LocId::FileNew), 'N', kbmod::CTRL | vk::N) {
if ctx.menubar_menu_button(loc(LocId::FileNew), 'N', MOD_CTRL | VK_N) {
draw_add_untitled_document(ctx, state);
}
if ctx.menubar_menu_button(loc(LocId::FileOpen), 'O', kbmod::CTRL | vk::O) {
if ctx.menubar_menu_button(loc(LocId::FileOpen), 'O', MOD_CTRL | VK_O) {
state.wants_file_picker = StateFilePicker::Open;
}
if state.documents.active().is_some() {
if ctx.menubar_menu_button(loc(LocId::FileSave), 'S', kbmod::CTRL | vk::S) {
if ctx.menubar_menu_button(loc(LocId::FileSave), 'S', MOD_CTRL | VK_S) {
state.wants_save = true;
}
if ctx.menubar_menu_button(loc(LocId::FileSaveAs), 'A', vk::NULL) {
if ctx.menubar_menu_button(loc(LocId::FileSaveAs), 'A', VK_NULL) {
state.wants_file_picker = StateFilePicker::SaveAs;
}
if ctx.menubar_menu_button(loc(LocId::FileClose), 'C', kbmod::CTRL | vk::W) {
if ctx.menubar_menu_button(loc(LocId::FileClose), 'C', MOD_CTRL | VK_W) {
state.wants_close = true;
}
}
if ctx.menubar_menu_button(loc(LocId::FileExit), 'X', kbmod::CTRL | vk::Q) {
if ctx.menubar_menu_button(loc(LocId::FileExit), 'X', MOD_CTRL | VK_Q) {
state.wants_exit = true;
}
ctx.menubar_menu_end();
@ -65,37 +65,37 @@ fn draw_menu_edit(ctx: &mut Context, state: &mut State) {
let doc = state.documents.active().unwrap();
let mut tb = doc.buffer.borrow_mut();
if ctx.menubar_menu_button(loc(LocId::EditUndo), 'U', kbmod::CTRL | vk::Z) {
if ctx.menubar_menu_button(loc(LocId::EditUndo), 'U', MOD_CTRL | VK_Z) {
tb.undo();
ctx.needs_rerender();
}
if ctx.menubar_menu_button(loc(LocId::EditRedo), 'R', kbmod::CTRL | vk::Y) {
if ctx.menubar_menu_button(loc(LocId::EditRedo), 'R', MOD_CTRL | VK_Y) {
tb.redo();
ctx.needs_rerender();
}
if ctx.menubar_menu_button(loc(LocId::EditCut), 'T', kbmod::CTRL | vk::X) {
if ctx.menubar_menu_button(loc(LocId::EditCut), 'T', MOD_CTRL | VK_X) {
tb.cut(ctx.clipboard_mut());
ctx.needs_rerender();
}
if ctx.menubar_menu_button(loc(LocId::EditCopy), 'C', kbmod::CTRL | vk::C) {
if ctx.menubar_menu_button(loc(LocId::EditCopy), 'C', MOD_CTRL | VK_C) {
tb.copy(ctx.clipboard_mut());
ctx.needs_rerender();
}
if ctx.menubar_menu_button(loc(LocId::EditPaste), 'P', kbmod::CTRL | vk::V) {
if ctx.menubar_menu_button(loc(LocId::EditPaste), 'P', MOD_CTRL | VK_V) {
tb.paste(ctx.clipboard_ref());
ctx.needs_rerender();
}
if state.wants_search.kind != StateSearchKind::Disabled {
if ctx.menubar_menu_button(loc(LocId::EditFind), 'F', kbmod::CTRL | vk::F) {
if ctx.menubar_menu_button(loc(LocId::EditFind), 'F', MOD_CTRL | VK_F) {
state.wants_search.kind = StateSearchKind::Search;
state.wants_search.focus = true;
}
if ctx.menubar_menu_button(loc(LocId::EditReplace), 'L', kbmod::CTRL | vk::R) {
if ctx.menubar_menu_button(loc(LocId::EditReplace), 'L', MOD_CTRL | VK_R) {
state.wants_search.kind = StateSearchKind::Replace;
state.wants_search.focus = true;
}
}
if ctx.menubar_menu_button(loc(LocId::EditSelectAll), 'A', kbmod::CTRL | vk::A) {
if ctx.menubar_menu_button(loc(LocId::EditSelectAll), 'A', MOD_CTRL | VK_A) {
tb.select_all();
ctx.needs_rerender();
}
@ -108,16 +108,16 @@ fn draw_menu_view(ctx: &mut Context, state: &mut State) {
let word_wrap = tb.is_word_wrap_enabled();
// All values on the statusbar are currently document specific.
if ctx.menubar_menu_button(loc(LocId::ViewFocusStatusbar), 'S', vk::NULL) {
if ctx.menubar_menu_button(loc(LocId::ViewFocusStatusbar), 'S', VK_NULL) {
state.wants_statusbar_focus = true;
}
if ctx.menubar_menu_button(loc(LocId::ViewGoToFile), 'F', kbmod::CTRL | vk::P) {
if ctx.menubar_menu_button(loc(LocId::ViewGoToFile), 'F', MOD_CTRL | VK_P) {
state.wants_go_to_file = true;
}
if ctx.menubar_menu_button(loc(LocId::FileGoto), 'G', kbmod::CTRL | vk::G) {
if ctx.menubar_menu_button(loc(LocId::FileGoto), 'G', MOD_CTRL | VK_G) {
state.wants_goto = true;
}
if ctx.menubar_menu_checkbox(loc(LocId::ViewWordWrap), 'W', kbmod::ALT | vk::Z, word_wrap) {
if ctx.menubar_menu_checkbox(loc(LocId::ViewWordWrap), 'W', MOD_ALT | VK_Z, word_wrap) {
tb.set_word_wrap(!word_wrap);
ctx.needs_rerender();
}
@ -127,7 +127,7 @@ fn draw_menu_view(ctx: &mut Context, state: &mut State) {
}
fn draw_menu_help(ctx: &mut Context, state: &mut State) {
if ctx.menubar_menu_button(loc(LocId::HelpAbout), 'A', vk::NULL) {
if ctx.menubar_menu_button(loc(LocId::HelpAbout), 'A', VK_NULL) {
state.wants_about = true;
}
ctx.menubar_menu_end();

View file

@ -5,7 +5,7 @@ use edit::arena::scratch_arena;
use edit::framebuffer::{Attributes, IndexedColor};
use edit::fuzzy::score_fuzzy;
use edit::helpers::*;
use edit::input::vk;
use edit::kbd::*;
use edit::tui::*;
use edit::{arena_format, icu};
@ -97,7 +97,7 @@ pub fn draw_statusbar(ctx: &mut Context, state: &mut State) {
ctx.attr_padding(Rect::two(0, 1));
ctx.table_set_cell_gap(Size { width: 1, height: 0 });
{
if ctx.contains_focus() && ctx.consume_shortcut(vk::RETURN) {
if ctx.contains_focus() && ctx.consume_shortcut(VK_RETURN) {
ctx.toss_focus_up();
}

View file

@ -25,11 +25,11 @@ use draw_statusbar::*;
use edit::arena::{self, Arena, ArenaString, scratch_arena};
use edit::framebuffer::{self, IndexedColor};
use edit::helpers::{CoordType, KIBI, MEBI, MetricFormatter, Rect, Size};
use edit::input::{self, kbmod, vk};
use edit::kbd::*;
use edit::oklab::oklab_blend;
use edit::tui::*;
use edit::vt::{self, Token};
use edit::{apperr, arena_format, base64, path, sys, unicode};
use edit::{apperr, arena_format, base64, input, path, sys, unicode};
use localization::*;
use state::*;
@ -322,31 +322,29 @@ fn draw(ctx: &mut Context, state: &mut State) {
if let Some(key) = ctx.keyboard_input() {
// Shortcuts that are not handled as part of the textarea, etc.
if key == kbmod::CTRL | vk::N {
if key == MOD_CTRL | VK_N {
draw_add_untitled_document(ctx, state);
} else if key == kbmod::CTRL | vk::O {
} else if key == MOD_CTRL | VK_O {
state.wants_file_picker = StateFilePicker::Open;
} else if key == kbmod::CTRL | vk::S {
} else if key == MOD_CTRL | VK_S {
state.wants_save = true;
} else if key == kbmod::CTRL_SHIFT | vk::S {
} else if key == MOD_CTRL | MOD_SHIFT | VK_S {
state.wants_file_picker = StateFilePicker::SaveAs;
} else if key == kbmod::CTRL | vk::W {
} else if key == MOD_CTRL | VK_W {
state.wants_close = true;
} else if key == kbmod::CTRL | vk::P {
} else if key == MOD_CTRL | VK_P {
state.wants_go_to_file = true;
} else if key == kbmod::CTRL | vk::Q {
} else if key == MOD_CTRL | VK_Q {
state.wants_exit = true;
} else if key == kbmod::CTRL | vk::G {
} else if key == MOD_CTRL | VK_G {
state.wants_goto = true;
} else if key == kbmod::CTRL | vk::F && state.wants_search.kind != StateSearchKind::Disabled
{
} else if key == MOD_CTRL | VK_F && state.wants_search.kind != StateSearchKind::Disabled {
state.wants_search.kind = StateSearchKind::Search;
state.wants_search.focus = true;
} else if key == kbmod::CTRL | vk::R && state.wants_search.kind != StateSearchKind::Disabled
{
} else if key == MOD_CTRL | VK_R && state.wants_search.kind != StateSearchKind::Disabled {
state.wants_search.kind = StateSearchKind::Replace;
state.wants_search.focus = true;
} else if key == vk::F3 {
} else if key == VK_F3 {
search_execute(ctx, state, SearchAction::Search);
} else {
return;

View file

@ -6,14 +6,16 @@
//! In the future this allows us to take apart the application and
//! support input schemes that aren't VT, such as UEFI, or GUI.
use std::mem;
use std::ops::{BitOr, BitOrAssign};
use std::{fmt, mem};
use crate::helpers::{CoordType, Point, Size};
use crate::kbd::*;
use crate::vt;
/// Represents a key/modifier combination.
///
/// TODO: Is this a good idea? I did it to allow typing `kbmod::CTRL | vk::A`.
/// TODO: Is this a good idea? I did it to allow typing `MOD_CTRL | VK_A`.
/// The reason it's an awkward u32 and not a struct is to hopefully make ABIs easier later.
/// Of course you could just translate on the ABI boundary, but my hope is that this
/// design lets me realize some restrictions early on that I can't foresee yet.
@ -32,7 +34,7 @@ impl InputKey {
} else if ch >= 'a' && ch <= 'z' {
Some(Self(ch as u32 & !0x20)) // Shift a-z to A-Z
} else if ch >= 'A' && ch <= 'Z' {
Some(Self(kbmod::SHIFT.0 | ch as u32))
Some(Self(MOD_SHIFT.0 | ch as u32))
} else {
None
}
@ -59,13 +61,62 @@ impl InputKey {
}
}
impl fmt::Debug for InputKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self.0 {
0x00 => "NULL",
0x08 => "BACK",
0x09 => "TAB",
0x0D => "RETURN",
0x1B => "ESCAPE",
0x20 => "SPACE",
0x21 => "PRIOR",
0x22 => "NEXT",
0x23 => "END",
0x24 => "HOME",
0x25 => "LEFT",
0x26 => "UP",
0x27 => "RIGHT",
0x28 => "DOWN",
0x2D => "INSERT",
0x2E => "DELETE",
0x6A => "MULTIPLY",
0x6B => "ADD",
0x6C => "SEPARATOR",
0x6D => "SUBTRACT",
0x6E => "DECIMAL",
0x6F => "DIVIDE",
_ => {
return {
if matches!(self.0, 0x30..=0x39 | 0x41..=0x5A) {
// 0-9, A-Z
write!(f, "{}", char::from_u32(self.0).unwrap_or('\0'))
} else if matches!(self.0, 0x70..=0x87) {
// F1-F24
write!(f, "F{}", self.0 - 0x70 + 1)
} else if matches!(self.0, 0x60..=0x69) {
// NUMPAD0-NUMPAD9
write!(f, "NUMPAD{}", self.0 - 0x60)
} else {
write!(f, "VK_{:02X}", self.0)
}
};
}
})
}
}
/// A keyboard modifier. Ctrl/Alt/Shift.
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct InputKeyMod(u32);
impl InputKeyMod {
const fn new(v: u32) -> Self {
pub(crate) const fn new(v: u32) -> Self {
Self(v)
}
@ -74,7 +125,7 @@ impl InputKeyMod {
}
}
impl std::ops::BitOr<InputKeyMod> for InputKey {
impl BitOr<InputKeyMod> for InputKeyMod {
type Output = Self;
fn bitor(self, rhs: InputKeyMod) -> Self {
@ -82,7 +133,15 @@ impl std::ops::BitOr<InputKeyMod> for InputKey {
}
}
impl std::ops::BitOr<InputKey> for InputKeyMod {
impl BitOr<InputKeyMod> for InputKey {
type Output = Self;
fn bitor(self, rhs: InputKeyMod) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOr<InputKey> for InputKeyMod {
type Output = InputKey;
fn bitor(self, rhs: InputKey) -> InputKey {
@ -90,137 +149,30 @@ impl std::ops::BitOr<InputKey> for InputKeyMod {
}
}
impl std::ops::BitOrAssign for InputKeyMod {
impl BitOrAssign for InputKeyMod {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
/// Keyboard keys.
///
/// The codes defined here match the VK_* constants on Windows.
/// It's a convenient way to handle keyboard input, even on other platforms.
pub mod vk {
use super::InputKey;
pub const NULL: InputKey = InputKey::new('\0' as u32);
pub const BACK: InputKey = InputKey::new(0x08);
pub const TAB: InputKey = InputKey::new('\t' as u32);
pub const RETURN: InputKey = InputKey::new('\r' as u32);
pub const ESCAPE: InputKey = InputKey::new(0x1B);
pub const SPACE: InputKey = InputKey::new(' ' as u32);
pub const PRIOR: InputKey = InputKey::new(0x21);
pub const NEXT: InputKey = InputKey::new(0x22);
pub const END: InputKey = InputKey::new(0x23);
pub const HOME: InputKey = InputKey::new(0x24);
pub const LEFT: InputKey = InputKey::new(0x25);
pub const UP: InputKey = InputKey::new(0x26);
pub const RIGHT: InputKey = InputKey::new(0x27);
pub const DOWN: InputKey = InputKey::new(0x28);
pub const INSERT: InputKey = InputKey::new(0x2D);
pub const DELETE: InputKey = InputKey::new(0x2E);
pub const N0: InputKey = InputKey::new('0' as u32);
pub const N1: InputKey = InputKey::new('1' as u32);
pub const N2: InputKey = InputKey::new('2' as u32);
pub const N3: InputKey = InputKey::new('3' as u32);
pub const N4: InputKey = InputKey::new('4' as u32);
pub const N5: InputKey = InputKey::new('5' as u32);
pub const N6: InputKey = InputKey::new('6' as u32);
pub const N7: InputKey = InputKey::new('7' as u32);
pub const N8: InputKey = InputKey::new('8' as u32);
pub const N9: InputKey = InputKey::new('9' as u32);
pub const A: InputKey = InputKey::new('A' as u32);
pub const B: InputKey = InputKey::new('B' as u32);
pub const C: InputKey = InputKey::new('C' as u32);
pub const D: InputKey = InputKey::new('D' as u32);
pub const E: InputKey = InputKey::new('E' as u32);
pub const F: InputKey = InputKey::new('F' as u32);
pub const G: InputKey = InputKey::new('G' as u32);
pub const H: InputKey = InputKey::new('H' as u32);
pub const I: InputKey = InputKey::new('I' as u32);
pub const J: InputKey = InputKey::new('J' as u32);
pub const K: InputKey = InputKey::new('K' as u32);
pub const L: InputKey = InputKey::new('L' as u32);
pub const M: InputKey = InputKey::new('M' as u32);
pub const N: InputKey = InputKey::new('N' as u32);
pub const O: InputKey = InputKey::new('O' as u32);
pub const P: InputKey = InputKey::new('P' as u32);
pub const Q: InputKey = InputKey::new('Q' as u32);
pub const R: InputKey = InputKey::new('R' as u32);
pub const S: InputKey = InputKey::new('S' as u32);
pub const T: InputKey = InputKey::new('T' as u32);
pub const U: InputKey = InputKey::new('U' as u32);
pub const V: InputKey = InputKey::new('V' as u32);
pub const W: InputKey = InputKey::new('W' as u32);
pub const X: InputKey = InputKey::new('X' as u32);
pub const Y: InputKey = InputKey::new('Y' as u32);
pub const Z: InputKey = InputKey::new('Z' as u32);
pub const NUMPAD0: InputKey = InputKey::new(0x60);
pub const NUMPAD1: InputKey = InputKey::new(0x61);
pub const NUMPAD2: InputKey = InputKey::new(0x62);
pub const NUMPAD3: InputKey = InputKey::new(0x63);
pub const NUMPAD4: InputKey = InputKey::new(0x64);
pub const NUMPAD5: InputKey = InputKey::new(0x65);
pub const NUMPAD6: InputKey = InputKey::new(0x66);
pub const NUMPAD7: InputKey = InputKey::new(0x67);
pub const NUMPAD8: InputKey = InputKey::new(0x68);
pub const NUMPAD9: InputKey = InputKey::new(0x69);
pub const MULTIPLY: InputKey = InputKey::new(0x6A);
pub const ADD: InputKey = InputKey::new(0x6B);
pub const SEPARATOR: InputKey = InputKey::new(0x6C);
pub const SUBTRACT: InputKey = InputKey::new(0x6D);
pub const DECIMAL: InputKey = InputKey::new(0x6E);
pub const DIVIDE: InputKey = InputKey::new(0x6F);
pub const F1: InputKey = InputKey::new(0x70);
pub const F2: InputKey = InputKey::new(0x71);
pub const F3: InputKey = InputKey::new(0x72);
pub const F4: InputKey = InputKey::new(0x73);
pub const F5: InputKey = InputKey::new(0x74);
pub const F6: InputKey = InputKey::new(0x75);
pub const F7: InputKey = InputKey::new(0x76);
pub const F8: InputKey = InputKey::new(0x77);
pub const F9: InputKey = InputKey::new(0x78);
pub const F10: InputKey = InputKey::new(0x79);
pub const F11: InputKey = InputKey::new(0x7A);
pub const F12: InputKey = InputKey::new(0x7B);
pub const F13: InputKey = InputKey::new(0x7C);
pub const F14: InputKey = InputKey::new(0x7D);
pub const F15: InputKey = InputKey::new(0x7E);
pub const F16: InputKey = InputKey::new(0x7F);
pub const F17: InputKey = InputKey::new(0x80);
pub const F18: InputKey = InputKey::new(0x81);
pub const F19: InputKey = InputKey::new(0x82);
pub const F20: InputKey = InputKey::new(0x83);
pub const F21: InputKey = InputKey::new(0x84);
pub const F22: InputKey = InputKey::new(0x85);
pub const F23: InputKey = InputKey::new(0x86);
pub const F24: InputKey = InputKey::new(0x87);
}
/// Keyboard modifiers.
pub mod kbmod {
use super::InputKeyMod;
pub const NONE: InputKeyMod = InputKeyMod::new(0x00000000);
pub const CTRL: InputKeyMod = InputKeyMod::new(0x01000000);
pub const ALT: InputKeyMod = InputKeyMod::new(0x02000000);
pub const SHIFT: InputKeyMod = InputKeyMod::new(0x04000000);
pub const CTRL_ALT: InputKeyMod = InputKeyMod::new(0x03000000);
pub const CTRL_SHIFT: InputKeyMod = InputKeyMod::new(0x05000000);
pub const ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x06000000);
pub const CTRL_ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x07000000);
impl fmt::Debug for InputKeyMod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut sep = "";
for (modifier, name) in [(MOD_CTRL, "CTRL"), (MOD_ALT, "ALT"), (MOD_SHIFT, "SHIFT")] {
if self.contains(modifier) {
write!(f, "{}{}", sep, name)?;
sep = " | ";
}
}
if sep.is_empty() {
f.write_str("NONE")?;
}
Ok(())
}
}
/// Mouse input state. Up/Down, Left/Right, etc.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum InputMouseState {
#[default]
None,
@ -236,7 +188,7 @@ pub enum InputMouseState {
}
/// Mouse input.
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub struct InputMouse {
/// The state of the mouse.Up/Down, Left/Right, etc.
pub state: InputMouseState,
@ -316,14 +268,14 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
}
const KEYPAD_LUT: [u8; 8] = [
vk::UP.value() as u8, // A
vk::DOWN.value() as u8, // B
vk::RIGHT.value() as u8, // C
vk::LEFT.value() as u8, // D
0, // E
vk::END.value() as u8, // F
0, // G
vk::HOME.value() as u8, // H
VK_UP.value() as u8, // A
VK_DOWN.value() as u8, // B
VK_RIGHT.value() as u8, // C
VK_LEFT.value() as u8, // D
0, // E
VK_END.value() as u8, // F
0, // G
VK_HOME.value() as u8, // H
];
match self.stream.next()? {
@ -332,24 +284,24 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
}
vt::Token::Ctrl(ch) => match ch {
'\0' | '\t' | '\r' => return Some(Input::Keyboard(InputKey::new(ch as u32))),
'\n' => return Some(Input::Keyboard(kbmod::CTRL | vk::RETURN)),
'\n' => return Some(Input::Keyboard(MOD_CTRL | VK_RETURN)),
..='\x1a' => {
// Shift control code to A-Z
let key = ch as u32 | 0x40;
return Some(Input::Keyboard(kbmod::CTRL | InputKey::new(key)));
return Some(Input::Keyboard(MOD_CTRL | InputKey::new(key)));
}
'\x7f' => return Some(Input::Keyboard(vk::BACK)),
'\x7f' => return Some(Input::Keyboard(VK_BACK)),
_ => {}
},
vt::Token::Esc(ch) => {
match ch {
'\0' => return Some(Input::Keyboard(vk::ESCAPE)),
'\n' => return Some(Input::Keyboard(kbmod::CTRL_ALT | vk::RETURN)),
'\0' => return Some(Input::Keyboard(VK_ESCAPE)),
'\n' => return Some(Input::Keyboard(MOD_CTRL | MOD_ALT | VK_RETURN)),
' '..='~' => {
let ch = ch as u32;
let key = ch & !0x20; // Shift a-z to A-Z
let modifiers =
if (ch & 0x20) != 0 { kbmod::ALT } else { kbmod::ALT_SHIFT };
if (ch & 0x20) != 0 { MOD_ALT } else { MOD_ALT | MOD_SHIFT };
return Some(Input::Keyboard(modifiers | InputKey::new(key)));
}
_ => {}
@ -363,7 +315,7 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
}
}
'P'..='S' => {
let key = vk::F1.value() + ch as u32 - 'P' as u32;
let key = VK_F1.value() + ch as u32 - 'P' as u32;
return Some(Input::Keyboard(InputKey::new(key)));
}
_ => {}
@ -378,16 +330,16 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
));
}
}
'Z' => return Some(Input::Keyboard(kbmod::SHIFT | vk::TAB)),
'Z' => return Some(Input::Keyboard(MOD_SHIFT | VK_TAB)),
'~' => {
const LUT: [u8; 35] = [
0,
vk::HOME.value() as u8, // 1
vk::INSERT.value() as u8, // 2
vk::DELETE.value() as u8, // 3
vk::END.value() as u8, // 4
vk::PRIOR.value() as u8, // 5
vk::NEXT.value() as u8, // 6
VK_HOME.value() as u8, // 1
VK_INSERT.value() as u8, // 2
VK_DELETE.value() as u8, // 3
VK_END.value() as u8, // 4
VK_PRIOR.value() as u8, // 5
VK_NEXT.value() as u8, // 6
0,
0,
0,
@ -396,26 +348,26 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
0,
0,
0,
vk::F5.value() as u8, // 15
VK_F5.value() as u8, // 15
0,
vk::F6.value() as u8, // 17
vk::F7.value() as u8, // 18
vk::F8.value() as u8, // 19
vk::F9.value() as u8, // 20
vk::F10.value() as u8, // 21
VK_F6.value() as u8, // 17
VK_F7.value() as u8, // 18
VK_F8.value() as u8, // 19
VK_F9.value() as u8, // 20
VK_F10.value() as u8, // 21
0,
vk::F11.value() as u8, // 23
vk::F12.value() as u8, // 24
vk::F13.value() as u8, // 25
vk::F14.value() as u8, // 26
VK_F11.value() as u8, // 23
VK_F12.value() as u8, // 24
VK_F13.value() as u8, // 25
VK_F14.value() as u8, // 26
0,
vk::F15.value() as u8, // 28
vk::F16.value() as u8, // 29
VK_F15.value() as u8, // 28
VK_F16.value() as u8, // 29
0,
vk::F17.value() as u8, // 31
vk::F18.value() as u8, // 32
vk::F19.value() as u8, // 33
vk::F20.value() as u8, // 34
VK_F17.value() as u8, // 31
VK_F18.value() as u8, // 32
VK_F19.value() as u8, // 33
VK_F20.value() as u8, // 34
];
const LUT_LEN: u16 = LUT.len() as u16;
@ -436,7 +388,7 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
let btn = csi.params[0];
let mut mouse = InputMouse {
state: InputMouseState::None,
modifiers: kbmod::NONE,
modifiers: MOD_NONE,
position: Default::default(),
scroll: Default::default(),
};
@ -455,13 +407,10 @@ impl<'input> Iterator for Stream<'_, '_, 'input> {
mouse.state = STATES[(btn as usize) & 0x03];
}
mouse.modifiers = kbmod::NONE;
mouse.modifiers |=
if (btn & 0x04) != 0 { kbmod::SHIFT } else { kbmod::NONE };
mouse.modifiers |=
if (btn & 0x08) != 0 { kbmod::ALT } else { kbmod::NONE };
mouse.modifiers |=
if (btn & 0x10f) != 0 { kbmod::CTRL } else { kbmod::NONE };
mouse.modifiers = MOD_NONE;
mouse.modifiers |= if (btn & 0x04) != 0 { MOD_SHIFT } else { MOD_NONE };
mouse.modifiers |= if (btn & 0x08) != 0 { MOD_ALT } else { MOD_NONE };
mouse.modifiers |= if (btn & 0x10) != 0 { MOD_CTRL } else { MOD_NONE };
mouse.position.x = csi.params[1] as CoordType - 1;
mouse.position.y = csi.params[2] as CoordType - 1;
@ -552,10 +501,10 @@ impl<'input> Stream<'_, '_, 'input> {
_ => InputMouseState::None,
};
let modifiers = match modifier {
4 => kbmod::SHIFT,
8 => kbmod::ALT,
16 => kbmod::CTRL,
_ => kbmod::NONE,
4 => MOD_SHIFT,
8 => MOD_ALT,
16 => MOD_CTRL,
_ => MOD_NONE,
};
self.parser.x10_mouse_want = false;
@ -570,16 +519,16 @@ impl<'input> Stream<'_, '_, 'input> {
}
fn parse_modifiers(csi: &vt::Csi) -> InputKeyMod {
let mut modifiers = kbmod::NONE;
let mut modifiers = MOD_NONE;
let p1 = csi.params[1].saturating_sub(1);
if (p1 & 0x01) != 0 {
modifiers |= kbmod::SHIFT;
modifiers |= MOD_SHIFT;
}
if (p1 & 0x02) != 0 {
modifiers |= kbmod::ALT;
modifiers |= MOD_ALT;
}
if (p1 & 0x04) != 0 {
modifiers |= kbmod::CTRL;
modifiers |= MOD_CTRL;
}
modifiers
}

114
src/kbd.rs Normal file
View file

@ -0,0 +1,114 @@
//! The VK_* codes defined here match the VK_* constants on Windows.
//! It's a convenient way to handle keyboard input, even on other platforms.
use crate::input::{InputKey, InputKeyMod};
pub const VK_NULL: InputKey = InputKey::new('\0' as u32);
pub const VK_BACK: InputKey = InputKey::new(0x08);
pub const VK_TAB: InputKey = InputKey::new('\t' as u32);
pub const VK_RETURN: InputKey = InputKey::new('\r' as u32);
pub const VK_ESCAPE: InputKey = InputKey::new(0x1B);
pub const VK_SPACE: InputKey = InputKey::new(' ' as u32);
pub const VK_PRIOR: InputKey = InputKey::new(0x21);
pub const VK_NEXT: InputKey = InputKey::new(0x22);
pub const VK_END: InputKey = InputKey::new(0x23);
pub const VK_HOME: InputKey = InputKey::new(0x24);
pub const VK_LEFT: InputKey = InputKey::new(0x25);
pub const VK_UP: InputKey = InputKey::new(0x26);
pub const VK_RIGHT: InputKey = InputKey::new(0x27);
pub const VK_DOWN: InputKey = InputKey::new(0x28);
pub const VK_INSERT: InputKey = InputKey::new(0x2D);
pub const VK_DELETE: InputKey = InputKey::new(0x2E);
pub const VK_0: InputKey = InputKey::new('0' as u32);
pub const VK_1: InputKey = InputKey::new('1' as u32);
pub const VK_2: InputKey = InputKey::new('2' as u32);
pub const VK_3: InputKey = InputKey::new('3' as u32);
pub const VK_4: InputKey = InputKey::new('4' as u32);
pub const VK_5: InputKey = InputKey::new('5' as u32);
pub const VK_6: InputKey = InputKey::new('6' as u32);
pub const VK_7: InputKey = InputKey::new('7' as u32);
pub const VK_8: InputKey = InputKey::new('8' as u32);
pub const VK_9: InputKey = InputKey::new('9' as u32);
pub const VK_A: InputKey = InputKey::new('A' as u32);
pub const VK_B: InputKey = InputKey::new('B' as u32);
pub const VK_C: InputKey = InputKey::new('C' as u32);
pub const VK_D: InputKey = InputKey::new('D' as u32);
pub const VK_E: InputKey = InputKey::new('E' as u32);
pub const VK_F: InputKey = InputKey::new('F' as u32);
pub const VK_G: InputKey = InputKey::new('G' as u32);
pub const VK_H: InputKey = InputKey::new('H' as u32);
pub const VK_I: InputKey = InputKey::new('I' as u32);
pub const VK_J: InputKey = InputKey::new('J' as u32);
pub const VK_K: InputKey = InputKey::new('K' as u32);
pub const VK_L: InputKey = InputKey::new('L' as u32);
pub const VK_M: InputKey = InputKey::new('M' as u32);
pub const VK_N: InputKey = InputKey::new('N' as u32);
pub const VK_O: InputKey = InputKey::new('O' as u32);
pub const VK_P: InputKey = InputKey::new('P' as u32);
pub const VK_Q: InputKey = InputKey::new('Q' as u32);
pub const VK_R: InputKey = InputKey::new('R' as u32);
pub const VK_S: InputKey = InputKey::new('S' as u32);
pub const VK_T: InputKey = InputKey::new('T' as u32);
pub const VK_U: InputKey = InputKey::new('U' as u32);
pub const VK_V: InputKey = InputKey::new('V' as u32);
pub const VK_W: InputKey = InputKey::new('W' as u32);
pub const VK_X: InputKey = InputKey::new('X' as u32);
pub const VK_Y: InputKey = InputKey::new('Y' as u32);
pub const VK_Z: InputKey = InputKey::new('Z' as u32);
pub const VK_NUMPAD0: InputKey = InputKey::new(0x60);
pub const VK_NUMPAD1: InputKey = InputKey::new(0x61);
pub const VK_NUMPAD2: InputKey = InputKey::new(0x62);
pub const VK_NUMPAD3: InputKey = InputKey::new(0x63);
pub const VK_NUMPAD4: InputKey = InputKey::new(0x64);
pub const VK_NUMPAD5: InputKey = InputKey::new(0x65);
pub const VK_NUMPAD6: InputKey = InputKey::new(0x66);
pub const VK_NUMPAD7: InputKey = InputKey::new(0x67);
pub const VK_NUMPAD8: InputKey = InputKey::new(0x68);
pub const VK_NUMPAD9: InputKey = InputKey::new(0x69);
pub const VK_MULTIPLY: InputKey = InputKey::new(0x6A);
pub const VK_ADD: InputKey = InputKey::new(0x6B);
pub const VK_SEPARATOR: InputKey = InputKey::new(0x6C);
pub const VK_SUBTRACT: InputKey = InputKey::new(0x6D);
pub const VK_DECIMAL: InputKey = InputKey::new(0x6E);
pub const VK_DIVIDE: InputKey = InputKey::new(0x6F);
pub const VK_F1: InputKey = InputKey::new(0x70);
pub const VK_F2: InputKey = InputKey::new(0x71);
pub const VK_F3: InputKey = InputKey::new(0x72);
pub const VK_F4: InputKey = InputKey::new(0x73);
pub const VK_F5: InputKey = InputKey::new(0x74);
pub const VK_F6: InputKey = InputKey::new(0x75);
pub const VK_F7: InputKey = InputKey::new(0x76);
pub const VK_F8: InputKey = InputKey::new(0x77);
pub const VK_F9: InputKey = InputKey::new(0x78);
pub const VK_F10: InputKey = InputKey::new(0x79);
pub const VK_F11: InputKey = InputKey::new(0x7A);
pub const VK_F12: InputKey = InputKey::new(0x7B);
pub const VK_F13: InputKey = InputKey::new(0x7C);
pub const VK_F14: InputKey = InputKey::new(0x7D);
pub const VK_F15: InputKey = InputKey::new(0x7E);
pub const VK_F16: InputKey = InputKey::new(0x7F);
pub const VK_F17: InputKey = InputKey::new(0x80);
pub const VK_F18: InputKey = InputKey::new(0x81);
pub const VK_F19: InputKey = InputKey::new(0x82);
pub const VK_F20: InputKey = InputKey::new(0x83);
pub const VK_F21: InputKey = InputKey::new(0x84);
pub const VK_F22: InputKey = InputKey::new(0x85);
pub const VK_F23: InputKey = InputKey::new(0x86);
pub const VK_F24: InputKey = InputKey::new(0x87);
pub const MOD_NONE: InputKeyMod = InputKeyMod::new(0x00000000);
pub const MOD_CTRL: InputKeyMod = InputKeyMod::new(0x01000000);
pub const MOD_ALT: InputKeyMod = InputKeyMod::new(0x02000000);
pub const MOD_SHIFT: InputKeyMod = InputKeyMod::new(0x04000000);
pub const MOD_CTRL_ALT: InputKeyMod = InputKeyMod::new(0x03000000);
pub const MOD_CTRL_SHIFT: InputKeyMod = InputKeyMod::new(0x05000000);
pub const MOD_ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x06000000);
pub const MOD_CTRL_ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x07000000);

View file

@ -32,6 +32,7 @@ pub mod hash;
pub mod helpers;
pub mod icu;
pub mod input;
pub mod kbd;
pub mod oklab;
pub mod path;
pub mod simd;

View file

@ -157,13 +157,13 @@ use crate::document::WriteableDocument;
use crate::framebuffer::{Attributes, Framebuffer, INDEXED_COLORS_COUNT, IndexedColor};
use crate::hash::*;
use crate::helpers::*;
use crate::input::{InputKeyMod, kbmod, vk};
use crate::input::InputKeyMod;
use crate::kbd::*;
use crate::{apperr, arena_format, input, simd, unicode};
const ROOT_ID: u64 = 0x14057B7EF767814F; // Knuth's MMIX constant
const SHIFT_TAB: InputKey = vk::TAB.with_modifiers(kbmod::SHIFT);
const KBMOD_FOR_WORD_NAV: InputKeyMod =
if cfg!(target_os = "macos") { kbmod::ALT } else { kbmod::CTRL };
const SHIFT_TAB: InputKey = VK_TAB.with_modifiers(MOD_SHIFT);
const KBMOD_FOR_WORD_NAV: InputKeyMod = if cfg!(target_os = "macos") { MOD_ALT } else { MOD_CTRL };
type Input<'input> = input::Input<'input>;
type InputKey = input::InputKey;
@ -520,7 +520,7 @@ impl Tui {
let now = std::time::Instant::now();
let mut input_text = None;
let mut input_keyboard = None;
let mut input_mouse_modifiers = kbmod::NONE;
let mut input_mouse_modifiers = MOD_NONE;
let mut input_mouse_click = 0;
let mut input_scroll_delta = Point { x: 0, y: 0 };
// `input_consumed` should be `true` if we're in the settling phase which is indicated by
@ -556,7 +556,7 @@ impl Tui {
let clipboard = self.clipboard_mut();
clipboard.write(paste);
clipboard.mark_as_synchronized();
input_keyboard = Some(kbmod::CTRL | vk::V);
input_keyboard = Some(MOD_CTRL | VK_V);
}
Some(Input::Keyboard(keyboard)) => {
input_keyboard = Some(keyboard);
@ -743,7 +743,7 @@ impl Tui {
let mut focus_path_pop_min = 0;
// If the user pressed Escape, we move the focus to a parent node.
if !ctx.input_consumed && ctx.consume_shortcut(vk::ESCAPE) {
if !ctx.input_consumed && ctx.consume_shortcut(VK_ESCAPE) {
focus_path_pop_min = 1;
}
@ -1463,11 +1463,11 @@ impl<'a> Context<'a, '_> {
let Some(input) = self.input_keyboard else {
return;
};
if !matches!(input, SHIFT_TAB | vk::TAB) {
if !matches!(input, SHIFT_TAB | VK_TAB) {
return;
}
let forward = input == vk::TAB;
let forward = input == VK_TAB;
let mut focused_start = focused;
let mut focused_next = focused;
@ -1749,7 +1749,7 @@ impl<'a> Context<'a, '_> {
// Consume the input unconditionally, so that the root (the "main window")
// doesn't accidentally receive any input via `consume_shortcut()`.
if self.contains_focus() {
let exit = !self.input_consumed && self.input_keyboard == Some(vk::ESCAPE);
let exit = !self.input_consumed && self.input_keyboard == Some(VK_ESCAPE);
self.set_input_consumed_unchecked();
exit
} else {
@ -1820,7 +1820,7 @@ impl<'a> Context<'a, '_> {
}
fn table_end_row(&mut self) {
self.table_move_focus(vk::LEFT, vk::RIGHT);
self.table_move_focus(VK_LEFT, VK_RIGHT);
}
/// Ends the current table block.
@ -1835,7 +1835,7 @@ impl<'a> Context<'a, '_> {
}
self.block_end(); // table
self.table_move_focus(vk::UP, vk::DOWN);
self.table_move_focus(VK_UP, VK_DOWN);
}
fn table_move_focus(&mut self, prev_key: InputKey, next_key: InputKey) {
@ -2031,8 +2031,8 @@ impl<'a> Context<'a, '_> {
fn button_activated(&mut self) -> bool {
if !self.input_consumed
&& ((self.input_mouse_click != 0 && self.contains_mouse_down())
|| self.input_keyboard == Some(vk::RETURN)
|| self.input_keyboard == Some(vk::SPACE))
|| self.input_keyboard == Some(VK_RETURN)
|| self.input_keyboard == Some(VK_SPACE))
&& self.is_focused()
{
self.set_input_consumed();
@ -2267,7 +2267,7 @@ impl<'a> Context<'a, '_> {
2 => tb.select_word(),
_ => match self.tui.mouse_state {
InputMouseState::Left => {
if self.input_mouse_modifiers.contains(kbmod::SHIFT) {
if self.input_mouse_modifiers.contains(MOD_SHIFT) {
// TODO: Untested because Windows Terminal surprisingly doesn't support Shift+Click.
tb.selection_update_visual(pos);
} else {
@ -2321,29 +2321,29 @@ impl<'a> Context<'a, '_> {
make_cursor_visible = true;
match key {
vk::BACK => {
let granularity = if modifiers == kbmod::CTRL {
VK_BACK => {
let granularity = if modifiers == MOD_CTRL {
CursorMovement::Word
} else {
CursorMovement::Grapheme
};
tb.delete(granularity, -1);
}
vk::TAB => {
VK_TAB => {
if single_line {
// If this is just a simple input field, don't consume Tab (= early return).
return false;
}
tb.indent_change(if modifiers == kbmod::SHIFT { -1 } else { 1 });
tb.indent_change(if modifiers == MOD_SHIFT { -1 } else { 1 });
}
vk::RETURN => {
VK_RETURN => {
if single_line {
// If this is just a simple input field, don't consume Enter (= early return).
return false;
}
write = b"\n";
}
vk::ESCAPE => {
VK_ESCAPE => {
// If there was a selection, clear it and show the cursor (= fallthrough).
if !tb.clear_selection() {
if single_line {
@ -2357,7 +2357,7 @@ impl<'a> Context<'a, '_> {
make_cursor_visible = false;
}
}
vk::PRIOR => {
VK_PRIOR => {
let height = node_prev.inner.height() - 1;
// If the cursor was already on the first line,
@ -2366,7 +2366,7 @@ impl<'a> Context<'a, '_> {
tc.preferred_column = 0;
}
if modifiers == kbmod::SHIFT {
if modifiers == MOD_SHIFT {
tb.selection_update_visual(Point {
x: tc.preferred_column,
y: tb.cursor_visual_pos().y - height,
@ -2378,7 +2378,7 @@ impl<'a> Context<'a, '_> {
});
}
}
vk::NEXT => {
VK_NEXT => {
let height = node_prev.inner.height() - 1;
// If the cursor was already on the last line,
@ -2387,7 +2387,7 @@ impl<'a> Context<'a, '_> {
tc.preferred_column = CoordType::MAX;
}
if modifiers == kbmod::SHIFT {
if modifiers == MOD_SHIFT {
tb.selection_update_visual(Point {
x: tc.preferred_column,
y: tb.cursor_visual_pos().y + height,
@ -2403,28 +2403,28 @@ impl<'a> Context<'a, '_> {
tc.preferred_column = tb.cursor_visual_pos().x;
}
}
vk::END => {
VK_END => {
let logical_before = tb.cursor_logical_pos();
let destination = if modifiers.contains(kbmod::CTRL) {
let destination = if modifiers.contains(MOD_CTRL) {
Point::MAX
} else {
Point { x: CoordType::MAX, y: tb.cursor_visual_pos().y }
};
if modifiers.contains(kbmod::SHIFT) {
if modifiers.contains(MOD_SHIFT) {
tb.selection_update_visual(destination);
} else {
tb.cursor_move_to_visual(destination);
}
if !modifiers.contains(kbmod::CTRL) {
if !modifiers.contains(MOD_CTRL) {
let logical_after = tb.cursor_logical_pos();
// If word-wrap is enabled and the user presses End the first time,
// it moves to the start of the visual line. The second time they
// press it, it moves to the start of the logical line.
if tb.is_word_wrap_enabled() && logical_after == logical_before {
if modifiers == kbmod::SHIFT {
if modifiers == MOD_SHIFT {
tb.selection_update_logical(Point {
x: CoordType::MAX,
y: tb.cursor_logical_pos().y,
@ -2438,28 +2438,28 @@ impl<'a> Context<'a, '_> {
}
}
}
vk::HOME => {
VK_HOME => {
let logical_before = tb.cursor_logical_pos();
let destination = if modifiers.contains(kbmod::CTRL) {
let destination = if modifiers.contains(MOD_CTRL) {
Default::default()
} else {
Point { x: 0, y: tb.cursor_visual_pos().y }
};
if modifiers.contains(kbmod::SHIFT) {
if modifiers.contains(MOD_SHIFT) {
tb.selection_update_visual(destination);
} else {
tb.cursor_move_to_visual(destination);
}
if !modifiers.contains(kbmod::CTRL) {
if !modifiers.contains(MOD_CTRL) {
let mut logical_after = tb.cursor_logical_pos();
// If word-wrap is enabled and the user presses Home the first time,
// it moves to the start of the visual line. The second time they
// press it, it moves to the start of the logical line.
if tb.is_word_wrap_enabled() && logical_after == logical_before {
if modifiers == kbmod::SHIFT {
if modifiers == MOD_SHIFT {
tb.selection_update_logical(Point {
x: 0,
y: tb.cursor_logical_pos().y,
@ -2483,7 +2483,7 @@ impl<'a> Context<'a, '_> {
&& let indent_end = tb.indent_end_logical_pos()
&& (logical_before > indent_end || logical_before.x == 0)
{
if modifiers == kbmod::SHIFT {
if modifiers == MOD_SHIFT {
tb.selection_update_logical(indent_end);
} else {
tb.cursor_move_to_logical(indent_end);
@ -2491,13 +2491,13 @@ impl<'a> Context<'a, '_> {
}
}
}
vk::LEFT => {
VK_LEFT => {
let granularity = if modifiers.contains(KBMOD_FOR_WORD_NAV) {
CursorMovement::Word
} else {
CursorMovement::Grapheme
};
if modifiers.contains(kbmod::SHIFT) {
if modifiers.contains(MOD_SHIFT) {
tb.selection_update_delta(granularity, -1);
} else if let Some((beg, _)) = tb.selection_range() {
unsafe { tb.set_cursor(beg) };
@ -2505,12 +2505,12 @@ impl<'a> Context<'a, '_> {
tb.cursor_move_delta(granularity, -1);
}
}
vk::UP => {
VK_UP => {
if single_line {
return false;
}
match modifiers {
kbmod::NONE => {
MOD_NONE => {
let mut x = tc.preferred_column;
let mut y = tb.cursor_visual_pos().y - 1;
@ -2530,11 +2530,11 @@ impl<'a> Context<'a, '_> {
tb.cursor_move_to_visual(Point { x, y });
}
kbmod::CTRL => {
MOD_CTRL => {
tc.scroll_offset.y -= 1;
make_cursor_visible = false;
}
kbmod::SHIFT => {
MOD_SHIFT => {
// If the cursor was already on the first line,
// move it to the start of the buffer.
if tb.cursor_visual_pos().y == 0 {
@ -2546,20 +2546,20 @@ impl<'a> Context<'a, '_> {
y: tb.cursor_visual_pos().y - 1,
});
}
kbmod::ALT => tb.move_selected_lines(MoveLineDirection::Up),
kbmod::CTRL_ALT => {
MOD_ALT => tb.move_selected_lines(MoveLineDirection::Up),
MOD_CTRL_ALT => {
// TODO: Add cursor above
}
_ => return false,
}
}
vk::RIGHT => {
VK_RIGHT => {
let granularity = if modifiers.contains(KBMOD_FOR_WORD_NAV) {
CursorMovement::Word
} else {
CursorMovement::Grapheme
};
if modifiers.contains(kbmod::SHIFT) {
if modifiers.contains(MOD_SHIFT) {
tb.selection_update_delta(granularity, 1);
} else if let Some((_, end)) = tb.selection_range() {
unsafe { tb.set_cursor(end) };
@ -2567,12 +2567,12 @@ impl<'a> Context<'a, '_> {
tb.cursor_move_delta(granularity, 1);
}
}
vk::DOWN => {
VK_DOWN => {
if single_line {
return false;
}
match modifiers {
kbmod::NONE => {
MOD_NONE => {
let mut x = tc.preferred_column;
let mut y = tb.cursor_visual_pos().y + 1;
@ -2597,11 +2597,11 @@ impl<'a> Context<'a, '_> {
tc.preferred_column = tb.cursor_visual_pos().x;
}
}
kbmod::CTRL => {
MOD_CTRL => {
tc.scroll_offset.y += 1;
make_cursor_visible = false;
}
kbmod::SHIFT => {
MOD_SHIFT => {
// If the cursor was already on the last line,
// move it to the end of the buffer.
if tb.cursor_visual_pos().y >= tb.visual_line_count() - 1 {
@ -2617,77 +2617,77 @@ impl<'a> Context<'a, '_> {
tc.preferred_column = tb.cursor_visual_pos().x;
}
}
kbmod::ALT => tb.move_selected_lines(MoveLineDirection::Down),
kbmod::CTRL_ALT => {
MOD_ALT => tb.move_selected_lines(MoveLineDirection::Down),
MOD_CTRL_ALT => {
// TODO: Add cursor above
}
_ => return false,
}
}
vk::INSERT => match modifiers {
kbmod::SHIFT => tb.paste(self.clipboard_ref()),
kbmod::CTRL => tb.copy(self.clipboard_mut()),
VK_INSERT => match modifiers {
MOD_SHIFT => tb.paste(self.clipboard_ref()),
MOD_CTRL => tb.copy(self.clipboard_mut()),
_ => tb.set_overtype(!tb.is_overtype()),
},
vk::DELETE => match modifiers {
kbmod::SHIFT => tb.cut(self.clipboard_mut()),
kbmod::CTRL => tb.delete(CursorMovement::Word, 1),
VK_DELETE => match modifiers {
MOD_SHIFT => tb.cut(self.clipboard_mut()),
MOD_CTRL => tb.delete(CursorMovement::Word, 1),
_ => tb.delete(CursorMovement::Grapheme, 1),
},
vk::A => match modifiers {
kbmod::CTRL => tb.select_all(),
VK_A => match modifiers {
MOD_CTRL => tb.select_all(),
_ => return false,
},
vk::B => match modifiers {
kbmod::ALT if cfg!(target_os = "macos") => {
VK_B => match modifiers {
MOD_ALT if cfg!(target_os = "macos") => {
// On macOS, terminals commonly emit the Emacs style
// Alt+B (ESC b) sequence for Alt+Left.
tb.cursor_move_delta(CursorMovement::Word, -1);
}
_ => return false,
},
vk::F => match modifiers {
kbmod::ALT if cfg!(target_os = "macos") => {
VK_F => match modifiers {
MOD_ALT if cfg!(target_os = "macos") => {
// On macOS, terminals commonly emit the Emacs style
// Alt+F (ESC f) sequence for Alt+Right.
tb.cursor_move_delta(CursorMovement::Word, 1);
}
_ => return false,
},
vk::H => match modifiers {
kbmod::CTRL => tb.delete(CursorMovement::Word, -1),
VK_H => match modifiers {
MOD_CTRL => tb.delete(CursorMovement::Word, -1),
_ => return false,
},
vk::L => match modifiers {
kbmod::CTRL => tb.select_line(),
VK_L => match modifiers {
MOD_CTRL => tb.select_line(),
_ => return false,
},
vk::X => match modifiers {
kbmod::CTRL => tb.cut(self.clipboard_mut()),
VK_X => match modifiers {
MOD_CTRL => tb.cut(self.clipboard_mut()),
_ => return false,
},
vk::C => match modifiers {
kbmod::CTRL => tb.copy(self.clipboard_mut()),
VK_C => match modifiers {
MOD_CTRL => tb.copy(self.clipboard_mut()),
_ => return false,
},
vk::V => match modifiers {
kbmod::CTRL => tb.paste(self.clipboard_ref()),
VK_V => match modifiers {
MOD_CTRL => tb.paste(self.clipboard_ref()),
_ => return false,
},
vk::Y => match modifiers {
kbmod::CTRL => tb.redo(),
VK_Y => match modifiers {
MOD_CTRL => tb.redo(),
_ => return false,
},
vk::Z => match modifiers {
kbmod::CTRL => tb.undo(),
kbmod::CTRL_SHIFT => tb.redo(),
kbmod::ALT => tb.set_word_wrap(!tb.is_word_wrap_enabled()),
VK_Z => match modifiers {
MOD_CTRL => tb.undo(),
MOD_CTRL_SHIFT => tb.redo(),
MOD_ALT => tb.set_word_wrap(!tb.is_word_wrap_enabled()),
_ => return false,
},
_ => return false,
}
change_preferred_column = !matches!(key, vk::PRIOR | vk::NEXT | vk::UP | vk::DOWN);
change_preferred_column = !matches!(key, VK_PRIOR | VK_NEXT | VK_UP | VK_DOWN);
} else {
return false;
}
@ -2863,10 +2863,10 @@ impl<'a> Context<'a, '_> {
&& let Some(key) = self.input_keyboard
{
match key {
vk::PRIOR => sc.scroll_offset.y -= prev_container.inner_clipped.height(),
vk::NEXT => sc.scroll_offset.y += prev_container.inner_clipped.height(),
vk::END => sc.scroll_offset.y = CoordType::MAX,
vk::HOME => sc.scroll_offset.y = 0,
VK_PRIOR => sc.scroll_offset.y -= prev_container.inner_clipped.height(),
VK_NEXT => sc.scroll_offset.y += prev_container.inner_clipped.height(),
VK_END => sc.scroll_offset.y = CoordType::MAX,
VK_HOME => sc.scroll_offset.y = 0,
_ => return,
}
self.set_input_consumed();
@ -2956,7 +2956,7 @@ impl<'a> Context<'a, '_> {
let entered = focused
&& selected_before
&& !self.input_consumed
&& matches!(self.input_keyboard, Some(vk::RETURN));
&& matches!(self.input_keyboard, Some(VK_RETURN));
let activated = clicked || entered;
if activated {
self.set_input_consumed();
@ -3020,7 +3020,7 @@ impl<'a> Context<'a, '_> {
let mut consumed = true;
match key {
vk::PRIOR => {
VK_PRIOR => {
selected_next = selected_now;
for _ in 0..prev_container.borrow().inner_clipped.height() - 1 {
let node = selected_next.borrow();
@ -3030,7 +3030,7 @@ impl<'a> Context<'a, '_> {
};
}
}
vk::NEXT => {
VK_NEXT => {
selected_next = selected_now;
for _ in 0..prev_container.borrow().inner_clipped.height() - 1 {
let node = selected_next.borrow();
@ -3040,13 +3040,13 @@ impl<'a> Context<'a, '_> {
};
}
}
vk::END => {
VK_END => {
selected_next = list.children.last.unwrap_or(selected_next);
}
vk::HOME => {
VK_HOME => {
selected_next = list.children.first.unwrap_or(selected_next);
}
vk::UP => {
VK_UP => {
selected_next = selected_now
.borrow()
.siblings
@ -3054,7 +3054,7 @@ impl<'a> Context<'a, '_> {
.or(list.children.last)
.unwrap_or(selected_next);
}
vk::DOWN => {
VK_DOWN => {
selected_next = selected_now
.borrow()
.siblings
@ -3122,7 +3122,7 @@ impl<'a> Context<'a, '_> {
let contains_focus = self.contains_focus();
let keyboard_focus = accelerator != '\0'
&& !contains_focus
&& self.consume_shortcut(kbmod::ALT | InputKey::new(accelerator as u32));
&& self.consume_shortcut(MOD_ALT | InputKey::new(accelerator as u32));
if contains_focus || keyboard_focus {
self.attr_background_rgba(self.tui.floater_default_bg);
@ -3212,15 +3212,15 @@ impl<'a> Context<'a, '_> {
if !self.input_consumed
&& let Some(key) = self.input_keyboard
&& matches!(key, vk::ESCAPE | vk::UP | vk::DOWN)
&& matches!(key, VK_ESCAPE | VK_UP | VK_DOWN)
{
if matches!(key, vk::UP | vk::DOWN) {
if matches!(key, VK_UP | VK_DOWN) {
// If the focus is on the menubar, and the user presses up/down,
// focus the first/last item of the flyout respectively.
let ln = self.tree.last_node.borrow();
if self.tui.is_node_focused(ln.parent.map_or(0, |n| n.borrow().id)) {
let selected_next =
if key == vk::UP { ln.children.last } else { ln.children.first };
if key == VK_UP { ln.children.last } else { ln.children.first };
if let Some(selected_next) = selected_next {
self.steal_focus_for(selected_next);
self.set_input_consumed();
@ -3303,15 +3303,15 @@ impl<'a> Context<'a, '_> {
let shortcut_letter = shortcut.value() as u8 as char;
if shortcut_letter.is_ascii_uppercase() {
let mut shortcut_text = ArenaString::new_in(self.arena());
if shortcut.modifiers_contains(kbmod::CTRL) {
if shortcut.modifiers_contains(MOD_CTRL) {
shortcut_text.push_str(self.tui.modifier_translations.ctrl);
shortcut_text.push('+');
}
if shortcut.modifiers_contains(kbmod::ALT) {
if shortcut.modifiers_contains(MOD_ALT) {
shortcut_text.push_str(self.tui.modifier_translations.alt);
shortcut_text.push('+');
}
if shortcut.modifiers_contains(kbmod::SHIFT) {
if shortcut.modifiers_contains(MOD_SHIFT) {
shortcut_text.push_str(self.tui.modifier_translations.shift);
shortcut_text.push('+');
}