filter based on app names or category

This commit is contained in:
Ashley Wulber 2021-12-03 12:40:51 -05:00 committed by Jeremy Soller
parent 9d3be77538
commit 3779b7ec5e
4 changed files with 84 additions and 24 deletions

View file

@ -2,6 +2,7 @@ use gtk::glib;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use gtk4 as gtk; use gtk4 as gtk;
use std::cell::Cell;
use gtk::CompositeTemplate; use gtk::CompositeTemplate;
@ -12,6 +13,7 @@ pub struct GridItem {
pub name: TemplateChild<gtk::Label>, pub name: TemplateChild<gtk::Label>,
#[template_child] #[template_child]
pub image: TemplateChild<gtk::Image>, pub image: TemplateChild<gtk::Image>,
pub index: Cell<u32>,
} }
#[glib::object_subclass] #[glib::object_subclass]

View file

@ -7,8 +7,9 @@ use gtk::subclass::prelude::*;
use gtk::{gio, glib}; use gtk::{gio, glib};
glib::wrapper! { glib::wrapper! {
pub struct GridItem(ObjectSubclass<imp::GridItem>) pub struct GridItem(ObjectSubclass<imp::GridItem>)
@extends gtk::Widget, gtk::Box; @extends gtk::Widget, gtk::Box,
@implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
} }
impl Default for GridItem { impl Default for GridItem {
@ -40,11 +41,15 @@ impl GridItem {
); );
} }
if let Ok(icon) = app_group.property("icon") { if let Ok(icon) = app_group.property("icon") {
&self_.image.set_from_icon_name(Some( self_.image.set_from_icon_name(Some(
&icon &icon
.get::<String>() .get::<String>()
.expect("Property name needs to be a String."), .expect("Property name needs to be a String."),
)); ));
} }
} }
pub fn set_index(&self, index: u32) {
imp::GridItem::from_instance(self).index.set(index);
}
} }

View file

@ -1,7 +1,10 @@
mod imp; mod imp;
use crate::app_group::AppGroup; use crate::app_group::AppGroup;
use crate::app_group::AppGroupData; use crate::app_group::AppGroupData;
use glib::FromVariant;
use glib::Variant;
use gtk4 as gtk; use gtk4 as gtk;
use gtk4::Button;
use std::path::Path; use std::path::Path;
use crate::grid_item::GridItem; use crate::grid_item::GridItem;
@ -127,13 +130,21 @@ impl Window {
app_names: Vec::new(), app_names: Vec::new(),
category: "Utility".to_string(), category: "Utility".to_string(),
}), }),
AppGroup::new(AppGroupData {
id: 0,
name: "Web".to_string(),
icon: "folder".to_string(),
mutable: true,
app_names: vec!["Firefox Web Browser".to_string()],
category: "".to_string(),
}),
] ]
.iter() .iter()
.for_each(|group| { .for_each(|group| {
group_model.append(group); group_model.append(group);
}); });
imp.group_grid_view let group_selection = gtk4::SingleSelection::new(Some(&group_model));
.set_model(Some(&gtk4::SingleSelection::new(Some(&group_model)))); imp.group_grid_view.set_model(Some(&group_selection));
} }
fn setup_callbacks(&self) { fn setup_callbacks(&self) {
@ -187,7 +198,6 @@ impl Window {
} }
}); });
// TODO connect to custom signal from app group grid item activation
// on activation change the group filter model to use the app names, and category // on activation change the group filter model to use the app names, and category
group_grid_view.connect_activate(glib::clone!(@weak app_filter_model => move |grid_view, position| { group_grid_view.connect_activate(glib::clone!(@weak app_filter_model => move |grid_view, position| {
let model = grid_view.model().unwrap(); let model = grid_view.model().unwrap();
@ -196,16 +206,27 @@ impl Window {
.unwrap() .unwrap()
.downcast::<AppGroup>() .downcast::<AppGroup>()
.unwrap(); .unwrap();
let category: String; let category =
if let Ok(category_prop) = app_info.property("category") { if let Ok(category_prop) = app_info.property("category") {
category = category_prop.get::<String>().unwrap_or("".to_string()).to_lowercase(); category_prop.get::<String>().unwrap_or("".to_string()).to_lowercase()
} else { } else {
category = "".to_string(); "".to_string()
} };
let app_names =
if let Ok(app_names_prop) = app_info.property("appnames") {
<Vec<String>>::from_variant(&app_names_prop.get::<Variant>().expect("appnames nneds to be a variant.")).unwrap_or_default()
} else {
vec![]
};
dbg!(&app_names);
let new_filter: gtk::CustomFilter = gtk::CustomFilter::new(move |obj| { let new_filter: gtk::CustomFilter = gtk::CustomFilter::new(move |obj| {
let app = obj let app = obj
.downcast_ref::<gio::DesktopAppInfo>() .downcast_ref::<gio::DesktopAppInfo>()
.expect("The Object needs to be of type AppInfo"); .expect("The Object needs to be of type AppInfo");
if app_names.len() > 0 {
return app_names.contains(&String::from(app.name().as_str()));
}
match app.categories() { match app.categories() {
Some(categories) => categories.to_string().to_lowercase().contains(&category), Some(categories) => categories.to_string().to_lowercase().contains(&category),
None => false, None => false,
@ -213,6 +234,35 @@ impl Window {
}); });
group_filter_model.set_filter(Some(new_filter).as_ref()); group_filter_model.set_filter(Some(new_filter).as_ref());
})); }));
// can't listen to select signal on grid view, but this a good to know example...
// if group_grid_view.connect_local("select", true, glib::clone!(@weak group_filter_model => @default-return None, move |args| {
// let grid_view = args[0].get::<gtk::GridView>().unwrap();
// let position = args[1].get::<u32>().unwrap();
// // on activation change the group filter model to use the app names, and category
// let model = grid_view.model().unwrap();
// let app_info = model
// .item(position)
// .unwrap()
// .downcast::<AppGroup>()
// .unwrap();
// let category: String;
// if let Ok(category_prop) = app_info.property("category") {
// category = category_prop.get::<String>().unwrap_or("".to_string()).to_lowercase();
// } else {
// category = "".to_string();
// }
// let new_filter: gtk::CustomFilter = gtk::CustomFilter::new(move |obj| {
// let app = obj
// .downcast_ref::<gio::DesktopAppInfo>()
// .expect("The Object needs to be of type AppInfo");
// match app.categories() {
// Some(categories) => categories.to_string().to_lowercase().contains(&category),
// None => false,
// }
// });
// group_filter_model.set_filter(Some(new_filter).as_ref());
// None
// })).is_err() { println!("Failed to connect to grid view select...") };
entry.connect_changed( entry.connect_changed(
glib::clone!(@weak app_filter_model, @weak sorted_model => move |search: &gtk::SearchEntry| { glib::clone!(@weak app_filter_model, @weak sorted_model => move |search: &gtk::SearchEntry| {
@ -289,20 +339,23 @@ impl Window {
item.set_child(Some(&row)); item.set_child(Some(&row));
}); });
// the bind stage is used for "binding" the data to the created widgets on the "setup" stage
app_factory.connect_bind(move |_factory, grid_item| {
let app_info = grid_item
.item()
.unwrap()
.downcast::<gio::DesktopAppInfo>()
.unwrap();
let child = grid_item.child().unwrap().downcast::<GridItem>().unwrap();
child.set_app_info(&app_info);
});
// Set the factory of the list view
let imp = imp::Window::from_instance(self); let imp = imp::Window::from_instance(self);
imp.app_grid_view.set_factory(Some(&app_factory)); // the bind stage is used for "binding" the data to the created widgets on the "setup" stage
let app_grid_view = &imp.app_grid_view.get();
app_factory.connect_bind(
glib::clone!(@weak app_grid_view => move |_factory, grid_item| {
let app_info = grid_item
.item()
.unwrap()
.downcast::<gio::DesktopAppInfo>()
.unwrap();
let child = grid_item.child().unwrap().downcast::<GridItem>().unwrap();
child.set_app_info(&app_info);
}),
);
// Set the factory of the list view
app_grid_view.set_factory(Some(&app_factory));
let group_factory = SignalListItemFactory::new(); let group_factory = SignalListItemFactory::new();
group_factory.connect_setup(move |_factory, item| { group_factory.connect_setup(move |_factory, item| {

View file

@ -47,7 +47,7 @@
<object class="GtkGridView" id="group_grid_view"> <object class="GtkGridView" id="group_grid_view">
<property name="min-columns">8</property> <property name="min-columns">8</property>
<property name="max-columns">8</property> <property name="max-columns">8</property>
<property name="single-click-activate">false</property> <property name="single-click-activate">true</property>
<property name="enable-rubberband">false</property> <property name="enable-rubberband">false</property>
</object> </object>
</child> </child>