use crate::{
Injector, Render,
event::{MatchListEvent, PromptEvent},
match_list::MatchList,
prompt::{Prompt, PromptStatus},
util::as_u32,
};
pub struct LazyMatchList<'a, T: Send + Sync + 'static, R: Render<T>, Q> {
match_list: &'a mut MatchList<T, R>,
queued: &'a mut Q,
buffered_selection: u32,
toggled: bool,
}
impl<'a, T: Send + Sync + 'static, R: Render<T>, Q: crate::Queued> LazyMatchList<'a, T, R, Q> {
pub fn new(match_list: &'a mut MatchList<T, R>, queued: &'a mut Q) -> Self {
let buffered_selection = match_list.selection();
Self {
match_list,
buffered_selection,
queued,
toggled: false,
}
}
pub fn restart(&mut self) -> Injector<T, R> {
self.match_list.restart();
self.buffered_selection = 0;
self.match_list.injector()
}
pub fn is_empty(&self) -> bool {
self.match_list.is_empty()
}
pub fn has_queued_items(&self) -> bool {
!self.queued.is_empty()
}
pub fn selection(&self) -> Option<u32> {
if self.is_empty() {
None
} else {
Some(self.buffered_selection)
}
}
pub fn toggle_selection(&mut self) -> bool {
if !self.is_empty()
&& self
.match_list
.toggle_queued_item(self.queued, self.buffered_selection)
{
self.toggled = true;
true
} else {
false
}
}
fn decr(&mut self, n: usize) {
self.buffered_selection = self.buffered_selection.saturating_sub(as_u32(n));
}
fn incr(&mut self, n: usize) {
self.buffered_selection = self
.buffered_selection
.saturating_add(as_u32(n))
.min(self.match_list.max_selection());
}
#[inline]
pub fn handle(&mut self, event: MatchListEvent) {
match event {
MatchListEvent::Up(n) => {
if self.match_list.reversed() {
self.decr(n);
} else {
self.incr(n);
}
}
MatchListEvent::ToggleUp(n) => {
if self.toggle_selection() {
if self.match_list.reversed() {
self.decr(n);
} else {
self.incr(n);
}
}
}
MatchListEvent::Down(n) => {
if self.match_list.reversed() {
self.incr(n);
} else {
self.decr(n);
}
}
MatchListEvent::DeselectAll => {
if self.queued.clear() {
self.toggled = true;
}
}
MatchListEvent::ToggleDown(n) => {
if self.toggle_selection() {
if self.match_list.reversed() {
self.incr(n);
} else {
self.decr(n);
}
}
}
MatchListEvent::Reset => {
self.buffered_selection = 0;
}
}
}
pub fn finish(self) -> bool {
self.match_list.set_selection(self.buffered_selection) || self.toggled
}
}
pub struct LazyPrompt<'a> {
prompt: &'a mut Prompt,
buffered_event: Option<PromptEvent>,
status: PromptStatus,
}
impl<'a> LazyPrompt<'a> {
pub fn is_empty(&self) -> bool {
self.prompt.is_empty()
}
pub fn new(prompt: &'a mut Prompt) -> Self {
Self {
prompt,
buffered_event: None,
status: PromptStatus::default(),
}
}
fn swap_and_process_buffer(&mut self, mut event: PromptEvent) {
std::mem::swap(
unsafe { self.buffered_event.as_mut().unwrap_unchecked() },
&mut event,
);
self.status |= self.prompt.handle(event);
}
pub fn finish(mut self) -> PromptStatus {
if let Some(event) = self.buffered_event {
self.status |= self.prompt.handle(event);
}
self.status
}
pub fn handle(&mut self, mut event: PromptEvent) {
match self.buffered_event {
None => {
self.buffered_event = Some(event);
}
Some(ref mut buffered) => match event {
PromptEvent::Left(ref mut n1) => {
if let PromptEvent::Left(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::WordLeft(ref mut n1) => {
if let PromptEvent::WordLeft(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::Right(ref mut n1) => {
if let PromptEvent::Right(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::WordRight(ref mut n1) => {
if let PromptEvent::WordRight(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::ToStart => {
if buffered.is_cursor_movement() {
*buffered = PromptEvent::ToStart;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::ToEnd => {
if buffered.is_cursor_movement() {
*buffered = PromptEvent::ToEnd;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::Backspace(ref mut n1) => {
if let PromptEvent::Backspace(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::Delete(ref mut n1) => {
if let PromptEvent::Delete(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::BackspaceWord(ref mut n1) => {
if let PromptEvent::BackspaceWord(n2) = buffered {
*n1 += *n2;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::ClearBefore => {
if matches!(
buffered,
PromptEvent::Backspace(_)
| PromptEvent::ClearBefore
| PromptEvent::BackspaceWord(_)
) {
*buffered = PromptEvent::ClearBefore;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::ClearAfter => {
if matches!(buffered, PromptEvent::Delete(_) | PromptEvent::ClearAfter) {
*buffered = PromptEvent::ClearAfter;
} else {
self.swap_and_process_buffer(event);
}
}
PromptEvent::Insert(ch1) => match buffered {
PromptEvent::Insert(ch2) => {
let mut s = ch1.to_string();
s.push(*ch2);
*buffered = PromptEvent::Paste(s);
}
PromptEvent::Paste(new) => {
let mut s = ch1.to_string();
s.push_str(new);
*buffered = PromptEvent::Paste(s);
}
_ => {
self.swap_and_process_buffer(event);
}
},
PromptEvent::Paste(ref mut s) => match buffered {
PromptEvent::Insert(ch2) => {
s.push(*ch2);
}
PromptEvent::Paste(new) => {
s.push_str(new);
}
_ => {
self.swap_and_process_buffer(event);
}
},
PromptEvent::Reset(_) => {
*buffered = event;
}
},
};
}
}