2016-07-15 03:27:15 +00:00
|
|
|
use direction::Direction;
|
2017-10-13 03:13:49 +00:00
|
|
|
use event::{Callback, Event, EventResult, Key, MouseEvent};
|
2018-03-22 21:15:57 +00:00
|
|
|
use rect::Rect;
|
2017-02-05 05:03:35 +00:00
|
|
|
use std::cell::RefCell;
|
2016-10-02 22:22:29 +00:00
|
|
|
use std::rc::Rc;
|
2016-07-01 06:38:01 +00:00
|
|
|
use theme::{ColorStyle, Effect};
|
2016-10-02 22:22:29 +00:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
2018-03-15 23:07:28 +00:00
|
|
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
2018-01-08 11:33:43 +00:00
|
|
|
use utils::lines::simple::{simple_prefix, simple_suffix};
|
2015-05-26 23:11:22 +00:00
|
|
|
use vec::Vec2;
|
2016-07-25 06:00:13 +00:00
|
|
|
use view::View;
|
2018-05-18 00:37:39 +00:00
|
|
|
use {Cursive, Printer, With};
|
2015-05-26 22:46:05 +00:00
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
/// Closure type for callbacks when the content is modified.
|
|
|
|
///
|
|
|
|
/// Arguments are the `Cursive`, current content of the input and cursor
|
|
|
|
/// position
|
2017-10-11 16:09:49 +00:00
|
|
|
pub type OnEdit = Fn(&mut Cursive, &str, usize);
|
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
/// Closure type for callbacks when Enter is pressed.
|
|
|
|
///
|
|
|
|
/// Arguments are the `Cursive` and the content of the input.
|
2017-10-11 16:09:49 +00:00
|
|
|
pub type OnSubmit = Fn(&mut Cursive, &str);
|
2016-07-04 23:04:32 +00:00
|
|
|
|
2015-07-30 13:40:03 +00:00
|
|
|
/// Input box where the user can enter and edit text.
|
2016-07-21 05:08:06 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
2017-10-15 04:18:50 +00:00
|
|
|
/// From the [edit example][1].
|
2016-07-21 05:08:06 +00:00
|
|
|
///
|
2017-10-15 04:18:50 +00:00
|
|
|
/// [1]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs
|
2016-07-21 05:08:06 +00:00
|
|
|
///
|
2018-04-01 23:39:03 +00:00
|
|
|
/// ```rust
|
2016-07-21 05:08:06 +00:00
|
|
|
/// # extern crate cursive;
|
2016-09-29 05:45:27 +00:00
|
|
|
/// # use cursive::Cursive;
|
|
|
|
/// # use cursive::traits::*;
|
|
|
|
/// # use cursive::views::{Dialog, EditView, TextView};
|
2016-07-21 05:08:06 +00:00
|
|
|
/// # fn main() {
|
2018-04-01 23:39:03 +00:00
|
|
|
/// let mut siv = Cursive::dummy();
|
2016-07-21 05:08:06 +00:00
|
|
|
///
|
|
|
|
/// // Create a dialog with an edit text and a button.
|
2016-08-01 00:53:00 +00:00
|
|
|
/// // The user can either hit the <Ok> button,
|
|
|
|
/// // or press Enter on the edit text.
|
2017-10-15 04:18:50 +00:00
|
|
|
/// siv.add_layer(
|
|
|
|
/// Dialog::new()
|
|
|
|
/// .title("Enter your name")
|
|
|
|
/// .padding((1, 1, 1, 0))
|
|
|
|
/// .content(
|
|
|
|
/// EditView::new()
|
|
|
|
/// .on_submit(show_popup)
|
|
|
|
/// .with_id("name")
|
|
|
|
/// .fixed_width(20),
|
|
|
|
/// )
|
|
|
|
/// .button("Ok", |s| {
|
|
|
|
/// let name = s.call_on_id(
|
|
|
|
/// "name",
|
|
|
|
/// |view: &mut EditView| view.get_content(),
|
|
|
|
/// ).unwrap();
|
|
|
|
/// show_popup(s, &name);
|
|
|
|
/// }),
|
|
|
|
/// );
|
2016-08-01 00:53:00 +00:00
|
|
|
///
|
|
|
|
/// fn show_popup(s: &mut Cursive, name: &str) {
|
|
|
|
/// if name.is_empty() {
|
|
|
|
/// s.add_layer(Dialog::info("Please enter a name!"));
|
|
|
|
/// } else {
|
|
|
|
/// let content = format!("Hello {}!", name);
|
|
|
|
/// s.pop_layer();
|
2016-10-02 22:15:30 +00:00
|
|
|
/// s.add_layer(Dialog::around(TextView::new(content))
|
2016-08-01 00:53:00 +00:00
|
|
|
/// .button("Quit", |s| s.quit()));
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
2016-07-21 05:08:06 +00:00
|
|
|
/// # }
|
|
|
|
/// ```
|
2015-05-26 22:46:05 +00:00
|
|
|
pub struct EditView {
|
2016-07-04 23:04:32 +00:00
|
|
|
/// Current content.
|
2016-07-30 08:18:12 +00:00
|
|
|
content: Rc<String>,
|
2018-03-15 23:07:28 +00:00
|
|
|
|
2016-07-04 23:04:32 +00:00
|
|
|
/// Cursor position in the content, in bytes.
|
2015-05-26 22:46:05 +00:00
|
|
|
cursor: usize,
|
2015-07-30 13:40:03 +00:00
|
|
|
|
2016-07-04 23:04:32 +00:00
|
|
|
/// Number of bytes to skip at the beginning of the content.
|
|
|
|
///
|
|
|
|
/// (When the content is too long for the display, we hide part of it)
|
2015-07-30 13:40:03 +00:00
|
|
|
offset: usize,
|
2018-03-15 23:07:28 +00:00
|
|
|
|
|
|
|
/// Optional limit to the content width.
|
|
|
|
///
|
|
|
|
/// Input will be rejected if it would make the content exceed this width.
|
|
|
|
max_content_width: Option<usize>,
|
|
|
|
|
2015-07-30 13:40:03 +00:00
|
|
|
/// Last display length, to know the possible offset range
|
2016-07-17 01:18:33 +00:00
|
|
|
last_length: usize,
|
|
|
|
|
2016-07-30 08:18:12 +00:00
|
|
|
/// Callback when the content is modified.
|
|
|
|
///
|
2017-02-05 05:03:35 +00:00
|
|
|
/// Will be called with the current content and the cursor position.
|
2017-10-11 16:09:49 +00:00
|
|
|
on_edit: Option<Rc<OnEdit>>,
|
2016-07-30 08:18:12 +00:00
|
|
|
|
2016-07-30 09:34:47 +00:00
|
|
|
/// Callback when <Enter> is pressed.
|
2017-10-11 16:09:49 +00:00
|
|
|
on_submit: Option<Rc<OnSubmit>>,
|
2016-07-30 09:34:47 +00:00
|
|
|
|
2016-07-30 19:26:41 +00:00
|
|
|
/// When `true`, only print `*` instead of the true content.
|
|
|
|
secret: bool,
|
|
|
|
|
2017-04-03 18:03:56 +00:00
|
|
|
/// Character to fill empty space
|
|
|
|
filler: String,
|
|
|
|
|
2016-07-17 01:18:33 +00:00
|
|
|
enabled: bool,
|
2017-04-02 22:38:00 +00:00
|
|
|
|
|
|
|
style: ColorStyle,
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-17 00:28:42 +00:00
|
|
|
new_default!(EditView);
|
2016-06-28 05:40:11 +00:00
|
|
|
|
2015-05-26 22:46:05 +00:00
|
|
|
impl EditView {
|
2015-05-26 23:11:22 +00:00
|
|
|
/// Creates a new, empty edit view.
|
2015-05-26 22:46:05 +00:00
|
|
|
pub fn new() -> Self {
|
|
|
|
EditView {
|
2016-07-30 08:18:12 +00:00
|
|
|
content: Rc::new(String::new()),
|
2015-05-26 22:46:05 +00:00
|
|
|
cursor: 0,
|
2015-07-30 13:40:03 +00:00
|
|
|
offset: 0,
|
2016-06-25 23:36:22 +00:00
|
|
|
last_length: 0, // scrollable: false,
|
2016-07-30 08:18:12 +00:00
|
|
|
on_edit: None,
|
2016-07-30 09:34:47 +00:00
|
|
|
on_submit: None,
|
2018-03-15 23:07:28 +00:00
|
|
|
max_content_width: None,
|
2016-07-30 19:26:41 +00:00
|
|
|
secret: false,
|
2017-04-03 18:03:56 +00:00
|
|
|
filler: "_".to_string(),
|
2016-07-17 01:18:33 +00:00
|
|
|
enabled: true,
|
2018-01-17 17:35:57 +00:00
|
|
|
style: ColorStyle::secondary(),
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-15 23:07:28 +00:00
|
|
|
/// Sets a maximum width for the content.
|
|
|
|
///
|
|
|
|
/// Input will be rejected if it would make the content exceed this width.
|
|
|
|
///
|
|
|
|
/// Giving `None` means no maximum width is applied.
|
|
|
|
pub fn set_max_content_width(&mut self, width: Option<usize>) {
|
|
|
|
self.max_content_width = width;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets a maximum width for the content.
|
|
|
|
///
|
|
|
|
/// Input will be rejected if it would make the content exceed this width.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn max_content_width(self, width: usize) -> Self {
|
|
|
|
self.with(|s| s.set_max_content_width(Some(width)))
|
|
|
|
}
|
|
|
|
|
2016-07-30 19:26:41 +00:00
|
|
|
/// If `secret` is `true`, the content won't be displayed in clear.
|
|
|
|
///
|
|
|
|
/// Only `*` will be shown.
|
|
|
|
pub fn set_secret(&mut self, secret: bool) {
|
|
|
|
self.secret = secret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Hides the content of the view.
|
|
|
|
///
|
|
|
|
/// Only `*` will be shown.
|
|
|
|
pub fn secret(self) -> Self {
|
|
|
|
self.with(|s| s.set_secret(true))
|
|
|
|
}
|
|
|
|
|
2017-04-03 18:09:46 +00:00
|
|
|
/// Sets the character to fill in blank space.
|
2017-04-03 18:03:56 +00:00
|
|
|
///
|
2017-04-03 18:09:46 +00:00
|
|
|
/// Defaults to "_".
|
|
|
|
pub fn set_filler<S: Into<String>>(&mut self, filler: S) {
|
|
|
|
self.filler = filler.into();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the character to fill in blank space.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use cursive::views::EditView;
|
2017-10-15 04:18:50 +00:00
|
|
|
/// let edit = EditView::new().filler(" ");
|
2017-04-03 18:09:46 +00:00
|
|
|
/// ```
|
|
|
|
pub fn filler<S: Into<String>>(self, filler: S) -> Self {
|
|
|
|
self.with(|s| s.set_filler(filler))
|
2017-04-03 18:03:56 +00:00
|
|
|
}
|
|
|
|
|
2016-07-17 01:18:33 +00:00
|
|
|
/// Disables this view.
|
|
|
|
///
|
|
|
|
/// A disabled view cannot be selected.
|
|
|
|
pub fn disable(&mut self) {
|
|
|
|
self.enabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Disables this view.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn disabled(self) -> Self {
|
|
|
|
self.with(Self::disable)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Re-enables this view.
|
|
|
|
pub fn enable(&mut self) {
|
|
|
|
self.enabled = true;
|
|
|
|
}
|
|
|
|
|
2017-04-02 22:38:00 +00:00
|
|
|
/// Sets the style used for this view.
|
|
|
|
///
|
|
|
|
/// When the view is enabled, the style will be reversed.
|
|
|
|
///
|
|
|
|
/// Defaults to `ColorStyle::Secondary`.
|
|
|
|
pub fn set_style(&mut self, style: ColorStyle) {
|
|
|
|
self.style = style;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the style used for this view.
|
|
|
|
///
|
|
|
|
/// When the view is enabled, the style will be reversed.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn style(self, style: ColorStyle) -> Self {
|
|
|
|
self.with(|s| s.set_style(style))
|
|
|
|
}
|
|
|
|
|
2017-02-05 05:03:35 +00:00
|
|
|
/// 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)
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: FnMut(&mut Cursive, &str, usize) + 'static,
|
2017-02-05 05:03:35 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-07-30 08:18:12 +00:00
|
|
|
/// Sets a callback to be called whenever the content is modified.
|
|
|
|
///
|
|
|
|
/// `callback` will be called with the view
|
|
|
|
/// content and the current cursor position.
|
2017-02-05 05:03:35 +00:00
|
|
|
///
|
|
|
|
/// 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)
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: Fn(&mut Cursive, &str, usize) + 'static,
|
2016-08-13 08:03:40 +00:00
|
|
|
{
|
2016-07-30 08:18:12 +00:00
|
|
|
self.on_edit = Some(Rc::new(callback));
|
2017-02-05 05:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: FnMut(&mut Cursive, &str, usize) + 'static,
|
2017-02-05 05:03:35 +00:00
|
|
|
{
|
|
|
|
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
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: Fn(&mut Cursive, &str, usize) + 'static,
|
2017-02-05 05:03:35 +00:00
|
|
|
{
|
|
|
|
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)
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: FnMut(&mut Cursive, &str) + 'static,
|
2017-02-05 05:03:35 +00:00
|
|
|
{
|
|
|
|
// 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);
|
2017-12-30 22:03:42 +00:00
|
|
|
self.set_on_submit(move |s, text| {
|
|
|
|
if let Ok(mut f) = callback.try_borrow_mut() {
|
2017-10-12 23:38:55 +00:00
|
|
|
(&mut *f)(s, text);
|
2017-12-30 22:03:42 +00:00
|
|
|
}
|
|
|
|
});
|
2016-07-30 08:18:12 +00:00
|
|
|
}
|
|
|
|
|
2016-07-30 09:34:47 +00:00
|
|
|
/// Sets a callback to be called when `<Enter>` is pressed.
|
|
|
|
///
|
|
|
|
/// `callback` will be given the content of the view.
|
2017-02-05 05:03:35 +00:00
|
|
|
///
|
|
|
|
/// 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)
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: Fn(&mut Cursive, &str) + 'static,
|
2016-08-13 08:03:40 +00:00
|
|
|
{
|
2016-07-30 09:34:47 +00:00
|
|
|
self.on_submit = Some(Rc::new(callback));
|
2017-02-05 05:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets a mutable callback to be called when `<Enter>` is pressed.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn on_submit_mut<F>(self, callback: F) -> Self
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: FnMut(&mut Cursive, &str) + 'static,
|
2017-02-05 05:03:35 +00:00
|
|
|
{
|
|
|
|
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
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: Fn(&mut Cursive, &str) + 'static,
|
2017-02-05 05:03:35 +00:00
|
|
|
{
|
|
|
|
self.with(|v| v.set_on_submit(callback))
|
2016-07-30 09:34:47 +00:00
|
|
|
}
|
|
|
|
|
2016-07-17 01:18:33 +00:00
|
|
|
/// Enable or disable this view.
|
|
|
|
pub fn set_enabled(&mut self, enabled: bool) {
|
|
|
|
self.enabled = enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if this view is enabled.
|
|
|
|
pub fn is_enabled(&self) -> bool {
|
|
|
|
self.enabled
|
|
|
|
}
|
|
|
|
|
2015-05-26 23:11:22 +00:00
|
|
|
/// Replace the entire content of the view with the given one.
|
2018-03-15 18:50:22 +00:00
|
|
|
///
|
|
|
|
/// Returns a callback in response to content change.
|
|
|
|
///
|
|
|
|
/// You should run this callback with a `&mut Cursive`.
|
|
|
|
pub fn set_content<S: Into<String>>(&mut self, content: S) -> Callback {
|
2016-09-01 18:49:47 +00:00
|
|
|
let content = content.into();
|
|
|
|
let len = content.len();
|
|
|
|
|
|
|
|
self.content = Rc::new(content);
|
2016-09-01 18:37:03 +00:00
|
|
|
self.offset = 0;
|
2016-09-01 18:49:47 +00:00
|
|
|
self.set_cursor(len);
|
2018-03-15 18:50:22 +00:00
|
|
|
|
2018-05-18 00:37:39 +00:00
|
|
|
self.make_edit_cb().unwrap_or_else(Callback::dummy)
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
|
2015-05-26 23:11:22 +00:00
|
|
|
/// Get the current text.
|
2016-07-30 19:58:25 +00:00
|
|
|
pub fn get_content(&self) -> Rc<String> {
|
2017-10-13 18:22:02 +00:00
|
|
|
Rc::clone(&self.content)
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-10 02:05:51 +00:00
|
|
|
/// Sets the current content to the given value.
|
|
|
|
///
|
|
|
|
/// Convenient chainable method.
|
2018-03-15 18:50:22 +00:00
|
|
|
///
|
|
|
|
/// Does not run the `on_edit` callback.
|
2016-09-01 18:49:47 +00:00
|
|
|
pub fn content<S: Into<String>>(mut self, content: S) -> Self {
|
2015-05-26 22:46:05 +00:00
|
|
|
self.set_content(content);
|
|
|
|
self
|
|
|
|
}
|
2015-05-26 23:11:22 +00:00
|
|
|
|
2016-09-01 18:10:12 +00:00
|
|
|
/// Sets the cursor position.
|
|
|
|
pub fn set_cursor(&mut self, cursor: usize) {
|
|
|
|
self.cursor = cursor;
|
|
|
|
|
|
|
|
self.keep_cursor_in_view();
|
|
|
|
}
|
|
|
|
|
2016-07-30 08:18:12 +00:00
|
|
|
/// Insert `ch` at the current cursor position.
|
2018-03-15 18:50:22 +00:00
|
|
|
///
|
|
|
|
/// Returns a callback in response to content change.
|
|
|
|
///
|
|
|
|
/// You should run this callback with a `&mut Cursive`.
|
|
|
|
pub fn insert(&mut self, ch: char) -> Callback {
|
2018-03-15 23:07:28 +00:00
|
|
|
// First, make sure we can actually insert anything.
|
|
|
|
if let Some(width) = self.max_content_width {
|
|
|
|
// XXX: we assume here that the widths are linearly additive.
|
|
|
|
// Is that true? What about weird combined unicode thingies?
|
|
|
|
// Also, say the user copy+paste some content, do we want to
|
|
|
|
// stop halfway through a possibly split grapheme?
|
|
|
|
if ch.width().unwrap_or(0) + self.content.width() > width {
|
|
|
|
// ABORT
|
|
|
|
return Callback::dummy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-30 08:18:12 +00:00
|
|
|
// `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.
|
2018-03-15 23:07:28 +00:00
|
|
|
|
2016-07-30 08:18:12 +00:00
|
|
|
Rc::make_mut(&mut self.content).insert(self.cursor, ch);
|
2016-08-02 07:32:16 +00:00
|
|
|
self.cursor += ch.len_utf8();
|
2018-03-15 18:50:22 +00:00
|
|
|
|
2018-03-15 23:07:28 +00:00
|
|
|
self.keep_cursor_in_view();
|
|
|
|
|
2018-05-18 00:37:39 +00:00
|
|
|
self.make_edit_cb().unwrap_or_else(Callback::dummy)
|
2016-07-30 08:18:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove the character at the current cursor position.
|
2018-03-15 18:50:22 +00:00
|
|
|
///
|
|
|
|
/// Returns a callback in response to content change.
|
|
|
|
///
|
|
|
|
/// You should run this callback with a `&mut Cursive`.
|
|
|
|
pub fn remove(&mut self, len: usize) -> Callback {
|
2016-07-30 08:18:12 +00:00
|
|
|
let start = self.cursor;
|
|
|
|
let end = self.cursor + len;
|
2016-07-31 18:29:55 +00:00
|
|
|
for _ in Rc::make_mut(&mut self.content).drain(start..end) {}
|
2018-03-15 18:50:22 +00:00
|
|
|
|
2018-03-15 23:07:28 +00:00
|
|
|
self.keep_cursor_in_view();
|
|
|
|
|
2018-05-18 00:37:39 +00:00
|
|
|
self.make_edit_cb().unwrap_or_else(Callback::dummy)
|
2018-03-15 18:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn make_edit_cb(&self) -> Option<Callback> {
|
|
|
|
self.on_edit.clone().map(|cb| {
|
|
|
|
// Get a new Rc on the content
|
|
|
|
let content = Rc::clone(&self.content);
|
|
|
|
let cursor = self.cursor;
|
|
|
|
|
|
|
|
Callback::from_fn(move |s| {
|
|
|
|
cb(s, &content, cursor);
|
|
|
|
})
|
|
|
|
})
|
2016-07-30 08:18:12 +00:00
|
|
|
}
|
2016-09-01 18:10:12 +00:00
|
|
|
|
|
|
|
fn keep_cursor_in_view(&mut self) {
|
|
|
|
// keep cursor in [offset, offset+last_length] by changing offset
|
|
|
|
// so keep offset in [last_length-cursor,cursor]
|
|
|
|
// Also call this on resize,
|
|
|
|
// but right now it is an event like any other
|
|
|
|
if self.cursor < self.offset {
|
|
|
|
self.offset = self.cursor;
|
|
|
|
} else {
|
|
|
|
// So we're against the right wall.
|
|
|
|
// Let's find how much space will be taken by the selection
|
|
|
|
// (either a char, or _)
|
|
|
|
let c_len = self.content[self.cursor..]
|
|
|
|
.graphemes(true)
|
|
|
|
.map(|g| g.width())
|
|
|
|
.next()
|
|
|
|
.unwrap_or(1);
|
2017-08-14 23:32:01 +00:00
|
|
|
|
2016-09-01 18:10:12 +00:00
|
|
|
// Now, we have to fit self.content[..self.cursor]
|
|
|
|
// into self.last_length - c_len.
|
2017-08-14 23:32:01 +00:00
|
|
|
let available = match self.last_length.checked_sub(c_len) {
|
|
|
|
Some(s) => s,
|
|
|
|
// Weird - no available space?
|
|
|
|
None => return,
|
|
|
|
};
|
2016-09-01 18:10:12 +00:00
|
|
|
// Look at the content before the cursor (we will print its tail).
|
|
|
|
// From the end, count the length until we reach `available`.
|
|
|
|
// Then sum the byte lengths.
|
2017-10-12 23:38:55 +00:00
|
|
|
let suffix_length = simple_suffix(
|
|
|
|
&self.content[self.offset..self.cursor],
|
|
|
|
available,
|
|
|
|
).length;
|
2017-08-14 23:32:01 +00:00
|
|
|
|
|
|
|
assert!(suffix_length <= self.cursor);
|
2016-09-01 18:10:12 +00:00
|
|
|
self.offset = self.cursor - suffix_length;
|
2016-10-05 00:49:40 +00:00
|
|
|
// Make sure the cursor is in view
|
2016-09-01 18:10:12 +00:00
|
|
|
assert!(self.cursor >= self.offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have too much space
|
|
|
|
if self.content[self.offset..].width() < self.last_length {
|
2017-08-14 23:32:01 +00:00
|
|
|
assert!(self.last_length >= 1);
|
2017-02-07 02:18:17 +00:00
|
|
|
let suffix_length =
|
|
|
|
simple_suffix(&self.content, self.last_length - 1).length;
|
2017-08-14 23:32:01 +00:00
|
|
|
|
2017-08-15 16:33:50 +00:00
|
|
|
assert!(self.content.len() >= suffix_length);
|
2016-09-01 18:10:12 +00:00
|
|
|
self.offset = self.content.len() - suffix_length;
|
|
|
|
}
|
|
|
|
}
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-30 19:26:41 +00:00
|
|
|
/// Returns a `&str` with `length` characters `*`.
|
|
|
|
///
|
|
|
|
/// Only works for small `length` (1 or 2).
|
|
|
|
/// Best used for single character replacement.
|
|
|
|
fn make_small_stars(length: usize) -> &'static str {
|
2017-04-03 18:09:46 +00:00
|
|
|
// TODO: be able to use any character as hidden mode?
|
2016-07-30 19:26:41 +00:00
|
|
|
&"****"[..length]
|
|
|
|
}
|
|
|
|
|
2015-05-26 22:46:05 +00:00
|
|
|
impl View for EditView {
|
2016-07-16 06:44:38 +00:00
|
|
|
fn draw(&self, printer: &Printer) {
|
2017-10-12 23:38:55 +00:00
|
|
|
assert_eq!(
|
2018-01-22 22:37:27 +00:00
|
|
|
printer.size.x, self.last_length,
|
2017-10-12 23:38:55 +00:00
|
|
|
"Was promised {}, received {}",
|
2018-01-22 22:37:27 +00:00
|
|
|
self.last_length, printer.size.x
|
2017-10-12 23:38:55 +00:00
|
|
|
);
|
2016-07-04 23:04:32 +00:00
|
|
|
|
|
|
|
let width = self.content.width();
|
2017-04-02 22:38:00 +00:00
|
|
|
printer.with_color(self.style, |printer| {
|
2016-07-17 01:18:33 +00:00
|
|
|
let effect = if self.enabled {
|
|
|
|
Effect::Reverse
|
|
|
|
} else {
|
|
|
|
Effect::Simple
|
|
|
|
};
|
|
|
|
printer.with_effect(effect, |printer| {
|
2016-07-04 23:04:32 +00:00
|
|
|
if width < self.last_length {
|
|
|
|
// No problem, everything fits.
|
2017-08-14 23:32:01 +00:00
|
|
|
assert!(printer.size.x >= width);
|
2016-07-30 19:26:41 +00:00
|
|
|
if self.secret {
|
|
|
|
printer.print_hline((0, 0), width, "*");
|
|
|
|
} else {
|
2016-07-30 19:58:25 +00:00
|
|
|
printer.print((0, 0), &self.content);
|
2016-07-30 19:26:41 +00:00
|
|
|
}
|
2017-10-12 23:38:55 +00:00
|
|
|
let filler_len =
|
|
|
|
(printer.size.x - width) / self.filler.width();
|
|
|
|
printer.print_hline(
|
|
|
|
(width, 0),
|
|
|
|
filler_len,
|
|
|
|
self.filler.as_str(),
|
|
|
|
);
|
2015-07-30 13:40:03 +00:00
|
|
|
} else {
|
2016-07-04 23:04:32 +00:00
|
|
|
let content = &self.content[self.offset..];
|
2017-10-12 23:38:55 +00:00
|
|
|
let display_bytes = content
|
|
|
|
.graphemes(true)
|
2016-07-10 02:05:51 +00:00
|
|
|
.scan(0, |w, g| {
|
|
|
|
*w += g.width();
|
2017-10-12 23:38:55 +00:00
|
|
|
if *w > self.last_length {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(g)
|
|
|
|
}
|
2018-08-22 20:33:29 +00:00
|
|
|
}).map(|g| g.len())
|
2018-01-22 22:47:56 +00:00
|
|
|
.sum();
|
2016-07-04 23:04:32 +00:00
|
|
|
|
|
|
|
let content = &content[..display_bytes];
|
|
|
|
let width = content.width();
|
|
|
|
|
2016-07-30 19:26:41 +00:00
|
|
|
if self.secret {
|
|
|
|
printer.print_hline((0, 0), width, "*");
|
|
|
|
} else {
|
|
|
|
printer.print((0, 0), content);
|
|
|
|
}
|
|
|
|
|
2016-07-04 23:04:32 +00:00
|
|
|
if width < self.last_length {
|
2017-10-12 23:38:55 +00:00
|
|
|
let filler_len =
|
|
|
|
(self.last_length - width) / self.filler.width();
|
|
|
|
printer.print_hline(
|
|
|
|
(width, 0),
|
|
|
|
filler_len,
|
|
|
|
self.filler.as_str(),
|
|
|
|
);
|
2015-07-30 13:40:03 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-27 04:45:00 +00:00
|
|
|
});
|
2015-05-27 07:01:23 +00:00
|
|
|
|
|
|
|
// Now print cursor
|
2015-05-31 04:05:34 +00:00
|
|
|
if printer.focused {
|
2016-07-30 19:26:41 +00:00
|
|
|
let c: &str = if self.cursor == self.content.len() {
|
2017-04-03 18:09:46 +00:00
|
|
|
&self.filler
|
2015-05-27 07:01:23 +00:00
|
|
|
} else {
|
|
|
|
// Get the char from the string... Is it so hard?
|
2016-07-30 19:26:41 +00:00
|
|
|
let selected = self.content[self.cursor..]
|
2016-06-26 22:03:12 +00:00
|
|
|
.graphemes(true)
|
2016-07-04 23:04:32 +00:00
|
|
|
.next()
|
2018-06-21 21:44:30 +00:00
|
|
|
.unwrap_or_else(|| {
|
|
|
|
panic!(
|
|
|
|
"Found no char at cursor {} in {}",
|
|
|
|
self.cursor, &self.content
|
|
|
|
)
|
|
|
|
});
|
2016-07-30 19:26:41 +00:00
|
|
|
if self.secret {
|
|
|
|
make_small_stars(selected.width())
|
|
|
|
} else {
|
|
|
|
selected
|
|
|
|
}
|
2015-05-27 07:01:23 +00:00
|
|
|
};
|
2016-07-04 23:04:32 +00:00
|
|
|
let offset = self.content[self.offset..self.cursor].width();
|
|
|
|
printer.print((offset, 0), c);
|
2015-05-27 07:01:23 +00:00
|
|
|
}
|
2015-05-26 22:46:05 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-07-30 13:40:03 +00:00
|
|
|
fn layout(&mut self, size: Vec2) {
|
|
|
|
self.last_length = size.x;
|
2017-08-23 14:49:09 +00:00
|
|
|
debug!("Promised: {}", size.x);
|
2015-07-30 13:40:03 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 03:27:15 +00:00
|
|
|
fn take_focus(&mut self, _: Direction) -> bool {
|
2016-07-17 01:18:33 +00:00
|
|
|
self.enabled
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
|
2015-05-28 01:04:33 +00:00
|
|
|
fn on_event(&mut self, event: Event) -> EventResult {
|
|
|
|
match event {
|
2018-03-15 18:50:22 +00:00
|
|
|
Event::Char(ch) => {
|
2018-03-15 23:07:28 +00:00
|
|
|
return EventResult::Consumed(Some(self.insert(ch)));
|
2018-03-15 18:50:22 +00:00
|
|
|
}
|
2016-07-11 01:27:26 +00:00
|
|
|
// TODO: handle ctrl-key?
|
2018-05-03 15:36:17 +00:00
|
|
|
Event::Key(Key::Home) => self.set_cursor(0),
|
|
|
|
Event::Key(Key::End) => {
|
|
|
|
// When possible, NLL to the rescue!
|
|
|
|
let len = self.content.len();
|
|
|
|
self.set_cursor(len);
|
|
|
|
}
|
2016-07-11 01:27:26 +00:00
|
|
|
Event::Key(Key::Left) if self.cursor > 0 => {
|
|
|
|
let len = self.content[..self.cursor]
|
|
|
|
.graphemes(true)
|
|
|
|
.last()
|
|
|
|
.unwrap()
|
|
|
|
.len();
|
2018-05-03 15:36:17 +00:00
|
|
|
let cursor = self.cursor - len;
|
|
|
|
self.set_cursor(cursor);
|
2016-07-11 01:27:26 +00:00
|
|
|
}
|
|
|
|
Event::Key(Key::Right) if self.cursor < self.content.len() => {
|
|
|
|
let len = self.content[self.cursor..]
|
|
|
|
.graphemes(true)
|
|
|
|
.next()
|
|
|
|
.unwrap()
|
|
|
|
.len();
|
2018-05-03 15:36:17 +00:00
|
|
|
let cursor = self.cursor + len;
|
|
|
|
self.set_cursor(cursor);
|
2016-07-11 01:27:26 +00:00
|
|
|
}
|
|
|
|
Event::Key(Key::Backspace) if self.cursor > 0 => {
|
|
|
|
let len = self.content[..self.cursor]
|
|
|
|
.graphemes(true)
|
|
|
|
.last()
|
|
|
|
.unwrap()
|
|
|
|
.len();
|
|
|
|
self.cursor -= len;
|
2018-03-15 23:07:28 +00:00
|
|
|
return EventResult::Consumed(Some(self.remove(len)));
|
2016-07-11 01:27:26 +00:00
|
|
|
}
|
|
|
|
Event::Key(Key::Del) if self.cursor < self.content.len() => {
|
2016-07-30 08:18:12 +00:00
|
|
|
let len = self.content[self.cursor..]
|
|
|
|
.graphemes(true)
|
|
|
|
.next()
|
|
|
|
.unwrap()
|
|
|
|
.len();
|
2018-03-15 23:07:28 +00:00
|
|
|
return EventResult::Consumed(Some(self.remove(len)));
|
2016-06-25 23:36:22 +00:00
|
|
|
}
|
2016-07-30 09:34:47 +00:00
|
|
|
Event::Key(Key::Enter) if self.on_submit.is_some() => {
|
|
|
|
let cb = self.on_submit.clone().unwrap();
|
2017-10-13 18:22:02 +00:00
|
|
|
let content = Rc::clone(&self.content);
|
2017-10-12 23:38:55 +00:00
|
|
|
return EventResult::with_cb(move |s| {
|
|
|
|
cb(s, &content);
|
|
|
|
});
|
2016-07-30 09:34:47 +00:00
|
|
|
}
|
2017-10-13 00:38:21 +00:00
|
|
|
Event::Mouse {
|
|
|
|
event: MouseEvent::Press(_),
|
|
|
|
position,
|
|
|
|
offset,
|
2018-07-20 02:44:59 +00:00
|
|
|
}
|
|
|
|
if position.fits_in_rect(offset, (self.last_length, 1)) =>
|
2017-10-13 00:38:21 +00:00
|
|
|
{
|
2018-05-18 00:35:57 +00:00
|
|
|
if let Some(position) = position.checked_sub(offset) {
|
2018-08-22 20:33:29 +00:00
|
|
|
self.cursor = self.offset + simple_prefix(
|
|
|
|
&self.content[self.offset..],
|
|
|
|
position.x,
|
|
|
|
).length;
|
2018-05-18 00:35:57 +00:00
|
|
|
}
|
2017-10-13 00:38:21 +00:00
|
|
|
}
|
2016-07-11 01:27:26 +00:00
|
|
|
_ => return EventResult::Ignored,
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
|
|
|
|
2018-03-15 23:07:28 +00:00
|
|
|
// self.keep_cursor_in_view();
|
2015-07-30 13:40:03 +00:00
|
|
|
|
2018-03-15 18:50:22 +00:00
|
|
|
EventResult::Consumed(self.make_edit_cb())
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|
2018-03-22 21:15:57 +00:00
|
|
|
|
|
|
|
fn important_area(&self, _: Vec2) -> Rect {
|
|
|
|
let char_width = if self.cursor >= self.content.len() {
|
|
|
|
// Show a space if we're at the end of the content
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
// Otherwise look at the selected character.
|
|
|
|
self.content[self.cursor..]
|
|
|
|
.graphemes(true)
|
|
|
|
.next()
|
|
|
|
.unwrap()
|
|
|
|
.width()
|
|
|
|
};
|
|
|
|
|
|
|
|
let x = self.content[..self.cursor].width();
|
|
|
|
|
|
|
|
Rect::from_size((x, 0), (char_width, 1))
|
|
|
|
}
|
2015-05-26 22:46:05 +00:00
|
|
|
}
|