summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/edit.rs144
1 files changed, 126 insertions, 18 deletions
diff --git a/src/edit.rs b/src/edit.rs
index e40bfd7..97c1abf 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -1,10 +1,11 @@
use vtcol::{Palette, Rgb, Scheme};
+use std::fmt;
use std::rc::Rc;
use anyhow::{anyhow, Result};
-use slint::{Color, VecModel};
+use slint::{re_exports::KeyEvent, Color, VecModel};
slint::slint! {
import { HorizontalBox, VerticalBox } from "std-widgets.slint";
@@ -17,6 +18,7 @@ slint::slint! {
callback format-rgb-component(int) -> string;
callback color-of-rgb-component(string, int) -> color;
callback component-of-rgb(string, color) -> int;
+ callback handle-command-buffer(KeyEvent, string, int) -> string;
property<string> mode: "normal"; /* normal | insert | command */
}
@@ -132,10 +134,26 @@ slint::slint! {
height : t.preferred-height + 3pt;
background : @linear-gradient(90deg, #002b36 0%, #073642 100%);
+ property <int> cursor : 0;
property text <=> t.text;
+ callback input (KeyEvent) -> bool;
+ callback clear;
- t:= TextInput {
- text: ":";
+ clear () => { t.text = ""; }
+
+ input (k) => {
+ t.text = Aux.handle-command-buffer (k, t.text, cursor);
+
+ if (t.text == "") {
+ /* User backspaced her way to the left. */
+ return true;
+ }
+
+ return false;
+ }
+
+ t := Text {
+ text: "";
color: #fdf6e3;
vertical-alignment: center;
}
@@ -161,12 +179,11 @@ slint::slint! {
/* Escape always gets you back to normal mode. */
if (event.text == Keys.Escape) {
debug("enter normal");
- master.enter-normal-mode();
+ master.enter-normal-mode ();
}
else if (Aux.mode == "command") {
- if (event.text == Keys.Return) {
- // TODO: execute typed command
- master.enter-normal-mode();
+ if (command.input (event)) {
+ master.enter-normal-mode ();
}
}
else if (Aux.mode == "insert") {
@@ -258,8 +275,16 @@ slint::slint! {
debug ("selected row above/below, now", Aux.selected);
}
- enter-command-mode () => { Aux.mode = "command"; }
- enter-normal-mode () => { Aux.mode = "normal" ; }
+ enter-command-mode () => {
+ Aux.mode = "command";
+ command.text = ":";
+ }
+
+ enter-normal-mode () => {
+ Aux.mode = "normal" ;
+ command.clear ();
+ }
+
enter-insert-mode () => { Aux.mode = "insert" ; }
status := HorizontalBox {
@@ -298,21 +323,76 @@ slint::slint! {
}
}
-pub struct Edit
-{
- name: Option<String>,
+#[derive(Debug)]
+enum KeyInput {
+ Printable(String),
+ Escape,
+ Return,
+ Backspace,
+ Delete,
+ Tab,
+ Left,
+ Right,
+ Up,
+ Down,
+ /** Not representable. */
+ Junk,
+}
+
+impl fmt::Display for KeyInput {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let k = match self {
+ Self::Printable(s) => return write!(f, "‘{}’", s),
+ Self::Escape => "ESC",
+ Self::Return => "\\r",
+ Self::Backspace => "\\b",
+ Self::Delete => "DEL",
+ Self::Tab => "\\t",
+ Self::Left => "←",
+ Self::Right => "→",
+ Self::Up => "↑",
+ Self::Down => "↓",
+ Self::Junk => return write!(f, "{}", "Ø"),
+ };
+
+ write!(f, "key:{}", k)
+ }
+}
+
+/** Crude filter for rejecting strings containing control chars. */
+fn is_printable(s: &str) -> bool {
+ s.chars().find(|c| c.is_control()).is_none()
+}
+
+impl From<&KeyEvent> for KeyInput {
+ fn from(ev: &KeyEvent) -> Self {
+ match ev.text.as_str() {
+ "\u{0008}" => KeyInput::Backspace,
+ "\t" | "\u{000b}" => KeyInput::Tab,
+ "\n" | "\r" => KeyInput::Return,
+ "\u{000e}" => KeyInput::Left,
+ "\u{000f}" => KeyInput::Right,
+ "\u{0010}" => KeyInput::Up,
+ "\u{0011}" => KeyInput::Down,
+ "\u{001b}" => KeyInput::Escape,
+ "\u{007F}" => KeyInput::Delete,
+ any if is_printable(any) => KeyInput::Printable(any.into()),
+ _ => KeyInput::Junk,
+ }
+ }
+}
+
+pub struct Edit {
+ name: Option<String>,
scheme: Scheme,
}
-impl Edit
-{
- pub fn new(name: Option<String>, scheme: Scheme) -> Self
- {
+impl Edit {
+ pub fn new(name: Option<String>, scheme: Scheme) -> Self {
Self { name, scheme }
}
- pub fn run(self) -> Result<()>
- {
+ pub fn run(self) -> Result<()> {
let Self { name, scheme } = self;
let pal = Palette::try_from(&scheme)?.iter().collect::<Vec<Rgb>>();
@@ -387,6 +467,34 @@ impl Edit
}
});
+ gui.global::<Aux>().on_handle_command_buffer(|ev, text, _pos| {
+ let text = match KeyInput::from(&ev) {
+ KeyInput::Printable(s) => {
+ let mut text = text.to_string();
+ text.push_str(&s);
+ text
+ },
+ KeyInput::Return => {
+ todo!();
+ /* The empty string signals to leave command mode. */
+ String::new()
+ },
+ KeyInput::Backspace => match text.char_indices().next_back() {
+ Some((i, _)) => text[..i].into(),
+ None => text.to_string(),
+ },
+ other => {
+ eprintln!(
+ "»»» command mode input: “{}”",
+ other.to_string()
+ );
+ text.to_string()
+ },
+ };
+ eprintln!("»»» command buffer: “{}”", text);
+ text.into()
+ });
+
if let Some(name) = name {
gui.set_scheme_name(name.into());
}