Files
sheet-organizer/src/ui/sheet_listing.rs
2024-05-26 12:12:33 +02:00

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);
}
}