Selection, quick draw, primary action

This commit is contained in:
Armin Friedl 2021-10-17 19:23:50 +02:00
parent 8e38a59432
commit c6ed343acd
4 changed files with 168 additions and 90 deletions

View file

@ -13,6 +13,8 @@ mod ui;
mod sources; mod sources;
mod matcher; mod matcher;
use roftl::{Roftl, Entry};
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
env_logger::init(); env_logger::init();
@ -36,50 +38,88 @@ fn main() -> Result<(), Box<dyn Error>> {
debug!{"Window id: {:?}", window.id()} debug!{"Window id: {:?}", window.id()}
debug!{"Draw empty state to window"} debug!{"Draw empty state to window"}
ui::draw_on_window(&window, "", vec![]); ui::draw(&window, "", vec![], 0);
let mut input_buffer = String::new();
debug!{"Start event loop"} debug!{"Start event loop"}
let roftl_loop = RoftlLoop::new(roftl, window);
roftl_loop.run(event_loop);
}
struct RoftlLoop {
input_buffer: String,
input_changed: bool,
selection: usize,
roftl: Roftl,
window: Window,
}
impl RoftlLoop {
fn new(roftl: Roftl, window: Window) -> Self
{
RoftlLoop{input_buffer: String::default(), input_changed: true, selection: 0, roftl, window}
}
fn run<T>(mut self, event_loop: EventLoop<T>) -> !
{
event_loop.run(move |evt, _win, flow| { event_loop.run(move |evt, _win, flow| {
*flow = ControlFlow::Wait; *flow = ControlFlow::Wait;
match evt { match evt {
Event::WindowEvent{event: CloseRequested, window_id} Event::WindowEvent{event: CloseRequested, window_id}
if window_id == window.id() => if window_id == self.window.id() =>
{ {
*flow = ControlFlow::Exit; *flow = ControlFlow::Exit;
} }
Event::WindowEvent{event: ReceivedCharacter(character), window_id} Event::WindowEvent{event: ReceivedCharacter(character), window_id}
if window_id == window.id() => if window_id == self.window.id() =>
{ {
*flow = process_character(character, &mut input_buffer, &window); *flow = self.process_character(character);
} }
Event::WindowEvent{event: winit::event::WindowEvent::KeyboardInput{input, ..}, window_id} Event::WindowEvent{event: winit::event::WindowEvent::KeyboardInput{input, ..}, window_id}
if window_id == window.id() => if window_id == self.window.id() =>
{ {
process_input(input, &mut input_buffer, &window); self.process_input(input);
} }
Event::RedrawRequested(window_id) Event::RedrawRequested(window_id)
if window_id == window.id() => if window_id == self.window.id() =>
{ {
trace!{"Redrawing with input {}", input_buffer} if self.input_changed {
trace!{"Redrawing with input {}", self.input_buffer}
let result = roftl.narrow(&input_buffer); let results = self.roftl.narrow(&self.input_buffer);
trace!{"Narrow result {:?}", result} trace!{"Narrow result {:?}", results}
ui::draw_on_window(&window, &input_buffer, result); // 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(input: KeyboardInput, input_buffer: &mut String, window: &Window) -> ControlFlow { }
fn process_input(&mut self, input: KeyboardInput) -> ControlFlow {
if let KeyboardInput { virtual_keycode: Some(code), state, .. } = input { if let KeyboardInput { virtual_keycode: Some(code), state, .. } = input {
return match (code, state) { return match (code, state) {
(VirtualKeyCode::Down, ElementState::Released) => { (VirtualKeyCode::Down, ElementState::Released) => {
@ -96,10 +136,9 @@ fn process_input(input: KeyboardInput, input_buffer: &mut String, window: &Windo
} }
ControlFlow::Wait ControlFlow::Wait
} }
fn process_character(&mut self, character: char) -> ControlFlow {
fn process_character(character: char, input_buffer: &mut String, window: &Window) -> ControlFlow {
match character { match character {
'q' | 'Q' => ControlFlow::Exit, 'q' | 'Q' => ControlFlow::Exit,
@ -109,20 +148,32 @@ fn process_character(character: char, input_buffer: &mut String, window: &Window
// Backspace // Backspace
c if c == char::from(0x08) => { c if c == char::from(0x08) => {
trace!{"Retrieved backspace. Pop char from input"} trace!{"Retrieved backspace. Pop char from input"}
input_buffer.pop(); self.input_buffer.pop();
window.request_redraw(); self.input_changed = true;
self.window.request_redraw();
ControlFlow::Wait ControlFlow::Wait
} }
// Ctrl+n // Enter
c if c == char::from(0x0e) => { c if c == char::from(0x0d) => {
debug!{"Received Ctrl+next"} trace!{"Retrieved enter. Trigger primary action"}
self.roftl.action(self.selection, roftl::Action::Primary);
ControlFlow::Exit
}
// 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 ControlFlow::Wait
} }
// Ctrl+p // Ctrl+p
c if c == char::from(0x10) => { c if c == char::from(0x10) => {
debug!{"Received Ctrl+previous"} debug!{"Received previous"}
if self.selection != 0 { self.selection -= 1 }
self.window.request_redraw();
ControlFlow::Wait ControlFlow::Wait
} }
@ -133,9 +184,14 @@ fn process_character(character: char, input_buffer: &mut String, window: &Window
_ => { _ => {
trace!{"Push {} to input buffer", character} trace!{"Push {} to input buffer", character}
input_buffer.push(character); self.input_buffer.push(character);
window.request_redraw(); self.input_changed = true;
self.window.request_redraw();
ControlFlow::Wait ControlFlow::Wait
} }
} }
}
} }

View file

@ -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 log::debug;
use rayon::{iter::{IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator}, slice::ParallelSliceMut}; use rayon::{iter::{IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator}, slice::ParallelSliceMut};
@ -100,10 +100,10 @@ impl Roftl {
.collect() .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()); 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 entry = self.entries.get(real_id).unwrap();
let source = self let source = self

View file

@ -1,4 +1,5 @@
mod ui; mod ui;
pub use ui::draw_on_window; pub use ui::draw;
pub use ui::redraw_quick;
mod painter; mod painter;

View file

@ -71,7 +71,7 @@ fn make_context(window: &Window) -> cairo::Context
cairo::Context::new(&surface).expect("Could not create drawing 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); let context = make_context(&window);
@ -85,7 +85,28 @@ pub fn draw_on_window(window: &Window, input: &str, result: Vec<&Entry>)
result.iter().enumerate() result.iter().enumerate()
.for_each(|(i, e)| { .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) }
});
}