use crate::{ui::GamePreview, CoreApi}; use adw::{prelude::*, subclass::prelude::*}; use gio::ListModel; use glib::Object; use gtk::{glib, prelude::*, subclass::prelude::*}; use kifu_core::ui::GamePreviewElement; use std::{cell::RefCell, rc::Rc}; #[derive(Default)] pub struct GameObjectPrivate { game: Rc<RefCell<Option<GamePreviewElement>>>, } #[glib::object_subclass] impl ObjectSubclass for GameObjectPrivate { const NAME: &'static str = "GameObject"; type Type = GameObject; } impl ObjectImpl for GameObjectPrivate {} glib::wrapper! { pub struct GameObject(ObjectSubclass<GameObjectPrivate>); } impl GameObject { pub fn new(game: GamePreviewElement) -> Self { let s: Self = Object::builder().build(); *s.imp().game.borrow_mut() = Some(game); s } pub fn game(&self) -> Option<GamePreviewElement> { self.imp().game.borrow().clone() } } pub struct LibraryPrivate { model: gio::ListStore, list_view: gtk::ColumnView, } impl Default for LibraryPrivate { fn default() -> Self { let vector: Vec<GameObject> = vec![]; let model = gio::ListStore::new(glib::types::Type::OBJECT); model.extend_from_slice(&vector); let selection_model = gtk::NoSelection::new(Some(model.clone())); let list_view = gtk::ColumnView::builder().model(&selection_model).build(); fn make_factory<F>(bind: F) -> gtk::SignalListItemFactory where F: Fn(GamePreviewElement) -> String + 'static, { let factory = gtk::SignalListItemFactory::new(); factory.connect_setup(|_, list_item| { let item = list_item.downcast_ref::<gtk::ListItem>().unwrap(); item.set_activatable(true); item.set_child(Some( >k::Label::builder() .halign(gtk::Align::Start) .ellipsize(pango::EllipsizeMode::End) .build(), )) }); factory.connect_bind(move |_, list_item| { let list_item = list_item.downcast_ref::<gtk::ListItem>().unwrap(); let game = list_item.item().and_downcast::<GameObject>().unwrap(); let preview = list_item.child().and_downcast::<gtk::Label>().unwrap(); match game.game() { Some(game) => preview.set_text(&bind(game)), None => (), }; }); factory } list_view.append_column(>k::ColumnViewColumn::new( Some("date"), Some(make_factory(|g| g.date)), )); list_view.append_column(>k::ColumnViewColumn::new( Some("title"), Some(make_factory(|g| g.name)), )); list_view.append_column(>k::ColumnViewColumn::new( Some("black"), Some(make_factory(|g| g.black_player)), )); list_view.append_column(>k::ColumnViewColumn::new( Some("white"), Some(make_factory(|g| g.white_player)), )); list_view.append_column(>k::ColumnViewColumn::new( Some("result"), Some(make_factory(|g| g.result)), )); Self { model, list_view } } } #[glib::object_subclass] impl ObjectSubclass for LibraryPrivate { const NAME: &'static str = "Library"; type Type = Library; type ParentType = adw::Bin; } impl ObjectImpl for LibraryPrivate {} impl WidgetImpl for LibraryPrivate {} impl BinImpl for LibraryPrivate {} glib::wrapper! { pub struct Library(ObjectSubclass<LibraryPrivate>) @extends adw::Bin, gtk::Widget; } impl Library { pub fn new(api: CoreApi) -> Self { let s: Self = Object::builder().build(); s.set_child(Some(&s.imp().list_view)); s.imp().list_view.connect_activate({ let s = s.clone(); move |_, row_id| { let object = s.imp().model.item(row_id).unwrap(); // let list_item = object.downcast_ref::<gtk::ListItem>().unwrap(); // let game = list_item.item().and_downcast::<GameObject>().unwrap(); // let game_id = game.game().unwrap().id; // let game = object.downcast_ref::<gtk::ListItem>() let game = object.downcast::<GameObject>().unwrap(); let game_id = game.game().unwrap().id; api.dispatch(kifu_core::CoreRequest::OpenGameReview(game_id)); } }); s } pub fn set_games(&self, games: Vec<GamePreviewElement>) { let games = games .into_iter() .map(|g| GameObject::new(g)) .collect::<Vec<GameObject>>(); self.imp().model.extend_from_slice(&games); } }