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,106 +38,160 @@ 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"}
event_loop.run(move |evt, _win, flow| { let roftl_loop = RoftlLoop::new(roftl, window);
*flow = ControlFlow::Wait; roftl_loop.run(event_loop);
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);
}
_ => ()
}
});
} }
fn process_input(input: KeyboardInput, input_buffer: &mut String, window: &Window) -> ControlFlow { struct RoftlLoop {
if let KeyboardInput { virtual_keycode: Some(code), state, .. } = input { input_buffer: String,
return match (code, state) { input_changed: bool,
(VirtualKeyCode::Down, ElementState::Released) => { selection: usize,
debug!("Received down"); roftl: Roftl,
ControlFlow::Wait window: Window,
}, }
(VirtualKeyCode::Up, ElementState::Released) => {
debug!("Received up");
ControlFlow::Wait
},
_ => 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<T>(mut self, event_loop: EventLoop<T>) -> !
} {
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 { Event::WindowEvent{event: ReceivedCharacter(character), window_id}
match character { if window_id == self.window.id() =>
'q' | 'Q' => ControlFlow::Exit, {
*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, c if c == char::from(0x1b) => ControlFlow::Exit,
// 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;
ControlFlow::Wait self.window.request_redraw();
} 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"}
ControlFlow::Wait self.roftl.action(self.selection, roftl::Action::Primary);
} ControlFlow::Exit
}
// Ctrl+p // Ctrl+n or tab
c if c == char::from(0x10) => { c if c == char::from(0x0e) || c == char::from(0x09) => {
debug!{"Received Ctrl+previous"} debug!{"Received next"}
ControlFlow::Wait self.selection += 1;
} self.window.request_redraw();
ControlFlow::Wait
}
c if c.is_control() => { // Ctrl+p
debug!{"Got unknown control character {:#x}", u32::from(c)} c if c == char::from(0x10) => {
ControlFlow::Wait debug!{"Received previous"}
} if self.selection != 0 { self.selection -= 1 }
self.window.request_redraw();
ControlFlow::Wait
}
_ => { c if c.is_control() => {
trace!{"Push {} to input buffer", character} debug!{"Got unknown control character {:#x}", u32::from(c)}
input_buffer.push(character); ControlFlow::Wait
window.request_redraw(); }
ControlFlow::Wait
_ => {
trace!{"Push {} to input buffer", character}
self.input_buffer.push(character);
self.input_changed = true;
self.window.request_redraw();
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) }
});
}