mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-04-16 17:09:56 -04:00
150 lines
4.5 KiB
Rust
150 lines
4.5 KiB
Rust
// This file is part of Moonfire NVR, a security camera network video recorder.
|
|
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
|
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
|
|
|
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, With,
|
|
};
|
|
|
|
type TabCompleteFn = Rc<dyn Fn(&str) -> Vec<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) -> Vec<String> + 'static) -> Self {
|
|
self.tab_completer = Some(Rc::new(handler));
|
|
self
|
|
}
|
|
|
|
pub fn get_content(&self) -> Rc<String> {
|
|
self.edit_view.borrow_mut().get_content()
|
|
}
|
|
}
|
|
|
|
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({
|
|
menu::Tree::new().with(|tree| {
|
|
for completion in completions {
|
|
let edit_view = edit_view.clone();
|
|
tree.add_leaf(&completion.clone(), move |siv| {
|
|
edit_view.borrow_mut().set_content(&completion)(siv)
|
|
})
|
|
}
|
|
})
|
|
})),
|
|
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)
|
|
}
|
|
}
|