diff --git a/shell.nix b/shell.nix index bbfffcd..2cfadc7 100644 --- a/shell.nix +++ b/shell.nix @@ -15,6 +15,7 @@ pkgs.mkShell { pkgs.pango pkgs.cairo pkgs.harfbuzz + pkgs.librsvg # Wayland + layer shell (GTK4 variant) pkgs.wayland diff --git a/src/main.rs b/src/main.rs index 342fbba..6b6988b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ use gio::prelude::*; use gio::{File, FileIcon, Icon as GioIcon, ThemedIcon}; +use gtk::gdk::Texture; +use gtk::gdk_pixbuf::Pixbuf; use gtk::{ Application, ApplicationWindow, Box as GtkBox, Entry, Image, Label, ListBox, Orientation, ScrolledWindow, @@ -8,6 +10,8 @@ use gtk::{IconLookupFlags, prelude::*}; use gtk4_layer_shell as layerShell; use layerShell::LayerShell; use std::cell::RefCell; +use std::fmt::format; +use std::path::PathBuf; use std::rc::Rc; use waycast::{LauncherListItem, drun}; @@ -31,106 +35,39 @@ impl ListItem { fn create_widget(&self) -> GtkBox { let container = GtkBox::new(Orientation::Horizontal, 10); - - // let icon = if self.icon.eq("com.discordapp.Discord") - // || self.icon.eq("preferences-desktop-theme") - // || self.icon.eq("solaar") - // || self.icon.eq("kvantum") - // { - // println!("Failed: {}", self.icon); - // gio::Icon::for_string("vscode") - // } else { - // let x = String::from(self.icon.clone()); - // gio::Icon::for_string(&x) - // }; - // // let icon = gio::Icon::for_string("kvantum"); - // let image: Image = match icon { - // Ok(ic) => Image::from_gicon(&ic), - // Err(_) => Image::from_icon_name("vscode"), - // }; - // let image = Image::from_icon_name("vscode"); - // Use IconTheme for safe validation like Wofi does - let display = gtk::gdk::Display::default().unwrap(); - let icon_theme = gtk::IconTheme::for_display(&display); + // let display = gtk::gdk::Display::default().unwrap(); + // let icon_theme = gtk::IconTheme::for_display(&display); // Get current paths and filter out problematic ones - let current_paths = icon_theme.search_path(); - let clean_pathbufs: Vec<_> = current_paths - .into_iter() - .filter(|path| { - let path_str = path.to_string_lossy(); - // Keep user paths, flatpak, and system paths - remove nix store - !path_str.starts_with("/nix/store/") - // !path_str.contains("patchelf") && - // !path_str.contains("vscode-1.") && // Individual app store paths - // !path_str.contains("gsettings-desktop-schemas") - }) - .collect(); - // Convert to &Path references for the API - let clean_paths: Vec<&std::path::Path> = - clean_pathbufs.iter().map(|p| p.as_path()).collect(); + // TODO: Use this in the find_icon_file function + // let current_paths = icon_theme.search_path(); - println!("Filtered paths:"); - for path in &clean_paths { - println!(" {}", path.to_string_lossy()); - } - - // Set the clean path list - icon_theme.set_search_path(&clean_paths); - - if let Ok(gicon) = GioIcon::for_string(&self.icon) { - // Check if it's a file icon (absolute path) - if let Some(file_icon) = gicon.downcast_ref::() { - let file = file_icon.file(); - if let Some(path) = file.path() { - if path.exists() { - println!("Loading file icon: {:?}", path); - // return gtk::Image::from_file(path); - } - } - } - - // Check if it's a themed icon - if let Some(themed_icon) = gicon.downcast_ref::() { - let icon_names = themed_icon.names(); - for name in &icon_names { - if icon_theme.has_icon(name) { - println!("Found themed icon: {}", name); - // let paintable = icon_theme.lookup_icon( - // name, - // &[], // Empty fallback array - let GTK handle it - // icon_size, - // scale, - // gtk::TextDirection::None, - // IconLookupFlags::empty(), - // ); - // return Image::from_paintable(Some(&paintable)); - } - } - } - } - - let scale = container.scale_factor(); let icon_size = 48; - let lookup_flags = IconLookupFlags::empty(); - let image = if icon_theme.has_icon(&self.icon) { - println!("Has icon: {}", self.icon); - let paintable = icon_theme.lookup_icon( - &self.icon, - &["vscode"], - icon_size, - scale, - gtk::TextDirection::None, - lookup_flags, - ); - if let Some(icon_name) = paintable.icon_name() { - println!("Got icon name: {}", icon_name.to_string_lossy()); + let image: gtk::Image; + if let Some(icon_path) = find_icon_file(&self.icon, "48", "Papirus") { + println!("Found icon: {}", icon_path.to_string_lossy()); + // let file = gio::File::for_path(&icon_path); + image = match Pixbuf::from_file_at_scale(icon_path, icon_size, icon_size, true) { + Ok(pb) => { + let tex = Texture::for_pixbuf(&pb); + gtk::Image::from_paintable(Some(&tex)) + } + Err(e) => { + eprintln!("err: {}", e); + Image::from_icon_name("application-x-executable") + } } - gtk::Image::from_paintable(Some(&paintable)) + // image = match gtk::gdk::Texture::from_file(&file) { + // Ok(tex) => gtk::Image::from_paintable(Some(&tex)), + // Err(e) => { + // eprintln!("err: {}", e); + // Image::from_icon_name("application-x-executable") + // } + // } } else { - println!("No icon: {}", self.icon); - gtk::Image::from_icon_name("application-x-executable") - }; + let default = find_icon_file("vscode", "48", "hicolor").unwrap(); + image = gtk::Image::from_file(default); + } image.set_pixel_size(icon_size); // image.set_icon_name(Some("application-x-executable")); // Safe fallback @@ -184,9 +121,7 @@ impl AppModel { window.set_keyboard_mode(layerShell::KeyboardMode::OnDemand); window.set_layer(layerShell::Layer::Top); - println!("Starting to load desktop entries..."); let entries = drun::all(); - println!("Found {} entries", entries.len()); let model = Rc::new(RefCell::new(AppModel { window, @@ -205,7 +140,6 @@ impl AppModel { model_clone.borrow().filter_list(&query); }); - println!("Finished loading entries"); model } @@ -220,37 +154,6 @@ impl AppModel { let widget = list_item.create_widget(); self.list_box.append(&widget); } - - // let display = gtk::gdk::Display::default().unwrap(); - // let icon_theme = gtk::IconTheme::for_display(&display); - // Get current paths and filter out problematic ones - // let current_paths = icon_theme.search_path(); - // let clean_pathbufs: Vec<_> = current_paths - // .into_iter() - // .filter(|path| { - // let path_str = path.to_string_lossy(); - // // Keep user paths, flatpak, and system paths - remove nix store - // !path_str.starts_with("/nix/store/") - // // !path_str.contains("patchelf") && - // // !path_str.contains("vscode-1.") && // Individual app store paths - // // !path_str.contains("gsettings-desktop-schemas") - // }) - // .collect(); - // // Convert to &Path references for the API - // let clean_paths: Vec<&std::path::Path> = - // clean_pathbufs.iter().map(|p| p.as_path()).collect(); - - // println!("Filtered paths:"); - // for path in &clean_paths { - // println!(" {}", path.to_string_lossy()); - // } - - // // Set the clean path list - // icon_theme.set_search_path(&clean_paths); - - // for p in icon_theme.search_path() { - // println!("{}", p.to_string_lossy()); - // } } fn filter_list(&self, query: &str) { @@ -275,88 +178,6 @@ impl AppModel { } } -// fn find_icon_file_directly(icon_name: &str) -> Option { -// // Search in the filtered paths manually, bypassing GTK entirely -// let search_paths = [ -// "/home/javi/.local/share/icons", -// "/home/javi/.icons", -// "/home/javi/.local/share/flatpak/exports/share/icons", -// "/var/lib/flatpak/exports/share/icons", -// "/home/javi/.nix-profile/share/icons", -// "/nix/profile/share/icons", -// "/home/javi/.local/state/nix/profile/share/icons", -// "/etc/profiles/per-user/javi/share/icons", -// "/nix/var/nix/profiles/default/share/icons", -// "/run/current-system/sw/share/icons", -// "/home/javi/.local/share/flatpak/exports/share/pixmaps", -// "/var/lib/flatpak/exports/share/pixmaps", -// "/home/javi/.nix-profile/share/pixmaps", -// "/nix/profile/share/pixmaps", -// "/home/javi/.local/state/nix/profile/share/pixmaps", -// "/etc/profiles/per-user/javi/share/pixmaps", -// "/nix/var/nix/profiles/default/share/pixmaps", -// "/run/current-system/sw/share/pixmaps", -// ]; - -// let sizes = ["48", "64", "32", "scalable"]; -// let categories = ["apps", "applications"]; -// let extensions = ["png", "svg", "xpm"]; - -// for base_path in &search_paths { -// let base = std::path::Path::new(base_path); -// if !base.exists() { -// continue; -// } - -// // First try: direct pixmaps -// for ext in &extensions { -// let direct = base.join("pixmaps").join(format!("{}.{}", icon_name, ext)); -// if direct.exists() { -// println!("Found direct pixmap: {:?}", direct); -// return Some(direct); -// } -// } - -// // Second try: theme structure -// if let Ok(theme_dirs) = std::fs::read_dir(base) { -// for theme_entry in theme_dirs.flatten() { -// println!( -// "theme: {} in {}", -// theme_entry.file_name().to_string_lossy(), -// base.to_string_lossy() -// ); -// let is_dir = theme_entry.metadata().map(|m| m.is_dir()).unwrap_or(false); -// if !is_dir { -// println!( -// "Skipping cuz not dir: {}", -// theme_entry.file_name().to_string_lossy() -// ); -// continue; -// } - -// let theme_path = theme_entry.path(); - -// for size in &sizes { -// for category in &categories { -// for ext in &extensions { -// let icon_path = theme_path -// .join(size) -// .join(category) -// .join(format!("{}.{}", icon_name, ext)); - -// if icon_path.exists() { -// println!("Found themed icon: {:?}", icon_path); -// return Some(icon_path); -// } -// } -// } -// } -// } -// } -// } - -// None -// } fn find_icon_file(icon_name: &str, size: &str, theme_name: &str) -> Option { let search_paths = [ "/home/javi/.local/share/icons", @@ -382,9 +203,9 @@ fn find_icon_file(icon_name: &str, size: &str, theme_name: &str) -> Option Option = Vec::new(); + // Do all the theme directories first and high color second + for path in &search_paths { + let base = std::path::Path::new(path); + for size in sizes { + for cat in &categories { + let path = base + .join(theme_name) + .join(if !(size == "scalable".to_string()) { + format!("{}x{}", size, size) + } else { + size.to_string() + }) + .join(cat); + + if path.exists() { + search_in.push(path); + } + } } + } + for path in &search_paths { + let base = std::path::Path::new(path); + for size in sizes { + for cat in &categories { + let path = base + .join("hicolor") + .join(if !(size == "scalable".to_string()) { + format!("{}x{}", size, size) + } else { + size.to_string() + }) + .join(cat); - if let Ok(theme_dirs) = std::fs::read_dir(base) { - for theme_entry in theme_dirs.flatten() { - let theme_name = theme_entry.file_name().to_string_lossy().to_string(); - println!( - "Checking theme: {} in {}", - theme_name, - base.to_string_lossy() - ); - - let is_dir = theme_entry.path().is_dir(); - - if !is_dir { - println!("Skipping no dir"); - continue; // Skip files in icon directories + if path.exists() { + search_in.push(path); } + } + } + } - let theme_path = theme_entry.path(); - for size in &sizes { - for category in &categories { - println!( - "{}", - theme_path - .join(format!("{}x{}", size, size)) - .join(category) - .to_string_lossy() - ); - for ext in &extensions { - let icon_path = theme_path - .join(format!("{}x{}", size, size)) - .join(category) - .join(format!("{}.{}", icon_name, ext)); - if icon_path.exists() { - println!("Found themed icon: {:?}", icon_path); - return Some(icon_path); - } - } - } - } + for s in &search_in { + for ext in &extensions { + let icon_path = s.join(format!("{}.{}", icon_name, ext)); + println!("- {}", format!("{}.{}", icon_name, ext)); + if icon_path.exists() { + return Some(icon_path); } } } @@ -455,17 +278,21 @@ fn find_icon_file(icon_name: &str, size: &str, theme_name: &str) -> Option() { - // // ThemedIcon may have multiple names, we take the first - // if let Some(name) = ti.names().first() { - // println!("Themed: {}", name.to_string()); + // println!("Icon: {}", x.to_string()); + // if let Some(path) = find_icon_file(&x, "48", icon_theme.theme_name().as_str()) { + // println!("Found at: {}", path.to_string_lossy()); + // } else { + // println!("Not found"); // } // } + // // if let Ok(ti) = icon.clone().downcast::() { + // // // ThemedIcon may have multiple names, we take the first + // // if let Some(name) = ti.names().first() { + // // println!("Themed: {}", name.to_string()); + // // } + // // } - // if let Ok(fi) = icon.clone().downcast::() { - // if let Some(path) = fi.file().path() { - // println!("File: {}", path.to_string_lossy().to_string()); - // } - // } + // // if let Ok(fi) = icon.clone().downcast::() { + // // if let Some(path) = fi.file().path() { + // // println!("File: {}", path.to_string_lossy().to_string()); + // // } + // // } // } // println!("\n"); // } - // let scale = 1; - // let icon_size = 48; - // let lookup_flags = IconLookupFlags::empty(); - // let icon = gio::ThemedIcon::new("vscode"); - // let finded = icon_theme.lookup_by_gicon( - // &icon, - // icon_size, - // scale, - // gtk::TextDirection::None, - // lookup_flags, - // ); - // let appinfo = gio::AppInfo::all(); - gtk::init().expect("Failed to init GTK"); - let display = gtk::gdk::Display::default().unwrap(); - let icon_theme = gtk::IconTheme::for_display(&display); - // for i in icon_theme.icon_names() { - // println!("Icon: {}", i); - // for s in icon_theme.icon_sizes(i) { - // println!("- {}", s); - // } - // } - println!("Current icon theme: {:?}", icon_theme.theme_name()); - if let Some(path) = find_icon_file_directly("application-x-trash") { - println!("Found at: {}", path.to_string_lossy()); - } else { - println!("Not working"); - } }