Find selected list item by id instead of by index

This commit is contained in:
Javier Feliz 2025-09-05 18:34:21 -04:00
parent 3cbf7dd341
commit d8aa3cf267
6 changed files with 71 additions and 25 deletions

View File

@ -10,6 +10,10 @@ pub struct DesktopEntry {
} }
impl LauncherListItem for DesktopEntry { impl LauncherListItem for DesktopEntry {
fn id(&self) -> String {
self.id.clone()
}
fn title(&self) -> String { fn title(&self) -> String {
return self.name.to_owned(); return self.name.to_owned();
} }

View File

@ -7,6 +7,7 @@ pub struct WaycastLauncher {
plugins_show_always: Vec<Arc<dyn LauncherPlugin>>, plugins_show_always: Vec<Arc<dyn LauncherPlugin>>,
plugins_by_prefix: HashMap<String, Arc<dyn LauncherPlugin>>, plugins_by_prefix: HashMap<String, Arc<dyn LauncherPlugin>>,
current_results: Vec<Box<dyn LauncherListItem>>, current_results: Vec<Box<dyn LauncherListItem>>,
current_results_by_id: HashMap<String, usize>,
} }
impl WaycastLauncher { impl WaycastLauncher {
@ -16,6 +17,7 @@ impl WaycastLauncher {
plugins_show_always: Vec::new(), plugins_show_always: Vec::new(),
plugins_by_prefix: HashMap::new(), plugins_by_prefix: HashMap::new(),
current_results: Vec::new(), current_results: Vec::new(),
current_results_by_id: HashMap::new(),
} }
} }
} }
@ -47,23 +49,39 @@ impl WaycastLauncher {
self self
} }
pub fn get_default_results(&mut self) -> &Vec<Box<dyn LauncherListItem>> { fn add_current_item(&mut self, item: Box<dyn LauncherListItem>) {
let id = item.id();
let index = self.current_results.len();
self.current_results.push(item);
self.current_results_by_id.insert(id, index);
}
fn clear_current_results(&mut self) {
self.current_results.clear(); self.current_results.clear();
self.current_results_by_id.clear();
}
pub fn get_default_results(&mut self) -> &Vec<Box<dyn LauncherListItem>> {
self.clear_current_results();
let mut all_entries = Vec::new();
for plugin in &self.plugins_show_always { for plugin in &self.plugins_show_always {
for entry in plugin.default_list() { all_entries.extend(plugin.default_list());
self.current_results.push(entry); }
} for entry in all_entries {
self.add_current_item(entry);
} }
&self.current_results &self.current_results
} }
pub fn search(&mut self, query: &str) -> &Vec<Box<dyn LauncherListItem>> { pub fn search(&mut self, query: &str) -> &Vec<Box<dyn LauncherListItem>> {
self.current_results.clear(); self.clear_current_results();
let mut all_entries = Vec::new();
for plugin in &self.plugins { for plugin in &self.plugins {
for entry in plugin.filter(query) { all_entries.extend(plugin.filter(query));
self.current_results.push(entry); }
} for entry in all_entries {
self.add_current_item(entry);
} }
&self.current_results &self.current_results
@ -77,6 +95,18 @@ impl WaycastLauncher {
} }
} }
pub fn execute_item_by_id(&self, id: &str) -> Result<(), crate::LaunchError> {
if let Some(&index) = self.current_results_by_id.get(id) {
if let Some(item) = self.current_results.get(index) {
item.execute()
} else {
Err(crate::LaunchError::CouldNotLaunch("Item index out of bounds".into()))
}
} else {
Err(crate::LaunchError::CouldNotLaunch("Item not found".into()))
}
}
pub fn current_results(&self) -> &Vec<Box<dyn LauncherListItem>> { pub fn current_results(&self) -> &Vec<Box<dyn LauncherListItem>> {
&self.current_results &self.current_results
} }

View File

@ -9,6 +9,7 @@ pub enum LaunchError {
} }
pub trait LauncherListItem { pub trait LauncherListItem {
fn id(&self) -> String;
fn title(&self) -> String; fn title(&self) -> String;
fn description(&self) -> Option<String>; fn description(&self) -> Option<String>;
fn execute(&self) -> Result<(), LaunchError>; fn execute(&self) -> Result<(), LaunchError>;

View File

@ -11,6 +11,10 @@ pub struct DesktopEntry {
} }
impl LauncherListItem for DesktopEntry { impl LauncherListItem for DesktopEntry {
fn id(&self) -> String {
self.id.clone()
}
fn title(&self) -> String { fn title(&self) -> String {
return self.name.to_owned(); return self.name.to_owned();
} }

View File

@ -21,6 +21,10 @@ impl FileEntry {
} }
impl LauncherListItem for FileEntry { impl LauncherListItem for FileEntry {
fn id(&self) -> String {
self.path.to_string_lossy().to_string()
}
fn title(&self) -> String { fn title(&self) -> String {
return String::from(self.path.file_name().unwrap().to_string_lossy()); return String::from(self.path.file_name().unwrap().to_string_lossy());
} }

View File

@ -26,7 +26,7 @@ mod imp {
pub title: RefCell<String>, pub title: RefCell<String>,
pub description: RefCell<Option<String>>, pub description: RefCell<Option<String>>,
pub icon: RefCell<String>, pub icon: RefCell<String>,
pub index: RefCell<usize>, // Store index to access original entry pub id: RefCell<String>, // Store id to access original entry
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -44,7 +44,7 @@ glib::wrapper! {
} }
impl LauncherItemObject { impl LauncherItemObject {
pub fn new(title: String, description: Option<String>, icon: String, index: usize) -> Self { pub fn new(title: String, description: Option<String>, icon: String, id: String) -> Self {
let obj: Self = glib::Object::new(); let obj: Self = glib::Object::new();
let imp = obj.imp(); let imp = obj.imp();
@ -52,7 +52,7 @@ impl LauncherItemObject {
*imp.title.borrow_mut() = title; *imp.title.borrow_mut() = title;
*imp.description.borrow_mut() = description; *imp.description.borrow_mut() = description;
*imp.icon.borrow_mut() = icon; *imp.icon.borrow_mut() = icon;
*imp.index.borrow_mut() = index; *imp.id.borrow_mut() = id;
obj obj
} }
@ -69,8 +69,8 @@ impl LauncherItemObject {
self.imp().description.borrow().clone() self.imp().description.borrow().clone()
} }
pub fn index(&self) -> usize { pub fn id(&self) -> String {
*self.imp().index.borrow() self.imp().id.borrow().clone()
} }
} }
@ -234,12 +234,12 @@ impl GtkLauncherUI {
// Update the list store // Update the list store
list_store_for_search.remove_all(); list_store_for_search.remove_all();
for (index, entry) in results.iter().enumerate() { for entry in results.iter() {
let item_obj = LauncherItemObject::new( let item_obj = LauncherItemObject::new(
entry.title(), entry.title(),
entry.description(), entry.description(),
entry.icon(), entry.icon(),
index, entry.id(),
); );
list_store_for_search.append(&item_obj); list_store_for_search.append(&item_obj);
} }
@ -252,8 +252,8 @@ impl GtkLauncherUI {
search_input.connect_activate(move |_| { search_input.connect_activate(move |_| {
if let Some(selected_item) = selection_for_enter.selected_item() { if let Some(selected_item) = selection_for_enter.selected_item() {
if let Some(item_obj) = selected_item.downcast_ref::<LauncherItemObject>() { if let Some(item_obj) = selected_item.downcast_ref::<LauncherItemObject>() {
let index = item_obj.index(); let id = item_obj.id();
match launcher_for_enter.borrow().execute_item(index) { match launcher_for_enter.borrow().execute_item_by_id(&id) {
Ok(_) => app_for_enter.quit(), Ok(_) => app_for_enter.quit(),
Err(e) => eprintln!("Failed to launch app: {:?}", e), Err(e) => eprintln!("Failed to launch app: {:?}", e),
} }
@ -304,22 +304,25 @@ impl GtkLauncherUI {
// Connect list activation signal // Connect list activation signal
let launcher_for_activate = launcher.clone(); let launcher_for_activate = launcher.clone();
let app_for_activate = app.clone(); let app_for_activate = app.clone();
let list_store_for_activate = list_store.clone();
list_view.connect_activate(move |_, position| { list_view.connect_activate(move |_, position| {
match launcher_for_activate if let Some(obj) = list_store_for_activate.item(position) {
.borrow() if let Some(item_obj) = obj.downcast_ref::<LauncherItemObject>() {
.execute_item(position as usize) let id = item_obj.id();
{ match launcher_for_activate.borrow().execute_item_by_id(&id) {
Ok(_) => app_for_activate.quit(), Ok(_) => app_for_activate.quit(),
Err(e) => eprintln!("Failed to launch app: {:?}", e), Err(e) => eprintln!("Failed to launch app: {:?}", e),
}
}
} }
}); });
// Initialize with default results // Initialize with default results
let mut launcher_ref = launcher.borrow_mut(); let mut launcher_ref = launcher.borrow_mut();
let results = launcher_ref.get_default_results(); let results = launcher_ref.get_default_results();
for (index, entry) in results.iter().enumerate() { for entry in results.iter() {
let item_obj = let item_obj =
LauncherItemObject::new(entry.title(), entry.description(), entry.icon(), index); LauncherItemObject::new(entry.title(), entry.description(), entry.icon(), entry.id());
list_store.append(&item_obj); list_store.append(&item_obj);
} }