205 lines
6.8 KiB
Rust

use std::{path::PathBuf, sync::Arc};
use chrono::Utc;
use gtk::prelude::*;
use relm4::{
component::{AsyncComponent, AsyncComponentParts},
gtk::Adjustment,
prelude::*,
AsyncComponentSender,
};
use crate::{
database::Database,
sheet::{I64DateTime, Sheet},
sheet_dao, sheet_validation,
ui::mcdu::McduOutput,
};
use super::{
mcdu::McduModel,
sheet_edit_dialog::{SheetEditDialogInput, SheetEditDialogModel},
sheet_listing::{SheetListingInput, SheetListingModel, SheetListingOutput},
};
pub struct AppModel {
database: Arc<Database>,
directory: Arc<PathBuf>,
mcdu: Controller<McduModel>,
sheets_listing: Controller<SheetListingModel>,
edit_mode: bool,
scroll_adjustment: Adjustment,
sheet_edit_dialog: Option<Controller<SheetEditDialogModel>>,
}
#[derive(Debug)]
pub enum AppInput {
SearchStarted(String),
SheetPressed(Sheet),
Refresh,
Sort,
Shuffle,
SetEditMode(bool),
SheetListingContentsChanged,
}
pub struct AppInitData {
pub sheets: Vec<Sheet>,
pub database: Database,
pub directory: PathBuf,
}
#[relm4::component(pub, async)]
impl AsyncComponent for AppModel {
type Input = AppInput;
type Output = ();
type Init = AppInitData;
type CommandOutput = Vec<Sheet>;
view! {
#[root]
gtk::Window{
set_default_width: 300,
set_default_height: 300,
set_title: Some("Play music!!!"),
set_maximized: true,
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
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,
set_hexpand: true,
set_vadjustment: Some(&model.scroll_adjustment),
},
},
model.mcdu.widget() {
set_margin_all: 10,
},
}
}
}
async fn init(
init_data: Self::Init,
window: Self::Root,
sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
relm4_icons::initialize_icons();
let mcdu = McduModel::builder()
.launch(())
.forward(sender.input_sender(), |response| match response {
McduOutput::SearchStarted(query) => AppInput::SearchStarted(query),
});
let mut sheets = init_data.sheets;
sheets.sort_by(|a, b| a.cmp(b).reverse());
let sheets_listing = SheetListingModel::builder().launch(sheets).forward(
sender.input_sender(),
|response| match response {
SheetListingOutput::SheetModelSelected(sheet) => AppInput::SheetPressed(sheet),
SheetListingOutput::ContentsChanged => AppInput::SheetListingContentsChanged,
},
);
let model = AppModel {
database: Arc::new(init_data.database),
directory: Arc::new(init_data.directory),
mcdu,
sheets_listing,
edit_mode: false,
scroll_adjustment: Adjustment::builder().build(),
sheet_edit_dialog: None,
};
let widgets = view_output!();
AsyncComponentParts { model, widgets }
}
async fn update(
&mut self,
message: Self::Input,
sender: AsyncComponentSender<Self>,
root: &Self::Root,
) {
match message {
AppInput::SearchStarted(query) => {
self.sheets_listing
.emit(SheetListingInput::Query(query.clone()));
}
AppInput::SheetPressed(sheet) => {
if self.edit_mode {
self.sheet_edit_dialog = Some(
SheetEditDialogModel::builder()
.transient_for(&root)
.launch(sheet)
.forward(sender.input_sender(), |_| todo!()),
);
} 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 => {
let db = Arc::clone(&self.database);
let dir = Arc::clone(&self.directory);
sender.oneshot_command(async move {
sheet_validation::load_and_validate_sheets(&db, dir.as_ref()).await
});
}
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,
AppInput::SheetListingContentsChanged => self.scroll_adjustment.set_value(0.0),
}
}
async fn update_cmd(
&mut self,
message: Self::CommandOutput,
_sender: AsyncComponentSender<Self>,
_: &Self::Root,
) {
let mut sheets = message;
sheets.sort_by(|a, b| a.cmp(b).reverse());
self.sheets_listing
.emit(SheetListingInput::ReloadSheets(sheets));
}
}