Add on_edit callback to EditView

Also fix <Del>/<Backspace> handling with precomposed characters
This commit is contained in:
Alexandre Bury 2016-07-30 01:18:12 -07:00
parent db9df3dfc9
commit 2e05a0825a
2 changed files with 73 additions and 24 deletions

View File

@ -1,6 +1,9 @@
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use std::rc::Rc;
use Cursive;
use With;
use direction::Direction;
use theme::{ColorStyle, Effect};
@ -49,7 +52,7 @@ use Printer;
/// ```
pub struct EditView {
/// Current content.
content: String,
content: Rc<String>,
/// Cursor position in the content, in bytes.
cursor: usize,
/// Minimum layout length asked to the parent.
@ -62,6 +65,11 @@ pub struct EditView {
/// Last display length, to know the possible offset range
last_length: usize,
/// Callback when the content is modified.
///
/// Will be called with the current content and the cursor position
on_edit: Option<Rc<Fn(&mut Cursive, &str, usize)>>,
enabled: bool,
}
@ -71,11 +79,12 @@ impl EditView {
/// Creates a new, empty edit view.
pub fn new() -> Self {
EditView {
content: String::new(),
content: Rc::new(String::new()),
cursor: 0,
offset: 0,
min_length: 1,
last_length: 0, // scrollable: false,
on_edit: None,
enabled: true,
}
}
@ -99,6 +108,15 @@ impl EditView {
self.enabled = true;
}
/// Sets a callback to be called whenever the content is modified.
///
/// `callback` will be called with the view
/// content and the current cursor position.
pub fn on_edit<F: Fn(&mut Cursive, &str, usize) + 'static>(mut self, callback: F) -> Self {
self.on_edit = Some(Rc::new(callback));
self
}
/// Enable or disable this view.
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
@ -112,12 +130,12 @@ impl EditView {
/// Replace the entire content of the view with the given one.
pub fn set_content(&mut self, content: &str) {
self.offset = 0;
self.content = content.to_string();
self.content = Rc::new(content.to_string());
}
/// Get the current text.
pub fn get_content(&self) -> &str {
&self.content
&&self.content
}
/// Sets the current content to the given value.
@ -135,6 +153,21 @@ impl EditView {
self
}
/// Insert `ch` at the current cursor position.
pub fn insert(&mut self, ch: char) {
// `make_mut` applies copy-on-write
// It means it'll just return a ref if no one else has a ref,
// and it will clone it into `self.content` otherwise.
Rc::make_mut(&mut self.content).insert(self.cursor, ch);
}
/// Remove the character at the current cursor position.
pub fn remove(&mut self, len: usize) {
let start = self.cursor;
let end = self.cursor + len;
Rc::make_mut(&mut self.content).drain(start..end).collect::<Vec<_>>();
}
}
impl View for EditView {
@ -154,7 +187,7 @@ impl View for EditView {
printer.with_effect(effect, |printer| {
if width < self.last_length {
// No problem, everything fits.
printer.print((0, 0), &self.content);
printer.print((0, 0), self.get_content());
printer.print_hline((width, 0),
printer.size.x - width,
"_");
@ -196,7 +229,7 @@ impl View for EditView {
.next()
.expect(&format!("Found no char at cursor {} in {}",
self.cursor,
self.content))
&self.content))
};
let offset = self.content[self.offset..self.cursor].width();
printer.print((offset, 0), c);
@ -222,7 +255,7 @@ impl View for EditView {
Event::Char(ch) => {
// Find the byte index of the char at self.cursor
self.content.insert(self.cursor, ch);
self.insert(ch);
self.cursor += ch.len_utf8();
}
// TODO: handle ctrl-key?
@ -251,10 +284,15 @@ impl View for EditView {
.unwrap()
.len();
self.cursor -= len;
self.content.remove(self.cursor);
self.remove(len);
}
Event::Key(Key::Del) if self.cursor < self.content.len() => {
self.content.remove(self.cursor);
let len = self.content[self.cursor..]
.graphemes(true)
.next()
.unwrap()
.len();
self.remove(len);
}
_ => return EventResult::Ignored,
}
@ -293,7 +331,17 @@ impl View for EditView {
self.offset = self.content.len() - tail_bytes;
}
EventResult::Consumed(None)
let cb = self.on_edit.clone().map(|cb| {
// Get a new Rc on it
let content = self.content.clone();
let cursor = self.cursor;
Callback::from_fn(move |s| {
cb(s, &content, cursor);
})
});
EventResult::Consumed(cb)
}
}

View File

@ -16,20 +16,6 @@ use Printer;
use unicode_width::UnicodeWidthStr;
struct Item<T> {
label: String,
value: Rc<T>,
}
impl<T> Item<T> {
fn new(label: &str, value: T) -> Self {
Item {
label: label.to_string(),
value: Rc::new(value),
}
}
}
/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
@ -424,3 +410,18 @@ impl<T: 'static> View for SelectView<T> {
}
}
}
struct Item<T> {
label: String,
value: Rc<T>,
}
impl<T> Item<T> {
fn new(label: &str, value: T) -> Self {
Item {
label: label.to_string(),
value: Rc::new(value),
}
}
}