use gtk::prelude::*; use relm4::factory::FactoryVecDeque; use relm4::RelmListBoxExt; use relm4::{gtk, ComponentParts, ComponentSender, SimpleComponent}; use crate::sheet::Sheet; use super::sheet_model::{OnQueryUpdate, SheetModel}; use rand::seq::SliceRandom; pub struct SheetListingModel { sheets: FactoryVecDeque, } #[derive(Debug)] pub enum SheetListingInput { Query(String), ListBoxRowClicked(i32), Sort, Shuffle, ReloadSheets(Vec), None, } #[derive(Debug)] pub enum SheetListingOutput { SheetModelSelected(Sheet), ContentsChanged, } #[relm4::component(pub)] impl SimpleComponent for SheetListingModel { type Init = Vec; type Input = SheetListingInput; type Output = SheetListingOutput; view! { #[root] gtk::Box { // set_orientation: gtk::Orientation::Vertical, model.sheets.widget() -> &relm4::gtk::ListBox { set_hexpand: true, // set_orientation: gtk::Orientation::Vertical, // set_spacing: 5, connect_row_activated[sender] => move |list_box, row| { let index = list_box.index_of_child(row).unwrap(); sender.input(SheetListingInput::ListBoxRowClicked(index)); }, }, } } fn init( init: Self::Init, root: Self::Root, sender: ComponentSender, ) -> ComponentParts { let mut sheets = FactoryVecDeque::builder() .launch(gtk::ListBox::default()) .forward(sender.input_sender(), |_| SheetListingInput::None); for sheet_model_type in init { sheets.guard().push_back(sheet_model_type); } let model = SheetListingModel { sheets }; let widgets = view_output!(); ComponentParts { model, widgets } } fn update(&mut self, message: Self::Input, sender: ComponentSender) { match message { SheetListingInput::Query(query) => { self.sheets.broadcast(OnQueryUpdate { query }); } SheetListingInput::ListBoxRowClicked(index) => { let sheet_model = self.sheets.get(index as usize).unwrap(); sender .output(SheetListingOutput::SheetModelSelected( sheet_model.sheet.clone(), )) .unwrap(); } SheetListingInput::Sort => { sort_sheets(&mut self.sheets); sender.output(SheetListingOutput::ContentsChanged).unwrap(); } SheetListingInput::Shuffle => { shuffle_sheets(&mut self.sheets); sender.output(SheetListingOutput::ContentsChanged).unwrap(); } SheetListingInput::ReloadSheets(sheets) => { self.sheets.guard().clear(); for sheet_model_type in sheets { self.sheets.guard().push_back(sheet_model_type); } sender.output(SheetListingOutput::ContentsChanged).unwrap(); } SheetListingInput::None => {} } } } fn shuffle_sheets(sheets: &mut FactoryVecDeque) { let mut new_order: Vec = (0..sheets.len()).collect(); new_order.shuffle(&mut rand::thread_rng()); order_sheets(sheets, &mut new_order); } fn sort_sheets(sheets: &mut FactoryVecDeque) { let mut order = Vec::new(); { let guard = sheets.guard(); let mut numerated_sheets: Vec<_> = guard.iter().enumerate().collect(); numerated_sheets.sort_by(|a, b| a.1.sheet.cmp(&b.1.sheet).reverse()); for (i, _) in numerated_sheets { order.push(i); } } order_sheets(sheets, &mut order); } fn order_sheets(sheets: &mut FactoryVecDeque, order: &mut Vec) { assert!(sheets.len() == order.len()); let mut wish_positions = vec![0; sheets.len()]; for (i, i2) in order.iter().enumerate() { wish_positions[*i2] = i; } for i in 0..sheets.len() { let new_i = order[i]; let old_i = i; if old_i != new_i { order.swap(old_i, wish_positions[old_i]); wish_positions.swap(old_i, new_i); sheets.guard().swap(old_i, new_i); } } } #[cfg(test)] mod tests { // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; #[test] fn test_sort() { let original: Vec = (0..100).collect(); let mut to_sort = original.clone(); to_sort.shuffle(&mut rand::thread_rng()); println!("To sort: {:?}", to_sort); let mut order_builder: Vec<_> = to_sort.clone().into_iter().enumerate().collect(); order_builder.sort_by(|a, b| a.1.cmp(&b.1)); let mut order: Vec<_> = order_builder.into_iter().map(|(i, _)| i).collect(); println!("Initial order: {:?}", order); let mut wish_positions = vec![0; to_sort.len()]; for (i, i2) in order.iter().enumerate() { wish_positions[*i2] = i; } for i in 0..to_sort.len() { let new_i = order[i]; let old_i = i; println!("Swap {} and {}", old_i, new_i); if old_i != new_i { order.swap(old_i, wish_positions[old_i]); wish_positions.swap(old_i, new_i); to_sort.swap(old_i, new_i); } println!("order: {:?} - to_sort: {:?}", order, to_sort); } assert_eq!(original, to_sort); } }