185 lines
5.5 KiB
Rust
185 lines
5.5 KiB
Rust
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<SheetModel>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SheetListingInput {
|
|
Query(String),
|
|
ListBoxRowClicked(i32),
|
|
Sort,
|
|
Shuffle,
|
|
ReloadSheets(Vec<Sheet>),
|
|
None,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SheetListingOutput {
|
|
SheetModelSelected(Sheet),
|
|
ContentsChanged,
|
|
}
|
|
|
|
#[relm4::component(pub)]
|
|
impl SimpleComponent for SheetListingModel {
|
|
type Init = Vec<Sheet>;
|
|
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<Self>,
|
|
) -> ComponentParts<Self> {
|
|
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<Self>) {
|
|
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<SheetModel>) {
|
|
let mut new_order: Vec<usize> = (0..sheets.len()).collect();
|
|
new_order.shuffle(&mut rand::thread_rng());
|
|
order_sheets(sheets, &mut new_order);
|
|
}
|
|
|
|
fn sort_sheets(sheets: &mut FactoryVecDeque<SheetModel>) {
|
|
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<SheetModel>, order: &mut Vec<usize>) {
|
|
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<usize> = (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);
|
|
}
|
|
}
|