mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Add on_edit
callback to EditView
Also fix <Del>/<Backspace> handling with precomposed characters
This commit is contained in:
parent
db9df3dfc9
commit
2e05a0825a
@ -1,6 +1,9 @@
|
|||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use Cursive;
|
||||||
use With;
|
use With;
|
||||||
use direction::Direction;
|
use direction::Direction;
|
||||||
use theme::{ColorStyle, Effect};
|
use theme::{ColorStyle, Effect};
|
||||||
@ -49,7 +52,7 @@ use Printer;
|
|||||||
/// ```
|
/// ```
|
||||||
pub struct EditView {
|
pub struct EditView {
|
||||||
/// Current content.
|
/// Current content.
|
||||||
content: String,
|
content: Rc<String>,
|
||||||
/// Cursor position in the content, in bytes.
|
/// Cursor position in the content, in bytes.
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
/// Minimum layout length asked to the parent.
|
/// Minimum layout length asked to the parent.
|
||||||
@ -62,6 +65,11 @@ pub struct EditView {
|
|||||||
/// Last display length, to know the possible offset range
|
/// Last display length, to know the possible offset range
|
||||||
last_length: usize,
|
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,
|
enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +79,12 @@ impl EditView {
|
|||||||
/// Creates a new, empty edit view.
|
/// Creates a new, empty edit view.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
EditView {
|
EditView {
|
||||||
content: String::new(),
|
content: Rc::new(String::new()),
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
min_length: 1,
|
min_length: 1,
|
||||||
last_length: 0, // scrollable: false,
|
last_length: 0, // scrollable: false,
|
||||||
|
on_edit: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,6 +108,15 @@ impl EditView {
|
|||||||
self.enabled = true;
|
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.
|
/// Enable or disable this view.
|
||||||
pub fn set_enabled(&mut self, enabled: bool) {
|
pub fn set_enabled(&mut self, enabled: bool) {
|
||||||
self.enabled = enabled;
|
self.enabled = enabled;
|
||||||
@ -112,12 +130,12 @@ impl EditView {
|
|||||||
/// Replace the entire content of the view with the given one.
|
/// Replace the entire content of the view with the given one.
|
||||||
pub fn set_content(&mut self, content: &str) {
|
pub fn set_content(&mut self, content: &str) {
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.content = content.to_string();
|
self.content = Rc::new(content.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current text.
|
/// Get the current text.
|
||||||
pub fn get_content(&self) -> &str {
|
pub fn get_content(&self) -> &str {
|
||||||
&self.content
|
&&self.content
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the current content to the given value.
|
/// Sets the current content to the given value.
|
||||||
@ -135,6 +153,21 @@ impl EditView {
|
|||||||
|
|
||||||
self
|
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 {
|
impl View for EditView {
|
||||||
@ -154,7 +187,7 @@ impl View for EditView {
|
|||||||
printer.with_effect(effect, |printer| {
|
printer.with_effect(effect, |printer| {
|
||||||
if width < self.last_length {
|
if width < self.last_length {
|
||||||
// No problem, everything fits.
|
// No problem, everything fits.
|
||||||
printer.print((0, 0), &self.content);
|
printer.print((0, 0), self.get_content());
|
||||||
printer.print_hline((width, 0),
|
printer.print_hline((width, 0),
|
||||||
printer.size.x - width,
|
printer.size.x - width,
|
||||||
"_");
|
"_");
|
||||||
@ -196,7 +229,7 @@ impl View for EditView {
|
|||||||
.next()
|
.next()
|
||||||
.expect(&format!("Found no char at cursor {} in {}",
|
.expect(&format!("Found no char at cursor {} in {}",
|
||||||
self.cursor,
|
self.cursor,
|
||||||
self.content))
|
&self.content))
|
||||||
};
|
};
|
||||||
let offset = self.content[self.offset..self.cursor].width();
|
let offset = self.content[self.offset..self.cursor].width();
|
||||||
printer.print((offset, 0), c);
|
printer.print((offset, 0), c);
|
||||||
@ -222,7 +255,7 @@ impl View for EditView {
|
|||||||
Event::Char(ch) => {
|
Event::Char(ch) => {
|
||||||
// Find the byte index of the char at self.cursor
|
// Find the byte index of the char at self.cursor
|
||||||
|
|
||||||
self.content.insert(self.cursor, ch);
|
self.insert(ch);
|
||||||
self.cursor += ch.len_utf8();
|
self.cursor += ch.len_utf8();
|
||||||
}
|
}
|
||||||
// TODO: handle ctrl-key?
|
// TODO: handle ctrl-key?
|
||||||
@ -251,10 +284,15 @@ impl View for EditView {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.len();
|
.len();
|
||||||
self.cursor -= len;
|
self.cursor -= len;
|
||||||
self.content.remove(self.cursor);
|
self.remove(len);
|
||||||
}
|
}
|
||||||
Event::Key(Key::Del) if self.cursor < self.content.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,
|
_ => return EventResult::Ignored,
|
||||||
}
|
}
|
||||||
@ -293,7 +331,17 @@ impl View for EditView {
|
|||||||
self.offset = self.content.len() - tail_bytes;
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,20 +16,6 @@ use Printer;
|
|||||||
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
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.
|
/// View to select an item among a list.
|
||||||
///
|
///
|
||||||
/// It contains a list of values of type T, with associated labels.
|
/// 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user