Lua host, mark match indices in results

This commit is contained in:
Armin Friedl 2021-10-19 22:11:16 +02:00
parent 1cade695c8
commit 48879aadd8
12 changed files with 295 additions and 29 deletions

52
Cargo.lock generated
View file

@ -64,6 +64,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "bstr"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.7.1" version = "3.7.1"
@ -640,6 +649,24 @@ dependencies = [
"cfg-if 1.0.0", "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]] [[package]]
name = "malloc_buf" name = "malloc_buf"
version = "0.0.6" version = "0.0.6"
@ -701,6 +728,21 @@ dependencies = [
"winapi", "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]] [[package]]
name = "ndk" name = "ndk"
version = "0.3.0" version = "0.3.0"
@ -792,6 +834,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.0"
@ -1025,6 +1076,7 @@ dependencies = [
"freedesktop_entry_parser", "freedesktop_entry_parser",
"fuzzy-matcher", "fuzzy-matcher",
"log", "log",
"mlua",
"rayon", "rayon",
"walkdir", "walkdir",
"winit", "winit",

View file

@ -19,4 +19,5 @@ xcb-util = {version = "0.3.0", features = ["ewmh", "icccm"]}
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
walkdir = "2.3.2" walkdir = "2.3.2"
freedesktop_entry_parser = "1.2.0" freedesktop_entry_parser = "1.2.0"
mlua = { version = "0.6", features = ["lua54", "vendored"] }

13
lua/date.lua Normal file
View file

@ -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

1
lua/hello.lua Normal file
View file

@ -0,0 +1 @@
print("Hello World")

69
lua/ui.lua Normal file
View file

@ -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()

View file

@ -23,6 +23,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.add_source(sources::TestSource::new("ts1")) .add_source(sources::TestSource::new("ts1"))
.add_source(sources::Window::new()) .add_source(sources::Window::new())
.add_source(sources::Apps::new()) .add_source(sources::Apps::new())
.add_source(sources::LuaHost::new())
.with_matcher(SkimMatcher::new()); .with_matcher(SkimMatcher::new());
debug!{"Source roftl sources"} debug!{"Source roftl sources"}

View file

@ -73,7 +73,7 @@ impl Roftl {
debug!("Entries {:?}", self.entries); debug!("Entries {:?}", self.entries);
} }
pub fn narrow(&self, input: &str) -> Vec<&Entry> { pub fn narrow(&self, input: &str) -> Vec<(&Entry, Vec<usize>)> {
let count = Arc::new(AtomicUsize::new(0)); let count = Arc::new(AtomicUsize::new(0));
self.narrow_map.lock().unwrap().clear(); self.narrow_map.lock().unwrap().clear();
@ -97,12 +97,12 @@ impl Roftl {
scored_entries scored_entries
.par_iter() .par_iter()
.map(|se| se.2) .map(|se| (se.2, se.3.clone()))
.collect() .collect()
} }
pub fn action(&self, entry_id: usize, action: Action) { 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 real_id = *self.narrow_map.lock().unwrap().get(&entry_id).unwrap();
let entry = self.entries.get(real_id).unwrap(); let entry = self.entries.get(real_id).unwrap();

View file

@ -4,12 +4,55 @@ use walkdir::WalkDir;
use crate::roftl::{Action, Entry, Source}; 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<App>
{
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<App>
}
impl Apps { impl Apps {
pub fn new() -> Box<Apps> pub fn new() -> Box<Apps>
{ {
Box::new(Apps{}) Box::new(Apps{entries: vec![]})
} }
} }
@ -35,18 +78,31 @@ impl Source for Apps {
.collect(); .collect();
let mut entries: Vec<Entry> = vec![]; let mut entries: Vec<Entry> = vec![];
let mut idx: u64 = 0;
for entry in desktop_entries { for entry in desktop_entries {
match entry.section("Desktop Entry").attr("Name") let mut app_builder = AppBuilder::default();
{ let section = entry.section("Desktop Entry");
Some(name) => entries.push(
Entry{name: name.clone().into(),
description: "".into(),
source: self.name(),
identifier: 0}
),
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} debug!{"Got desktop entries: {:?}", entries}
@ -55,6 +111,10 @@ impl Source for Apps {
} }
fn action(&self, entry: &Entry, action: Action) { 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}
} }
} }

56
src/sources/luahost.rs Normal file
View file

@ -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<PathBuf>,
}
impl LuaHost {
pub fn new() -> Box<LuaHost> {
Box::new(LuaHost{ scripts: vec![] })
}
}
impl Source for LuaHost {
fn name(&self) -> &'static str {
"lua"
}
fn entries(&mut self) -> Vec<Entry> {
// TODO read from config
let script_path = "/home/armin/dev/incubator/roftl/lua";
let scripts: Vec<PathBuf> = WalkDir::new(script_path).into_iter()
.map(|e| e.unwrap().path().to_path_buf())
.collect();
let mut entries: Vec<Entry> = 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};
}
}

View file

@ -6,3 +6,6 @@ pub use windows::Window;
mod apps; mod apps;
pub use apps::Apps; pub use apps::Apps;
mod luahost;
pub use luahost::LuaHost;

View file

@ -103,7 +103,7 @@ impl<'a> Painter<'a> {
ctx.show_text(input).unwrap(); ctx.show_text(input).unwrap();
} }
pub fn result_box<T>(&self, x: T, y: T, result: &'a str, selected: bool) pub fn result_box<T>(&self, x: T, y: T, result: &'a str, indices: &[usize], selected: bool)
where T: Into<f64> where T: Into<f64>
{ {
let ctx = self.context; let ctx = self.context;
@ -117,14 +117,22 @@ impl<'a> Painter<'a> {
ctx.fill().unwrap(); ctx.fill().unwrap();
// draw text // 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); let font_size = self.device_to_user(self.theme.result_font_size);
ctx.set_font_size(font_size); ctx.set_font_size(font_size);
let (r,g,b,a) = if selected { self.theme.result_font_selected } let (r,g,b,a) = if selected { self.theme.result_font_selected }
else { self.theme.result_font_color }; else { self.theme.result_font_color };
ctx.set_source_rgba(r,g,b,a); ctx.set_source_rgba(r,g,b,a);
ctx.move_to (x+0.2, y+0.8); 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<T>(&self, x: T, y: T) pub fn divider<T>(&self, x: T, y: T)

View file

@ -71,7 +71,7 @@ fn make_context(window: &Window) -> cairo::Context
cairo::Context::new(&surface).expect("Could not create drawing context") cairo::Context::new(&surface).expect("Could not create drawing context")
} }
pub fn draw(window: &Window, input: &str, result: Vec<&Entry>, selection: usize) pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, selection: usize)
{ {
let context = make_context(&window); 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); painter.divider(0, 1);
result.iter().enumerate() result.iter().enumerate()
.for_each(|(i, e)| { .for_each(|(i, r)| {
painter.result_box(0, 1+(i as u32), &e.name, selection==i); 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<usize>)>, selection: usize) {
let context = make_context(&window); let context = make_context(&window);
context.scale(30.0, 30.0); 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); let painter = painter::Painter::new(&context);
result.iter().enumerate() 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 // 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 == 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, 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 // 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 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, 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 // 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) }
}); });
} }