use crate::ui::Model;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Chord {
pub key: egui::Key,
pub ctrl: bool,
pub shift: bool,
}
impl Chord {
pub const fn plain(key: egui::Key) -> Self {
Self {
key,
ctrl: false,
shift: false,
}
}
pub const fn ctrl(key: egui::Key) -> Self {
Self {
key,
ctrl: true,
shift: false,
}
}
pub fn spell(self) -> String {
let key = match self.key {
egui::Key::Comma => ",",
egui::Key::Equals => "=",
egui::Key::Minus => "-",
egui::Key::Escape => "Esc",
key => key.name(),
};
let ctrl = if self.ctrl { "Ctrl+" } else { "" };
let shift = if self.shift { "Shift+" } else { "" };
format!("{ctrl}{shift}{key}")
}
pub fn pressed(self, ctx: &egui::Context) -> bool {
ctx.input(|i| {
i.events.iter().any(|event| {
matches!(
event,
egui::Event::Key {
key,
pressed: true,
modifiers,
..
} if *key == self.key
&& modifiers.command == self.ctrl
&& modifiers.shift == self.shift
)
})
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Act {
Switch,
Model,
Dock,
Settings,
Help,
Pin,
New,
CloseTab,
NextTab,
PrevTab,
PageDown,
PageUp,
End,
Head,
ScaleUp,
ScaleDown,
ScaleReset,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Keyed {
Fired(Chord, Act),
Told(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Binding {
pub keyed: Keyed,
pub does: &'static str,
}
impl Binding {
pub(super) const fn fired(chord: Chord, act: Act, does: &'static str) -> Self {
Self {
keyed: Keyed::Fired(chord, act),
does,
}
}
pub(super) const fn told(says: &'static str, does: &'static str) -> Self {
Self {
keyed: Keyed::Told(says),
does,
}
}
pub fn says(&self) -> String {
match self.keyed {
Keyed::Fired(chord, _) => chord.spell(),
Keyed::Told(says) => says.to_owned(),
}
}
}
use egui::Key;
pub const BINDINGS: &[Binding] = &[
Binding::told("Enter", "sends the box, or fires the chosen word"),
Binding::told("Shift+Enter", "breaks a line"),
Binding::told("/", "opens the words"),
Binding::told("Tab", "completes the chosen word into the box"),
Binding::told("Up, Down", "walk whatever is open"),
Binding::told(
"Esc",
"closes a dropup, then the overlay, then the menu, then the notice",
),
Binding::told("1 to 6", "answers the question it is asking, by its number"),
Binding::fired(
Chord::ctrl(Key::X),
Act::Switch,
"every server, its workspaces and their conversations",
),
Binding::fired(
Chord::ctrl(Key::O),
Act::Model,
"changes the model this workspace runs on",
),
Binding::fired(
Chord::ctrl(Key::T),
Act::Dock,
"unfolds what is running, or folds it",
),
Binding::fired(
Chord::ctrl(Key::Comma),
Act::Settings,
"opens this seat's settings",
),
Binding::fired(
Chord::ctrl(Key::B),
Act::Pin,
"pins the bar open, or unpins it",
),
Binding::fired(
Chord::ctrl(Key::N),
Act::New,
"begins a conversation on the aimed workspace",
),
Binding::fired(
Chord::ctrl(Key::W),
Act::CloseTab,
"closes the selected tab",
),
Binding::fired(Chord::ctrl(Key::PageDown), Act::NextTab, "the next tab"),
Binding::fired(Chord::ctrl(Key::PageUp), Act::PrevTab, "the previous tab"),
Binding::fired(
Chord::plain(Key::PageDown),
Act::PageDown,
"scrolls the transcript a page on",
),
Binding::fired(Chord::plain(Key::PageUp), Act::PageUp, "a page back"),
Binding::fired(Chord::ctrl(Key::End), Act::End, "the transcript's end"),
Binding::fired(Chord::ctrl(Key::Home), Act::Head, "its head"),
Binding::fired(
Chord::ctrl(Key::Equals),
Act::ScaleUp,
"scales the window up",
),
Binding::fired(Chord::ctrl(Key::Minus), Act::ScaleDown, "scales it down"),
Binding::fired(
Chord::ctrl(Key::Num0),
Act::ScaleReset,
"puts the scale back to 1",
),
Binding::fired(
Chord::plain(Key::F1),
Act::Help,
"what this window takes — /help",
),
];
pub const ALIAS: (Chord, Act) = (Chord::ctrl(Key::Plus), Act::ScaleUp);
pub fn chord_for(act: Act) -> String {
BINDINGS
.iter()
.find_map(|row| match row.keyed {
Keyed::Fired(chord, fires) if fires == act => Some(chord.spell()),
_ => None,
})
.unwrap_or_default()
}
pub fn cited(help: &str, acts: &[Act]) -> String {
if acts.is_empty() {
return help.to_owned();
}
let chords: Vec<String> = acts.iter().map(|act| chord_for(*act)).collect();
format!("{help} · {}", chords.join(" / "))
}
fn word_of(act: Act) -> Option<&'static str> {
match act {
Act::Switch => Some(crate::ui::switcher::WORD),
Act::Model => Some(crate::ui::tuning::MODEL),
Act::Settings => Some(crate::ui::settings::WORD),
Act::Help => Some(crate::ui::commands::WORD),
_ => None,
}
}
pub fn cited_word(help: &str, word: &str) -> String {
let act = BINDINGS.iter().find_map(|row| match row.keyed {
Keyed::Fired(_, act) if word_of(act) == Some(word) => Some(act),
_ => None,
});
act.map_or_else(|| help.to_owned(), |act| cited(help, &[act]))
}
pub fn run(ctx: &egui::Context, model: &mut Model) {
for row in BINDINGS {
if let Keyed::Fired(chord, act) = row.keyed
&& chord.pressed(ctx)
{
super::acts::fire(act, ctx, model);
}
}
if ALIAS.0.pressed(ctx) {
super::acts::fire(ALIAS.1, ctx, model);
}
}