diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-07-23 22:09:07 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-07-23 22:09:07 +0200 |
| commit | 84ad0403b30f0ec1aecd7ea0c78c8baea7baa454 (patch) | |
| tree | 0c41e27625ef73d2dcc54d17ea9c263857499bf0 /src | |
first sketch of hyprmenu
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 187 | ||||
| -rw-r--r-- | src/style.css | 29 |
2 files changed, 216 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: >k::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(); +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..242ed49 --- /dev/null +++ b/src/style.css @@ -0,0 +1,29 @@ +#hyprmenu { + background-color: transparent; + font-family: SauceCodePro Nerd Font,FontAwesome, Roboto, Helvetica, Arial, sans-serif; + font-weight: normal; +} + +#search { + background-color: rgba(29, 32, 33, 1); + border: none; + box-shadow: none; + padding: 10px; + border-radius: 0px; +} + +#search:focus { + border: none; + box-shadow: none; +} + +#item { + padding: 10px; + background-color: rgba(40, 40, 40, 0.8); +} + +#item:selected { + background-color: #b16286; + padding: 10px; +} + |