Quick switch and primary mode
This commit is contained in:
parent
bdb1520cf0
commit
9fc30022ec
6 changed files with 63 additions and 32 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -376,9 +376,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "flx-rs"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078541905b40af0ab9d964d367796f3b8eb009ba010c1864b9735f707188ff5b"
|
||||
checksum = "dd8bff3fe3d46781cd9a98a8dbb0174b87a0a88ca04e78c269c06ade46d5f289"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
|
|
|
@ -19,8 +19,7 @@ xcb-util = {version = "0.3.0", features = ["ewmh", "icccm"]}
|
|||
|
||||
fuzzy-matcher = "0.3.7"
|
||||
fuse-rust = "0.3.0"
|
||||
# Currently buggy, waiting for fix
|
||||
flx-rs = "0.1.1"
|
||||
flx-rs = "0.1.2"
|
||||
|
||||
walkdir = "2.3.2"
|
||||
freedesktop_entry_parser = "1.2.0"
|
||||
|
|
3
sh/htop.sh
Executable file
3
sh/htop.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
htop
|
50
src/main.rs
50
src/main.rs
|
@ -1,12 +1,14 @@
|
|||
use log::{debug, trace};
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode,
|
||||
WindowEvent::{CloseRequested, ReceivedCharacter}};
|
||||
|
||||
use crate::matcher::{FuseMatcher, SkimMatcher, FlxMatcher};
|
||||
use crate::matcher::{FuseMatcher, PrefixMatcher, SkimMatcher, FlxMatcher};
|
||||
|
||||
mod roftl;
|
||||
mod ui;
|
||||
|
@ -24,7 +26,9 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
.add_source(sources::Window::new())
|
||||
.add_source(sources::Apps::new())
|
||||
.add_source(sources::ShellHost::new())
|
||||
.with_matcher(FuseMatcher::new());
|
||||
.with_matcher(FuseMatcher::new())
|
||||
.with_primary_matcher(PrefixMatcher::new());
|
||||
|
||||
|
||||
debug!{"Source roftl sources"}
|
||||
roftl.source();
|
||||
|
@ -49,7 +53,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
enum Mode {
|
||||
Selection,
|
||||
Actions
|
||||
Actions,
|
||||
}
|
||||
|
||||
struct RoftlLoop {
|
||||
|
@ -105,27 +109,25 @@ impl RoftlLoop {
|
|||
if window_id == self.window.id()
|
||||
&& matches!{self.mode, Mode::Selection} =>
|
||||
{
|
||||
let results = self.roftl.narrow(&self.input_buffer);
|
||||
trace!{"Narrow result {:?}", results}
|
||||
|
||||
// quick switch if only one result
|
||||
if results.len() == 1 {
|
||||
self.roftl.do_action(0, roftl::Action::Primary);
|
||||
*flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
|
||||
// correct selection for results
|
||||
if results.len() > 0 { self.selection = self.selection % results.len() }
|
||||
else { self.selection = 0 }
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -185,7 +187,7 @@ impl RoftlLoop {
|
|||
// Enter
|
||||
c if c == char::from(0x0d) => {
|
||||
trace!{"Retrieved enter. Trigger primary action"}
|
||||
self.roftl.action(self.selection, roftl::Action::Primary);
|
||||
self.roftl.do_action(self.selection, roftl::Action::Primary);
|
||||
ControlFlow::Exit
|
||||
}
|
||||
|
||||
|
@ -235,22 +237,22 @@ impl RoftlLoop {
|
|||
match character {
|
||||
'1' => {
|
||||
trace!{"Retrieved action selection 1. Trigger primary action"}
|
||||
self.roftl.action(self.selection, roftl::Action::Primary);
|
||||
self.roftl.do_action(self.selection, roftl::Action::Primary);
|
||||
ControlFlow::Exit
|
||||
},
|
||||
'2' => {
|
||||
trace!{"Retrieved action selection 2. Trigger secondary action"}
|
||||
self.roftl.action(self.selection, roftl::Action::Secondary);
|
||||
self.roftl.do_action(self.selection, roftl::Action::Secondary);
|
||||
ControlFlow::Exit
|
||||
},
|
||||
'3' => {
|
||||
trace!{"Retrieved action selection 3. Trigger tertiary action"}
|
||||
self.roftl.action(self.selection, roftl::Action::Tertiary);
|
||||
self.roftl.do_action(self.selection, roftl::Action::Tertiary);
|
||||
ControlFlow::Exit
|
||||
},
|
||||
'4' => {
|
||||
trace!{"Retrieved action selection 4. Trigger quaternary action"}
|
||||
self.roftl.action(self.selection, roftl::Action::Quaternary);
|
||||
self.roftl.do_action(self.selection, roftl::Action::Quaternary);
|
||||
ControlFlow::Exit
|
||||
},
|
||||
_ => {
|
||||
|
|
|
@ -12,7 +12,7 @@ impl Matcher for PrefixMatcher {
|
|||
fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec<usize>)> {
|
||||
let mut indices = vec![];
|
||||
|
||||
if haystack.starts_with(needle) {
|
||||
if haystack.to_lowercase().starts_with(&needle.to_lowercase()) {
|
||||
(0..needle.len()).for_each(|i| indices.push(i));
|
||||
return Option::Some((1.0, indices));
|
||||
}
|
||||
|
|
33
src/roftl.rs
33
src/roftl.rs
|
@ -26,7 +26,7 @@ pub trait Source: Send + Sync {
|
|||
fn name(&self) -> &'static str;
|
||||
fn entries(&mut self) -> Vec<Entry>;
|
||||
fn action(&self, entry: &Entry, action: Action);
|
||||
fn actions(&self, entry: &Entry) -> Vec<String>
|
||||
fn actions(&self, _entry: &Entry) -> Vec<String>
|
||||
{ vec![] }
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,11 @@ pub trait Matcher: Send + Sync {
|
|||
pub struct Roftl {
|
||||
sources: Vec<SourceRef>,
|
||||
matcher: MatcherRef,
|
||||
// the standard matcher can be inadequate for primary mode. E.g. a
|
||||
// FuseMatcher would yield hard to guess and sometimes surprising results,
|
||||
// whereas a PrefixMatcher is more deterministic. This is relevant for quick
|
||||
// switch where surprises run counter to muscle memory and are annoying.
|
||||
primary_matcher: MatcherRef,
|
||||
entries: Vec<Entry>,
|
||||
narrow_map: Vec<usize>
|
||||
}
|
||||
|
@ -46,6 +51,7 @@ impl Roftl {
|
|||
Roftl {
|
||||
sources: vec![],
|
||||
matcher: PrefixMatcher::new(),
|
||||
primary_matcher: PrefixMatcher::new(),
|
||||
entries: vec![],
|
||||
narrow_map: vec![]
|
||||
}
|
||||
|
@ -65,6 +71,11 @@ impl Roftl {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_primary_matcher(mut self, matcher: MatcherRef) -> Self {
|
||||
self.primary_matcher = matcher;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn source(&mut self) {
|
||||
self.entries = self.sources
|
||||
.par_iter_mut()
|
||||
|
@ -79,13 +90,29 @@ impl Roftl {
|
|||
.par_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, entry)| {
|
||||
if entry.source != "window" { return None }
|
||||
|
||||
if input.is_empty() { return Some((0.0, idx, entry, vec![])) }
|
||||
|
||||
let match_result = self.matcher.try_match(&entry.name, input);
|
||||
let match_result = self.primary_matcher.try_match(&entry.name, input);
|
||||
return match_result.map(|(score, indices)| (score, idx, entry, indices) )
|
||||
})
|
||||
.collect();
|
||||
|
||||
if scored_entries.is_empty() || input.starts_with(",") {
|
||||
let input = input.strip_prefix(",").unwrap_or(input);
|
||||
scored_entries = self.entries
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, entry)| {
|
||||
if input.is_empty() { return Some((0.0, idx, entry, vec![])) }
|
||||
|
||||
let match_result = self.matcher.try_match(&entry.name, input);
|
||||
return match_result.map(|(score, indices)| (score, idx, entry, indices) )
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
scored_entries
|
||||
.par_sort_by(|e1, e2| e1.0.partial_cmp(&e2.0).unwrap());
|
||||
|
||||
|
@ -100,7 +127,7 @@ impl Roftl {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn action(&self, selection_id: usize, action: Action) {
|
||||
pub fn do_action(&self, selection_id: usize, action: Action) {
|
||||
let (entry, source) = self.find_selection(selection_id);
|
||||
source.action(entry, action);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue