gui improvements
* implement normal window * set placeholder text from config / args * key handler is now using key codes instead of hard coded values * drun now is able to spawn an application, although process is not forked yet
This commit is contained in:
parent
43cb14b9e8
commit
ff22c0c9c6
7 changed files with 194 additions and 106 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
.idea
|
||||||
|
|
|
||||||
51
Cargo.lock
generated
51
Cargo.lock
generated
|
|
@ -318,6 +318,56 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe1d7dcda7d1da79e444bdfba1465f2f849a58b07774e1df473ee77030cb47a7"
|
checksum = "fe1d7dcda7d1da79e444bdfba1465f2f849a58b07774e1df473ee77030cb47a7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.21"
|
||||||
|
|
@ -1904,6 +1954,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"calloop 0.14.2",
|
"calloop 0.14.2",
|
||||||
"clap",
|
"clap",
|
||||||
|
"crossbeam",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"gdk4",
|
"gdk4",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,5 @@ wayland-client = "0.31.8"
|
||||||
wayland-protocols = "0.32.6"
|
wayland-protocols = "0.32.6"
|
||||||
smithay-client-toolkit = { version = "0.19.2", features = ["calloop"]}
|
smithay-client-toolkit = { version = "0.19.2", features = ["calloop"]}
|
||||||
calloop = "0.14.2"
|
calloop = "0.14.2"
|
||||||
|
crossbeam = "0.8.4"
|
||||||
libc = "0.2.171"
|
libc = "0.2.171"
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,11 @@ pub struct Args {
|
||||||
|
|
||||||
/// The x offset
|
/// The x offset
|
||||||
#[clap(short = 'x', long = "xoffset")]
|
#[clap(short = 'x', long = "xoffset")]
|
||||||
x: Option<String>,
|
x: Option<i32>,
|
||||||
|
|
||||||
/// The y offset
|
/// The y offset
|
||||||
#[clap(short = 'y', long = "yoffset")]
|
#[clap(short = 'y', long = "yoffset")]
|
||||||
y: Option<String>,
|
y: Option<i32>,
|
||||||
|
|
||||||
/// Render to a normal window
|
/// Render to a normal window
|
||||||
#[clap(short = 'n', long = "normal-window")]
|
#[clap(short = 'n', long = "normal-window")]
|
||||||
|
|
@ -149,7 +149,7 @@ pub struct Args {
|
||||||
|
|
||||||
/// Sets the number of columns to display
|
/// Sets the number of columns to display
|
||||||
#[clap(short = 'w', long = "columns")]
|
#[clap(short = 'w', long = "columns")]
|
||||||
columns: Option<String>,
|
columns: Option<u8>,
|
||||||
|
|
||||||
/// Sets the sort order
|
/// Sets the sort order
|
||||||
#[clap(short = 'O', long = "sort-order")]
|
#[clap(short = 'O', long = "sort-order")]
|
||||||
|
|
|
||||||
|
|
@ -144,12 +144,12 @@ impl Default for Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_normal_window() -> Option<bool> {
|
fn default_normal_window() -> Option<bool> {
|
||||||
Some(true)
|
Some(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// GtkOrientation orientation = config_get_mnemonic(config, "orientation", "vertical", 2, "vertical", "horizontal");
|
// GtkOrientation orientation = config_get_mnemonic(config, "orientation", "vertical", 2, "vertical", "horizontal");
|
||||||
// outer_orientation = config_get_mnemonic(config, "orientation", "vertical", 2, "horizontal", "vertical");
|
// outer_orientation = config_get_mnemonic(cstoonfig, "orientation", "vertical", 2, "horizontal", "vertical");
|
||||||
// GtkAlign halign = config_get_mnemonic(config, "halign", "fill", 4, "fill", "start", "end", "center");
|
// GtkAlign halign = config_get_mnemonic(config, "halign", "fill", 4, "fill", "start", "end", "center");
|
||||||
// content_halign = config_get_mnemonic(config, "content_halign", "fill", 4, "fill", "start", "end", "center");
|
// content_halign = config_get_mnemonic(config, "content_halign", "fill", 4, "fill", "start", "end", "center");
|
||||||
// char* default_valign = "start";
|
// char* default_valign = "start";
|
||||||
|
|
@ -252,7 +252,9 @@ fn merge_json(a: &mut Value, b: &Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(a_val, b_val) => {
|
(a_val, b_val) => {
|
||||||
|
if *b_val != Value::Null {
|
||||||
*a_val = b_val.clone();
|
*a_val = b_val.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
106
src/gui.rs
106
src/gui.rs
|
|
@ -1,11 +1,13 @@
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use anyhow::Context;
|
use anyhow::{Context, anyhow};
|
||||||
use gdk4::Display;
|
use crossbeam::channel;
|
||||||
|
use crossbeam::channel::Sender;
|
||||||
use gdk4::gio::File;
|
use gdk4::gio::File;
|
||||||
use gdk4::glib::Propagation;
|
use gdk4::glib::Propagation;
|
||||||
use gdk4::prelude::{Cast, DisplayExt, MonitorExt};
|
use gdk4::prelude::{Cast, DisplayExt, MonitorExt};
|
||||||
|
use gdk4::{Display, Key};
|
||||||
use gtk4::prelude::{
|
use gtk4::prelude::{
|
||||||
ApplicationExt, ApplicationExtManual, BoxExt, ButtonExt, EditableExt, EntryExt,
|
ApplicationExt, ApplicationExtManual, BoxExt, ButtonExt, EditableExt, EntryExt, FileChooserExt,
|
||||||
FlowBoxChildExt, GtkWindowExt, ListBoxRowExt, NativeExt, WidgetExt,
|
FlowBoxChildExt, GtkWindowExt, ListBoxRowExt, NativeExt, WidgetExt,
|
||||||
};
|
};
|
||||||
use gtk4::{
|
use gtk4::{
|
||||||
|
|
@ -14,17 +16,18 @@ use gtk4::{
|
||||||
};
|
};
|
||||||
use gtk4::{Application, ApplicationWindow, CssProvider, Orientation};
|
use gtk4::{Application, ApplicationWindow, CssProvider, Orientation};
|
||||||
use gtk4_layer_shell::{KeyboardMode, LayerShell};
|
use gtk4_layer_shell::{KeyboardMode, LayerShell};
|
||||||
use log::error;
|
use log::{debug, error, info};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct EntryElement {
|
pub struct EntryElement {
|
||||||
pub label: String, // todo support empty label?
|
pub label: String, // todo support empty label?
|
||||||
pub icon_path: Option<String>,
|
pub icon_path: Option<String>,
|
||||||
pub action: Box<dyn Fn() + Send + 'static>,
|
pub action: Option<String>,
|
||||||
pub sub_elements: Option<Vec<EntryElement>>,
|
pub sub_elements: Option<Vec<EntryElement>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
pub fn show(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<(i32)> {
|
||||||
// Load CSS
|
// Load CSS
|
||||||
let provider = CssProvider::new();
|
let provider = CssProvider::new();
|
||||||
let css_file_path = File::for_path("/home/me/.config/wofi/style.css");
|
let css_file_path = File::for_path("/home/me/.config/wofi/style.css");
|
||||||
|
|
@ -48,6 +51,7 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
|
|
||||||
// No need for application_id unless you want portal support
|
// No need for application_id unless you want portal support
|
||||||
let app = Application::builder().application_id("ravi").build();
|
let app = Application::builder().application_id("ravi").build();
|
||||||
|
let (sender, receiver) = channel::bounded(1);
|
||||||
|
|
||||||
app.connect_activate(move |app| {
|
app.connect_activate(move |app| {
|
||||||
// Create a toplevel undecorated window
|
// Create a toplevel undecorated window
|
||||||
|
|
@ -59,11 +63,16 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
.default_height(20)
|
.default_height(20)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
window.set_widget_name("window");
|
||||||
|
|
||||||
|
config.normal_window.map(|normal| {
|
||||||
|
if !normal {
|
||||||
|
window.set_layer(gtk4_layer_shell::Layer::Overlay);
|
||||||
window.init_layer_shell();
|
window.init_layer_shell();
|
||||||
window.set_keyboard_mode(KeyboardMode::Exclusive);
|
window.set_keyboard_mode(KeyboardMode::Exclusive);
|
||||||
window.set_widget_name("window");
|
window.set_namespace(Some("worf"));
|
||||||
window.set_layer(gtk4_layer_shell::Layer::Overlay);
|
}
|
||||||
window.set_namespace(Some("ravi"));
|
});
|
||||||
|
|
||||||
let outer_box = gtk4::Box::new(Orientation::Vertical, 0);
|
let outer_box = gtk4::Box::new(Orientation::Vertical, 0);
|
||||||
outer_box.set_widget_name("outer-box");
|
outer_box.set_widget_name("outer-box");
|
||||||
|
|
@ -72,18 +81,11 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
let entry = SearchEntry::new();
|
let entry = SearchEntry::new();
|
||||||
entry.set_widget_name("input");
|
entry.set_widget_name("input");
|
||||||
entry.set_css_classes(&["input"]);
|
entry.set_css_classes(&["input"]);
|
||||||
entry.set_placeholder_text(Some("Enter search..."));
|
entry.set_placeholder_text(config.prompt.as_deref());
|
||||||
|
|
||||||
// Create key event controller
|
|
||||||
let entry_clone = entry.clone();
|
|
||||||
setup_key_event_handler(&window, entry_clone);
|
|
||||||
|
|
||||||
// Example `search` and `password_char` usage
|
// Example `search` and `password_char` usage
|
||||||
let password_char = Some('*');
|
// let password_char = Some('*');
|
||||||
|
// todo\
|
||||||
entry.set_placeholder_text(Some("placeholder"));
|
|
||||||
|
|
||||||
// todo
|
|
||||||
// if let Some(c) = password_char {
|
// if let Some(c) = password_char {
|
||||||
// let entry_casted: Entry = entry.clone().upcast();
|
// let entry_casted: Entry = entry.clone().upcast();
|
||||||
// entry_casted.set_visibility(false);
|
// entry_casted.set_visibility(false);
|
||||||
|
|
@ -119,7 +121,6 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
add_entry_element(&inner_box, &entry);
|
add_entry_element(&inner_box, &entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo
|
|
||||||
// Set focus after everything is realized
|
// Set focus after everything is realized
|
||||||
inner_box.connect_map(|fb| {
|
inner_box.connect_map(|fb| {
|
||||||
fb.grab_focus();
|
fb.grab_focus();
|
||||||
|
|
@ -130,17 +131,19 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
wrapper_box.append(&inner_box);
|
wrapper_box.append(&inner_box);
|
||||||
scroll.set_child(Some(&wrapper_box));
|
scroll.set_child(Some(&wrapper_box));
|
||||||
|
|
||||||
// todo
|
// todo implement search function
|
||||||
// // Dummy filter and sort funcs – replace with actual logic
|
// // Dummy filter and sort funcs – replace with actual logic
|
||||||
// inner_box.set_filter_func(Some(Box::new(|_child| {
|
// inner_box.set_filter_func(Some(Box::new(|_child| {
|
||||||
// true // filter logic here
|
// true // filter logic here
|
||||||
// })));
|
// })));
|
||||||
|
|
||||||
// todo
|
|
||||||
// inner_box.set_sort_func(Some(Box::new(|child1, child2| {
|
// inner_box.set_sort_func(Some(Box::new(|child1, child2| {
|
||||||
// child1.widget_name().cmp(&child2.widget_name())
|
// child1.widget_name().cmp(&child2.widget_name())
|
||||||
// })));
|
// })));
|
||||||
|
|
||||||
|
// Create key event controller
|
||||||
|
let entry_clone = entry.clone();
|
||||||
|
setup_key_event_handler(&window, entry_clone, inner_box, app.clone(), sender.clone());
|
||||||
|
|
||||||
window.show();
|
window.show();
|
||||||
|
|
||||||
// Get the display where the window resides
|
// Get the display where the window resides
|
||||||
|
|
@ -151,18 +154,14 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
let monitor = display.monitor_at_surface(&surface);
|
let monitor = display.monitor_at_surface(&surface);
|
||||||
if let Some(monitor) = monitor {
|
if let Some(monitor) = monitor {
|
||||||
let geometry = monitor.geometry();
|
let geometry = monitor.geometry();
|
||||||
if let Some(w) = percent_or_absolute(
|
config.width.as_ref().map(|width| {
|
||||||
&config.width.clone().unwrap_or("800".to_owned()),
|
percent_or_absolute(&width, geometry.width())
|
||||||
geometry.width(),
|
.map(|w| window.set_width_request(w))
|
||||||
) {
|
});
|
||||||
window.set_width_request(w);
|
config.height.as_ref().map(|height| {
|
||||||
}
|
percent_or_absolute(&height, geometry.height())
|
||||||
if let Some(h) = percent_or_absolute(
|
.map(|h| window.set_height_request(h))
|
||||||
&config.height.clone().unwrap_or("500".to_owned()),
|
});
|
||||||
geometry.height(),
|
|
||||||
) {
|
|
||||||
window.set_height_request(h);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
error!("failed to get monitor to init window size");
|
error!("failed to get monitor to init window size");
|
||||||
}
|
}
|
||||||
|
|
@ -172,17 +171,35 @@ pub fn init(config: Config, elements: Vec<EntryElement>) -> anyhow::Result<()> {
|
||||||
let empty_array: [&str; 0] = [];
|
let empty_array: [&str; 0] = [];
|
||||||
|
|
||||||
app.run_with_args(&empty_array);
|
app.run_with_args(&empty_array);
|
||||||
Ok(())
|
let selected_index = receiver.recv()?;
|
||||||
|
Ok(selected_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_key_event_handler(window: &ApplicationWindow, entry_clone: SearchEntry) {
|
fn setup_key_event_handler(
|
||||||
|
window: &ApplicationWindow,
|
||||||
|
entry_clone: SearchEntry,
|
||||||
|
inner_box: FlowBox,
|
||||||
|
app: Application,
|
||||||
|
sender: Sender<i32>,
|
||||||
|
) {
|
||||||
let key_controller = EventControllerKey::new();
|
let key_controller = EventControllerKey::new();
|
||||||
let x = key_controller.connect_key_pressed(move |_controller, key_value, code, mode| {
|
key_controller.connect_key_pressed(move |_, key_value, _, _| {
|
||||||
if code == 9 {
|
match key_value {
|
||||||
// todo find better way to handle escape
|
Key::Escape => exit(1),
|
||||||
exit(1);
|
Key::Return => {
|
||||||
|
for s in &inner_box.selected_children() {
|
||||||
|
// let element : &Option<&EntryElement> = &elements.get(s.index() as usize);
|
||||||
|
// if let Some(element) = *element {
|
||||||
|
// debug!("Running action on element with name {}", element.label);
|
||||||
|
// (element.action)();
|
||||||
|
// }
|
||||||
|
if let Err(e) = sender.send(s.index()) {
|
||||||
|
error!("failed to send selected child {e:?}")
|
||||||
}
|
}
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
if let Some(c) = key_value.name() {
|
if let Some(c) = key_value.name() {
|
||||||
// Only proceed if it's a single alphanumeric character
|
// Only proceed if it's a single alphanumeric character
|
||||||
if c.len() == 1 && c.chars().all(|ch| ch.is_alphanumeric()) {
|
if c.len() == 1 && c.chars().all(|ch| ch.is_alphanumeric()) {
|
||||||
|
|
@ -190,13 +207,16 @@ fn setup_key_event_handler(window: &ApplicationWindow, entry_clone: SearchEntry)
|
||||||
entry_clone.set_text(&format!("{current}{c}"));
|
entry_clone.set_text(&format!("{current}{c}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Propagation::Proceed
|
Propagation::Proceed
|
||||||
});
|
});
|
||||||
// Add the controller to the window
|
// Add the controller to the window
|
||||||
window.add_controller(key_controller);
|
window.add_controller(key_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_entry_element(inner_box: >k4::FlowBox, entry_element: &EntryElement) {
|
fn add_entry_element(inner_box: &FlowBox, entry_element: &EntryElement) {
|
||||||
let parent: Widget = if entry_element.sub_elements.is_some() {
|
let parent: Widget = if entry_element.sub_elements.is_some() {
|
||||||
let expander = Expander::new(None);
|
let expander = Expander::new(None);
|
||||||
|
|
||||||
|
|
|
||||||
55
src/main.rs
55
src/main.rs
|
|
@ -2,7 +2,7 @@
|
||||||
#![allow(clippy::implicit_return)]
|
#![allow(clippy::implicit_return)]
|
||||||
|
|
||||||
use crate::args::{Args, Mode};
|
use crate::args::{Args, Mode};
|
||||||
use crate::config::Config;
|
use crate::config::{Config, merge_config_with_args};
|
||||||
use crate::desktop::find_desktop_files;
|
use crate::desktop::find_desktop_files;
|
||||||
use crate::gui::EntryElement;
|
use crate::gui::EntryElement;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
@ -13,12 +13,13 @@ use gtk4::prelude::{
|
||||||
};
|
};
|
||||||
use gtk4_layer_shell::LayerShell;
|
use gtk4_layer_shell::LayerShell;
|
||||||
use merge::Merge;
|
use merge::Merge;
|
||||||
use std::fs;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::{fs, time};
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
@ -45,10 +46,9 @@ fn main() -> anyhow::Result<()> {
|
||||||
PathBuf::from(home_dir.clone()).join(".config"),
|
PathBuf::from(home_dir.clone()).join(".config"),
|
||||||
|xdg_conf_home| PathBuf::from(&xdg_conf_home),
|
|xdg_conf_home| PathBuf::from(&xdg_conf_home),
|
||||||
)
|
)
|
||||||
.join("wofi") // todo change to ravi
|
.join("wofi") // todo change to worf
|
||||||
.join("config")
|
.join("config")
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo use this?
|
// todo use this?
|
||||||
let colors_dir = std::env::var("XDG_CACHE_HOME")
|
let colors_dir = std::env::var("XDG_CACHE_HOME")
|
||||||
.map_or(
|
.map_or(
|
||||||
|
|
@ -58,21 +58,36 @@ fn main() -> anyhow::Result<()> {
|
||||||
.join("wal")
|
.join("wal")
|
||||||
.join("colors");
|
.join("colors");
|
||||||
|
|
||||||
|
let drun_cache = std::env::var("XDG_CACHE_HOME")
|
||||||
|
.map_or(
|
||||||
|
PathBuf::from(home_dir.clone()).join(".cache"),
|
||||||
|
|xdg_conf_home| PathBuf::from(&xdg_conf_home),
|
||||||
|
)
|
||||||
|
.join("worf-drun"); // todo change to worf
|
||||||
|
|
||||||
let toml_content = fs::read_to_string(config_path)?;
|
let toml_content = fs::read_to_string(config_path)?;
|
||||||
let mut config: Config = toml::from_str(&toml_content)?; // todo bail out properly
|
let mut config: Config = toml::from_str(&toml_content)?; // todo bail out properly
|
||||||
|
let config = merge_config_with_args(&mut config, &args)?;
|
||||||
|
|
||||||
let icon_resolver = desktop::IconResolver::new();
|
|
||||||
match args.mode {
|
match args.mode {
|
||||||
Mode::Run => {}
|
Mode::Run => {}
|
||||||
Mode::Drun => {
|
Mode::Drun => {
|
||||||
|
drun(config)?;
|
||||||
|
}
|
||||||
|
Mode::Dmenu => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drun(mut config: Config) -> anyhow::Result<()> {
|
||||||
let mut entries: Vec<EntryElement> = Vec::new();
|
let mut entries: Vec<EntryElement> = Vec::new();
|
||||||
for file in &find_desktop_files() {
|
for file in &find_desktop_files() {
|
||||||
if let Some(desktop_entry) = file.get("desktop entry") {
|
if let Some(desktop_entry) = file.get("desktop entry") {
|
||||||
let icon = desktop_entry
|
let icon = desktop_entry
|
||||||
.get("icon")
|
.get("icon")
|
||||||
.and_then(|x| x.as_ref().map(|x| x.to_owned()));
|
.and_then(|x| x.as_ref().map(|x| x.to_owned()));
|
||||||
let Some(exec) = desktop_entry.get("exec").and_then(|x| x.as_ref().cloned())
|
let Some(exec) = desktop_entry.get("exec").and_then(|x| x.as_ref().cloned()) else {
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -82,14 +97,6 @@ fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let exec: Arc<String> = Arc::new(exec.into());
|
|
||||||
let action: Box<dyn Fn() + Send> = {
|
|
||||||
let exec = Arc::clone(&exec); // ✅ now it's correct
|
|
||||||
Box::new(move || {
|
|
||||||
spawn_fork(&exec);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = desktop_entry
|
let name = desktop_entry
|
||||||
.get("name")
|
.get("name")
|
||||||
.and_then(|x| x.as_ref().map(|x| x.to_owned()));
|
.and_then(|x| x.as_ref().map(|x| x.to_owned()));
|
||||||
|
|
@ -98,32 +105,38 @@ fn main() -> anyhow::Result<()> {
|
||||||
EntryElement {
|
EntryElement {
|
||||||
label: name,
|
label: name,
|
||||||
icon_path: icon,
|
icon_path: icon,
|
||||||
action,
|
action: Some(exec),
|
||||||
sub_elements: None,
|
sub_elements: None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.sort_by(|l, r| l.label.cmp(&r.label));
|
entries.sort_by(|l, r| l.label.cmp(&r.label));
|
||||||
if config.prompt.is_none() {
|
if config.prompt.is_none() {
|
||||||
config.prompt = Some("dmenu".to_owned());
|
config.prompt = Some("drun".to_owned());
|
||||||
}
|
|
||||||
gui::init(config.clone(), entries)?;
|
|
||||||
}
|
|
||||||
Mode::Dmenu => {}
|
|
||||||
}
|
}
|
||||||
|
// todo ues a arc instead of cloning the config
|
||||||
|
let selected_index = gui::show(config.clone(), entries.clone())?;
|
||||||
|
entries.get(selected_index as usize).map(|e| {
|
||||||
|
e.action.as_ref().map(|a| {
|
||||||
|
spawn_fork(&a);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_fork(cmd: &str) {
|
fn spawn_fork(cmd: &str) {
|
||||||
|
// todo fork this for real
|
||||||
// Unix-like systems (Linux, macOS)
|
// Unix-like systems (Linux, macOS)
|
||||||
let _ = Command::new(cmd)
|
let _ = Command::new(cmd)
|
||||||
.stdin(Stdio::null()) // Disconnect stdin
|
.stdin(Stdio::null()) // Disconnect stdin
|
||||||
.stdout(Stdio::null()) // Disconnect stdout
|
.stdout(Stdio::null()) // Disconnect stdout
|
||||||
.stderr(Stdio::null()) // Disconnect stderr
|
.stderr(Stdio::null()) // Disconnect stderr
|
||||||
.spawn();
|
.spawn();
|
||||||
|
sleep(time::Duration::from_secs(30));
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// fn main() -> anyhow::Result<()> {
|
// fn main() -> anyhow::Result<()> {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue