Commit 8f066c52 authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui

Search: implement search by name, category or tags

It's a bit slow, usable. Should revisit the code later
parent 3a8f4f39
Pipeline #97629 passed with stages
in 7 minutes and 4 seconds
......@@ -29,7 +29,7 @@
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<property name="placeholder_text" translatable="yes">Search for icons by name, category, or tag</property>
<property name="placeholder_text" translatable="yes">Search for icons by name, category or tag</property>
</object>
</child>
</object>
......
......@@ -10,6 +10,16 @@ pub struct Icon {
pub tags: Vec<String>,
}
impl Icon {
pub fn should_display(&self, search_str: &String) -> bool {
let search_str = search_str.to_lowercase();
// Check if the icon should be shown for the searched string
let mut found_tags = self.tags.clone();
found_tags.retain(|tag| tag.to_lowercase().contains(search_str.as_str()));
self.name.to_lowercase().contains(search_str.as_str()) || found_tags.len() != 0
}
}
// Until we get https://github.com/serde-rs/serde/issues/368 fixed
fn not_system() -> bool {
false
......
......@@ -19,6 +19,9 @@ impl ExportDialog {
pub fn new(icon: Icon) -> ExportDialog {
let builder = gtk::Builder::new_from_resource("/org/gnome/design/IconLibrary/export_dialog.ui");
let widget: libhandy::Dialog = builder.get_object("export_dialog").unwrap();
// libhandy sets a 2px border width, let's remove it
let icon_size_16: gtk::Image = builder.get_object("icon_size_16").unwrap();
let icon_size_32: gtk::Image = builder.get_object("icon_size_32").unwrap();
let icon_size_64: gtk::Image = builder.get_object("icon_size_64").unwrap();
......
......@@ -115,7 +115,7 @@ impl IconsView {
.map(|icon_name| Icon {
name: icon_name.as_str().to_string(),
is_system: true,
tags: [].to_vec(),
tags: [context_str.to_string()].to_vec(),
})
.collect();
icons.sort_by(|a, b| a.name.as_str().cmp(&b.name.as_str()));
......@@ -156,8 +156,6 @@ struct IconsViewContext {
impl IconsViewContext {
pub fn new(context: String, icons: Vec<Icon>, parent: &gtk::ApplicationWindow) -> IconsViewContext {
let widget = gtk::Box::new(gtk::Orientation::Vertical, 6);
widget.set_valign(gtk::Align::Fill);
widget.set_halign(gtk::Align::Fill);
let icons_container = gtk::FlowBox::new();
let icons_views: Vec<RefCell<IconView>> = Vec::new();
let n_last_search_results: u32 = 0;
......@@ -170,19 +168,15 @@ impl IconsViewContext {
};
icons_view_context.init();
icons_view_context.fill(icons, &parent);
icons_view_context.fill(icons);
icons_view_context.connect_signals(&parent);
icons_view_context
}
fn init(&self) {
let context_label = gtk::Label::new(Some(self.context.as_str()));
context_label.set_halign(gtk::Align::Start);
context_label.get_style_context().add_class("icons-view-title");
self.widget.set_valign(gtk::Align::Fill);
self.widget.set_halign(gtk::Align::Fill);
self.widget.add(&context_label);
}
fn fill(&mut self, icons: Vec<Icon>, parent: &gtk::ApplicationWindow) {
self.icons_container.set_valign(gtk::Align::Fill);
self.icons_container.set_halign(gtk::Align::Fill);
self.icons_container.set_row_spacing(12);
......@@ -190,23 +184,61 @@ impl IconsViewContext {
self.icons_container.set_max_children_per_line(14);
self.icons_container.set_selection_mode(gtk::SelectionMode::None);
let parent_window = parent.clone();
let context_label = gtk::Label::new(Some(self.context.as_str()));
context_label.set_halign(gtk::Align::Start);
context_label.get_style_context().add_class("icons-view-title");
self.widget.add(&context_label);
}
fn fill(&mut self, icons: Vec<Icon>) {
for icon in icons {
let icon_view = IconView::new(icon, &parent_window);
let icon_view = IconView::new(icon.clone());
self.icons_container.add(&icon_view.widget);
self.icons_views.push(RefCell::new(icon_view));
}
self.widget.add(&self.icons_container);
}
fn connect_signals(&self, parent: &gtk::ApplicationWindow) {
let parent_window = parent.clone();
let icons_views = self.icons_views.to_vec().into_iter();
self.icons_container.connect_child_activated(move |_, child| {
let icons_views = icons_views.clone();
let views: Vec<RefCell<IconView>> = icons_views
.filter(|icon_view| {
let flowbox = &icon_view.borrow_mut().widget;
flowbox.get_name().unwrap().as_str() == child.get_name().unwrap().as_str()
})
.collect();
match views.get(0) {
Some(icon_view) => {
let export_dialog = ExportDialog::new(icon_view.borrow_mut().icon.clone());
export_dialog.widget.set_transient_for(Some(&parent_window));
export_dialog.widget.show_all();
}
None => println!("Couldn't find the IconView"),
};
});
}
pub fn filter(&mut self, search_str: String) {
self.n_last_search_results = 0;
let filter_closure = move |flowboxchild: &gtk::FlowBoxChild| -> bool {
let row_container = &flowboxchild.get_children()[0];
let icon_name = &row_container.get_name().unwrap().as_str().to_string();
let searched_str = search_str.as_str();
icon_name.contains(searched_str)
let icons_views = self.icons_views.to_vec().into_iter();
let filter_closure = move |child: &gtk::FlowBoxChild| -> bool {
let icons_views = icons_views.clone();
let views: Vec<RefCell<IconView>> = icons_views
.filter(|icon_view| {
let flowbox = &icon_view.borrow_mut().widget;
flowbox.get_name().unwrap().as_str() == child.get_name().unwrap().as_str()
})
.collect();
match views.get(0) {
Some(icon_view) => {
let icon: &Icon = &icon_view.borrow_mut().icon;
icon.should_display(&search_str)
}
None => false,
}
};
self.icons_container.set_filter_func(Some(Box::new(filter_closure)));
......@@ -218,35 +250,23 @@ impl IconsViewContext {
self.widget.set_visible(self.n_last_search_results != 0);
}
}
#[derive(Clone)]
struct IconView {
pub widget: gtk::EventBox,
pub widget: gtk::FlowBoxChild,
icon: Icon,
}
impl IconView {
pub fn new(icon: Icon, parent: &gtk::ApplicationWindow) -> IconView {
pub fn new(icon: Icon) -> IconView {
let icon_name = icon.name.as_str();
let widget = gtk::EventBox::new();
let widget = gtk::FlowBoxChild::new();
widget.set_name(icon_name);
let icon_img = gtk::Image::new_from_icon_name(Some(icon_name), gtk::IconSize::Dnd);
widget.set_tooltip_text(Some(icon_name));
widget.add(&icon_img);
let icon_view = IconView { widget, icon };
icon_view.setup_signals(parent);
icon_view
}
fn setup_signals(&self, parent: &gtk::ApplicationWindow) {
let icon = self.icon.clone();
let parent_window = parent.clone();
self.widget.connect_button_press_event(move |_, _| {
let export_dialog = ExportDialog::new(icon.clone());
export_dialog.widget.set_transient_for(Some(&parent_window));
export_dialog.widget.show_all();
gtk::Inhibit(true)
});
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment