arreliny-term 0.1.1

A simple terminal for keeping multiple shells in one window
use eframe::egui;

pub(super) fn terminal_bytes(
    key: egui::Key,
    modifiers: egui::Modifiers,
    application_cursor: bool,
) -> Option<Vec<u8>> {
    if modifiers.ctrl
        && let Some(character) = key.name().chars().next()
        && character.is_ascii_alphabetic()
        && key.name().len() == 1
    {
        return Some(vec![character.to_ascii_uppercase() as u8 - b'@']);
    }
    let bytes = match key {
        egui::Key::Enter => b"\r".as_slice(),
        egui::Key::Backspace => b"\x7f",
        egui::Key::Tab if modifiers.shift => b"\x1b[Z",
        egui::Key::Tab => b"\t",
        egui::Key::Escape => b"\x1b",
        egui::Key::ArrowUp if application_cursor => b"\x1bOA",
        egui::Key::ArrowDown if application_cursor => b"\x1bOB",
        egui::Key::ArrowRight if application_cursor => b"\x1bOC",
        egui::Key::ArrowLeft if application_cursor => b"\x1bOD",
        egui::Key::ArrowUp => b"\x1b[A",
        egui::Key::ArrowDown => b"\x1b[B",
        egui::Key::ArrowRight => b"\x1b[C",
        egui::Key::ArrowLeft => b"\x1b[D",
        egui::Key::Home => b"\x1b[H",
        egui::Key::End => b"\x1b[F",
        egui::Key::Insert => b"\x1b[2~",
        egui::Key::Delete => b"\x1b[3~",
        egui::Key::PageUp => b"\x1b[5~",
        egui::Key::PageDown => b"\x1b[6~",
        egui::Key::F1 => b"\x1bOP",
        egui::Key::F2 => b"\x1bOQ",
        egui::Key::F3 => b"\x1bOR",
        egui::Key::F4 => b"\x1bOS",
        egui::Key::F5 => b"\x1b[15~",
        egui::Key::F6 => b"\x1b[17~",
        egui::Key::F7 => b"\x1b[18~",
        egui::Key::F8 => b"\x1b[19~",
        egui::Key::F9 => b"\x1b[20~",
        egui::Key::F10 => b"\x1b[21~",
        egui::Key::F11 => b"\x1b[23~",
        egui::Key::F12 => b"\x1b[24~",
        _ => return None,
    };
    let mut result = Vec::with_capacity(bytes.len() + usize::from(modifiers.alt));
    if modifiers.alt {
        result.push(0x1b);
    }
    result.extend_from_slice(bytes);
    Some(result)
}

pub(super) fn matches(accelerator: &str, key: egui::Key, modifiers: egui::Modifiers) -> bool {
    let alt = accelerator.contains("<Alt>");
    let control = accelerator.contains("<Control>");
    let shift = accelerator.contains("<Shift>");
    let super_key = accelerator.contains("<Super>") || accelerator.contains("<Meta>");
    if modifiers.alt != alt
        || modifiers.ctrl != control
        || modifiers.shift != shift
        || modifiers.mac_cmd != super_key
    {
        return false;
    }
    let expected = accelerator.rsplit('>').next().unwrap_or(accelerator);
    let expected = if expected.eq_ignore_ascii_case("Return") {
        "Enter"
    } else {
        expected
    };
    key.name().eq_ignore_ascii_case(expected)
}

pub(super) fn is_modifier(key: egui::Key) -> bool {
    matches!(
        key,
        egui::Key::ShiftLeft
            | egui::Key::ShiftRight
            | egui::Key::ControlLeft
            | egui::Key::ControlRight
            | egui::Key::AltLeft
            | egui::Key::AltRight
            | egui::Key::SuperLeft
            | egui::Key::SuperRight
    )
}

pub(super) fn accelerator(key: egui::Key, modifiers: egui::Modifiers) -> String {
    let mut accelerator = String::new();
    if modifiers.ctrl {
        accelerator.push_str("<Control>");
    }
    if modifiers.alt {
        accelerator.push_str("<Alt>");
    }
    if modifiers.shift {
        accelerator.push_str("<Shift>");
    }
    if modifiers.mac_cmd || modifiers.command && !modifiers.ctrl {
        accelerator.push_str("<Super>");
    }
    let name = if key == egui::Key::Enter {
        "Return"
    } else {
        key.name()
    };
    accelerator.push_str(name);
    accelerator
}