Run cargo format
This commit is contained in:
parent
3b7bff5bd7
commit
a34a6a2ed4
20 changed files with 625 additions and 392 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Rofi Then Light
|
# Rofi Then Light
|
||||||
|
|
||||||
> In the beginning there was Rofi. Rofi was Fast. And Fast was Rofi.
|
> In the beginning there was Rofi. Rofi was good.
|
||||||
>
|
>
|
||||||
> Then Light.
|
> Then Light.
|
||||||
|
|
||||||
|
|
15
default.toml
15
default.toml
|
@ -6,9 +6,22 @@ log_level = "trace"
|
||||||
"t" = "terminal"
|
"t" = "terminal"
|
||||||
|
|
||||||
[sources.primary]
|
[sources.primary]
|
||||||
matcher = "Prefix"
|
matcher = "Flex"
|
||||||
source = "Windows"
|
source = "Windows"
|
||||||
|
|
||||||
[sources.additional]
|
[sources.additional]
|
||||||
matcher = "Flex"
|
matcher = "Flex"
|
||||||
source = ["Apps", "Shell", "Test"]
|
source = ["Apps", "Shell", "Test"]
|
||||||
|
|
||||||
|
[theme]
|
||||||
|
font = ["Sans Regular", 13]
|
||||||
|
border = 2
|
||||||
|
divider = 3
|
||||||
|
|
||||||
|
[theme.color_scheme]
|
||||||
|
base = [0.13, 0.05, 0.23, 0.9]
|
||||||
|
border = [0.23, 0.05, 0.11, 1.0]
|
||||||
|
highlight = [0.12, 0.04, 0.08, 0.9]
|
||||||
|
divider = [0.23, 0.05, 0.11, 1.0]
|
||||||
|
text = [0.87, 0.95, 0.77, 1.0]
|
||||||
|
text_highlight = [0.6, 0.8, 0.4, 1.0]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod shared;
|
|
||||||
pub mod roftl;
|
pub mod roftl;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
pub mod shared;
|
||||||
|
|
||||||
use super::matcher;
|
use super::matcher;
|
||||||
use super::sources;
|
use super::sources;
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::RwLock;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use super::matcher::PrefixMatcher;
|
use super::matcher::PrefixMatcher;
|
||||||
|
|
||||||
|
use super::shared::Entry;
|
||||||
use super::shared::Matcher;
|
use super::shared::Matcher;
|
||||||
use super::shared::Source;
|
use super::shared::Source;
|
||||||
use super::shared::Entry;
|
|
||||||
|
|
||||||
|
|
||||||
pub type SourceRef = Box<dyn Source>;
|
pub type SourceRef = Box<dyn Source>;
|
||||||
pub type MatcherRef = Box<dyn Matcher>;
|
pub type MatcherRef = Box<dyn Matcher>;
|
||||||
|
@ -26,7 +25,7 @@ pub struct Roftl {
|
||||||
primary_matcher: MatcherRef,
|
primary_matcher: MatcherRef,
|
||||||
entries: Vec<Entry>,
|
entries: Vec<Entry>,
|
||||||
narrow_map: Vec<usize>,
|
narrow_map: Vec<usize>,
|
||||||
completions: HashMap<String, String>
|
completions: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Roftl {
|
impl Roftl {
|
||||||
|
@ -39,18 +38,23 @@ impl Roftl {
|
||||||
primary_matcher: PrefixMatcher::new(),
|
primary_matcher: PrefixMatcher::new(),
|
||||||
entries: vec![],
|
entries: vec![],
|
||||||
narrow_map: vec![],
|
narrow_map: vec![],
|
||||||
completions: HashMap::default()
|
completions: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_primary_source(mut self, source: SourceRef) -> Self {
|
pub fn with_primary_source(mut self, source: SourceRef) -> Self {
|
||||||
self.primary_source = Some(source);
|
self.primary_source = Some(source);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_source(self, source: SourceRef) -> Self {
|
pub fn add_source(self, source: SourceRef) -> Self {
|
||||||
if self.sources.read().unwrap().par_iter().any(|s| s.name().eq(source.name())) {
|
if self
|
||||||
|
.sources
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.par_iter()
|
||||||
|
.any(|s| s.name().eq(source.name()))
|
||||||
|
{
|
||||||
panic! {"Source with name '{}' already exists", source.name()}
|
panic! {"Source with name '{}' already exists", source.name()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +83,8 @@ impl Roftl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_primary(&mut self) {
|
fn source_primary(&mut self) {
|
||||||
self.entries = self.primary_source
|
self.entries = self
|
||||||
|
.primary_source
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.flat_map(|s| s.entries())
|
.flat_map(|s| s.entries())
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -93,12 +98,18 @@ impl Roftl {
|
||||||
|
|
||||||
let sources = self.sources.clone();
|
let sources = self.sources.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let entries: Vec<Entry> = sources.write().unwrap()
|
let entries: Vec<Entry> = sources
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.flat_map(|s| s.entries())
|
.flat_map(|s| s.entries())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
debug!("Sourced {} additional entries from {} sources", entries.len(), sources.read().unwrap().len());
|
debug!(
|
||||||
|
"Sourced {} additional entries from {} sources",
|
||||||
|
entries.len(),
|
||||||
|
sources.read().unwrap().len()
|
||||||
|
);
|
||||||
tx.send(entries).unwrap();
|
tx.send(entries).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -118,20 +129,26 @@ impl Roftl {
|
||||||
// and not self is captured by the filter_map lambda
|
// and not self is captured by the filter_map lambda
|
||||||
let completions = &self.completions;
|
let completions = &self.completions;
|
||||||
let primary_matcher = &self.primary_matcher;
|
let primary_matcher = &self.primary_matcher;
|
||||||
let mut scored_entries: Vec<(f64, usize, &Entry, Vec<usize>)> = self.entries
|
let mut scored_entries: Vec<(f64, usize, &Entry, Vec<usize>)> = self
|
||||||
|
.entries
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(idx, entry)| {
|
.filter_map(|(idx, entry)| {
|
||||||
if entry.source != "window" { return None }
|
if entry.source != "window" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if input.is_empty() { return Some((0.0, idx, entry, vec![])) }
|
if input.is_empty() {
|
||||||
|
return Some((0.0, idx, entry, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
let input = completions.get(input)
|
let input = completions
|
||||||
|
.get(input)
|
||||||
.map(|s| s.as_str()) // &String -> &str
|
.map(|s| s.as_str()) // &String -> &str
|
||||||
.unwrap_or(input);
|
.unwrap_or(input);
|
||||||
|
|
||||||
let match_result = primary_matcher.try_match(&entry.name, input);
|
let match_result = primary_matcher.try_match(&entry.name, input);
|
||||||
return match_result.map(|(score, indices)| (score, idx, entry, indices) )
|
return match_result.map(|(score, indices)| (score, idx, entry, indices));
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -141,20 +158,22 @@ impl Roftl {
|
||||||
let input = input.strip_prefix(",").unwrap_or(input);
|
let input = input.strip_prefix(",").unwrap_or(input);
|
||||||
let matcher = &self.matcher;
|
let matcher = &self.matcher;
|
||||||
|
|
||||||
scored_entries = self.entries
|
scored_entries = self
|
||||||
|
.entries
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(idx, entry)| {
|
.filter_map(|(idx, entry)| {
|
||||||
if input.is_empty() { return Some((0.0, idx, entry, vec![])) }
|
if input.is_empty() {
|
||||||
|
return Some((0.0, idx, entry, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
let match_result = matcher.try_match(&entry.name, input);
|
let match_result = matcher.try_match(&entry.name, input);
|
||||||
return match_result.map(|(score, indices)| (score, idx, entry, indices) )
|
return match_result.map(|(score, indices)| (score, idx, entry, indices));
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
scored_entries
|
scored_entries.par_sort_by(|e1, e2| e1.0.partial_cmp(&e2.0).unwrap());
|
||||||
.par_sort_by(|e1, e2| e1.0.partial_cmp(&e2.0).unwrap());
|
|
||||||
|
|
||||||
self.narrow_map.clear();
|
self.narrow_map.clear();
|
||||||
for se in &scored_entries {
|
for se in &scored_entries {
|
||||||
|
@ -171,7 +190,13 @@ impl Roftl {
|
||||||
let (entry, source) = self.find_selection(selection_id);
|
let (entry, source) = self.find_selection(selection_id);
|
||||||
match source {
|
match source {
|
||||||
usize::MAX => self.primary_source.as_ref().unwrap().actions(entry),
|
usize::MAX => self.primary_source.as_ref().unwrap().actions(entry),
|
||||||
_ => self.sources.read().unwrap().get(source).unwrap().actions(entry)
|
_ => self
|
||||||
|
.sources
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(source)
|
||||||
|
.unwrap()
|
||||||
|
.actions(entry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,20 +204,35 @@ impl Roftl {
|
||||||
let (entry, source) = self.find_selection(selection_id);
|
let (entry, source) = self.find_selection(selection_id);
|
||||||
match source {
|
match source {
|
||||||
usize::MAX => self.primary_source.as_ref().unwrap().exec_default(entry),
|
usize::MAX => self.primary_source.as_ref().unwrap().exec_default(entry),
|
||||||
_ => self.sources.read().unwrap().get(source).unwrap().exec_default(entry)
|
_ => self
|
||||||
|
.sources
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(source)
|
||||||
|
.unwrap()
|
||||||
|
.exec_default(entry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_action(&self, selection_id: usize, action: u8) {
|
pub fn exec_action(&self, selection_id: usize, action: u8) {
|
||||||
let (entry, source) = self.find_selection(selection_id);
|
let (entry, source) = self.find_selection(selection_id);
|
||||||
match source {
|
match source {
|
||||||
usize::MAX => self.primary_source.as_ref().unwrap().exec_action(entry, action),
|
usize::MAX => self
|
||||||
_ => self.sources.read().unwrap().get(source).unwrap().exec_action(entry, action)
|
.primary_source
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.exec_action(entry, action),
|
||||||
|
_ => self
|
||||||
|
.sources
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(source)
|
||||||
|
.unwrap()
|
||||||
|
.exec_action(entry, action),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_selection(&self, selection_id: usize) -> (&Entry, usize)
|
fn find_selection(&self, selection_id: usize) -> (&Entry, usize) {
|
||||||
{
|
|
||||||
let entry_id = self.narrow_map[selection_id];
|
let entry_id = self.narrow_map[selection_id];
|
||||||
let entry = &self.entries[entry_id];
|
let entry = &self.entries[entry_id];
|
||||||
debug! {"Got entry {:?} for id {} ", entry, entry_id};
|
debug! {"Got entry {:?} for id {} ", entry, entry_id};
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use config::ConfigError;
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
use config::ConfigError;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Color(f64, f64, f64, f64);
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ColorScheme {
|
||||||
|
base: Color,
|
||||||
|
border: Color,
|
||||||
|
highlight: Color,
|
||||||
|
|
||||||
|
divider: Color,
|
||||||
|
|
||||||
|
text: Color,
|
||||||
|
text_highlight: Color,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
|
color_scheme: ColorScheme,
|
||||||
|
font: (String, i32),
|
||||||
border: f64,
|
border: f64,
|
||||||
divider: f64
|
divider: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub enum Matcher {
|
pub enum Matcher {
|
||||||
Prefix, Fuse, Flex, Skim
|
Prefix,
|
||||||
|
Fuse,
|
||||||
|
Flex,
|
||||||
|
Skim,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Matcher {
|
impl Matcher {
|
||||||
|
@ -20,14 +40,17 @@ impl Matcher {
|
||||||
Matcher::Prefix => super::matcher::PrefixMatcher::new(),
|
Matcher::Prefix => super::matcher::PrefixMatcher::new(),
|
||||||
Matcher::Fuse => super::matcher::FuseMatcher::new(),
|
Matcher::Fuse => super::matcher::FuseMatcher::new(),
|
||||||
Matcher::Flex => super::matcher::FlxMatcher::new(),
|
Matcher::Flex => super::matcher::FlxMatcher::new(),
|
||||||
Matcher::Skim => super::matcher::SkimMatcher::new()
|
Matcher::Skim => super::matcher::SkimMatcher::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
Apps, Shell, Windows, Test
|
Apps,
|
||||||
|
Shell,
|
||||||
|
Windows,
|
||||||
|
Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
|
@ -36,7 +59,7 @@ impl Source {
|
||||||
Source::Apps => super::sources::Apps::new(),
|
Source::Apps => super::sources::Apps::new(),
|
||||||
Source::Shell => super::sources::ShellHost::new(),
|
Source::Shell => super::sources::ShellHost::new(),
|
||||||
Source::Windows => super::sources::Window::new(),
|
Source::Windows => super::sources::Window::new(),
|
||||||
Source::Test => super::sources::TestSource::new("ts1")
|
Source::Test => super::sources::TestSource::new("ts1"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +67,7 @@ impl Source {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct SourceConfig<T> {
|
pub struct SourceConfig<T> {
|
||||||
matcher: Matcher,
|
matcher: Matcher,
|
||||||
source: T
|
source: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -56,16 +79,20 @@ pub struct Sources {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase", remote = "log::Level")]
|
#[serde(rename_all = "lowercase", remote = "log::Level")]
|
||||||
enum LogLevelDef {
|
enum LogLevelDef {
|
||||||
Error, Warn, Info, Debug, Trace,
|
Error,
|
||||||
|
Warn,
|
||||||
|
Info,
|
||||||
|
Debug,
|
||||||
|
Trace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
#[serde(with = "LogLevelDef")]
|
#[serde(with = "LogLevelDef")]
|
||||||
log_level: log::Level,
|
log_level: log::Level,
|
||||||
completions: HashMap<String, String>,
|
completions: HashMap<String, String>,
|
||||||
sources: Sources,
|
sources: Sources,
|
||||||
|
theme: Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
@ -74,10 +101,12 @@ impl Settings {
|
||||||
|
|
||||||
config.merge(config::File::with_name("default"))?;
|
config.merge(config::File::with_name("default"))?;
|
||||||
|
|
||||||
config.merge(config::Environment::new()
|
config.merge(
|
||||||
|
config::Environment::new()
|
||||||
.prefix("roftl")
|
.prefix("roftl")
|
||||||
.separator("_")
|
.separator("_")
|
||||||
.ignore_empty(true))?;
|
.ignore_empty(true),
|
||||||
|
)?;
|
||||||
|
|
||||||
config.try_into()
|
config.try_into()
|
||||||
}
|
}
|
||||||
|
@ -95,7 +124,9 @@ impl Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sources(&self) -> Vec<Box<dyn super::shared::Source>> {
|
pub fn sources(&self) -> Vec<Box<dyn super::shared::Source>> {
|
||||||
self.sources.additional.source
|
self.sources
|
||||||
|
.additional
|
||||||
|
.source
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.init())
|
.map(|s| s.init())
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -9,11 +9,13 @@ pub struct Entry {
|
||||||
pub trait Source: Send + Sync {
|
pub trait Source: Send + Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
fn entries(&mut self) -> Vec<Entry>;
|
fn entries(&mut self) -> Vec<Entry>;
|
||||||
fn actions(&self, _entry: &Entry) -> Vec<String>
|
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
||||||
{ vec![] }
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
fn exec_default(&self, entry: &Entry)
|
fn exec_default(&self, entry: &Entry) {
|
||||||
{ self.exec_action(entry, 0) }
|
self.exec_action(entry, 0)
|
||||||
|
}
|
||||||
|
|
||||||
fn exec_action(&self, entry: &Entry, action: u8);
|
fn exec_action(&self, entry: &Entry, action: u8);
|
||||||
}
|
}
|
||||||
|
|
125
src/main.rs
125
src/main.rs
|
@ -1,17 +1,19 @@
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use winit::window::{Window, WindowBuilder};
|
use winit::event::{
|
||||||
|
ElementState, Event, KeyboardInput, VirtualKeyCode,
|
||||||
|
WindowEvent::{CloseRequested, ReceivedCharacter},
|
||||||
|
};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode,
|
use winit::window::{Window, WindowBuilder};
|
||||||
WindowEvent::{CloseRequested, ReceivedCharacter}};
|
|
||||||
|
|
||||||
use self::core::roftl::Roftl;
|
use self::core::roftl::Roftl;
|
||||||
|
|
||||||
mod ui;
|
|
||||||
mod sources;
|
|
||||||
mod matcher;
|
|
||||||
mod core;
|
mod core;
|
||||||
|
mod matcher;
|
||||||
|
mod sources;
|
||||||
|
mod ui;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
@ -66,48 +68,52 @@ struct RoftlLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RoftlLoop {
|
impl RoftlLoop {
|
||||||
fn new(roftl: Roftl, window: Window) -> Self
|
fn new(roftl: Roftl, window: Window) -> Self {
|
||||||
{
|
RoftlLoop {
|
||||||
RoftlLoop{input_buffer: String::default(), input_changed: true, selection: 0,
|
input_buffer: String::default(),
|
||||||
|
input_changed: true,
|
||||||
|
selection: 0,
|
||||||
mode: Mode::Selection,
|
mode: Mode::Selection,
|
||||||
roftl, window}
|
roftl,
|
||||||
|
window,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<T>(mut self, event_loop: EventLoop<T>) -> !
|
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 {
|
||||||
if window_id == self.window.id() =>
|
event: CloseRequested,
|
||||||
{
|
window_id,
|
||||||
|
} if window_id == self.window.id() => {
|
||||||
*flow = ControlFlow::Exit;
|
*flow = ControlFlow::Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::WindowEvent{event: ReceivedCharacter(character), window_id}
|
Event::WindowEvent {
|
||||||
if window_id == self.window.id()
|
event: ReceivedCharacter(character),
|
||||||
&& matches!(self.mode, Mode::Selection) =>
|
window_id,
|
||||||
{
|
} if window_id == self.window.id() && matches!(self.mode, Mode::Selection) => {
|
||||||
*flow = self.process_character(character);
|
*flow = self.process_character(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::WindowEvent{event: ReceivedCharacter(character), window_id}
|
Event::WindowEvent {
|
||||||
if window_id == self.window.id()
|
event: ReceivedCharacter(character),
|
||||||
&& matches!(self.mode, Mode::Actions) =>
|
window_id,
|
||||||
{
|
} if window_id == self.window.id() && matches!(self.mode, Mode::Actions) => {
|
||||||
*flow = self.process_action(character);
|
*flow = self.process_action(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::WindowEvent{event: winit::event::WindowEvent::KeyboardInput{input, ..}, window_id}
|
Event::WindowEvent {
|
||||||
if window_id == self.window.id() =>
|
event: winit::event::WindowEvent::KeyboardInput { input, .. },
|
||||||
{
|
window_id,
|
||||||
|
} if window_id == self.window.id() => {
|
||||||
self.process_input(input);
|
self.process_input(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::RedrawRequested(window_id)
|
Event::RedrawRequested(window_id)
|
||||||
if window_id == self.window.id()
|
if window_id == self.window.id() && matches! {self.mode, Mode::Selection} =>
|
||||||
&& matches!{self.mode, Mode::Selection} =>
|
|
||||||
{
|
{
|
||||||
let results = self.roftl.narrow(&self.input_buffer);
|
let results = self.roftl.narrow(&self.input_buffer);
|
||||||
trace! {"Narrow result {:?}", results}
|
trace! {"Narrow result {:?}", results}
|
||||||
|
@ -122,8 +128,11 @@ impl RoftlLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
// correct selection for results
|
// correct selection for results
|
||||||
if results.len() > 0 { self.selection = self.selection % results.len() }
|
if results.len() > 0 {
|
||||||
else { self.selection = 0 }
|
self.selection = self.selection % results.len()
|
||||||
|
} else {
|
||||||
|
self.selection = 0
|
||||||
|
}
|
||||||
|
|
||||||
if self.input_changed {
|
if self.input_changed {
|
||||||
trace! {"Redrawing with input {}", self.input_buffer}
|
trace! {"Redrawing with input {}", self.input_buffer}
|
||||||
|
@ -137,34 +146,37 @@ impl RoftlLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::RedrawRequested(window_id)
|
Event::RedrawRequested(window_id)
|
||||||
if window_id == self.window.id()
|
if window_id == self.window.id() && matches! {self.mode, Mode::Actions} =>
|
||||||
&& matches!{self.mode, Mode::Actions} =>
|
|
||||||
{
|
{
|
||||||
let actions = self.roftl.actions(self.selection);
|
let actions = self.roftl.actions(self.selection);
|
||||||
debug! {"Actions for current selection: {:?}", actions}
|
debug! {"Actions for current selection: {:?}", actions}
|
||||||
ui::draw_actions(&self.window, actions, &self.input_buffer);
|
ui::draw_actions(&self.window, actions, &self.input_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_input(&mut self, input: KeyboardInput) -> 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) => {
|
||||||
debug!("Received down");
|
debug!("Received down");
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
},
|
}
|
||||||
(VirtualKeyCode::Up, ElementState::Released) => {
|
(VirtualKeyCode::Up, ElementState::Released) => {
|
||||||
debug!("Received up");
|
debug!("Received up");
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => ControlFlow::Wait,
|
_ => ControlFlow::Wait,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
|
@ -204,7 +216,9 @@ impl RoftlLoop {
|
||||||
// Ctrl+p
|
// Ctrl+p
|
||||||
c if c == char::from(0x10) => {
|
c if c == char::from(0x10) => {
|
||||||
debug! {"Received previous"}
|
debug! {"Received previous"}
|
||||||
if self.selection != 0 { self.selection -= 1 }
|
if self.selection != 0 {
|
||||||
|
self.selection -= 1
|
||||||
|
}
|
||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
}
|
}
|
||||||
|
@ -213,8 +227,14 @@ impl RoftlLoop {
|
||||||
c if c == char::from(0x09) => {
|
c if c == char::from(0x09) => {
|
||||||
debug! {"Received actions"}
|
debug! {"Received actions"}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Selection => { self.mode = Mode::Actions; self.input_changed = true },
|
Mode::Selection => {
|
||||||
Mode::Actions => { self.mode = Mode::Selection; self.input_changed = true }
|
self.mode = Mode::Actions;
|
||||||
|
self.input_changed = true
|
||||||
|
}
|
||||||
|
Mode::Actions => {
|
||||||
|
self.mode = Mode::Selection;
|
||||||
|
self.input_changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
|
@ -241,22 +261,22 @@ impl RoftlLoop {
|
||||||
trace! {"Retrieved action selection 1. Trigger primary action"}
|
trace! {"Retrieved action selection 1. Trigger primary action"}
|
||||||
self.roftl.exec_action(self.selection, 0);
|
self.roftl.exec_action(self.selection, 0);
|
||||||
ControlFlow::Exit
|
ControlFlow::Exit
|
||||||
},
|
}
|
||||||
'2' => {
|
'2' => {
|
||||||
trace! {"Retrieved action selection 2. Trigger secondary action"}
|
trace! {"Retrieved action selection 2. Trigger secondary action"}
|
||||||
self.roftl.exec_action(self.selection, 1);
|
self.roftl.exec_action(self.selection, 1);
|
||||||
ControlFlow::Exit
|
ControlFlow::Exit
|
||||||
},
|
}
|
||||||
'3' => {
|
'3' => {
|
||||||
trace! {"Retrieved action selection 3. Trigger tertiary action"}
|
trace! {"Retrieved action selection 3. Trigger tertiary action"}
|
||||||
self.roftl.exec_action(self.selection, 2);
|
self.roftl.exec_action(self.selection, 2);
|
||||||
ControlFlow::Exit
|
ControlFlow::Exit
|
||||||
},
|
}
|
||||||
'4' => {
|
'4' => {
|
||||||
trace! {"Retrieved action selection 4. Trigger quaternary action"}
|
trace! {"Retrieved action selection 4. Trigger quaternary action"}
|
||||||
self.roftl.exec_action(self.selection, 3);
|
self.roftl.exec_action(self.selection, 3);
|
||||||
ControlFlow::Exit
|
ControlFlow::Exit
|
||||||
},
|
}
|
||||||
|
|
||||||
'q' | 'Q' => ControlFlow::Exit,
|
'q' | 'Q' => ControlFlow::Exit,
|
||||||
|
|
||||||
|
@ -267,18 +287,23 @@ impl RoftlLoop {
|
||||||
c if c == char::from(0x09) => {
|
c if c == char::from(0x09) => {
|
||||||
debug! {"Received actions"}
|
debug! {"Received actions"}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Selection => { self.mode = Mode::Actions; self.input_changed = true },
|
Mode::Selection => {
|
||||||
Mode::Actions => { self.mode = Mode::Selection; self.input_changed = true }
|
self.mode = Mode::Actions;
|
||||||
|
self.input_changed = true
|
||||||
|
}
|
||||||
|
Mode::Actions => {
|
||||||
|
self.mode = Mode::Selection;
|
||||||
|
self.input_changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
},
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
debug! {"Retrieved unknown action selection. Processing character {}", character}
|
debug! {"Retrieved unknown action selection. Processing character {}", character}
|
||||||
ControlFlow::Wait
|
ControlFlow::Wait
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,7 @@ impl Matcher for FlxMatcher {
|
||||||
let res: Option<Score> = flx_rs::score(haystack, needle);
|
let res: Option<Score> = flx_rs::score(haystack, needle);
|
||||||
res.and_then(|score| {
|
res.and_then(|score| {
|
||||||
let s = score.score as f64;
|
let s = score.score as f64;
|
||||||
let i = score.indices.into_iter()
|
let i = score.indices.into_iter().map(|idx| idx as usize).collect();
|
||||||
.map(|idx| idx as usize)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Some((s, i))
|
Some((s, i))
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,18 +5,20 @@ use fuse_rust::Fuse;
|
||||||
use super::core::shared::Matcher;
|
use super::core::shared::Matcher;
|
||||||
|
|
||||||
fn flatten_ranges(ranges: Vec<Range<usize>>) -> Vec<usize> {
|
fn flatten_ranges(ranges: Vec<Range<usize>>) -> Vec<usize> {
|
||||||
ranges.into_iter().flat_map(|range| {range.collect::<Vec<usize>>().into_iter()})
|
ranges
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|range| range.collect::<Vec<usize>>().into_iter())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FuseMatcher {
|
pub struct FuseMatcher {
|
||||||
matcher: Fuse
|
matcher: Fuse,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FuseMatcher {
|
impl FuseMatcher {
|
||||||
pub fn new() -> Box<Self> {
|
pub fn new() -> Box<Self> {
|
||||||
Box::new(FuseMatcher {
|
Box::new(FuseMatcher {
|
||||||
matcher: Fuse::default()
|
matcher: Fuse::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,9 +27,7 @@ impl Matcher for FuseMatcher {
|
||||||
fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec<usize>)> {
|
fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec<usize>)> {
|
||||||
match self.matcher.search_text_in_string(needle, haystack) {
|
match self.matcher.search_text_in_string(needle, haystack) {
|
||||||
Some(score) => Some((score.score, flatten_ranges(score.ranges))),
|
Some(score) => Some((score.score, flatten_ranges(score.ranges))),
|
||||||
None => None
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,3 @@ pub use fuse::FuseMatcher;
|
||||||
|
|
||||||
mod flx;
|
mod flx;
|
||||||
pub use flx::FlxMatcher;
|
pub use flx::FlxMatcher;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
|
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||||
|
|
||||||
use super::core::shared::Matcher;
|
use super::core::shared::Matcher;
|
||||||
|
|
||||||
pub struct SkimMatcher {
|
pub struct SkimMatcher {
|
||||||
matcher: SkimMatcherV2
|
matcher: SkimMatcherV2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SkimMatcher {
|
impl SkimMatcher {
|
||||||
pub fn new() -> Box<Self> {
|
pub fn new() -> Box<Self> {
|
||||||
Box::new(SkimMatcher {
|
Box::new(SkimMatcher {
|
||||||
matcher: SkimMatcherV2::default()
|
matcher: SkimMatcherV2::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ impl Matcher for SkimMatcher {
|
||||||
fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec<usize>)> {
|
fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec<usize>)> {
|
||||||
match self.matcher.fuzzy_indices(haystack, needle) {
|
match self.matcher.fuzzy_indices(haystack, needle) {
|
||||||
Some((score, indices)) => Some(((i64::MAX as f64 / score as f64), indices)),
|
Some((score, indices)) => Some(((i64::MAX as f64 / score as f64), indices)),
|
||||||
None => None
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
use std::{os::unix::prelude::CommandExt, process::{Command, Stdio}};
|
use std::{
|
||||||
|
os::unix::prelude::CommandExt,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
use freedesktop_entry_parser as parser;
|
use freedesktop_entry_parser as parser;
|
||||||
use log::{debug, error, trace};
|
use log::{debug, error, trace};
|
||||||
use walkdir::WalkDir;
|
|
||||||
use nix::unistd::{getpid, setpgid};
|
use nix::unistd::{getpid, setpgid};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use super::core::shared::{Entry, Source};
|
use super::core::shared::{Entry, Source};
|
||||||
|
|
||||||
|
@ -11,7 +14,7 @@ use super::core::shared::{Entry, Source};
|
||||||
struct App {
|
struct App {
|
||||||
name: String,
|
name: String,
|
||||||
command: String,
|
command: String,
|
||||||
terminal: bool
|
terminal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_command_string<'a>(exec: &'a str) -> (String, Vec<String>) {
|
pub fn parse_command_string<'a>(exec: &'a str) -> (String, Vec<String>) {
|
||||||
|
@ -50,7 +53,6 @@ impl App {
|
||||||
command.args(&args);
|
command.args(&args);
|
||||||
let _ = run_bg(command);
|
let _ = run_bg(command);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
debug!("Running `{} {}`", cmd, args.join(" "));
|
debug!("Running `{} {}`", cmd, args.join(" "));
|
||||||
let mut command = Command::new(&cmd);
|
let mut command = Command::new(&cmd);
|
||||||
command.args(&args);
|
command.args(&args);
|
||||||
|
@ -59,18 +61,15 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&App> for Entry
|
impl From<&App> for Entry {
|
||||||
{
|
|
||||||
fn from(app: &App) -> Self {
|
fn from(app: &App) -> Self {
|
||||||
Entry {
|
Entry {
|
||||||
name: app.name.clone(),
|
name: app.name.clone(),
|
||||||
description: "".into(),
|
description: "".into(),
|
||||||
source: "apps",
|
source: "apps",
|
||||||
identifier: 0
|
identifier: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -78,33 +77,37 @@ struct AppBuilder<'a> {
|
||||||
name: Option<&'a str>,
|
name: Option<&'a str>,
|
||||||
exec: Option<&'a str>,
|
exec: Option<&'a str>,
|
||||||
hidden: Option<&'a str>,
|
hidden: Option<&'a str>,
|
||||||
terminal: Option<&'a str>
|
terminal: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AppBuilder<'a>
|
impl<'a> AppBuilder<'a> {
|
||||||
{
|
fn build(&self) -> Option<App> {
|
||||||
fn build(&self) -> Option<App>
|
if self.name.is_none() {
|
||||||
{
|
return None;
|
||||||
if self.name.is_none() { return None }
|
}
|
||||||
if self.exec.is_none() { return None }
|
if self.exec.is_none() {
|
||||||
if self.hidden.is_some() { return None }
|
return None;
|
||||||
|
}
|
||||||
|
if self.hidden.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
Some (
|
Some(App {
|
||||||
App {
|
|
||||||
name: self.name.unwrap().into(),
|
name: self.name.unwrap().into(),
|
||||||
command: strip_entry_args(self.exec.unwrap()).into(),
|
command: strip_entry_args(self.exec.unwrap()).into(),
|
||||||
terminal: self.terminal.map_or(false, |term| term.parse().unwrap_or(false))
|
terminal: self
|
||||||
|
.terminal
|
||||||
|
.map_or(false, |term| term.parse().unwrap_or(false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Apps {
|
pub struct Apps {
|
||||||
entries: Vec<App>
|
entries: Vec<App>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Apps {
|
impl Apps {
|
||||||
pub fn new() -> Box<Apps>
|
pub fn new() -> Box<Apps> {
|
||||||
{
|
|
||||||
Box::new(Apps { entries: vec![] })
|
Box::new(Apps { entries: vec![] })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,9 +115,12 @@ impl Apps {
|
||||||
fn strip_entry_args(exec: &str) -> String {
|
fn strip_entry_args(exec: &str) -> String {
|
||||||
trace! {"Stripping {}", exec}
|
trace! {"Stripping {}", exec}
|
||||||
let iter = exec.split(' ');
|
let iter = exec.split(' ');
|
||||||
let result = iter.filter(|item| !item.starts_with('%'))
|
let result = iter
|
||||||
|
.filter(|item| !item.starts_with('%'))
|
||||||
.collect::<Vec<&str>>()
|
.collect::<Vec<&str>>()
|
||||||
.join(" ");
|
.join(" ")
|
||||||
|
.trim_matches('"')
|
||||||
|
.into();
|
||||||
|
|
||||||
trace! {"Stripped into {}", result}
|
trace! {"Stripped into {}", result}
|
||||||
result
|
result
|
||||||
|
@ -130,7 +136,7 @@ impl Source for Apps {
|
||||||
"/usr/share/applications/",
|
"/usr/share/applications/",
|
||||||
"/home/armin/.local/share/applications/",
|
"/home/armin/.local/share/applications/",
|
||||||
"/var/lib/snapd/desktop/applications",
|
"/var/lib/snapd/desktop/applications",
|
||||||
"/var/lib/flatpak/exports/share/applications"
|
"/var/lib/flatpak/exports/share/applications",
|
||||||
];
|
];
|
||||||
|
|
||||||
let desktop_entries: Vec<parser::Entry> = paths
|
let desktop_entries: Vec<parser::Entry> = paths
|
||||||
|
@ -147,28 +153,24 @@ impl Source for Apps {
|
||||||
let mut app_builder = AppBuilder::default();
|
let mut app_builder = AppBuilder::default();
|
||||||
let section = entry.section("Desktop Entry");
|
let section = entry.section("Desktop Entry");
|
||||||
|
|
||||||
for attr in section.attrs()
|
for attr in section.attrs() {
|
||||||
{
|
match attr.name {
|
||||||
match attr.name
|
|
||||||
{
|
|
||||||
"Name" => app_builder.name = attr.value,
|
"Name" => app_builder.name = attr.value,
|
||||||
"Exec" => app_builder.exec = attr.value,
|
"Exec" => app_builder.exec = attr.value,
|
||||||
"NoDisplay" => app_builder.hidden = attr.value,
|
"NoDisplay" => app_builder.hidden = attr.value,
|
||||||
"Hidden" => app_builder.hidden = attr.value,
|
"Hidden" => app_builder.hidden = attr.value,
|
||||||
"Terminal" => app_builder.terminal = attr.value,
|
"Terminal" => app_builder.terminal = attr.value,
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(app) = app_builder.build()
|
if let Some(app) = app_builder.build() {
|
||||||
{
|
|
||||||
let mut entry: Entry = (&app).into();
|
let mut entry: Entry = (&app).into();
|
||||||
entry.identifier = idx;
|
entry.identifier = idx;
|
||||||
idx += 1;
|
idx += 1;
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
self.entries.push(app);
|
self.entries.push(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trace! {"Got desktop entries: {:?}", entries}
|
trace! {"Got desktop entries: {:?}", entries}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use std::{os::unix::prelude::CommandExt, path::PathBuf, process::{Command, Stdio}};
|
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use nix::unistd::{getpid, setpgid};
|
use nix::unistd::{getpid, setpgid};
|
||||||
|
use std::{
|
||||||
|
os::unix::prelude::CommandExt,
|
||||||
|
path::PathBuf,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use super::core::shared::{Entry, Source};
|
use super::core::shared::{Entry, Source};
|
||||||
|
@ -25,18 +29,18 @@ impl Source for ShellHost {
|
||||||
let script_path = "/home/armin/dev/incubator/roftl/sh";
|
let script_path = "/home/armin/dev/incubator/roftl/sh";
|
||||||
|
|
||||||
// TODO make robust if path does not exist
|
// TODO make robust if path does not exist
|
||||||
let scripts: Vec<PathBuf> = WalkDir::new(script_path).into_iter()
|
let scripts: Vec<PathBuf> = WalkDir::new(script_path)
|
||||||
|
.into_iter()
|
||||||
.map(|e| e.unwrap().path().to_path_buf())
|
.map(|e| e.unwrap().path().to_path_buf())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut entries: Vec<Entry> = vec![];
|
let mut entries: Vec<Entry> = vec![];
|
||||||
for (idx, script) in scripts.iter().enumerate() {
|
for (idx, script) in scripts.iter().enumerate() {
|
||||||
entries.push(
|
entries.push(Entry {
|
||||||
Entry {
|
|
||||||
source: self.name(),
|
source: self.name(),
|
||||||
name: script.file_stem().unwrap().to_str().unwrap().into(),
|
name: script.file_stem().unwrap().to_str().unwrap().into(),
|
||||||
description: "".into(),
|
description: "".into(),
|
||||||
identifier: idx as u64
|
identifier: idx as u64,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.scripts = scripts;
|
self.scripts = scripts;
|
||||||
|
@ -44,7 +48,6 @@ impl Source for ShellHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_action(&self, entry: &Entry, action: u8) {
|
fn exec_action(&self, entry: &Entry, action: u8) {
|
||||||
|
|
||||||
let script: &PathBuf = self.scripts.get(entry.identifier as usize).unwrap();
|
let script: &PathBuf = self.scripts.get(entry.identifier as usize).unwrap();
|
||||||
debug! {"Got script {:?}", script};
|
debug! {"Got script {:?}", script};
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ impl Source for TestSource {
|
||||||
|
|
||||||
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
||||||
["Primary", "Secondary", "Tertiary", "Quaternary"]
|
["Primary", "Secondary", "Tertiary", "Quaternary"]
|
||||||
.iter().map(|s| (*s).into()).collect()
|
.iter()
|
||||||
|
.map(|s| (*s).into())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,15 +8,21 @@ use xcb_util::ffi::ewmh::XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER;
|
||||||
use xcb_util::icccm;
|
use xcb_util::icccm;
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
action_data: HashMap<u64, (u32, i32)>
|
action_data: HashMap<u64, (u32, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new() -> Box<Window> {
|
pub fn new() -> Box<Window> {
|
||||||
Box::new(Window { action_data: HashMap::<u64, (u32,i32)>::new() })
|
Box::new(Window {
|
||||||
|
action_data: HashMap::<u64, (u32, i32)>::new(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_normal_window(&self, con: &ewmh::Connection, window: u32) -> Result<bool, xcb::ReplyError> {
|
fn is_normal_window(
|
||||||
|
&self,
|
||||||
|
con: &ewmh::Connection,
|
||||||
|
window: u32,
|
||||||
|
) -> Result<bool, xcb::ReplyError> {
|
||||||
let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?;
|
let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?;
|
||||||
|
|
||||||
for atom in wm_type_reply.atoms() {
|
for atom in wm_type_reply.atoms() {
|
||||||
|
@ -28,48 +34,95 @@ impl Window {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_category(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::ReplyError> {
|
fn window_category(
|
||||||
Ok(icccm::get_wm_class(&con, window).get_reply()?.class().into())
|
&self,
|
||||||
|
con: &ewmh::Connection,
|
||||||
|
window: u32,
|
||||||
|
) -> Result<String, xcb::ReplyError> {
|
||||||
|
Ok(icccm::get_wm_class(&con, window)
|
||||||
|
.get_reply()?
|
||||||
|
.class()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::ReplyError> {
|
fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::ReplyError> {
|
||||||
Ok(ewmh::get_wm_name(con, window).get_reply()?.string().into())
|
Ok(ewmh::get_wm_name(con, window).get_reply()?.string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
fn switch_window(
|
||||||
|
&self,
|
||||||
|
con: &ewmh::Connection,
|
||||||
|
window: u32,
|
||||||
|
screen: i32,
|
||||||
|
) -> Result<(), xcb::ReplyError> {
|
||||||
let window_desktop = ewmh::get_wm_desktop(con, window).get_reply()?;
|
let window_desktop = ewmh::get_wm_desktop(con, window).get_reply()?;
|
||||||
let active_window = ewmh::get_active_window(con, screen).get_reply()?;
|
let active_window = ewmh::get_active_window(con, screen).get_reply()?;
|
||||||
ewmh::set_current_desktop(con, screen, window_desktop);
|
ewmh::set_current_desktop(con, screen, window_desktop);
|
||||||
ewmh::request_change_active_window(con, screen, window, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER, XCB_CURRENT_TIME, active_window).request_check().unwrap();
|
ewmh::request_change_active_window(
|
||||||
ewmh::set_active_window(con, screen, window).request_check().unwrap();
|
con,
|
||||||
|
screen,
|
||||||
|
window,
|
||||||
|
XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER,
|
||||||
|
XCB_CURRENT_TIME,
|
||||||
|
active_window,
|
||||||
|
)
|
||||||
|
.request_check()
|
||||||
|
.unwrap();
|
||||||
|
ewmh::set_active_window(con, screen, window)
|
||||||
|
.request_check()
|
||||||
|
.unwrap();
|
||||||
xcb::set_input_focus(&con, 0, window, XCB_CURRENT_TIME);
|
xcb::set_input_focus(&con, 0, window, XCB_CURRENT_TIME);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_maximize_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
fn toggle_maximize_window(
|
||||||
|
&self,
|
||||||
|
con: &ewmh::Connection,
|
||||||
|
window: u32,
|
||||||
|
screen: i32,
|
||||||
|
) -> Result<(), xcb::ReplyError> {
|
||||||
let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
||||||
let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
||||||
let action_atom = ewmh::STATE_TOGGLE;
|
let action_atom = ewmh::STATE_TOGGLE;
|
||||||
|
|
||||||
debug! {"Toggle maximize for {}", window}
|
debug! {"Toggle maximize for {}", window}
|
||||||
ewmh::request_change_wm_state(con, screen, window, action_atom, max_horz_atom, max_vert_atom, 0).request_check()?;
|
ewmh::request_change_wm_state(
|
||||||
|
con,
|
||||||
|
screen,
|
||||||
|
window,
|
||||||
|
action_atom,
|
||||||
|
max_horz_atom,
|
||||||
|
max_vert_atom,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.request_check()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
fn close_window(
|
||||||
|
&self,
|
||||||
|
con: &ewmh::Connection,
|
||||||
|
window: u32,
|
||||||
|
screen: i32,
|
||||||
|
) -> Result<(), xcb::ReplyError> {
|
||||||
debug! {"Toggle maximize for {}", window}
|
debug! {"Toggle maximize for {}", window}
|
||||||
ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_hide_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> {
|
fn toggle_hide_window(
|
||||||
|
&self,
|
||||||
|
con: &ewmh::Connection,
|
||||||
|
window: u32,
|
||||||
|
screen: i32,
|
||||||
|
) -> Result<(), xcb::ReplyError> {
|
||||||
let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
||||||
let action_atom = ewmh::STATE_TOGGLE;
|
let action_atom = ewmh::STATE_TOGGLE;
|
||||||
|
|
||||||
debug! {"Toggle hidden for {}", window}
|
debug! {"Toggle hidden for {}", window}
|
||||||
ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0).request_check()?;
|
ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0)
|
||||||
|
.request_check()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -81,10 +134,15 @@ impl Source for Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entries(&mut self) -> Vec<Entry> {
|
fn entries(&mut self) -> Vec<Entry> {
|
||||||
let (xcb_conn, screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
let (xcb_conn, screen_num) =
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||||
|
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||||
|
.map_err(|(e, _)| e)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let client_list_reply = xcb_util::ewmh::get_client_list(&ewmh_conn, screen_num).get_reply().unwrap();
|
let client_list_reply = xcb_util::ewmh::get_client_list(&ewmh_conn, screen_num)
|
||||||
|
.get_reply()
|
||||||
|
.unwrap();
|
||||||
let windows = client_list_reply.windows();
|
let windows = client_list_reply.windows();
|
||||||
|
|
||||||
let mut entries: Vec<Entry> = vec![];
|
let mut entries: Vec<Entry> = vec![];
|
||||||
|
@ -92,7 +150,7 @@ impl Source for Window {
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
for w in windows {
|
for w in windows {
|
||||||
match self.is_normal_window(&ewmh_conn, *w) {
|
match self.is_normal_window(&ewmh_conn, *w) {
|
||||||
Ok(true) => {},
|
Ok(true) => {}
|
||||||
Ok(false) => continue,
|
Ok(false) => continue,
|
||||||
Err(_err) => {}
|
Err(_err) => {}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +160,12 @@ impl Source for Window {
|
||||||
|
|
||||||
debug!("Found window {} - {}", title, category);
|
debug!("Found window {} - {}", title, category);
|
||||||
|
|
||||||
entries.push(Entry{source: self.name(), name: category, description: title, identifier: count});
|
entries.push(Entry {
|
||||||
|
source: self.name(),
|
||||||
|
name: category,
|
||||||
|
description: title,
|
||||||
|
identifier: count,
|
||||||
|
});
|
||||||
self.action_data.insert(count, (*w, screen_num));
|
self.action_data.insert(count, (*w, screen_num));
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
|
@ -117,36 +180,54 @@ impl Source for Window {
|
||||||
match action {
|
match action {
|
||||||
0 => {
|
0 => {
|
||||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||||
let (xcb_conn, _screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
let (xcb_conn, _screen_num) =
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||||
|
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||||
|
.map_err(|(e, _)| e)
|
||||||
|
.unwrap();
|
||||||
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
||||||
},
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||||
let (xcb_conn, _screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
let (xcb_conn, _screen_num) =
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||||
self.toggle_maximize_window(&ewmh_conn, window, screen).unwrap();
|
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||||
|
.map_err(|(e, _)| e)
|
||||||
|
.unwrap();
|
||||||
|
self.toggle_maximize_window(&ewmh_conn, window, screen)
|
||||||
|
.unwrap();
|
||||||
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
||||||
},
|
}
|
||||||
2 => {
|
2 => {
|
||||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||||
let (xcb_conn, _screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
let (xcb_conn, _screen_num) =
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||||
|
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||||
|
.map_err(|(e, _)| e)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
self.toggle_hide_window(&ewmh_conn, window, screen).unwrap();
|
self.toggle_hide_window(&ewmh_conn, window, screen).unwrap();
|
||||||
},
|
}
|
||||||
3 => {
|
3 => {
|
||||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||||
let (xcb_conn, _screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
let (xcb_conn, _screen_num) =
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||||
|
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||||
|
.map_err(|(e, _)| e)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
self.close_window(&ewmh_conn, window, screen).unwrap();
|
self.close_window(&ewmh_conn, window, screen).unwrap();
|
||||||
},
|
}
|
||||||
_ => panic!{"Unknown action {:?}", action}
|
_ => panic! {"Unknown action {:?}", action},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
||||||
vec!["Switch".into(), "Toggle maximize".into(), "Toggle hide".into(), "Close".into()]
|
vec![
|
||||||
|
"Switch".into(),
|
||||||
|
"Toggle maximize".into(),
|
||||||
|
"Toggle hide".into(),
|
||||||
|
"Close".into(),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ use super::core;
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
pub use ui::draw;
|
pub use ui::draw;
|
||||||
pub use ui::redraw_quick;
|
|
||||||
pub use ui::draw_actions;
|
pub use ui::draw_actions;
|
||||||
|
pub use ui::redraw_quick;
|
||||||
|
|
||||||
mod painter;
|
mod painter;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
|
@ -9,11 +9,13 @@ pub struct Painter<'a> {
|
||||||
|
|
||||||
impl<'a> Painter<'a> {
|
impl<'a> Painter<'a> {
|
||||||
pub fn new(context: &'a cairo::Context) -> Self {
|
pub fn new(context: &'a cairo::Context) -> Self {
|
||||||
Painter{context, theme: Theme::default()}
|
Painter {
|
||||||
|
context,
|
||||||
|
theme: Theme::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn background(&self)
|
pub fn background(&self) {
|
||||||
{
|
|
||||||
let ctx = self.context;
|
let ctx = self.context;
|
||||||
|
|
||||||
// reset clip so that we can draw the entire background of the surface
|
// reset clip so that we can draw the entire background of the surface
|
||||||
|
@ -37,8 +39,7 @@ impl<'a> Painter<'a> {
|
||||||
self.border_clip();
|
self.border_clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn border_clip(&self)
|
pub fn border_clip(&self) {
|
||||||
{
|
|
||||||
let ctx = self.context;
|
let ctx = self.context;
|
||||||
|
|
||||||
ctx.reset_clip();
|
ctx.reset_clip();
|
||||||
|
@ -53,14 +54,20 @@ impl<'a> Painter<'a> {
|
||||||
|
|
||||||
// let (x1,y1,x2,y2) = (x1+border, y1+border, x2-border, y2-border);
|
// let (x1,y1,x2,y2) = (x1+border, y1+border, x2-border, y2-border);
|
||||||
debug! {"Border rect: {},{},{},{}", x1, y1, x2, y2};
|
debug! {"Border rect: {},{},{},{}", x1, y1, x2, y2};
|
||||||
let (x1,y1,x2,y2) = (x1+border,y1+border,x2-x1-border,y2-y1-border-border);
|
let (x1, y1, x2, y2) = (
|
||||||
|
x1 + border,
|
||||||
|
y1 + border,
|
||||||
|
x2 - x1 - border,
|
||||||
|
y2 - y1 - border - border,
|
||||||
|
);
|
||||||
debug! {"Border clip: {},{},{},{}", x1, y1, x2, y2};
|
debug! {"Border clip: {},{},{},{}", x1, y1, x2, y2};
|
||||||
ctx.rectangle(x1, y1, x2, y2);
|
ctx.rectangle(x1, y1, x2, y2);
|
||||||
ctx.clip();
|
ctx.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_box<T>(&self, x: T, y: T, input: &'a str)
|
pub fn input_box<T>(&self, x: T, y: T, input: &'a str)
|
||||||
where T: Into<f64>
|
where
|
||||||
|
T: Into<f64>,
|
||||||
{
|
{
|
||||||
let ctx = self.context;
|
let ctx = self.context;
|
||||||
let (x, y): (f64, f64) = (x.into(), y.into());
|
let (x, y): (f64, f64) = (x.into(), y.into());
|
||||||
|
@ -75,16 +82,27 @@ impl<'a> Painter<'a> {
|
||||||
ctx.theme_text(&input, false, &[], &self.theme);
|
ctx.theme_text(&input, false, &[], &self.theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn result_box<T>(&self, x: T, y: T, source: &'a str, result: &'a str, indices: &[usize], selected: bool)
|
pub fn result_box<T>(
|
||||||
where T: Into<f64>
|
&self,
|
||||||
|
x: T,
|
||||||
|
y: T,
|
||||||
|
source: &'a str,
|
||||||
|
result: &'a str,
|
||||||
|
indices: &[usize],
|
||||||
|
selected: bool,
|
||||||
|
) where
|
||||||
|
T: Into<f64>,
|
||||||
{
|
{
|
||||||
let ctx = self.context;
|
let ctx = self.context;
|
||||||
let (x, y): (f64, f64) = (x.into(), y.into());
|
let (x, y): (f64, f64) = (x.into(), y.into());
|
||||||
|
|
||||||
// draw background box
|
// draw background box
|
||||||
ctx.rectangle(x.into(), y.into(), self.clip_width(), 1.0);
|
ctx.rectangle(x.into(), y.into(), self.clip_width(), 1.0);
|
||||||
if selected { ctx.theme_color_highlight(&self.theme) }
|
if selected {
|
||||||
else { ctx.theme_color_base(&self.theme) };
|
ctx.theme_color_highlight(&self.theme)
|
||||||
|
} else {
|
||||||
|
ctx.theme_color_base(&self.theme)
|
||||||
|
};
|
||||||
ctx.set_operator(cairo::Operator::Source);
|
ctx.set_operator(cairo::Operator::Source);
|
||||||
ctx.fill().unwrap();
|
ctx.fill().unwrap();
|
||||||
|
|
||||||
|
@ -96,7 +114,8 @@ impl<'a> Painter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divider<T>(&self, x: T, y: T)
|
pub fn divider<T>(&self, x: T, y: T)
|
||||||
where T: Into<f64>
|
where
|
||||||
|
T: Into<f64>,
|
||||||
{
|
{
|
||||||
let ctx = self.context;
|
let ctx = self.context;
|
||||||
let (x, y): (f64, f64) = (x.into(), y.into());
|
let (x, y): (f64, f64) = (x.into(), y.into());
|
||||||
|
@ -111,7 +130,10 @@ impl<'a> Painter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_to_user(&self, point: f64) -> f64 {
|
fn device_to_user(&self, point: f64) -> f64 {
|
||||||
self.context.device_to_user_distance(point, point).unwrap().0
|
self.context
|
||||||
|
.device_to_user_distance(point, point)
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clip_width(&self) -> f64 {
|
fn clip_width(&self) -> f64 {
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct ColorScheme {
|
||||||
divider: Color,
|
divider: Color,
|
||||||
|
|
||||||
text: Color,
|
text: Color,
|
||||||
text_highlight: Color
|
text_highlight: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
|
@ -22,7 +22,7 @@ pub struct Theme {
|
||||||
font: Font,
|
font: Font,
|
||||||
|
|
||||||
border: f64,
|
border: f64,
|
||||||
divider: f64
|
divider: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
@ -41,7 +41,7 @@ impl Default for Theme {
|
||||||
divider: Color(0.23, 0.05, 0.11, 1.0),
|
divider: Color(0.23, 0.05, 0.11, 1.0),
|
||||||
|
|
||||||
text: Color(0.87, 0.95, 0.77, 1.0),
|
text: Color(0.87, 0.95, 0.77, 1.0),
|
||||||
text_highlight: Color(0.6, 0.8, 0.4, 1.0)
|
text_highlight: Color(0.6, 0.8, 0.4, 1.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
Theme {
|
Theme {
|
||||||
|
@ -49,7 +49,7 @@ impl Default for Theme {
|
||||||
font: Font("Sans Regular".into(), 13),
|
font: Font("Sans Regular".into(), 13),
|
||||||
|
|
||||||
border: 2.0,
|
border: 2.0,
|
||||||
divider: 3.0
|
divider: 3.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,6 @@ pub trait ThemedContextExt {
|
||||||
|
|
||||||
fn theme_text(&self, text: &str, selected: bool, indices: &[usize], theme: &Theme);
|
fn theme_text(&self, text: &str, selected: bool, indices: &[usize], theme: &Theme);
|
||||||
|
|
||||||
|
|
||||||
fn device_to_user_point(&self, point: f64) -> f64;
|
fn device_to_user_point(&self, point: f64) -> f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,8 +101,11 @@ impl ThemedContextExt for cairo::Context {
|
||||||
// gets totally confused by active cairo transformations
|
// gets totally confused by active cairo transformations
|
||||||
// which then messes with font size, size of attributes, etc
|
// which then messes with font size, size of attributes, etc
|
||||||
|
|
||||||
let Color(r,g,b,a) = if selected { theme.colors.text_highlight }
|
let Color(r, g, b, a) = if selected {
|
||||||
else { theme.colors.text };
|
theme.colors.text_highlight
|
||||||
|
} else {
|
||||||
|
theme.colors.text
|
||||||
|
};
|
||||||
self.set_source_rgba(r, g, b, a);
|
self.set_source_rgba(r, g, b, a);
|
||||||
|
|
||||||
let layout = pangocairo::create_layout(self).unwrap();
|
let layout = pangocairo::create_layout(self).unwrap();
|
||||||
|
|
162
src/ui/ui.rs
162
src/ui/ui.rs
|
@ -5,13 +5,88 @@ use log::debug;
|
||||||
use winit::{platform::unix::WindowExtUnix, window::Window};
|
use winit::{platform::unix::WindowExtUnix, window::Window};
|
||||||
|
|
||||||
use super::core::shared::Entry;
|
use super::core::shared::Entry;
|
||||||
|
|
||||||
use super::painter;
|
use super::painter;
|
||||||
|
|
||||||
|
pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, selection: usize) {
|
||||||
|
let context = make_context(&window);
|
||||||
|
|
||||||
|
context.scale(30.0, 30.0);
|
||||||
|
|
||||||
|
let painter = painter::Painter::new(&context);
|
||||||
|
|
||||||
|
painter.background();
|
||||||
|
painter.input_box(0, 0, input);
|
||||||
|
painter.divider(0, 1);
|
||||||
|
|
||||||
|
result.iter().enumerate().for_each(|(i, r)| {
|
||||||
|
let e = r.0;
|
||||||
|
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, selection == i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redraw_quick(window: &Window, result: Vec<(&Entry, Vec<usize>)>, selection: usize) {
|
||||||
|
let context = make_context(&window);
|
||||||
|
|
||||||
|
context.scale(30.0, 30.0);
|
||||||
|
|
||||||
|
let painter = painter::Painter::new(&context);
|
||||||
|
painter.border_clip();
|
||||||
|
|
||||||
|
result.iter().enumerate().for_each(|(i, r)| {
|
||||||
|
let e = r.0;
|
||||||
|
// clear first and last as these may produce artifacts otherwise
|
||||||
|
if i == 0 {
|
||||||
|
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, false)
|
||||||
|
}
|
||||||
|
if i == result.len() - 1 {
|
||||||
|
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, 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.source, &e.name, &r.1, false)
|
||||||
|
}
|
||||||
|
if i == (selection + 1) {
|
||||||
|
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, 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.source, &e.name, &r.1, true)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_actions(window: &Window, actions: Vec<String>, input: &str) {
|
||||||
|
let context = make_context(&window);
|
||||||
|
|
||||||
|
context.scale(30.0, 30.0);
|
||||||
|
|
||||||
|
let painter = painter::Painter::new(&context);
|
||||||
|
|
||||||
|
painter.background();
|
||||||
|
painter.input_box(0, 0, input);
|
||||||
|
painter.divider(0, 1);
|
||||||
|
|
||||||
|
actions.iter().enumerate().for_each(|(i, r)| {
|
||||||
|
painter.result_box(
|
||||||
|
0,
|
||||||
|
1 + (i as u32),
|
||||||
|
&usize::to_string(&(i + 1)),
|
||||||
|
r,
|
||||||
|
&[],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn get_visual_type<T>(mut connection: T, window_id: u32) -> xcb::Visualtype
|
fn get_visual_type<T>(mut connection: T, window_id: u32) -> xcb::Visualtype
|
||||||
where T: BorrowMut<*mut c_void>
|
where
|
||||||
|
T: BorrowMut<*mut c_void>,
|
||||||
{
|
{
|
||||||
let xcb_conn = unsafe { xcb::Connection::from_raw_conn(*connection.borrow_mut() as *mut xcb::ffi::xcb_connection_t) };
|
let xcb_conn = unsafe {
|
||||||
|
xcb::Connection::from_raw_conn(*connection.borrow_mut() as *mut xcb::ffi::xcb_connection_t)
|
||||||
|
};
|
||||||
|
|
||||||
let window_visualid = xcb::get_window_attributes(&xcb_conn, window_id)
|
let window_visualid = xcb::get_window_attributes(&xcb_conn, window_id)
|
||||||
.get_reply()
|
.get_reply()
|
||||||
|
@ -21,10 +96,12 @@ where T: BorrowMut<*mut c_void>
|
||||||
debug! {"Found visualid {} for window", window_visualid}
|
debug! {"Found visualid {} for window", window_visualid}
|
||||||
|
|
||||||
debug! {"Trying to map visualid to visualtype {}", window_visualid}
|
debug! {"Trying to map visualid to visualtype {}", window_visualid}
|
||||||
let visualtype = xcb_conn.get_setup().roots()
|
let visualtype = xcb_conn
|
||||||
|
.get_setup()
|
||||||
|
.roots()
|
||||||
.flat_map(|screen| screen.allowed_depths())
|
.flat_map(|screen| screen.allowed_depths())
|
||||||
.flat_map(|depth| depth.visuals())
|
.flat_map(|depth| depth.visuals())
|
||||||
.find(|visualtype| {visualtype.visual_id() == window_visualid})
|
.find(|visualtype| visualtype.visual_id() == window_visualid)
|
||||||
.expect("Could not match visualid to visualtype");
|
.expect("Could not match visualid to visualtype");
|
||||||
|
|
||||||
// xcb::Connection calls disconnect on the underlying xcb_connection_t
|
// xcb::Connection calls disconnect on the underlying xcb_connection_t
|
||||||
|
@ -37,20 +114,18 @@ where T: BorrowMut<*mut c_void>
|
||||||
visualtype
|
visualtype
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_context(window: &Window) -> cairo::Context
|
fn make_context(window: &Window) -> cairo::Context {
|
||||||
{
|
|
||||||
let winit_xcb_conn = window
|
let winit_xcb_conn = window
|
||||||
.xcb_connection()
|
.xcb_connection()
|
||||||
.expect("Could not get connection from window");
|
.expect("Could not get connection from window");
|
||||||
|
|
||||||
let xlib_window_id = window
|
let xlib_window_id = window.xlib_window().expect("Could not get xlib window");
|
||||||
.xlib_window()
|
|
||||||
.expect("Could not get xlib window");
|
|
||||||
|
|
||||||
let visual_type = unsafe {
|
let visual_type = unsafe {
|
||||||
let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32).base;
|
let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32).base;
|
||||||
cairo::XCBVisualType::from_raw_none(
|
cairo::XCBVisualType::from_raw_none(
|
||||||
&mut visual_type as *mut xcb::ffi::xcb_visualtype_t as *mut cairo::ffi::xcb_visualtype_t
|
&mut visual_type as *mut xcb::ffi::xcb_visualtype_t
|
||||||
|
as *mut cairo::ffi::xcb_visualtype_t,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,69 +141,8 @@ fn make_context(window: &Window) -> cairo::Context
|
||||||
&visual_type,
|
&visual_type,
|
||||||
window.inner_size().width as i32,
|
window.inner_size().width as i32,
|
||||||
window.inner_size().height as i32,
|
window.inner_size().height as i32,
|
||||||
).expect("Could not create drawing surface");
|
)
|
||||||
|
.expect("Could not create drawing surface");
|
||||||
|
|
||||||
cairo::Context::new(&surface).expect("Could not create drawing context")
|
cairo::Context::new(&surface).expect("Could not create drawing context")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, selection: usize)
|
|
||||||
{
|
|
||||||
let context = make_context(&window);
|
|
||||||
|
|
||||||
context.scale(30.0, 30.0);
|
|
||||||
|
|
||||||
let painter = painter::Painter::new(&context);
|
|
||||||
|
|
||||||
painter.background();
|
|
||||||
painter.input_box(0, 0, input);
|
|
||||||
painter.divider(0, 1);
|
|
||||||
|
|
||||||
result.iter().enumerate()
|
|
||||||
.for_each(|(i, r)| {
|
|
||||||
let e = r.0;
|
|
||||||
painter.result_box(0, 1+(i as u32), &e.source, &e.name, &r.1, selection==i);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn redraw_quick(window: &Window, result: Vec<(&Entry, Vec<usize>)>, selection: usize) {
|
|
||||||
let context = make_context(&window);
|
|
||||||
|
|
||||||
context.scale(30.0, 30.0);
|
|
||||||
|
|
||||||
let painter = painter::Painter::new(&context);
|
|
||||||
painter.border_clip();
|
|
||||||
|
|
||||||
result.iter().enumerate()
|
|
||||||
.for_each(|(i, r)| {
|
|
||||||
let e = r.0;
|
|
||||||
// clear first and last as these may produce artifacts otherwise
|
|
||||||
if i == 0 { painter.result_box(0, 1+(i as u32), &e.source, &e.name, &r.1, false) }
|
|
||||||
if i == result.len()-1 { painter.result_box(0, 1+(i as u32), &e.source, &e.name, &r.1, 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.source, &e.name, &r.1, false) }
|
|
||||||
if i == (selection+1) { painter.result_box(0, 1+(i as u32), &e.source, &e.name, &r.1, 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.source, &e.name, &r.1, true) }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_actions(window: &Window, actions: Vec<String>, input: &str)
|
|
||||||
{
|
|
||||||
let context = make_context(&window);
|
|
||||||
|
|
||||||
context.scale(30.0, 30.0);
|
|
||||||
|
|
||||||
let painter = painter::Painter::new(&context);
|
|
||||||
|
|
||||||
painter.background();
|
|
||||||
painter.input_box(0, 0, input);
|
|
||||||
painter.divider(0, 1);
|
|
||||||
|
|
||||||
actions.iter().enumerate()
|
|
||||||
.for_each(|(i, r)| {
|
|
||||||
painter.result_box(0, 1+(i as u32), &usize::to_string(&(i+1)), r, &[], false);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue