fimfareader/gtk/src/components/app.rs

189 lines
4.6 KiB
Rust
Raw Normal View History

2019-08-17 23:14:19 +02:00
//! Application window.
2019-09-28 20:10:45 +02:00
use std::cell::RefCell;
2019-08-17 23:14:19 +02:00
use std::fs::File;
use std::io::BufReader;
2019-09-28 20:10:45 +02:00
use std::mem::replace;
use std::path::Path;
2019-08-17 23:14:19 +02:00
use std::rc::Rc;
use gtk::*;
2019-09-28 20:10:45 +02:00
use rayon::prelude::*;
2019-08-17 23:14:19 +02:00
use fimfareader::archive::Fetcher;
use fimfareader::archive::Story;
use fimfareader::query::parse;
2019-09-28 20:10:45 +02:00
pub enum Store {
Attached(StoryView),
Detached(TreeView),
Empty,
}
pub struct StoryView {
2019-08-17 23:14:19 +02:00
fetcher: Fetcher<BufReader<File>>,
2019-09-28 20:10:45 +02:00
filter: TreeModelFilter,
rows: Vec<TreeIter>,
store: ListStore,
2019-10-09 16:53:51 +02:00
view: TreeView,
2019-09-28 20:10:45 +02:00
visible: Vec<bool>,
}
pub struct AppWindow {
query: Entry,
store: Store,
2019-08-17 23:14:19 +02:00
window: ApplicationWindow,
2019-09-28 20:10:45 +02:00
open: FileChooserButton,
}
impl StoryView {
2019-10-09 16:53:51 +02:00
pub fn new(fetcher: Fetcher<BufReader<File>>, view: TreeView) -> Self {
2019-09-28 20:10:45 +02:00
const LENGTH: usize = 4;
let types: [Type; LENGTH] = [
i64::static_type(),
bool::static_type(),
String::static_type(),
String::static_type(),
];
let store = ListStore::new(&types);
let filter = TreeModelFilter::new(&store, None);
let columns: [u32; LENGTH] = [0, 1, 2, 3];
let mut rows: Vec<TreeIter> = Vec::with_capacity(fetcher.len());
let mut visible: Vec<bool> = Vec::with_capacity(fetcher.len());
for (i, story) in fetcher.iter().enumerate() {
let row = Some(i as u32);
let values: [&dyn ToValue; LENGTH] =
[&story.id, &true, &story.title, &story.author.name];
let row = store.insert_with_values(row, &columns, &values);
visible.push(true);
rows.push(row);
}
filter.set_visible_column(1);
2019-10-09 16:53:51 +02:00
view.set_model(Some(&filter));
2019-09-28 20:10:45 +02:00
StoryView {
fetcher,
filter,
rows,
store,
view,
visible,
}
}
2019-10-09 16:53:51 +02:00
pub fn detach(mut self) -> TreeView {
2019-10-09 19:08:37 +02:00
self.view.set_model(None::<&ListStore>);
2019-10-09 16:53:51 +02:00
replace(&mut self.view, TreeView::new())
2019-09-28 20:10:45 +02:00
}
2019-10-09 16:53:51 +02:00
pub fn filter<T>(&mut self, f: &T)
2019-09-28 20:10:45 +02:00
where
T: Sync + Fn(&Story) -> bool,
{
2019-10-09 16:53:51 +02:00
self.view.set_model(None::<&ListStore>);
let visible: Vec<bool> = self.fetcher.par_iter().map(f).collect();
2019-09-28 20:10:45 +02:00
for (i, row) in self.rows.iter().enumerate() {
2019-10-09 16:53:51 +02:00
let old = self.visible[i];
let new = visible[i];
2019-09-28 20:10:45 +02:00
if new != old {
2019-10-09 16:53:51 +02:00
self.store.set_value(row, 1, &new.to_value());
2019-09-28 20:10:45 +02:00
}
}
2019-10-09 16:53:51 +02:00
self.visible = visible;
self.view.set_model(Some(&self.filter));
2019-09-28 20:10:45 +02:00
}
}
impl Drop for StoryView {
fn drop(&mut self) {
2019-10-09 16:53:51 +02:00
self.view.set_model(None::<&ListStore>);
2019-09-28 20:10:45 +02:00
}
2019-08-17 23:14:19 +02:00
}
impl AppWindow {
2019-09-28 20:10:45 +02:00
pub fn new() -> Option<Rc<RefCell<Self>>> {
2019-08-17 23:14:19 +02:00
let ui = include_str!("app.ui");
let builder = Builder::new_from_string(ui);
2019-09-28 20:10:45 +02:00
let result = builder.get_object("result")?;
2019-08-17 23:14:19 +02:00
2019-09-28 20:10:45 +02:00
let this = Rc::new(RefCell::new(Self {
query: builder.get_object("entry")?,
store: Store::Detached(result),
2019-08-17 23:14:19 +02:00
window: builder.get_object("app")?,
2019-09-28 20:10:45 +02:00
open: builder.get_object("open")?,
}));
2019-08-17 23:14:19 +02:00
2019-09-28 20:10:45 +02:00
Self::connect(this.clone());
2019-09-14 23:12:03 +02:00
2019-09-28 20:10:45 +02:00
Some(this)
2019-08-17 23:14:19 +02:00
}
2019-09-28 20:10:45 +02:00
fn connect(wrapper: Rc<RefCell<Self>>) {
let this = wrapper.borrow();
2019-08-17 23:14:19 +02:00
2019-09-28 20:10:45 +02:00
this.window.connect_destroy(move |_| gtk::main_quit());
let clone = wrapper.clone();
this.query.connect_activate(move |entry| {
let mut this = clone.borrow_mut();
2019-08-17 23:14:19 +02:00
let text = entry.get_text().unwrap();
2019-09-28 20:10:45 +02:00
this.filter(text.as_str().trim());
2019-08-17 23:14:19 +02:00
});
2019-09-28 20:10:45 +02:00
let clone = wrapper.clone();
this.open.connect_file_set(move |dialog| {
let mut this = clone.borrow_mut();
let path = dialog.get_filename().unwrap();
this.load(path);
});
2019-08-17 23:14:19 +02:00
}
2019-09-28 20:10:45 +02:00
pub fn load(&mut self, path: impl AsRef<Path>) {
use Store::*;
let view = match replace(&mut self.store, Empty) {
Empty => panic!(),
Detached(view) => view,
2019-10-09 16:53:51 +02:00
Attached(store) => store.detach(),
2019-09-28 20:10:45 +02:00
};
let fetcher = Fetcher::from(path).unwrap();
2019-10-09 16:53:51 +02:00
let store = StoryView::new(fetcher, view);
2019-09-28 20:10:45 +02:00
self.store = Attached(store);
2019-08-17 23:14:19 +02:00
}
2019-09-28 20:10:45 +02:00
pub fn filter(&mut self, query: &str) {
use Store::*;
let model = match &mut self.store {
Attached(model) => model,
Detached(_) => return,
Empty => panic!(),
};
2019-09-14 23:12:03 +02:00
if query.trim() == "" {
2019-10-09 16:53:51 +02:00
model.filter(&|_| true);
2019-09-14 23:12:03 +02:00
return;
}
2019-09-28 20:10:45 +02:00
match parse(query) {
2019-10-09 16:53:51 +02:00
Err(_) => model.filter(&|_| false),
Ok(filter) => model.filter(&filter),
2019-08-17 23:14:19 +02:00
}
}
}