diff --git a/README.md b/README.md index fb8103b..c2ee34d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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. @@ -11,4 +11,4 @@ just found recently. Anyways. They found an even older git repository in an _ante-prehistoric_ cave. If your -mind is not blown away by now already: Here's a mirror. +mind is not blown away by now already: Here's a mirror. \ No newline at end of file diff --git a/default.toml b/default.toml index f0ffb67..1d92d36 100644 --- a/default.toml +++ b/default.toml @@ -6,9 +6,22 @@ log_level = "trace" "t" = "terminal" [sources.primary] -matcher = "Prefix" +matcher = "Flex" source = "Windows" [sources.additional] matcher = "Flex" 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] diff --git a/src/core/mod.rs b/src/core/mod.rs index 035a6e9..bdee4d2 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,6 +1,6 @@ -pub mod shared; pub mod roftl; pub mod settings; +pub mod shared; use super::matcher; use super::sources; diff --git a/src/core/roftl.rs b/src/core/roftl.rs index cdf9989..a1c798d 100644 --- a/src/core/roftl.rs +++ b/src/core/roftl.rs @@ -1,15 +1,14 @@ -use std::collections::HashMap; -use std::sync::RwLock; -use std::sync::Arc; use log::debug; use rayon::prelude::*; +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::RwLock; use super::matcher::PrefixMatcher; +use super::shared::Entry; use super::shared::Matcher; use super::shared::Source; -use super::shared::Entry; - pub type SourceRef = Box; pub type MatcherRef = Box; @@ -26,7 +25,7 @@ pub struct Roftl { primary_matcher: MatcherRef, entries: Vec, narrow_map: Vec, - completions: HashMap + completions: HashMap, } impl Roftl { @@ -39,18 +38,23 @@ impl Roftl { primary_matcher: PrefixMatcher::new(), entries: vec![], narrow_map: vec![], - completions: HashMap::default() + completions: HashMap::default(), } } pub fn with_primary_source(mut self, source: SourceRef) -> Self { self.primary_source = Some(source); - 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()} } @@ -79,7 +83,8 @@ impl Roftl { } fn source_primary(&mut self) { - self.entries = self.primary_source + self.entries = self + .primary_source .par_iter_mut() .flat_map(|s| s.entries()) .collect(); @@ -93,12 +98,18 @@ impl Roftl { let sources = self.sources.clone(); std::thread::spawn(move || { - let entries: Vec = sources.write().unwrap() - .par_iter_mut() - .flat_map(|s| s.entries()) - .collect(); + let entries: Vec = sources + .write() + .unwrap() + .par_iter_mut() + .flat_map(|s| s.entries()) + .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(); }); } @@ -110,7 +121,7 @@ impl Roftl { debug!("Processing {} additional entries", entries.len()); self.entries.append(&mut entries); } else { - debug!("No additional entries for processing (yet)"); + debug!("No additional entries for processing (yet)"); } } @@ -118,20 +129,26 @@ impl Roftl { // and not self is captured by the filter_map lambda let completions = &self.completions; let primary_matcher = &self.primary_matcher; - let mut scored_entries: Vec<(f64, usize, &Entry, Vec)> = self.entries + let mut scored_entries: Vec<(f64, usize, &Entry, Vec)> = self + .entries .par_iter() .enumerate() .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 .unwrap_or(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(); @@ -141,20 +158,22 @@ impl Roftl { let input = input.strip_prefix(",").unwrap_or(input); let matcher = &self.matcher; - scored_entries = self.entries + scored_entries = self + .entries .par_iter() .enumerate() .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); - return match_result.map(|(score, indices)| (score, idx, entry, indices) ) + 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()); + scored_entries.par_sort_by(|e1, e2| e1.0.partial_cmp(&e2.0).unwrap()); self.narrow_map.clear(); for se in &scored_entries { @@ -171,7 +190,13 @@ impl Roftl { let (entry, source) = self.find_selection(selection_id); match source { 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,23 +204,38 @@ impl Roftl { let (entry, source) = self.find_selection(selection_id); match source { 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) { let (entry, source) = self.find_selection(selection_id); match source { - usize::MAX => self.primary_source.as_ref().unwrap().exec_action(entry, action), - _ => self.sources.read().unwrap().get(source).unwrap().exec_action(entry, action) + usize::MAX => self + .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 = &self.entries[entry_id]; - debug!{"Got entry {:?} for id {} ", entry, entry_id}; + debug! {"Got entry {:?} for id {} ", entry, entry_id}; match self.primary_source { Some(ref s) if s.name().eq(entry.source) => return (entry, usize::MAX), diff --git a/src/core/settings.rs b/src/core/settings.rs index 2a0ed1b..82d2324 100644 --- a/src/core/settings.rs +++ b/src/core/settings.rs @@ -1,17 +1,37 @@ -use std::collections::HashMap; -use config::ConfigError; use config::Config; +use config::ConfigError; 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)] pub struct Theme { + color_scheme: ColorScheme, + font: (String, i32), border: f64, - divider: f64 + divider: f64, } #[derive(Debug, Deserialize)] pub enum Matcher { - Prefix, Fuse, Flex, Skim + Prefix, + Fuse, + Flex, + Skim, } impl Matcher { @@ -20,14 +40,17 @@ impl Matcher { Matcher::Prefix => super::matcher::PrefixMatcher::new(), Matcher::Fuse => super::matcher::FuseMatcher::new(), Matcher::Flex => super::matcher::FlxMatcher::new(), - Matcher::Skim => super::matcher::SkimMatcher::new() + Matcher::Skim => super::matcher::SkimMatcher::new(), } } } #[derive(Debug, Deserialize)] pub enum Source { - Apps, Shell, Windows, Test + Apps, + Shell, + Windows, + Test, } impl Source { @@ -36,7 +59,7 @@ impl Source { Source::Apps => super::sources::Apps::new(), Source::Shell => super::sources::ShellHost::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)] pub struct SourceConfig { matcher: Matcher, - source: T + source: T, } #[derive(Debug, Deserialize)] @@ -56,16 +79,20 @@ pub struct Sources { #[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase", remote = "log::Level")] enum LogLevelDef { - Error, Warn, Info, Debug, Trace, + Error, + Warn, + Info, + Debug, + Trace, } - #[derive(Debug, Deserialize)] pub struct Settings { #[serde(with = "LogLevelDef")] log_level: log::Level, completions: HashMap, sources: Sources, + theme: Theme, } impl Settings { @@ -74,10 +101,12 @@ impl Settings { config.merge(config::File::with_name("default"))?; - config.merge(config::Environment::new() - .prefix("roftl") - .separator("_") - .ignore_empty(true))?; + config.merge( + config::Environment::new() + .prefix("roftl") + .separator("_") + .ignore_empty(true), + )?; config.try_into() } @@ -95,7 +124,9 @@ impl Settings { } pub fn sources(&self) -> Vec> { - self.sources.additional.source + self.sources + .additional + .source .iter() .map(|s| s.init()) .collect() diff --git a/src/core/shared.rs b/src/core/shared.rs index 55b1610..dfd3f5d 100644 --- a/src/core/shared.rs +++ b/src/core/shared.rs @@ -9,11 +9,13 @@ pub struct Entry { pub trait Source: Send + Sync { fn name(&self) -> &'static str; fn entries(&mut self) -> Vec; - fn actions(&self, _entry: &Entry) -> Vec - { vec![] } + fn actions(&self, _entry: &Entry) -> Vec { + vec![] + } - fn exec_default(&self, entry: &Entry) - { self.exec_action(entry, 0) } + fn exec_default(&self, entry: &Entry) { + self.exec_action(entry, 0) + } fn exec_action(&self, entry: &Entry, action: u8); } diff --git a/src/main.rs b/src/main.rs index 612ee90..2e908fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,25 +1,27 @@ use log::{debug, trace}; 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::{ElementState, Event, KeyboardInput, VirtualKeyCode, - WindowEvent::{CloseRequested, ReceivedCharacter}}; +use winit::window::{Window, WindowBuilder}; use self::core::roftl::Roftl; -mod ui; -mod sources; -mod matcher; mod core; +mod matcher; +mod sources; +mod ui; fn main() -> Result<(), Box> { env_logger::init(); let settings = core::settings::Settings::parse()?; - debug!{"Settings {:?}", settings} + debug! {"Settings {:?}", settings} - debug!{"Set up roftl"}; + debug! {"Set up roftl"}; let mut roftl = Roftl::new() .with_primary_source(settings.primary_source()) .with_primary_matcher(settings.primary_matcher()) @@ -30,10 +32,10 @@ fn main() -> Result<(), Box> { roftl = roftl.add_source(source); } - debug!{"Source roftl sources"} + debug! {"Source roftl sources"} roftl.source(); - debug!{"Build window"} + debug! {"Build window"} let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_decorations(false) @@ -41,12 +43,12 @@ fn main() -> Result<(), Box> { .build(&event_loop) .expect("Could not create window"); - debug!{"Window id: {:?}", window.id()} + debug! {"Window id: {:?}", window.id()} - debug!{"Draw primary state to window"} + debug! {"Draw primary state to window"} ui::draw(&window, "", roftl.narrow(""), 0); - debug!{"Start event loop"} + debug! {"Start event loop"} let roftl_loop = RoftlLoop::new(roftl, window); roftl_loop.run(event_loop); } @@ -66,51 +68,55 @@ struct RoftlLoop { } impl RoftlLoop { - fn new(roftl: Roftl, window: Window) -> Self - { - RoftlLoop{input_buffer: String::default(), input_changed: true, selection: 0, - mode: Mode::Selection, - roftl, window} + fn new(roftl: Roftl, window: Window) -> Self { + RoftlLoop { + input_buffer: String::default(), + input_changed: true, + selection: 0, + mode: Mode::Selection, + roftl, + window, + } } - fn run(mut self, event_loop: EventLoop) -> ! - { + fn run(mut self, event_loop: EventLoop) -> ! { event_loop.run(move |evt, _win, flow| { *flow = ControlFlow::Wait; match evt { - Event::WindowEvent{event: CloseRequested, window_id} - if window_id == self.window.id() => - { + Event::WindowEvent { + event: CloseRequested, + window_id, + } if window_id == self.window.id() => { *flow = ControlFlow::Exit; } - Event::WindowEvent{event: ReceivedCharacter(character), window_id} - if window_id == self.window.id() - && matches!(self.mode, Mode::Selection) => - { + Event::WindowEvent { + event: ReceivedCharacter(character), + window_id, + } if window_id == self.window.id() && matches!(self.mode, Mode::Selection) => { *flow = self.process_character(character); } - Event::WindowEvent{event: ReceivedCharacter(character), window_id} - if window_id == self.window.id() - && matches!(self.mode, Mode::Actions) => - { + Event::WindowEvent { + event: ReceivedCharacter(character), + window_id, + } if window_id == self.window.id() && matches!(self.mode, Mode::Actions) => { *flow = self.process_action(character); } - Event::WindowEvent{event: winit::event::WindowEvent::KeyboardInput{input, ..}, window_id} - if window_id == self.window.id() => - { + 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() - && matches!{self.mode, Mode::Selection} => + if window_id == self.window.id() && matches! {self.mode, Mode::Selection} => { let results = self.roftl.narrow(&self.input_buffer); - trace!{"Narrow result {:?}", results} + trace! {"Narrow result {:?}", results} // quick switch if only one result // but make sure that input buffer is filled, otherwise will immediately close @@ -122,14 +128,17 @@ impl RoftlLoop { } // correct selection for results - if results.len() > 0 { self.selection = self.selection % results.len() } - else { self.selection = 0 } + 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} + trace! {"Redrawing with input {}", self.input_buffer} ui::draw(&self.window, &self.input_buffer, results, self.selection); } else { - trace!{"Quick redraw with input {}", self.input_buffer} + trace! {"Quick redraw with input {}", self.input_buffer} ui::redraw_quick(&self.window, results, self.selection); } @@ -137,34 +146,37 @@ impl RoftlLoop { } Event::RedrawRequested(window_id) - if window_id == self.window.id() - && matches!{self.mode, Mode::Actions} => + if window_id == self.window.id() && matches! {self.mode, Mode::Actions} => { - let actions = self.roftl.actions(self.selection); - debug!{"Actions for current selection: {:?}", actions} + let actions = self.roftl.actions(self.selection); + debug! {"Actions for current selection: {:?}", actions} ui::draw_actions(&self.window, actions, &self.input_buffer); } - _ => () + _ => (), } }); - } 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) { (VirtualKeyCode::Down, ElementState::Released) => { debug!("Received down"); ControlFlow::Wait - }, + } (VirtualKeyCode::Up, ElementState::Released) => { debug!("Received up"); ControlFlow::Wait - }, + } _ => ControlFlow::Wait, - } + }; } ControlFlow::Wait @@ -172,14 +184,14 @@ impl RoftlLoop { fn process_character(&mut self, character: char) -> ControlFlow { match character { - 'q' | 'Q' => ControlFlow::Exit, + 'q' | 'Q' => ControlFlow::Exit, // Escape c if c == char::from(0x1b) => ControlFlow::Exit, // Backspace c if c == char::from(0x08) => { - trace!{"Retrieved backspace. Pop char from input"} + trace! {"Retrieved backspace. Pop char from input"} self.input_buffer.pop(); self.input_changed = true; self.window.request_redraw(); @@ -188,14 +200,14 @@ impl RoftlLoop { // Enter c if c == char::from(0x0d) => { - trace!{"Retrieved enter. Trigger primary action"} + trace! {"Retrieved enter. Trigger primary action"} self.roftl.exec_action(self.selection, 0); ControlFlow::Exit } // Ctrl+n c if c == char::from(0x0e) => { - debug!{"Received next"} + debug! {"Received next"} self.selection += 1; self.window.request_redraw(); ControlFlow::Wait @@ -203,30 +215,38 @@ impl RoftlLoop { // Ctrl+p c if c == char::from(0x10) => { - debug!{"Received previous"} - if self.selection != 0 { self.selection -= 1 } + debug! {"Received previous"} + if self.selection != 0 { + self.selection -= 1 + } self.window.request_redraw(); ControlFlow::Wait } // tab c if c == char::from(0x09) => { - debug!{"Received actions"} + debug! {"Received actions"} match self.mode { - Mode::Selection => { self.mode = Mode::Actions; self.input_changed = true }, - Mode::Actions => { self.mode = Mode::Selection; self.input_changed = true } + Mode::Selection => { + self.mode = Mode::Actions; + self.input_changed = true + } + Mode::Actions => { + self.mode = Mode::Selection; + self.input_changed = true + } } self.window.request_redraw(); ControlFlow::Wait } c if c.is_control() => { - debug!{"Got unknown control character {:#x}", u32::from(c)} + debug! {"Got unknown control character {:#x}", u32::from(c)} ControlFlow::Wait } _ => { - trace!{"Push {} to input buffer", character} + trace! {"Push {} to input buffer", character} self.input_buffer.push(character); self.input_changed = true; self.window.request_redraw(); @@ -238,47 +258,52 @@ impl RoftlLoop { fn process_action(&mut self, character: char) -> ControlFlow { match character { '1' => { - trace!{"Retrieved action selection 1. Trigger primary action"} + trace! {"Retrieved action selection 1. Trigger primary action"} self.roftl.exec_action(self.selection, 0); ControlFlow::Exit - }, + } '2' => { - trace!{"Retrieved action selection 2. Trigger secondary action"} + trace! {"Retrieved action selection 2. Trigger secondary action"} self.roftl.exec_action(self.selection, 1); ControlFlow::Exit - }, + } '3' => { - trace!{"Retrieved action selection 3. Trigger tertiary action"} + trace! {"Retrieved action selection 3. Trigger tertiary action"} self.roftl.exec_action(self.selection, 2); ControlFlow::Exit - }, + } '4' => { - trace!{"Retrieved action selection 4. Trigger quaternary action"} + trace! {"Retrieved action selection 4. Trigger quaternary action"} self.roftl.exec_action(self.selection, 3); ControlFlow::Exit - }, + } - 'q' | 'Q' => ControlFlow::Exit, + 'q' | 'Q' => ControlFlow::Exit, // Escape c if c == char::from(0x1b) => ControlFlow::Exit, // tab c if c == char::from(0x09) => { - debug!{"Received actions"} + debug! {"Received actions"} match self.mode { - Mode::Selection => { self.mode = Mode::Actions; self.input_changed = true }, - Mode::Actions => { self.mode = Mode::Selection; self.input_changed = true } + Mode::Selection => { + self.mode = Mode::Actions; + self.input_changed = true + } + Mode::Actions => { + self.mode = Mode::Selection; + self.input_changed = true + } } self.window.request_redraw(); ControlFlow::Wait - }, + } - _ => { - debug!{"Retrieved unknown action selection. Processing character {}", character} - ControlFlow::Wait - }, + _ => { + debug! {"Retrieved unknown action selection. Processing character {}", character} + ControlFlow::Wait + } } } - } diff --git a/src/matcher/flx.rs b/src/matcher/flx.rs index 1b6fdb7..27d3ab0 100644 --- a/src/matcher/flx.rs +++ b/src/matcher/flx.rs @@ -7,19 +7,17 @@ pub struct FlxMatcher; impl FlxMatcher { pub fn new() -> Box { - Box::new(FlxMatcher{}) + Box::new(FlxMatcher {}) } } impl Matcher for FlxMatcher { fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec)> { - debug!{"Searching {} in {}", needle, haystack} + debug! {"Searching {} in {}", needle, haystack} let res: Option = flx_rs::score(haystack, needle); res.and_then(|score| { let s = score.score as f64; - let i = score.indices.into_iter() - .map(|idx| idx as usize) - .collect(); + let i = score.indices.into_iter().map(|idx| idx as usize).collect(); Some((s, i)) }) diff --git a/src/matcher/fuse.rs b/src/matcher/fuse.rs index 3ab24f8..e89a76f 100644 --- a/src/matcher/fuse.rs +++ b/src/matcher/fuse.rs @@ -5,18 +5,20 @@ use fuse_rust::Fuse; use super::core::shared::Matcher; fn flatten_ranges(ranges: Vec>) -> Vec { - ranges.into_iter().flat_map(|range| {range.collect::>().into_iter()}) + ranges + .into_iter() + .flat_map(|range| range.collect::>().into_iter()) .collect() } pub struct FuseMatcher { - matcher: Fuse + matcher: Fuse, } impl FuseMatcher { pub fn new() -> Box { - Box::new(FuseMatcher{ - matcher: Fuse::default() + Box::new(FuseMatcher { + matcher: Fuse::default(), }) } } @@ -25,9 +27,7 @@ impl Matcher for FuseMatcher { fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec)> { match self.matcher.search_text_in_string(needle, haystack) { Some(score) => Some((score.score, flatten_ranges(score.ranges))), - None => None + None => None, } } } - - diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs index d9b4bd5..050441b 100644 --- a/src/matcher/mod.rs +++ b/src/matcher/mod.rs @@ -11,4 +11,3 @@ pub use fuse::FuseMatcher; mod flx; pub use flx::FlxMatcher; - diff --git a/src/matcher/prefix.rs b/src/matcher/prefix.rs index af5c074..cf778db 100644 --- a/src/matcher/prefix.rs +++ b/src/matcher/prefix.rs @@ -4,7 +4,7 @@ pub struct PrefixMatcher; impl PrefixMatcher { pub fn new() -> Box { - Box::new(PrefixMatcher{}) + Box::new(PrefixMatcher {}) } } diff --git a/src/matcher/skim.rs b/src/matcher/skim.rs index 11ede15..0e70051 100644 --- a/src/matcher/skim.rs +++ b/src/matcher/skim.rs @@ -1,15 +1,15 @@ -use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2}; +use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; use super::core::shared::Matcher; pub struct SkimMatcher { - matcher: SkimMatcherV2 + matcher: SkimMatcherV2, } impl SkimMatcher { pub fn new() -> Box { - Box::new(SkimMatcher{ - matcher: SkimMatcherV2::default() + Box::new(SkimMatcher { + matcher: SkimMatcherV2::default(), }) } } @@ -17,8 +17,8 @@ impl SkimMatcher { impl Matcher for SkimMatcher { fn try_match(&self, haystack: &str, needle: &str) -> Option<(f64, Vec)> { match self.matcher.fuzzy_indices(haystack, needle) { - Some((score, indices)) => Some(((i64::MAX as f64/score as f64), indices)), - None => None + Some((score, indices)) => Some(((i64::MAX as f64 / score as f64), indices)), + None => None, } } } diff --git a/src/sources/apps.rs b/src/sources/apps.rs index f96988d..f05b545 100644 --- a/src/sources/apps.rs +++ b/src/sources/apps.rs @@ -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 log::{debug, error, trace}; -use walkdir::WalkDir; use nix::unistd::{getpid, setpgid}; +use walkdir::WalkDir; use super::core::shared::{Entry, Source}; @@ -11,7 +14,7 @@ use super::core::shared::{Entry, Source}; struct App { name: String, command: String, - terminal: bool + terminal: bool, } pub fn parse_command_string<'a>(exec: &'a str) -> (String, Vec) { @@ -27,13 +30,13 @@ pub fn run_bg(mut command: Command) { command.pre_exec(|| { let pid = getpid(); if let Err(_) = setpgid(pid, pid) { - error!{"Failed to set pgid of child process with pid {}", pid}; + error! {"Failed to set pgid of child process with pid {}", pid}; } Ok(()) }); } - debug!{"Running command {:?}", command} + debug! {"Running command {:?}", command} command.spawn().unwrap(); } @@ -50,27 +53,23 @@ impl App { command.args(&args); let _ = run_bg(command); } else { - - debug!("Running `{} {}`", cmd, args.join(" ")); - let mut command = Command::new(&cmd); - command.args(&args); - let _ = run_bg(command); + debug!("Running `{} {}`", cmd, args.join(" ")); + let mut command = Command::new(&cmd); + command.args(&args); + let _ = run_bg(command); } } } -impl From<&App> for Entry -{ +impl From<&App> for Entry { fn from(app: &App) -> Self { Entry { name: app.name.clone(), description: "".into(), source: "apps", - identifier: 0 + identifier: 0, } } - - } #[derive(Default, Debug)] @@ -78,45 +77,52 @@ struct AppBuilder<'a> { name: Option<&'a str>, exec: Option<&'a str>, hidden: Option<&'a str>, - terminal: Option<&'a str> + terminal: Option<&'a str>, } -impl<'a> AppBuilder<'a> -{ - fn build(&self) -> Option - { - if self.name.is_none() { return None } - if self.exec.is_none() { return None } - if self.hidden.is_some() { return None } +impl<'a> AppBuilder<'a> { + fn build(&self) -> Option { + if self.name.is_none() { + return None; + } + if self.exec.is_none() { + return None; + } + if self.hidden.is_some() { + return None; + } - Some ( - App { - name: self.name.unwrap().into(), - command: strip_entry_args(self.exec.unwrap()).into(), - terminal: self.terminal.map_or(false, |term| term.parse().unwrap_or(false)) - }) + Some(App { + name: self.name.unwrap().into(), + command: strip_entry_args(self.exec.unwrap()).into(), + terminal: self + .terminal + .map_or(false, |term| term.parse().unwrap_or(false)), + }) } } pub struct Apps { - entries: Vec + entries: Vec, } impl Apps { - pub fn new() -> Box - { - Box::new(Apps{entries: vec![]}) + pub fn new() -> Box { + Box::new(Apps { entries: vec![] }) } } fn strip_entry_args(exec: &str) -> String { - trace!{"Stripping {}", exec} + trace! {"Stripping {}", exec} let iter = exec.split(' '); - let result = iter.filter(|item| !item.starts_with('%')) + let result = iter + .filter(|item| !item.starts_with('%')) .collect::>() - .join(" "); + .join(" ") + .trim_matches('"') + .into(); - trace!{"Stripped into {}", result} + trace! {"Stripped into {}", result} result } @@ -130,7 +136,7 @@ impl Source for Apps { "/usr/share/applications/", "/home/armin/.local/share/applications/", "/var/lib/snapd/desktop/applications", - "/var/lib/flatpak/exports/share/applications" + "/var/lib/flatpak/exports/share/applications", ]; let desktop_entries: Vec = paths @@ -147,41 +153,37 @@ impl Source for Apps { let mut app_builder = AppBuilder::default(); let section = entry.section("Desktop Entry"); - for attr in section.attrs() - { - match attr.name - { + for attr in section.attrs() { + match attr.name { "Name" => app_builder.name = attr.value, "Exec" => app_builder.exec = attr.value, "NoDisplay" => app_builder.hidden = attr.value, "Hidden" => app_builder.hidden = 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(); entry.identifier = idx; idx += 1; entries.push(entry); self.entries.push(app); } - } - trace!{"Got desktop entries: {:?}", entries} + trace! {"Got desktop entries: {:?}", entries} entries } fn exec_action(&self, entry: &Entry, action: u8) { - debug!{"Got desktop entry {:?} for action", entry} - debug!{"Desktop entry has id {}", entry.identifier} - trace!{"Apps has entries {:?}", self.entries} + debug! {"Got desktop entry {:?} for action", entry} + debug! {"Desktop entry has id {}", entry.identifier} + trace! {"Apps has entries {:?}", self.entries} let app = self.entries.get(entry.identifier as usize).unwrap(); - debug!{"Doing primary action {} for {}", app.name, app.command} + debug! {"Doing primary action {} for {}", app.name, app.command} app.run(); } } diff --git a/src/sources/shellhost.rs b/src/sources/shellhost.rs index 802ed89..41e2f62 100644 --- a/src/sources/shellhost.rs +++ b/src/sources/shellhost.rs @@ -1,6 +1,10 @@ -use std::{os::unix::prelude::CommandExt, path::PathBuf, process::{Command, Stdio}}; use log::{debug, error}; use nix::unistd::{getpid, setpgid}; +use std::{ + os::unix::prelude::CommandExt, + path::PathBuf, + process::{Command, Stdio}, +}; use walkdir::WalkDir; use super::core::shared::{Entry, Source}; @@ -11,7 +15,7 @@ pub struct ShellHost { impl ShellHost { pub fn new() -> Box { - Box::new(ShellHost{ scripts: vec![] }) + Box::new(ShellHost { scripts: vec![] }) } } @@ -25,35 +29,34 @@ impl Source for ShellHost { let script_path = "/home/armin/dev/incubator/roftl/sh"; // TODO make robust if path does not exist - let scripts: Vec = WalkDir::new(script_path).into_iter() + let scripts: Vec = WalkDir::new(script_path) + .into_iter() .map(|e| e.unwrap().path().to_path_buf()) .collect(); let mut entries: Vec = vec![]; for (idx, script) in scripts.iter().enumerate() { - entries.push( - Entry { - source: self.name(), - name: script.file_stem().unwrap().to_str().unwrap().into(), - description: "".into(), - identifier: idx as u64 - }); + entries.push(Entry { + source: self.name(), + name: script.file_stem().unwrap().to_str().unwrap().into(), + description: "".into(), + identifier: idx as u64, + }); } self.scripts = scripts; entries } fn exec_action(&self, entry: &Entry, action: u8) { - let script: &PathBuf = self.scripts.get(entry.identifier as usize).unwrap(); - debug!{"Got script {:?}", script}; + debug! {"Got script {:?}", script}; let mut command = Command::new("xfce4-terminal"); let args = vec!["-x", script.to_str().unwrap()]; command.args(&args); let _ = run_bg(command); - debug!{"Executed {:?}", script}; + debug! {"Executed {:?}", script}; } } @@ -63,12 +66,12 @@ pub fn run_bg(mut command: Command) { command.pre_exec(|| { let pid = getpid(); if let Err(_) = setpgid(pid, pid) { - error!{"Failed to set pgid of child process with pid {}", pid}; + error! {"Failed to set pgid of child process with pid {}", pid}; } Ok(()) }); } - debug!{"Running command {:?}", command} + debug! {"Running command {:?}", command} command.spawn().unwrap(); } diff --git a/src/sources/test.rs b/src/sources/test.rs index b500517..116dae2 100644 --- a/src/sources/test.rs +++ b/src/sources/test.rs @@ -44,7 +44,8 @@ impl Source for TestSource { fn actions(&self, _entry: &Entry) -> Vec { ["Primary", "Secondary", "Tertiary", "Quaternary"] - .iter().map(|s| (*s).into()).collect() + .iter() + .map(|s| (*s).into()) + .collect() } } - diff --git a/src/sources/windows.rs b/src/sources/windows.rs index 909a302..0ff4aca 100644 --- a/src/sources/windows.rs +++ b/src/sources/windows.rs @@ -8,15 +8,21 @@ use xcb_util::ffi::ewmh::XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER; use xcb_util::icccm; pub struct Window { - action_data: HashMap + action_data: HashMap, } impl Window { pub fn new() -> Box { - Box::new(Window { action_data: HashMap::::new() }) + Box::new(Window { + action_data: HashMap::::new(), + }) } - fn is_normal_window(&self, con: &ewmh::Connection, window: u32) -> Result { + fn is_normal_window( + &self, + con: &ewmh::Connection, + window: u32, + ) -> Result { let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?; for atom in wm_type_reply.atoms() { @@ -28,48 +34,95 @@ impl Window { Ok(false) } - fn window_category(&self, con: &ewmh::Connection, window: u32) -> Result { - Ok(icccm::get_wm_class(&con, window).get_reply()?.class().into()) + fn window_category( + &self, + con: &ewmh::Connection, + window: u32, + ) -> Result { + Ok(icccm::get_wm_class(&con, window) + .get_reply()? + .class() + .into()) } fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result { 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 active_window = ewmh::get_active_window(con, screen).get_reply()?; 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::set_active_window(con, screen, window).request_check().unwrap(); + ewmh::request_change_active_window( + 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); 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_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con); let action_atom = ewmh::STATE_TOGGLE; - debug!{"Toggle maximize for {}", window} - ewmh::request_change_wm_state(con, screen, window, action_atom, max_horz_atom, max_vert_atom, 0).request_check()?; + debug! {"Toggle maximize for {}", window} + ewmh::request_change_wm_state( + con, + screen, + window, + action_atom, + max_horz_atom, + max_vert_atom, + 0, + ) + .request_check()?; Ok(()) } - fn close_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::ReplyError> { - debug!{"Toggle maximize for {}", window} + fn close_window( + &self, + con: &ewmh::Connection, + window: u32, + screen: i32, + ) -> Result<(), xcb::ReplyError> { + debug! {"Toggle maximize for {}", window} 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 action_atom = ewmh::STATE_TOGGLE; - debug!{"Toggle hidden for {}", window} - ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0).request_check()?; + debug! {"Toggle hidden for {}", window} + ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0) + .request_check()?; Ok(()) } @@ -81,10 +134,15 @@ impl Source for Window { } fn entries(&mut self) -> Vec { - let (xcb_conn, screen_num) = 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 (xcb_conn, screen_num) = + 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 mut entries: Vec = vec![]; @@ -92,7 +150,7 @@ impl Source for Window { let mut count: u64 = 0; for w in windows { match self.is_normal_window(&ewmh_conn, *w) { - Ok(true) => {}, + Ok(true) => {} Ok(false) => continue, Err(_err) => {} } @@ -102,10 +160,15 @@ impl Source for Window { 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)); - count+=1; + count += 1; } entries @@ -117,36 +180,54 @@ impl Source for Window { match action { 0 => { 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 ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap(); + let (xcb_conn, _screen_num) = + 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() - }, + } 1 => { 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 ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap(); - self.toggle_maximize_window(&ewmh_conn, window, screen).unwrap(); + let (xcb_conn, _screen_num) = + 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_maximize_window(&ewmh_conn, window, screen) + .unwrap(); self.switch_window(&ewmh_conn, window, screen).unwrap() - }, + } 2 => { 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 ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap(); + let (xcb_conn, _screen_num) = + 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(); - }, + } 3 => { 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 ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap(); + let (xcb_conn, _screen_num) = + 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(); - }, - _ => panic!{"Unknown action {:?}", action} + } + _ => panic! {"Unknown action {:?}", action}, } } fn actions(&self, _entry: &Entry) -> Vec { - vec!["Switch".into(), "Toggle maximize".into(), "Toggle hide".into(), "Close".into()] + vec![ + "Switch".into(), + "Toggle maximize".into(), + "Toggle hide".into(), + "Close".into(), + ] } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ea7b154..14a836c 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -2,8 +2,8 @@ use super::core; mod ui; pub use ui::draw; -pub use ui::redraw_quick; pub use ui::draw_actions; +pub use ui::redraw_quick; mod painter; mod theme; diff --git a/src/ui/painter.rs b/src/ui/painter.rs index 7711011..9c5a70d 100644 --- a/src/ui/painter.rs +++ b/src/ui/painter.rs @@ -9,11 +9,13 @@ pub struct Painter<'a> { impl<'a> Painter<'a> { 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; // reset clip so that we can draw the entire background of the surface @@ -25,42 +27,47 @@ impl<'a> Painter<'a> { ctx.paint().expect("Could not paint background"); // draw the border - let (x1,y1,x2,y2) = ctx.clip_extents().unwrap(); - debug!{"Border width: {}", self.theme.border(ctx)}; - let border = self.theme.border(ctx)/2.0; - let (x1,y1,x2,y2) = (x1+border,y1+border,x2-x1-border,y2-y1-border); - debug!{"Border rect: {},{},{},{}", x1, y1, x2, y2}; - ctx.rectangle(x1,y1,x2-x1,y2-y1); + let (x1, y1, x2, y2) = ctx.clip_extents().unwrap(); + debug! {"Border width: {}", self.theme.border(ctx)}; + let border = self.theme.border(ctx) / 2.0; + let (x1, y1, x2, y2) = (x1 + border, y1 + border, x2 - x1 - border, y2 - y1 - border); + debug! {"Border rect: {},{},{},{}", x1, y1, x2, y2}; + ctx.rectangle(x1, y1, x2 - x1, y2 - y1); ctx.theme_border(&self.theme); ctx.stroke().unwrap(); self.border_clip(); } - pub fn border_clip(&self) - { + pub fn border_clip(&self) { let ctx = self.context; ctx.reset_clip(); // clip any future content so the border stays alive - let (x1,y1,x2,y2) = ctx.clip_extents().unwrap(); // the path of the clip - // region, i.e. the surface + let (x1, y1, x2, y2) = ctx.clip_extents().unwrap(); // the path of the clip + // region, i.e. the surface - debug!{"Border extent: {},{},{},{}", x1, y1, x2, y2}; + debug! {"Border extent: {},{},{},{}", x1, y1, x2, y2}; let border = self.theme.border(ctx); // width of the border, note // that half of the border is outside // the clip_extents - // let (x1,y1,x2,y2) = (x1+border, y1+border, x2-border, y2-border); - debug!{"Border rect: {},{},{},{}", x1, y1, x2, y2}; - let (x1,y1,x2,y2) = (x1+border,y1+border,x2-x1-border,y2-y1-border-border); - debug!{"Border clip: {},{},{},{}", x1, y1, x2, y2}; - ctx.rectangle(x1,y1,x2,y2); + // let (x1,y1,x2,y2) = (x1+border, y1+border, x2-border, y2-border); + debug! {"Border rect: {},{},{},{}", x1, y1, x2, y2}; + let (x1, y1, x2, y2) = ( + x1 + border, + y1 + border, + x2 - x1 - border, + y2 - y1 - border - border, + ); + debug! {"Border clip: {},{},{},{}", x1, y1, x2, y2}; + ctx.rectangle(x1, y1, x2, y2); ctx.clip(); } pub fn input_box(&self, x: T, y: T, input: &'a str) - where T: Into + where + T: Into, { let ctx = self.context; let (x, y): (f64, f64) = (x.into(), y.into()); @@ -75,28 +82,40 @@ impl<'a> Painter<'a> { ctx.theme_text(&input, false, &[], &self.theme); } - pub fn result_box(&self, x: T, y: T, source: &'a str, result: &'a str, indices: &[usize], selected: bool) - where T: Into + pub fn result_box( + &self, + x: T, + y: T, + source: &'a str, + result: &'a str, + indices: &[usize], + selected: bool, + ) where + T: Into, { let ctx = self.context; let (x, y): (f64, f64) = (x.into(), y.into()); // draw background box ctx.rectangle(x.into(), y.into(), self.clip_width(), 1.0); - if selected { ctx.theme_color_highlight(&self.theme) } - else { ctx.theme_color_base(&self.theme) }; + if selected { + ctx.theme_color_highlight(&self.theme) + } else { + ctx.theme_color_base(&self.theme) + }; ctx.set_operator(cairo::Operator::Source); ctx.fill().unwrap(); // draw text - ctx.move_to (x+0.2, y); + ctx.move_to(x + 0.2, y); ctx.theme_text(&source, selected, &[], &self.theme); - ctx.move_to (x+3.5, y); + ctx.move_to(x + 3.5, y); ctx.theme_text(&result, selected, indices, &self.theme); } pub fn divider(&self, x: T, y: T) - where T: Into + where + T: Into, { let ctx = self.context; let (x, y): (f64, f64) = (x.into(), y.into()); @@ -106,21 +125,24 @@ impl<'a> Painter<'a> { ctx.line_to(self.clip_width(), y); ctx.stroke().unwrap(); -// ctx.rectangle(x, y+dash_width, self.clip_width(), self.clip_height()-dash_width); -// ctx.clip(); + // ctx.rectangle(x, y+dash_width, self.clip_width(), self.clip_height()-dash_width); + // ctx.clip(); } 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 { - let (x1,_y1,x2,_y2) = self.context.clip_extents().unwrap(); + let (x1, _y1, x2, _y2) = self.context.clip_extents().unwrap(); x2 - x1 } fn clip_height(&self) -> f64 { - let (_x1,y1,_x2,y2) = self.context.clip_extents().unwrap(); + let (_x1, y1, _x2, y2) = self.context.clip_extents().unwrap(); y2 - y1 } } diff --git a/src/ui/theme.rs b/src/ui/theme.rs index 08aaf53..4e7e0b2 100644 --- a/src/ui/theme.rs +++ b/src/ui/theme.rs @@ -14,7 +14,7 @@ struct ColorScheme { divider: Color, text: Color, - text_highlight: Color + text_highlight: Color, } pub struct Theme { @@ -22,7 +22,7 @@ pub struct Theme { font: Font, border: f64, - divider: f64 + divider: f64, } impl Theme { @@ -41,7 +41,7 @@ impl Default for Theme { divider: Color(0.23, 0.05, 0.11, 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 { @@ -49,7 +49,7 @@ impl Default for Theme { font: Font("Sans Regular".into(), 13), border: 2.0, - divider: 3.0 + divider: 3.0, } } } @@ -63,24 +63,23 @@ pub trait ThemedContextExt { fn theme_text(&self, text: &str, selected: bool, indices: &[usize], theme: &Theme); - fn device_to_user_point(&self, point: f64) -> f64; } impl ThemedContextExt for cairo::Context { fn theme_color_base(&self, theme: &Theme) { - let Color(r,g,b,a) = theme.colors.base; - self.set_source_rgba(r,g,b,a); + let Color(r, g, b, a) = theme.colors.base; + self.set_source_rgba(r, g, b, a); } fn theme_color_highlight(&self, theme: &Theme) { - let Color(r,g,b,a) = theme.colors.highlight; - self.set_source_rgba(r,g,b,a); + let Color(r, g, b, a) = theme.colors.highlight; + self.set_source_rgba(r, g, b, a); } fn theme_border(&self, theme: &Theme) { - let Color(r,g,b,a) = theme.colors.border; - self.set_source_rgba(r,g,b,a); + let Color(r, g, b, a) = theme.colors.border; + self.set_source_rgba(r, g, b, a); let border_width = self.device_to_user_point(theme.border); self.set_line_width(border_width); } @@ -91,7 +90,7 @@ impl ThemedContextExt for cairo::Context { self.set_dash(&[self.device_to_user_point(3.0)], 0.0); - let Color(r,g,b,a) = theme.colors.divider; + let Color(r, g, b, a) = theme.colors.divider; self.set_source_rgba(r, g, b, a); } @@ -102,16 +101,19 @@ impl ThemedContextExt for cairo::Context { // gets totally confused by active cairo transformations // which then messes with font size, size of attributes, etc - let Color(r,g,b,a) = if selected { theme.colors.text_highlight } - else { theme.colors.text }; - self.set_source_rgba(r,g,b,a); + let Color(r, g, b, a) = if selected { + theme.colors.text_highlight + } else { + theme.colors.text + }; + self.set_source_rgba(r, g, b, a); let layout = pangocairo::create_layout(self).unwrap(); pangocairo::update_layout(self, &layout); let attrlist = AttrList::new(); for i in indices { - let (start, end) = ((*i as u32), (*i as u32)+1); + let (start, end) = ((*i as u32), (*i as u32) + 1); let mut attr = Attribute::new_underline(pangocairo::pango::Underline::Single); attr.set_start_index(start); attr.set_end_index(end); @@ -121,18 +123,18 @@ impl ThemedContextExt for cairo::Context { let mut font = FontDescription::default(); font.set_family(&theme.font.0); - font.set_size(theme.font.1*pango::SCALE); + font.set_size(theme.font.1 * pango::SCALE); layout.set_font_description(Some(&font)); layout.set_spacing(0); layout.set_text(text); - let (_width,height) = layout.size(); - let device_height = (height as f64)/(pango::SCALE as f64); + let (_width, height) = layout.size(); + let device_height = (height as f64) / (pango::SCALE as f64); self.save().unwrap(); - self.scale(30.0,30.0); + self.scale(30.0, 30.0); let cairo_user_height = self.device_to_user_point(device_height); let offset = (1.0 - cairo_user_height) / 2.0; self.rel_move_to(0.0, offset); @@ -145,7 +147,7 @@ impl ThemedContextExt for cairo::Context { fn device_to_user_point(&self, point: f64) -> f64 { self.device_to_user(point, point) - .map(|(x,y)| if x>=y {x} else {y}) + .map(|(x, y)| if x >= y { x } else { y }) .expect("Could not convert device to user space") } } diff --git a/src/ui/ui.rs b/src/ui/ui.rs index c252d90..ce046a4 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -5,26 +5,103 @@ use log::debug; use winit::{platform::unix::WindowExtUnix, window::Window}; use super::core::shared::Entry; - use super::painter; +pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec)>, 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)>, 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, 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(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) .get_reply() .expect("Could not fetch attributes for window") .visual(); - debug!{"Found visualid {} for window", window_visualid} + debug! {"Found visualid {} for window", window_visualid} - debug!{"Trying to map visualid to visualtype {}", window_visualid} - let visualtype = xcb_conn.get_setup().roots() + debug! {"Trying to map visualid to visualtype {}", window_visualid} + let visualtype = xcb_conn + .get_setup() + .roots() .flat_map(|screen| screen.allowed_depths()) .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"); // xcb::Connection calls disconnect on the underlying xcb_connection_t @@ -37,20 +114,18 @@ where T: BorrowMut<*mut c_void> visualtype } -fn make_context(window: &Window) -> cairo::Context -{ +fn make_context(window: &Window) -> cairo::Context { let winit_xcb_conn = window .xcb_connection() .expect("Could not get connection from window"); - let xlib_window_id = window - .xlib_window() - .expect("Could not get xlib window"); + let xlib_window_id = window.xlib_window().expect("Could not get xlib window"); let visual_type = unsafe { let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32).base; 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, window.inner_size().width 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") } - -pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec)>, 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)>, 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, 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); - }); - -}