From 48879aadd83acf24728aeba2d77a259602c467e7 Mon Sep 17 00:00:00 2001 From: Armin Friedl Date: Tue, 19 Oct 2021 22:11:16 +0200 Subject: [PATCH] Lua host, mark match indices in results --- Cargo.lock | 52 ++++++++++++++++++++++++++ Cargo.toml | 3 +- lua/date.lua | 13 +++++++ lua/hello.lua | 1 + lua/ui.lua | 69 ++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/roftl.rs | 6 +-- src/sources/apps.rs | 84 ++++++++++++++++++++++++++++++++++++------ src/sources/luahost.rs | 56 ++++++++++++++++++++++++++++ src/sources/mod.rs | 3 ++ src/ui/painter.rs | 14 +++++-- src/ui/ui.rs | 22 ++++++----- 12 files changed, 295 insertions(+), 29 deletions(-) create mode 100644 lua/date.lua create mode 100644 lua/hello.lua create mode 100644 lua/ui.lua create mode 100644 src/sources/luahost.rs diff --git a/Cargo.lock b/Cargo.lock index 205f58f..db29fcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "bstr" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +dependencies = [ + "memchr", +] + [[package]] name = "bumpalo" version = "3.7.1" @@ -640,6 +649,24 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "lua-src" +version = "543.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72914332bf1ef0e1185b229135d639f11a4a8ccfd32852db8e52419c04c0247" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.2.0+resty5f13855" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f85722ea9e022305a077b916c9271011a195ee8dc9b2b764fc78b0378e3b72" +dependencies = [ + "cc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -701,6 +728,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "mlua" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4235d7e740d73d7429df6f176c81b248f05c39d67264d45a7d8cecb67c227f6f" +dependencies = [ + "bstr", + "cc", + "lua-src", + "luajit-src", + "num-traits", + "once_cell", + "pkg-config", +] + [[package]] name = "ndk" version = "0.3.0" @@ -792,6 +834,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -1025,6 +1076,7 @@ dependencies = [ "freedesktop_entry_parser", "fuzzy-matcher", "log", + "mlua", "rayon", "walkdir", "winit", diff --git a/Cargo.toml b/Cargo.toml index 618d231..f59e069 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ xcb-util = {version = "0.3.0", features = ["ewmh", "icccm"]} fuzzy-matcher = "0.3.7" walkdir = "2.3.2" -freedesktop_entry_parser = "1.2.0" \ No newline at end of file +freedesktop_entry_parser = "1.2.0" +mlua = { version = "0.6", features = ["lua54", "vendored"] } \ No newline at end of file diff --git a/lua/date.lua b/lua/date.lua new file mode 100644 index 0000000..c549fa8 --- /dev/null +++ b/lua/date.lua @@ -0,0 +1,13 @@ +local date = require "date" +-- prints all FRIDAY the 13TH dates between year 2000 and 2010 +for i = 2000, 2010 do + -- year jan 1 + x = date(i, 1, 1) + -- from january to december + for j = 1, 12 do + -- set date to 13, check if friday + if x:setmonth(j, 13):getweekday() == 6 then + print(x:fmt("%A, %B %d %Y")) + end + end +end \ No newline at end of file diff --git a/lua/hello.lua b/lua/hello.lua new file mode 100644 index 0000000..8e23576 --- /dev/null +++ b/lua/hello.lua @@ -0,0 +1 @@ +print("Hello World") \ No newline at end of file diff --git a/lua/ui.lua b/lua/ui.lua new file mode 100644 index 0000000..40a549f --- /dev/null +++ b/lua/ui.lua @@ -0,0 +1,69 @@ +local lwtk = require("lwtk") + +local Application = lwtk.Application +local Column = lwtk.Column +local Row = lwtk.Row +local PushButton = lwtk.PushButton +local TextInput = lwtk.TextInput +local TitleText = lwtk.TitleText +local Space = lwtk.Space + +local app = Application("example01.lua") + +local function quit() + app:close() +end + +local win = app:newWindow { + title = "example01", + Column { + id = "c1", + TitleText { text = "What's your name?" }, + TextInput { id = "i1", focus = true, style = { Columns = 40 } }, + Row { + Space {}, + PushButton { id = "b1", text = "&OK", disabled = true, + default = true }, + + PushButton { id = "b2", text = "&Quit", onClicked = quit }, + Space {} + } + }, + Column { + id = "c2", + visible = false, + Space {}, + TitleText { id = "t2", style = { TextAlign = "center" } }, + Space {}, + Row { + Space {}, + PushButton { id = "b3", text = "&Again" }, + + PushButton { id = "b4", text = "&Quit", default = true, + onClicked = quit }, + Space {} + } + } +} + +win:childById("c1"):setOnInputChanged(function(widget, input) + widget:childById("b1"):setDisabled(input.text == "") +end) + +win:childById("b1"):setOnClicked(function(widget) + win:childById("t2"):setText("Hello "..win:childById("i1").text.."!") + win:childById("c1"):setVisible(false) + win:childById("c2"):setVisible(true) +end) + +win:childById("b3"):setOnClicked(function(widget) + win:childById("i1"):setText("") + win:childById("i1"):setFocus() + win:childById("t2"):setText("") + win:childById("c1"):setVisible(true) + win:childById("c2"):setVisible(false) +end) + +win:show() + +app:runEventLoop() \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 32ef83b..f2f7b39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ fn main() -> Result<(), Box> { .add_source(sources::TestSource::new("ts1")) .add_source(sources::Window::new()) .add_source(sources::Apps::new()) + .add_source(sources::LuaHost::new()) .with_matcher(SkimMatcher::new()); debug!{"Source roftl sources"} diff --git a/src/roftl.rs b/src/roftl.rs index 86029bb..4716407 100644 --- a/src/roftl.rs +++ b/src/roftl.rs @@ -73,7 +73,7 @@ impl Roftl { debug!("Entries {:?}", self.entries); } - pub fn narrow(&self, input: &str) -> Vec<&Entry> { + pub fn narrow(&self, input: &str) -> Vec<(&Entry, Vec)> { let count = Arc::new(AtomicUsize::new(0)); self.narrow_map.lock().unwrap().clear(); @@ -97,12 +97,12 @@ impl Roftl { scored_entries .par_iter() - .map(|se| se.2) + .map(|se| (se.2, se.3.clone())) .collect() } pub fn action(&self, entry_id: usize, action: Action) { - println!("NarrowMap {:?}", self.narrow_map.lock().unwrap()); + debug!{"NarrowMap {:?}", self.narrow_map.lock().unwrap()}; let real_id = *self.narrow_map.lock().unwrap().get(&entry_id).unwrap(); let entry = self.entries.get(real_id).unwrap(); diff --git a/src/sources/apps.rs b/src/sources/apps.rs index e3ccfcf..03de961 100644 --- a/src/sources/apps.rs +++ b/src/sources/apps.rs @@ -4,12 +4,55 @@ use walkdir::WalkDir; use crate::roftl::{Action, Entry, Source}; -pub struct Apps {} +#[derive(Debug)] +struct App { + name: String, + command: String, +} + +impl From<&App> for Entry +{ + fn from(app: &App) -> Self { + Entry { + name: app.name.clone(), + description: "".into(), + source: "apps", + identifier: 0 + } + } +} + +#[derive(Default, Debug)] +struct AppBuilder<'a> { + name: Option<&'a str>, + exec: Option<&'a str>, + hidden: 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 } + + Some ( + App { + name: self.name.unwrap().into(), + command: self.exec.unwrap().into() + }) + } +} + +pub struct Apps { + entries: Vec +} impl Apps { pub fn new() -> Box { - Box::new(Apps{}) + Box::new(Apps{entries: vec![]}) } } @@ -35,18 +78,31 @@ impl Source for Apps { .collect(); let mut entries: Vec = vec![]; + let mut idx: u64 = 0; for entry in desktop_entries { - match entry.section("Desktop Entry").attr("Name") - { - Some(name) => entries.push( - Entry{name: name.clone().into(), - description: "".into(), - source: self.name(), - identifier: 0} - ), + let mut app_builder = AppBuilder::default(); + let section = entry.section("Desktop Entry"); - None => continue + for attr in section.attrs() + { + match attr.name + { + "Name" => app_builder.name = attr.value, + "Exec" => app_builder.exec = attr.value, + "Hidden" => app_builder.hidden = attr.value, + _ => () + } } + + 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); + } + } debug!{"Got desktop entries: {:?}", entries} @@ -55,6 +111,10 @@ impl Source for Apps { } fn action(&self, entry: &Entry, action: Action) { - todo!() + debug!{"Got desktop entry {:?} for action", entry} + debug!{"Desktop entry has id {}", entry.identifier} + debug!{"Apps has entries {:?}", self.entries} + let app = self.entries.get(entry.identifier as usize).unwrap(); + debug!{"Doing primary action {} for {}", app.name, app.command} } } diff --git a/src/sources/luahost.rs b/src/sources/luahost.rs new file mode 100644 index 0000000..49f7e99 --- /dev/null +++ b/src/sources/luahost.rs @@ -0,0 +1,56 @@ +use std::path::PathBuf; +use mlua::prelude::*; + +use log::debug; +use walkdir::WalkDir; + +use crate::roftl::{Entry, Source, Action}; + +pub struct LuaHost { + scripts: Vec, +} + +impl LuaHost { + pub fn new() -> Box { + Box::new(LuaHost{ scripts: vec![] }) + } +} + +impl Source for LuaHost { + fn name(&self) -> &'static str { + "lua" + } + + fn entries(&mut self) -> Vec { + // TODO read from config + let script_path = "/home/armin/dev/incubator/roftl/lua"; + + 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 + }); + } + self.scripts = scripts; + entries + } + + fn action(&self, entry: &Entry, action: Action) { + let script = self.scripts.get(entry.identifier as usize).unwrap(); + debug!{"Got script {:?}", script}; + + let script_content = std::fs::read_to_string(script).unwrap(); + let lua = Lua::new(); + lua.load(&script_content).exec().unwrap(); + + debug!{"Executed {:?}", script}; + } +} diff --git a/src/sources/mod.rs b/src/sources/mod.rs index f2ce762..6cce239 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -6,3 +6,6 @@ pub use windows::Window; mod apps; pub use apps::Apps; + +mod luahost; +pub use luahost::LuaHost; diff --git a/src/ui/painter.rs b/src/ui/painter.rs index 25b12b7..055cf31 100644 --- a/src/ui/painter.rs +++ b/src/ui/painter.rs @@ -103,7 +103,7 @@ impl<'a> Painter<'a> { ctx.show_text(input).unwrap(); } - pub fn result_box(&self, x: T, y: T, result: &'a str, selected: bool) + pub fn result_box(&self, x: T, y: T, result: &'a str, indices: &[usize], selected: bool) where T: Into { let ctx = self.context; @@ -117,14 +117,22 @@ impl<'a> Painter<'a> { ctx.fill().unwrap(); // draw text - ctx.select_font_face ("serif", cairo::FontSlant::Normal, cairo::FontWeight::Normal); let font_size = self.device_to_user(self.theme.result_font_size); ctx.set_font_size(font_size); let (r,g,b,a) = if selected { self.theme.result_font_selected } else { self.theme.result_font_color }; ctx.set_source_rgba(r,g,b,a); ctx.move_to (x+0.2, y+0.8); - ctx.show_text(result).unwrap(); + + for i in 0..result.len() { + if indices.contains(&i) { + ctx.select_font_face ("serif", cairo::FontSlant::Normal, cairo::FontWeight::Bold); + ctx.show_text(&result[i..i+1]).unwrap(); + } else { + ctx.select_font_face ("serif", cairo::FontSlant::Normal, cairo::FontWeight::Normal); + ctx.show_text(&result[i..i+1]).unwrap(); + } + } } pub fn divider(&self, x: T, y: T) diff --git a/src/ui/ui.rs b/src/ui/ui.rs index 07e1cd9..4a7239e 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -71,7 +71,7 @@ fn make_context(window: &Window) -> cairo::Context cairo::Context::new(&surface).expect("Could not create drawing context") } -pub fn draw(window: &Window, input: &str, result: Vec<&Entry>, selection: usize) +pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec)>, selection: usize) { let context = make_context(&window); @@ -84,12 +84,13 @@ pub fn draw(window: &Window, input: &str, result: Vec<&Entry>, selection: usize) painter.divider(0, 1); result.iter().enumerate() - .for_each(|(i, e)| { - painter.result_box(0, 1+(i as u32), &e.name, selection==i); + .for_each(|(i, r)| { + let e = r.0; + painter.result_box(0, 1+(i as u32), &e.name, &r.1, selection==i); }); } -pub fn redraw_quick(window: &Window, result: Vec<&Entry>, selection: usize) { +pub fn redraw_quick(window: &Window, result: Vec<(&Entry, Vec)>, selection: usize) { let context = make_context(&window); context.scale(30.0, 30.0); @@ -97,16 +98,17 @@ pub fn redraw_quick(window: &Window, result: Vec<&Entry>, selection: usize) { let painter = painter::Painter::new(&context); result.iter().enumerate() - .for_each(|(i, e)| { + .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.name, false) } - if i == result.len()-1 { painter.result_box(0, 1+(i as u32), &e.name, false) } + if i == 0 { painter.result_box(0, 1+(i as u32), &e.name, &r.1, false) } + if i == result.len()-1 { painter.result_box(0, 1+(i as u32), &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.name, false) } - if i == (selection+1) { painter.result_box(0, 1+(i as u32), &e.name, false) } + if selection > 0 && i == (selection-1) { painter.result_box(0, 1+(i as u32), &e.name, &r.1, false) } + if i == (selection+1) { painter.result_box(0, 1+(i as u32), &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.name, true) } + if i == selection { painter.result_box(0, 1+(i as u32), &e.name, &r.1, true) } }); }