diff --git a/src/main.rs b/src/main.rs index f77530d..50ff169 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,8 @@ mod ui; mod sources; mod matcher; +use roftl::{Roftl, Entry}; + fn main() -> Result<(), Box> { env_logger::init(); @@ -36,106 +38,160 @@ fn main() -> Result<(), Box> { debug!{"Window id: {:?}", window.id()} debug!{"Draw empty state to window"} - ui::draw_on_window(&window, "", vec![]); - - let mut input_buffer = String::new(); + ui::draw(&window, "", vec![], 0); debug!{"Start event loop"} - event_loop.run(move |evt, _win, flow| { - *flow = ControlFlow::Wait; - - match evt { - Event::WindowEvent{event: CloseRequested, window_id} - if window_id == window.id() => - { - *flow = ControlFlow::Exit; - } - - Event::WindowEvent{event: ReceivedCharacter(character), window_id} - if window_id == window.id() => - { - *flow = process_character(character, &mut input_buffer, &window); - } - - Event::WindowEvent{event: winit::event::WindowEvent::KeyboardInput{input, ..}, window_id} - if window_id == window.id() => - { - process_input(input, &mut input_buffer, &window); - } - - Event::RedrawRequested(window_id) - if window_id == window.id() => - { - trace!{"Redrawing with input {}", input_buffer} - - let result = roftl.narrow(&input_buffer); - trace!{"Narrow result {:?}", result} - - ui::draw_on_window(&window, &input_buffer, result); - } - - _ => () - } - }); + let roftl_loop = RoftlLoop::new(roftl, window); + roftl_loop.run(event_loop); } -fn process_input(input: KeyboardInput, input_buffer: &mut String, window: &Window) -> ControlFlow { - if let KeyboardInput { virtual_keycode: Some(code), state, .. } = input { - return match (code, state) { - (VirtualKeyCode::Down, ElementState::Released) => { - debug!("Received down"); - ControlFlow::Wait - }, - (VirtualKeyCode::Up, ElementState::Released) => { - debug!("Received up"); - ControlFlow::Wait - }, +struct RoftlLoop { + input_buffer: String, + input_changed: bool, + selection: usize, + roftl: Roftl, + window: Window, +} - _ => ControlFlow::Wait, - } +impl RoftlLoop { + fn new(roftl: Roftl, window: Window) -> Self + { + RoftlLoop{input_buffer: String::default(), input_changed: true, selection: 0, roftl, window} } - ControlFlow::Wait -} + fn run(mut self, event_loop: EventLoop) -> ! + { + event_loop.run(move |evt, _win, flow| { + *flow = ControlFlow::Wait; + match evt { + Event::WindowEvent{event: CloseRequested, window_id} + if window_id == self.window.id() => + { + *flow = ControlFlow::Exit; + } -fn process_character(character: char, input_buffer: &mut String, window: &Window) -> ControlFlow { - match character { - 'q' | 'Q' => ControlFlow::Exit, + Event::WindowEvent{event: ReceivedCharacter(character), window_id} + if window_id == self.window.id() => + { + *flow = self.process_character(character); + } - // Escape + Event::WindowEvent{event: winit::event::WindowEvent::KeyboardInput{input, ..}, window_id} + if window_id == self.window.id() => + { + self.process_input(input); + } + + Event::RedrawRequested(window_id) + if window_id == self.window.id() => + { + if self.input_changed { + trace!{"Redrawing with input {}", self.input_buffer} + + let results = self.roftl.narrow(&self.input_buffer); + trace!{"Narrow result {:?}", results} + + // correct selection for results + if results.len() > 0 { self.selection = self.selection % results.len() } + else { self.selection = 0 } + + ui::draw(&self.window, &self.input_buffer, results, self.selection); + } else { + trace!{"Quick redraw with input {}", self.input_buffer} + + let results = self.roftl.narrow(&self.input_buffer); + trace!{"Narrow result {:?}", results} + + // correct selection for results + if results.len() > 0 { self.selection = self.selection % results.len() } + else { self.selection = 0 } + + ui::redraw_quick(&self.window, results, self.selection); + } + + self.input_changed = false; + } + + _ => () + } + }); + + } + + fn process_input(&mut self, input: KeyboardInput) -> ControlFlow { + if let KeyboardInput { virtual_keycode: Some(code), state, .. } = input { + return match (code, state) { + (VirtualKeyCode::Down, ElementState::Released) => { + debug!("Received down"); + ControlFlow::Wait + }, + (VirtualKeyCode::Up, ElementState::Released) => { + debug!("Received up"); + ControlFlow::Wait + }, + + _ => ControlFlow::Wait, + } + } + + ControlFlow::Wait + } + + fn process_character(&mut self, character: char) -> ControlFlow { + match character { + 'q' | 'Q' => ControlFlow::Exit, + + // Escape c if c == char::from(0x1b) => ControlFlow::Exit, - // Backspace - c if c == char::from(0x08) => { - trace!{"Retrieved backspace. Pop char from input"} - input_buffer.pop(); - window.request_redraw(); - ControlFlow::Wait - } + // Backspace + c if c == char::from(0x08) => { + trace!{"Retrieved backspace. Pop char from input"} + self.input_buffer.pop(); + self.input_changed = true; + self.window.request_redraw(); + ControlFlow::Wait + } - // Ctrl+n - c if c == char::from(0x0e) => { - debug!{"Received Ctrl+next"} - ControlFlow::Wait - } + // Enter + c if c == char::from(0x0d) => { + trace!{"Retrieved enter. Trigger primary action"} + self.roftl.action(self.selection, roftl::Action::Primary); + ControlFlow::Exit + } - // Ctrl+p - c if c == char::from(0x10) => { - debug!{"Received Ctrl+previous"} - ControlFlow::Wait - } + // Ctrl+n or tab + c if c == char::from(0x0e) || c == char::from(0x09) => { + debug!{"Received next"} + self.selection += 1; + self.window.request_redraw(); + ControlFlow::Wait + } - c if c.is_control() => { - debug!{"Got unknown control character {:#x}", u32::from(c)} - ControlFlow::Wait - } + // Ctrl+p + c if c == char::from(0x10) => { + debug!{"Received previous"} + if self.selection != 0 { self.selection -= 1 } + self.window.request_redraw(); + ControlFlow::Wait + } - _ => { - trace!{"Push {} to input buffer", character} - input_buffer.push(character); - window.request_redraw(); - ControlFlow::Wait + c if c.is_control() => { + debug!{"Got unknown control character {:#x}", u32::from(c)} + ControlFlow::Wait + } + + _ => { + trace!{"Push {} to input buffer", character} + self.input_buffer.push(character); + self.input_changed = true; + self.window.request_redraw(); + ControlFlow::Wait + } } } + } + + diff --git a/src/roftl.rs b/src/roftl.rs index f49ee58..6d7d206 100644 --- a/src/roftl.rs +++ b/src/roftl.rs @@ -1,4 +1,4 @@ -use std::{any::type_name, collections::HashMap, sync::{Arc, Mutex, atomic::{AtomicUsize, Ordering}}}; +use std::{any::type_name, collections::HashMap, sync::{Arc, Mutex, atomic::{AtomicUsize, Ordering}}, usize}; use log::debug; use rayon::{iter::{IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator}, slice::ParallelSliceMut}; @@ -100,10 +100,10 @@ impl Roftl { .collect() } - pub fn action(&self, entry_id: i32, action: Action) { + pub fn action(&self, entry_id: usize, action: Action) { println!("NarrowMap {:?}", self.narrow_map.lock().unwrap()); - let real_id = *self.narrow_map.lock().unwrap().get(&((entry_id-1) as usize)).unwrap(); + let real_id = *self.narrow_map.lock().unwrap().get(&entry_id).unwrap(); let entry = self.entries.get(real_id).unwrap(); let source = self diff --git a/src/ui/mod.rs b/src/ui/mod.rs index c89ba6f..f9f3ae4 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,4 +1,5 @@ mod ui; -pub use ui::draw_on_window; +pub use ui::draw; +pub use ui::redraw_quick; mod painter; diff --git a/src/ui/ui.rs b/src/ui/ui.rs index 97ab9d3..07e1cd9 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -71,7 +71,7 @@ fn make_context(window: &Window) -> cairo::Context cairo::Context::new(&surface).expect("Could not create drawing context") } -pub fn draw_on_window(window: &Window, input: &str, result: Vec<&Entry>) +pub fn draw(window: &Window, input: &str, result: Vec<&Entry>, selection: usize) { let context = make_context(&window); @@ -85,7 +85,28 @@ pub fn draw_on_window(window: &Window, input: &str, result: Vec<&Entry>) result.iter().enumerate() .for_each(|(i, e)| { - painter.result_box(0, 1+(i as u32), &e.name, false); + painter.result_box(0, 1+(i as u32), &e.name, selection==i); }); } +pub fn redraw_quick(window: &Window, result: Vec<&Entry>, selection: usize) { + let context = make_context(&window); + + context.scale(30.0, 30.0); + + let painter = painter::Painter::new(&context); + + result.iter().enumerate() + .for_each(|(i, e)| { + // clear first and last as these may produce artifacts otherwise + if i == 0 { painter.result_box(0, 1+(i as u32), &e.name, false) } + if i == result.len()-1 { painter.result_box(0, 1+(i as u32), &e.name, false) } + + // clear boxes around selection as these could be the old selection + if selection > 0 && i == (selection-1) { painter.result_box(0, 1+(i as u32), &e.name, false) } + if i == (selection+1) { painter.result_box(0, 1+(i as u32), &e.name, false) } + + // mark selection, note that this negates any unmarking above in case they are the same + if i == selection { painter.result_box(0, 1+(i as u32), &e.name, true) } + }); +}