EditVIew: Add mutable and non-mutable callback methods

mutable callbacks disable recursive calls
This commit is contained in:
Alexandre Bury 2017-02-04 21:03:35 -08:00
parent 9850e93125
commit 0bbc10706e

View File

@ -3,6 +3,7 @@
use {Cursive, Printer, With}; use {Cursive, Printer, With};
use direction::Direction; use direction::Direction;
use event::{Callback, Event, EventResult, Key}; use event::{Callback, Event, EventResult, Key};
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use theme::{ColorStyle, Effect}; use theme::{ColorStyle, Effect};
@ -74,7 +75,7 @@ pub struct EditView {
/// Callback when the content is modified. /// Callback when the content is modified.
/// ///
/// Will be called with the current content and the cursor position /// Will be called with the current content and the cursor position.
on_edit: Option<Rc<Fn(&mut Cursive, &str, usize)>>, on_edit: Option<Rc<Fn(&mut Cursive, &str, usize)>>,
/// Callback when <Enter> is pressed. /// Callback when <Enter> is pressed.
@ -136,25 +137,118 @@ impl EditView {
self.enabled = true; self.enabled = true;
} }
/// Sets a mutable callback to be called whenever the content is modified.
///
/// `callback` will be called with the view
/// content and the current cursor position.
///
/// *Warning*: this callback cannot be called recursively. If you somehow
/// trigger this callback again in the given closure, it will be ignored.
///
/// If you don't need a mutable closure but want the possibility of
/// recursive calls, see [`set_on_edit`](#method.set_on_edit).
pub fn set_on_edit_mut<F>(&mut self, callback: F)
where F: FnMut(&mut Cursive, &str, usize) + 'static
{
let callback = RefCell::new(callback);
// Here's the weird trick: if we're already borrowed,
// just ignored the callback.
self.set_on_edit(move |s, text, cursor| {
if let Ok(mut f) = callback.try_borrow_mut() {
// Beeeaaah that's ugly.
// Why do we need to manually dereference here?
(&mut *f)(s, text, cursor);
}
});
}
/// Sets a callback to be called whenever the content is modified. /// Sets a callback to be called whenever the content is modified.
/// ///
/// `callback` will be called with the view /// `callback` will be called with the view
/// content and the current cursor position. /// content and the current cursor position.
pub fn on_edit<F>(mut self, callback: F) -> Self ///
/// This callback can safely trigger itself recursively if needed
/// (for instance if you call `on_event` on this view from the callback).
///
/// If you need a mutable closure and don't care about the recursive
/// aspect, see [`set_on_edit_mut`](#method.set_on_edit_mut).
pub fn set_on_edit<F>(&mut self, callback: F)
where F: Fn(&mut Cursive, &str, usize) + 'static where F: Fn(&mut Cursive, &str, usize) + 'static
{ {
self.on_edit = Some(Rc::new(callback)); self.on_edit = Some(Rc::new(callback));
self }
/// Sets a mutable callback to be called whenever the content is modified.
///
/// Chainable variant. See [`set_on_edit_mut`](#method.set_on_edit_mut).
pub fn on_edit_mut<F>(self, callback: F) -> Self
where F: FnMut(&mut Cursive, &str, usize) + 'static
{
self.with(|v| v.set_on_edit_mut(callback))
}
/// Sets a callback to be called whenever the content is modified.
///
/// Chainable variant. See [`set_on_edit`](#method.set_on_edit).
pub fn on_edit<F>(self, callback: F) -> Self
where F: Fn(&mut Cursive, &str, usize) + 'static
{
self.with(|v| v.set_on_edit(callback))
}
/// Sets a mutable callback to be called when `<Enter>` is pressed.
///
/// `callback` will be given the content of the view.
///
/// *Warning*: this callback cannot be called recursively. If you somehow
/// trigger this callback again in the given closure, it will be ignored.
///
/// If you don't need a mutable closure but want the possibility of
/// recursive calls, see [`set_on_submit`](#method.set_on_submit).
pub fn set_on_submit_mut<F>(&mut self, callback: F)
where F: FnMut(&mut Cursive, &str) + 'static
{
// TODO: don't duplicate all those methods.
// Instead, have some generic function immutify()
// or something that wraps a FnMut closure.
let callback = RefCell::new(callback);
self.set_on_submit(move |s, text| if let Ok(mut f) =
callback.try_borrow_mut() {
(&mut *f)(s, text);
});
} }
/// Sets a callback to be called when `<Enter>` is pressed. /// Sets a callback to be called when `<Enter>` is pressed.
/// ///
/// `callback` will be given the content of the view. /// `callback` will be given the content of the view.
pub fn on_submit<F>(mut self, callback: F) -> Self ///
/// This callback can safely trigger itself recursively if needed
/// (for instance if you call `on_event` on this view from the callback).
///
/// If you need a mutable closure and don't care about the recursive
/// aspect, see [`set_on_submit_mut`](#method.set_on_submit_mut).
pub fn set_on_submit<F>(&mut self, callback: F)
where F: Fn(&mut Cursive, &str) + 'static where F: Fn(&mut Cursive, &str) + 'static
{ {
self.on_submit = Some(Rc::new(callback)); self.on_submit = Some(Rc::new(callback));
self }
/// Sets a mutable callback to be called when `<Enter>` is pressed.
///
/// Chainable variant.
pub fn on_submit_mut<F>(self, callback: F) -> Self
where F: FnMut(&mut Cursive, &str) + 'static
{
self.with(|v| v.set_on_submit_mut(callback))
}
/// Sets a callback to be called when `<Enter>` is pressed.
///
/// Chainable variant.
pub fn on_submit<F>(self, callback: F) -> Self
where F: Fn(&mut Cursive, &str) + 'static
{
self.with(|v| v.set_on_submit(callback))
} }
/// Enable or disable this view. /// Enable or disable this view.
@ -239,9 +333,10 @@ impl EditView {
// Look at the content before the cursor (we will print its tail). // Look at the content before the cursor (we will print its tail).
// From the end, count the length until we reach `available`. // From the end, count the length until we reach `available`.
// Then sum the byte lengths. // Then sum the byte lengths.
let suffix_length = let suffix_length = simple_suffix(&self.content[self.offset..
simple_suffix(&self.content[self.offset..self.cursor], self.cursor],
available).length; available)
.length;
self.offset = self.cursor - suffix_length; self.offset = self.cursor - suffix_length;
// Make sure the cursor is in view // Make sure the cursor is in view
assert!(self.cursor >= self.offset); assert!(self.cursor >= self.offset);
@ -250,8 +345,8 @@ impl EditView {
// If we have too much space // If we have too much space
if self.content[self.offset..].width() < self.last_length { if self.content[self.offset..].width() < self.last_length {
let suffix_length = simple_suffix(&self.content, let suffix_length =
self.last_length - 1).length; simple_suffix(&self.content, self.last_length - 1).length;
self.offset = self.content.len() - suffix_length; self.offset = self.content.len() - suffix_length;
} }
} }
@ -393,9 +488,7 @@ impl View for EditView {
Event::Key(Key::Enter) if self.on_submit.is_some() => { Event::Key(Key::Enter) if self.on_submit.is_some() => {
let cb = self.on_submit.clone().unwrap(); let cb = self.on_submit.clone().unwrap();
let content = self.content.clone(); let content = self.content.clone();
return EventResult::with_cb(move |s| { return EventResult::with_cb(move |s| { cb(s, &content); });
cb(s, &content);
});
} }
_ => return EventResult::Ignored, _ => return EventResult::Ignored,
} }
@ -404,13 +497,11 @@ impl View for EditView {
let cb = self.on_edit.clone().map(|cb| { let cb = self.on_edit.clone().map(|cb| {
// Get a new Rc on it // Get a new Rc on the content
let content = self.content.clone(); let content = self.content.clone();
let cursor = self.cursor; let cursor = self.cursor;
Callback::from_fn(move |s| { Callback::from_fn(move |s| { cb(s, &content, cursor); })
cb(s, &content, cursor);
})
}); });
EventResult::Consumed(cb) EventResult::Consumed(cb)
} }