aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-07-23 22:09:07 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2023-07-23 22:09:07 +0200
commit84ad0403b30f0ec1aecd7ea0c78c8baea7baa454 (patch)
tree0c41e27625ef73d2dcc54d17ea9c263857499bf0 /src/main.rs
first sketch of hyprmenu
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..a5c4c8b
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,187 @@
+use gtk::gdk::keys::constants::{Escape as ESCAPE, Return as RETURN, Up as UP, Down as DOWN};
+use gtk::{ApplicationWindow, SearchEntry, Box, ListStore, TreeView, ScrolledWindow, TreePath};
+use gtk::prelude::*;
+use gtk::gdk;
+use std::io;
+use std::rc::Rc;
+use gtk::glib;
+
+fn get_options() -> Vec<String> {
+ let mut v = Vec::new();
+
+ for line in io::stdin().lines() {
+ v.push(line.unwrap().trim_end_matches("\n").to_string());
+ }
+
+ v
+}
+
+fn filter_matches(f : &String, options : &Vec<String>) -> Vec<String> {
+ let mut v = Vec::new();
+
+ if f.is_empty() {
+ return options.clone()
+ }
+
+ for option in options.iter() {
+ if option.starts_with(f) {
+ v.push(option.clone());
+ }
+ }
+
+ for option in options.iter() {
+ if option.ends_with(f) && !v.contains(option) {
+ v.push(option.clone());
+ }
+ }
+
+ for option in options.iter() {
+ if option.contains(f) && !v.contains(option) {
+ v.push(option.clone());
+ }
+ }
+
+ v
+}
+
+fn build_ui(application: &gtk::Application) {
+ let provider = gtk::CssProvider::new();
+ let style = include_bytes!("style.css");
+ provider.load_from_data(style).expect("failed to load default style");
+
+ gtk::StyleContext::add_provider_for_screen(
+ &gdk::Screen::default().expect("error initializing gtk css provider"),
+ &provider,
+ gtk::STYLE_PROVIDER_PRIORITY_APPLICATION
+ );
+
+ let options = Rc::new(get_options());
+
+ let window = Rc::new(ApplicationWindow::new(application));
+
+ window.set_title("Hyprmenu");
+ window.set_default_size(600, 200);
+ window.set_app_paintable(true);
+
+ let container = Box::new(gtk::Orientation::Vertical, 0);
+ container.set_widget_name("hyprmenu");
+ window.add(&container);
+
+ let entry = Rc::new(SearchEntry::new());
+ entry.set_widget_name("search");
+
+ let scroll = Rc::new(ScrolledWindow::new(
+ gtk::Adjustment::NONE,
+ gtk::Adjustment::NONE
+ ));
+ scroll.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Always);
+ scroll.set_widget_name("scolllist");
+ scroll.set_vexpand(true);
+ let liststore = Rc::new(ListStore::new(&[glib::Type::STRING]));
+
+ for option in options.iter() {
+ liststore.set(&liststore.append(), &[(0, option)]);
+ }
+
+ let treeview = Rc::new(TreeView::with_model(&*liststore));
+ treeview.set_widget_name("item");
+ treeview.set_vexpand(true);
+ scroll.add(&*treeview);
+
+ let renderer = gtk::CellRendererText::new();
+ let column = gtk::TreeViewColumn::new();
+ TreeViewColumnExt::pack_start(&column, &renderer, true);
+ TreeViewColumnExt::add_attribute(&column, &renderer, "text", 0);
+ treeview.set_headers_visible(false);
+ treeview.append_column(&column);
+ treeview.set_can_focus(false);
+ let path = TreePath::new_first();
+ treeview.set_cursor(&path, gtk::TreeViewColumn::NONE, false);
+
+ {
+ let scroll = Rc::clone(&scroll.clone());
+ let options = Rc::clone(&options);
+ let treeview = Rc::clone(&treeview);
+ let liststore = Rc::clone(&liststore);
+ entry.connect_changed(move |entry| {
+ if entry.text().is_empty() {
+ scroll.vadjustment().set_value(0.0);
+ }
+ liststore.clear();
+ for option in filter_matches(&entry.text().to_string(), &options) {
+ liststore.set(&liststore.append(), &[(0, &option)]);
+ }
+ treeview.set_model(Some(&*liststore));
+ let path = TreePath::new_first();
+ treeview.set_cursor(&path, gtk::TreeViewColumn::NONE, false);
+ });
+ }
+
+ {
+ let win = &window;
+ let window = Rc::clone(&window);
+ let treeview = Rc::clone(&treeview);
+ let entry = Rc::clone(&entry);
+ let liststore = Rc::clone(&liststore);
+ win.connect_key_press_event(move |_, event| {
+ match event.keyval() {
+ ESCAPE => {
+ window.close();
+ }
+ RETURN => {
+ let sel = treeview.cursor();
+ let sel = sel.0.unwrap();
+ let item : usize = sel.to_string().parse().unwrap();
+ let mut i = 0;
+
+ for option in filter_matches(&entry.text().to_string(), &options) {
+ if i == item {
+ println!("{}", option);
+ break;
+ }
+ i += 1;
+ }
+
+ window.close();
+ }
+ UP => {
+ if let Some(mut c) = treeview.cursor().0 {
+ c.prev();
+ let it = liststore.iter(&c).unwrap();
+ let next_path = liststore.path(&it).unwrap();
+ treeview.set_cursor(&next_path, gtk::TreeViewColumn::NONE, false);
+ }
+ return Inhibit(true)
+ }
+ DOWN => {
+ if let Some(mut c) = treeview.cursor().0 {
+ c.next();
+ if let Some(it) = liststore.iter(&c) {
+ let next_path = liststore.path(&it).unwrap();
+ treeview.set_cursor(&next_path, gtk::TreeViewColumn::NONE, false);
+ }
+ }
+ return Inhibit(true)
+ }
+ _ => ()
+ }
+ Inhibit(false)
+ });
+ }
+
+ container.add(&*entry);
+ container.add(&*scroll);
+
+ window.show_all();
+}
+
+fn main() {
+ let application = gtk::Application::new(
+ Some("org.n8.hyprmenu"),
+ Default::default(),
+ );
+
+ application.connect_activate(build_ui);
+
+ application.run();
+}