Add tab completion to Add sample file dir dialog
This commit is contained in:
parent
930decc766
commit
3d40a39b93
|
@ -5,8 +5,8 @@
|
|||
use base::strutil::{decode_size, encode_size};
|
||||
use cursive::traits::{Nameable, Resizable};
|
||||
use cursive::view::Scrollable;
|
||||
use cursive::views;
|
||||
use cursive::Cursive;
|
||||
use cursive::{views, With};
|
||||
use db::writer;
|
||||
use failure::Error;
|
||||
use log::{debug, trace};
|
||||
|
@ -16,6 +16,8 @@ use std::path::Path;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::tab_complete::TabCompleteEditView;
|
||||
|
||||
struct Stream {
|
||||
label: String,
|
||||
used: i64,
|
||||
|
@ -217,19 +219,47 @@ pub fn top_dialog(db: &Arc<db::Database>, siv: &mut Cursive) {
|
|||
);
|
||||
}
|
||||
|
||||
fn tab_completer(content: &str) -> Box<[String]> {
|
||||
let (parent, final_segment) = content.split_at(content.rfind('/').map(|i| i + 1).unwrap_or(0));
|
||||
Path::new(parent)
|
||||
.read_dir()
|
||||
.ok()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|entry| {
|
||||
let entry = entry.ok()?;
|
||||
if entry.file_type().ok()?.is_dir()
|
||||
&& entry.file_name().to_str()?.starts_with(final_segment)
|
||||
{
|
||||
Some(entry.path().as_os_str().to_str()?.to_owned() + "/")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Box<[String]>>()
|
||||
.with(|completions| {
|
||||
// Sort ignoring initial dot
|
||||
completions.sort_by(|a, b| {
|
||||
a.strip_prefix('.')
|
||||
.unwrap_or(a)
|
||||
.cmp(b.strip_prefix('.').unwrap_or(b))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn add_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive) {
|
||||
siv.add_layer(
|
||||
views::Dialog::around(
|
||||
views::LinearLayout::vertical()
|
||||
.child(views::TextView::new("path"))
|
||||
.child(
|
||||
views::EditView::new()
|
||||
.on_submit({
|
||||
let db = db.clone();
|
||||
move |siv, path| add_dir(&db, siv, path.as_ref())
|
||||
})
|
||||
.with_name("path")
|
||||
.fixed_width(60),
|
||||
TabCompleteEditView::new(views::EditView::new().on_submit({
|
||||
let db = db.clone();
|
||||
move |siv, path| add_dir(&db, siv, path.as_ref())
|
||||
}))
|
||||
.on_tab_complete(tab_completer)
|
||||
.with_name("path")
|
||||
.fixed_width(60),
|
||||
),
|
||||
)
|
||||
.button("Add", {
|
||||
|
|
|
@ -17,6 +17,7 @@ use std::sync::Arc;
|
|||
|
||||
mod cameras;
|
||||
mod dirs;
|
||||
mod tab_complete;
|
||||
mod users;
|
||||
|
||||
/// Interactively edits configuration.
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use cursive::{
|
||||
direction::Direction,
|
||||
event::{Event, EventResult, Key},
|
||||
menu,
|
||||
view::CannotFocus,
|
||||
views::{self, EditView, MenuPopup},
|
||||
Printer, Rect, Vec2, View,
|
||||
};
|
||||
|
||||
type TabCompleteFn = Rc<dyn Fn(&str) -> Box<[String]>>;
|
||||
|
||||
pub struct TabCompleteEditView {
|
||||
edit_view: Rc<RefCell<EditView>>,
|
||||
tab_completer: Option<TabCompleteFn>,
|
||||
}
|
||||
|
||||
impl TabCompleteEditView {
|
||||
pub fn new(edit_view: EditView) -> Self {
|
||||
Self {
|
||||
edit_view: Rc::new(RefCell::new(edit_view)),
|
||||
tab_completer: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_tab_complete(mut self, handler: impl Fn(&str) -> Box<[String]> + 'static) -> Self {
|
||||
self.tab_completer = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl View for TabCompleteEditView {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
self.edit_view.borrow().draw(printer)
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.edit_view.borrow_mut().layout(size)
|
||||
}
|
||||
|
||||
fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
|
||||
self.edit_view.borrow_mut().take_focus(source)
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
if !self.edit_view.borrow().is_enabled() {
|
||||
return EventResult::Ignored;
|
||||
}
|
||||
|
||||
if let Event::Key(Key::Tab) = event {
|
||||
if let Some(tab_completer) = self.tab_completer.clone() {
|
||||
tab_complete(self.edit_view.clone(), tab_completer, true)
|
||||
} else {
|
||||
EventResult::consumed()
|
||||
}
|
||||
} else {
|
||||
self.edit_view.borrow_mut().on_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn important_area(&self, view_size: Vec2) -> Rect {
|
||||
self.edit_view.borrow().important_area(view_size)
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_complete(
|
||||
edit_view: Rc<RefCell<EditView>>,
|
||||
tab_completer: TabCompleteFn,
|
||||
autofill_one: bool,
|
||||
) -> EventResult {
|
||||
let completions = tab_completer(edit_view.borrow().get_content().as_str());
|
||||
EventResult::with_cb_once(move |siv| match *completions {
|
||||
[] => {}
|
||||
[ref completion] if autofill_one => edit_view.borrow_mut().set_content(completion)(siv),
|
||||
[..] => {
|
||||
siv.add_layer(TabCompletePopup {
|
||||
popup: views::MenuPopup::new(Rc::new({
|
||||
let mut tree = menu::Tree::new();
|
||||
for completion in Vec::from(completions) {
|
||||
let edit_view = edit_view.clone();
|
||||
tree.add_leaf(&completion.clone(), move |siv| {
|
||||
edit_view.borrow_mut().set_content(&completion)(siv)
|
||||
})
|
||||
}
|
||||
tree
|
||||
})),
|
||||
edit_view,
|
||||
tab_completer,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct TabCompletePopup {
|
||||
edit_view: Rc<RefCell<EditView>>,
|
||||
popup: MenuPopup,
|
||||
tab_completer: TabCompleteFn,
|
||||
}
|
||||
impl TabCompletePopup {
|
||||
fn forward_event_and_refresh(&self, event: Event) -> EventResult {
|
||||
let edit_view = self.edit_view.clone();
|
||||
let tab_completer = self.tab_completer.clone();
|
||||
EventResult::with_cb_once(move |s| {
|
||||
s.pop_layer();
|
||||
edit_view.borrow_mut().on_event(event).process(s);
|
||||
tab_complete(edit_view, tab_completer, false).process(s);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl View for TabCompletePopup {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
self.popup.draw(printer)
|
||||
}
|
||||
|
||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||
self.popup.required_size(req)
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
match self.popup.on_event(event.clone()) {
|
||||
EventResult::Ignored => match event {
|
||||
e @ (Event::Char(_) | Event::Key(Key::Backspace)) => {
|
||||
self.forward_event_and_refresh(e)
|
||||
}
|
||||
Event::Key(Key::Tab) => self.popup.on_event(Event::Key(Key::Enter)),
|
||||
_ => EventResult::Ignored,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.popup.layout(size)
|
||||
}
|
||||
|
||||
fn important_area(&self, size: Vec2) -> Rect {
|
||||
self.popup.important_area(size)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue