use crate::{ui::Library, CoreApi}; use glib::Object; use gtk::{glib, prelude::*, subclass::prelude::*}; use otg_core::{ ui::{HomeView, PlayerElement}, CoreRequest, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest, }; use std::{cell::RefCell, rc::Rc}; struct PlayerDataEntryPrivate { name: gtk::Text, rank: gtk::DropDown, } impl Default for PlayerDataEntryPrivate { fn default() -> Self { let rank = gtk::DropDown::builder().build(); Self { name: gtk::Text::builder().build(), rank, } } } #[glib::object_subclass] impl ObjectSubclass for PlayerDataEntryPrivate { const NAME: &'static str = "PlayerDataEntry"; type Type = PlayerDataEntry; type ParentType = gtk::Box; } impl ObjectImpl for PlayerDataEntryPrivate {} impl WidgetImpl for PlayerDataEntryPrivate {} impl BoxImpl for PlayerDataEntryPrivate {} glib::wrapper! { struct PlayerDataEntry(ObjectSubclass) @extends gtk::Box, gtk::Widget; } impl PlayerDataEntry { pub fn new(element: PlayerElement) -> PlayerDataEntry { let s: Self = Object::builder().build(); let rank_model = gio::ListStore::new::(); s.imp().rank.set_model(Some(&rank_model)); match element { PlayerElement::Hotseat(player) => { if let Some(placeholder) = player.placeholder { s.imp().name.set_placeholder_text(Some(&placeholder)); } player.ranks.iter().for_each(|rank| rank_model.append(>k::StringObject::new(rank))); } // PlayerElement::Remote(_) => s.imp().placeholder.set_text("remote player"), // PlayerElement::Bot(_) => s.imp().placeholder.set_text("bot player"), } s.append(&s.imp().name); s.append(&s.imp().rank); s } pub fn text(&self) -> String { let name = self.imp().name.buffer().text().to_string(); if name.is_empty() { self.imp() .name .placeholder_text() .map(|s| s.to_string()) .unwrap_or("".to_owned()) } else { name } } pub fn rank(&self) -> Option { self.imp().rank.selected_item().and_then(|obj| { let str_obj = obj.downcast::().ok()?; Some(str_obj.string().clone().to_string()) }) } } pub struct HomePrivate { black_player: Rc>>, white_player: Rc>>, } impl Default for HomePrivate { fn default() -> Self { Self { black_player: Rc::new(RefCell::new(None)), white_player: Rc::new(RefCell::new(None)), } } } #[glib::object_subclass] impl ObjectSubclass for HomePrivate { const NAME: &'static str = "Home"; type Type = Home; type ParentType = gtk::Box; } impl ObjectImpl for HomePrivate {} impl WidgetImpl for HomePrivate {} impl BoxImpl for HomePrivate {} glib::wrapper! { pub struct Home(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable; } impl Home { pub fn new(api: CoreApi, view: HomeView) -> Home { let s: Self = Object::builder().build(); s.set_spacing(4); s.set_homogeneous(false); s.set_orientation(gtk::Orientation::Vertical); let players = gtk::Box::builder() .spacing(4) .orientation(gtk::Orientation::Horizontal) .build(); s.append(&players); let black_player = PlayerDataEntry::new(view.black_player); players.append(&black_player); *s.imp().black_player.borrow_mut() = Some(black_player.clone()); let white_player = PlayerDataEntry::new(view.white_player); players.append(&white_player); *s.imp().white_player.borrow_mut() = Some(white_player.clone()); let new_game_button = gtk::Button::builder() .css_classes(vec!["suggested-action"]) .label(&view.start_game.label) .build(); s.append(&new_game_button); let library = Library::default(); let library_view = gtk::ScrolledWindow::builder() .hscrollbar_policy(gtk::PolicyType::Never) .min_content_width(360) .vexpand(true) .hexpand(true) .child(&library) .build(); s.append(&library_view); library.set_games(view.games); new_game_button.connect_clicked({ move |_| { let black_player = black_player.clone(); let white_player = white_player.clone(); api.dispatch(CoreRequest::CreateGame(CreateGameRequest { black_player: player_info(black_player.clone()), white_player: player_info(white_player.clone()), })); } }); s } } fn player_info(player: PlayerDataEntry) -> PlayerInfoRequest { PlayerInfoRequest::Hotseat(HotseatPlayerRequest { name: player.text(), rank: player.rank(), }) }