diff --git a/Cargo.lock b/Cargo.lock index b9641f82..5dce5cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1304,6 +1304,7 @@ dependencies = [ "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "tui 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 770658f4..7ac7553a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8" dirs = "2.0.2" clap = "2.33.0" +unicode-width = "0.1.5" [[bin]] bench = false diff --git a/src/app.rs b/src/app.rs index 5f4c6111..9e835a71 100644 --- a/src/app.rs +++ b/src/app.rs @@ -201,6 +201,7 @@ pub struct App { pub current_playback_context: Option, pub devices: Option, pub input: String, + pub input_idx: usize, pub input_cursor_position: u16, pub large_search_limit: u32, pub library: Library, @@ -250,6 +251,7 @@ impl App { current_playback_context: None, devices: None, input: String::new(), + input_idx: 0, input_cursor_position: 0, playlist_tracks: vec![], playlists: None, diff --git a/src/handlers/input.rs b/src/handlers/input.rs index a6112fd7..2963b099 100644 --- a/src/handlers/input.rs +++ b/src/handlers/input.rs @@ -1,28 +1,42 @@ +extern crate unicode_width; + use super::super::app::{ActiveBlock, App, RouteId}; use std::convert::TryInto; use termion::event::Key; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; // Handle events when the search input block is active pub fn handler(key: Key, app: &mut App) { match key { Key::Ctrl('u') => { app.input = String::new(); + app.input_idx = 0; app.input_cursor_position = 0; } Key::Ctrl('e') => { - app.input_cursor_position = app.input.len().try_into().unwrap(); + app.input_idx = app.input.len(); + app.input_cursor_position = UnicodeWidthStr::width(app.input.as_str()) + .try_into() + .unwrap(); } Key::Ctrl('a') => { + app.input_idx = 0; app.input_cursor_position = 0; } Key::Left => { - if !app.input.is_empty() && app.input_cursor_position > 0 { - app.input_cursor_position -= 1; + if !app.input.is_empty() && app.input_idx > 0 { + let last_c = app.input.chars().nth(app.input_idx - 1).unwrap(); + app.input_idx -= 1; + let width: u16 = UnicodeWidthChar::width(last_c).unwrap().try_into().unwrap(); + app.input_cursor_position -= width; } } Key::Right => { if app.input_cursor_position < app.input.len().try_into().unwrap() { - app.input_cursor_position += 1; + let next_c = app.input.chars().nth(app.input_idx).unwrap(); + app.input_idx += 1; + let width: u16 = UnicodeWidthChar::width(next_c).unwrap().try_into().unwrap(); + app.input_cursor_position += width; } } Key::Esc => { @@ -94,15 +108,23 @@ pub fn handler(key: Key, app: &mut App) { } } Key::Char(c) => { - app.input - .insert(app.input_cursor_position.try_into().unwrap(), c); - app.input_cursor_position += 1; + let (insert_idx, _) = app + .input + .char_indices() + .nth(app.input_idx) + .unwrap_or((app.input.len(), ' ')); + app.input.insert(insert_idx, c); + app.input_idx += 1; + let width: u16 = UnicodeWidthChar::width(c).unwrap().try_into().unwrap(); + app.input_cursor_position += width; } Key::Backspace => { - if !app.input.is_empty() && app.input_cursor_position > 0 { - app.input - .remove((app.input_cursor_position - 1).try_into().unwrap()); - app.input_cursor_position -= 1; + if !app.input.is_empty() && app.input_idx > 0 { + let (remove_idx, last_c) = app.input.char_indices().nth(app.input_idx - 1).unwrap(); + app.input_idx -= 1; + app.input.remove(remove_idx); + let width: u16 = UnicodeWidthChar::width(last_c).unwrap().try_into().unwrap(); + app.input_cursor_position -= width; } } _ => {}