Start implementing sort and shuffle features

This commit is contained in:
2024-03-02 14:45:31 +01:00
parent 17f2985bb7
commit f73cc8f7ea
5 changed files with 132 additions and 16 deletions

View File

@@ -1,5 +1,6 @@
use chrono::Utc;
use gtk::prelude::*;
use log::debug;
use relm4::{
component::{AsyncComponent, AsyncComponentParts},
prelude::*,
@@ -22,12 +23,17 @@ pub struct AppModel {
database: Database,
mcdu: Controller<McduModel>,
sheets_listing: Controller<SheetListingModel>,
edit_mode: bool,
}
#[derive(Debug)]
pub enum AppInput {
SearchStarted(String),
SheetPressed(Sheet),
Refresh,
Sort,
Shuffle,
SetEditMode(bool),
}
pub struct AppInitData {
@@ -54,6 +60,32 @@ impl AsyncComponent for AppModel {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_hexpand: true,
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_margin_all: 10,
set_spacing: 3,
gtk::Button {
set_icon_name: "view-refresh-symbolic",
set_margin_end: 10,
connect_clicked[sender] => move |_| sender.input(AppInput::Refresh),
},
gtk::ToggleButton {
set_icon_name: "document-edit-symbolic",
set_margin_end: 10,
connect_clicked[sender] => move |button| sender.input(AppInput::SetEditMode(button.is_active())),
},
#[name = "button_sort"]
gtk::ToggleButton {
set_icon_name: "view-sort-descending-symbolic",
set_active: true,
connect_clicked[sender] => move |_| sender.input(AppInput::Sort),
},
gtk::ToggleButton {
set_icon_name: "media-playlist-shuffle-symbolic",
set_group: Some(&button_sort),
connect_clicked[sender] => move |_| sender.input(AppInput::Shuffle),
},
},
gtk::ScrolledWindow {
model.sheets_listing.widget(),
set_vexpand: true,
@@ -93,6 +125,7 @@ impl AsyncComponent for AppModel {
database: init_data.database,
mcdu,
sheets_listing,
edit_mode: false,
};
let widgets = view_output!();
@@ -112,14 +145,21 @@ impl AsyncComponent for AppModel {
.emit(SheetListingInput::Query(query.clone()));
}
AppInput::SheetPressed(sheet) => {
sheet.open_file();
// TODO: updating directly does not work
let mut sheet = sheet;
sheet.last_opened = I64DateTime(Utc::now());
sheet_dao::update_sheet_last_opened(&self.database, &sheet)
.await
.unwrap();
if self.edit_mode {
debug!("Sheet pressed, but we are in edit mode!");
} else {
sheet.open_file();
let mut sheet = sheet;
sheet.last_opened = I64DateTime(Utc::now());
sheet_dao::update_sheet_last_opened(&self.database, &sheet)
.await
.unwrap();
}
}
AppInput::Refresh => todo!(),
AppInput::Sort => self.sheets_listing.emit(SheetListingInput::Sort),
AppInput::Shuffle => self.sheets_listing.emit(SheetListingInput::Shuffle),
AppInput::SetEditMode(edit_mode) => self.edit_mode = edit_mode,
}
}
}

View File

@@ -1,3 +1,5 @@
use std::rc::Rc;
use gtk::prelude::*;
use relm4::factory::FactoryVecDeque;
@@ -8,6 +10,8 @@ use crate::sheet::Sheet;
use super::sheet_model::{OnQueryUpdate, SheetModel};
use rand::{seq::SliceRandom, thread_rng};
pub struct SheetListingModel {
sheets: FactoryVecDeque<SheetModel>,
}
@@ -16,6 +20,8 @@ pub struct SheetListingModel {
pub enum SheetListingInput {
Query(String),
ListBoxRowClicked(i32),
Sort,
Shuffle,
}
#[derive(Debug)]
@@ -74,6 +80,74 @@ impl SimpleComponent for SheetListingModel {
})
.unwrap();
}
SheetListingInput::Sort => sort_sheets(&mut self.sheets),
SheetListingInput::Shuffle => shuffle_sheets(&mut self.sheets),
}
}
}
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());
for i in 0..sheets.len() {
let new_i = order[i];
let old_i = i;
if old_i != new_i {
sheets.guard().swap(old_i, new_i);
order.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..10).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();
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 {
to_sort.swap(old_i, new_i);
order.swap(old_i, new_i);
}
println!("order: {:?} - to_sort: {:?}", order, to_sort);
}
assert_eq!(original, to_sort);
}
}