mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
219 lines
7.5 KiB
Rust
219 lines
7.5 KiB
Rust
use super::performance::PerformanceEditor;
|
|
use crate::navigator::{NavigationHandle, Screen};
|
|
use crate::selectors::WorkSelector;
|
|
use crate::widgets::{List, Widget};
|
|
use adw::prelude::*;
|
|
use anyhow::Result;
|
|
use gettextrs::gettext;
|
|
use glib::clone;
|
|
use gtk_macros::get_widget;
|
|
use musicus_backend::db::{generate_id, Performance, Recording, Work};
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
/// A widget for creating or editing a recording.
|
|
pub struct RecordingEditor {
|
|
handle: NavigationHandle<Recording>,
|
|
widget: gtk::Stack,
|
|
save_button: gtk::Button,
|
|
info_bar: gtk::InfoBar,
|
|
work_row: adw::ActionRow,
|
|
comment_entry: gtk::Entry,
|
|
upload_switch: gtk::Switch,
|
|
performance_list: Rc<List>,
|
|
id: String,
|
|
work: RefCell<Option<Work>>,
|
|
performances: RefCell<Vec<Performance>>,
|
|
}
|
|
|
|
impl Screen<Option<Recording>, Recording> for RecordingEditor {
|
|
/// Create a new recording editor widget and optionally initialize it.
|
|
fn new(recording: Option<Recording>, handle: NavigationHandle<Recording>) -> Rc<Self> {
|
|
// Create UI
|
|
|
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
|
|
|
get_widget!(builder, gtk::Stack, widget);
|
|
get_widget!(builder, gtk::Button, back_button);
|
|
get_widget!(builder, gtk::Button, save_button);
|
|
get_widget!(builder, gtk::InfoBar, info_bar);
|
|
get_widget!(builder, adw::ActionRow, work_row);
|
|
get_widget!(builder, gtk::Button, work_button);
|
|
get_widget!(builder, gtk::Entry, comment_entry);
|
|
get_widget!(builder, gtk::Switch, upload_switch);
|
|
get_widget!(builder, gtk::Frame, performance_frame);
|
|
get_widget!(builder, gtk::Button, add_performer_button);
|
|
|
|
upload_switch.set_active(handle.backend.use_server());
|
|
|
|
let performance_list = List::new();
|
|
performance_frame.set_child(Some(&performance_list.widget));
|
|
|
|
let (id, work, performances) = match recording {
|
|
Some(recording) => {
|
|
comment_entry.set_text(&recording.comment);
|
|
(recording.id, Some(recording.work), recording.performances)
|
|
}
|
|
None => (generate_id(), None, Vec::new()),
|
|
};
|
|
|
|
let this = Rc::new(RecordingEditor {
|
|
handle,
|
|
widget,
|
|
save_button,
|
|
info_bar,
|
|
work_row,
|
|
comment_entry,
|
|
upload_switch,
|
|
performance_list,
|
|
id,
|
|
work: RefCell::new(work),
|
|
performances: RefCell::new(performances),
|
|
});
|
|
|
|
// Connect signals and callbacks
|
|
|
|
back_button.connect_clicked(clone!(@weak this => move |_| {
|
|
this.handle.pop(None);
|
|
}));
|
|
|
|
this.save_button
|
|
.connect_clicked(clone!(@weak this => move |_| {
|
|
spawn!(@clone this, async move {
|
|
this.widget.set_visible_child_name("loading");
|
|
match this.save().await {
|
|
Ok(recording) => {
|
|
this.handle.pop(Some(recording));
|
|
}
|
|
Err(_) => {
|
|
this.info_bar.set_revealed(true);
|
|
this.widget.set_visible_child_name("content");
|
|
}
|
|
}
|
|
});
|
|
}));
|
|
|
|
work_button.connect_clicked(clone!(@weak this => move |_| {
|
|
spawn!(@clone this, async move {
|
|
if let Some(work) = push!(this.handle, WorkSelector).await {
|
|
this.work_selected(&work);
|
|
this.work.replace(Some(work));
|
|
}
|
|
});
|
|
}));
|
|
|
|
this.performance_list.set_make_widget_cb(clone!(@weak this => @default-panic, move |index| {
|
|
let performance = &this.performances.borrow()[index];
|
|
|
|
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
|
delete_button.set_valign(gtk::Align::Center);
|
|
|
|
delete_button.connect_clicked(clone!(@weak this => move |_| {
|
|
let length = {
|
|
let mut performances = this.performances.borrow_mut();
|
|
performances.remove(index);
|
|
performances.len()
|
|
};
|
|
|
|
this.performance_list.update(length);
|
|
}));
|
|
|
|
let edit_button = gtk::Button::from_icon_name(Some("document-edit-symbolic"));
|
|
edit_button.set_valign(gtk::Align::Center);
|
|
|
|
edit_button.connect_clicked(clone!(@weak this => move |_| {
|
|
spawn!(@clone this, async move {
|
|
let performance = &this.performances.borrow()[index];
|
|
if let Some(performance) = push!(this.handle, PerformanceEditor, Some(performance.to_owned())).await {
|
|
let length = {
|
|
let mut performances = this.performances.borrow_mut();
|
|
performances[index] = performance;
|
|
performances.len()
|
|
};
|
|
|
|
this.performance_list.update(length);
|
|
}
|
|
});
|
|
}));
|
|
|
|
let row = adw::ActionRow::new();
|
|
row.set_activatable(true);
|
|
row.set_title(&performance.get_title());
|
|
row.add_suffix(&delete_button);
|
|
row.add_suffix(&edit_button);
|
|
row.set_activatable_widget(Some(&edit_button));
|
|
|
|
row.upcast()
|
|
}));
|
|
|
|
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
|
spawn!(@clone this, async move {
|
|
if let Some(performance) = push!(this.handle, PerformanceEditor, None).await {
|
|
let length = {
|
|
let mut performances = this.performances.borrow_mut();
|
|
performances.push(performance);
|
|
performances.len()
|
|
};
|
|
|
|
this.performance_list.update(length);
|
|
}
|
|
});
|
|
}));
|
|
|
|
// Initialize
|
|
|
|
if let Some(work) = &*this.work.borrow() {
|
|
this.work_selected(work);
|
|
}
|
|
|
|
let length = this.performances.borrow().len();
|
|
this.performance_list.update(length);
|
|
|
|
this
|
|
}
|
|
}
|
|
|
|
impl RecordingEditor {
|
|
/// Update the UI according to work.
|
|
fn work_selected(&self, work: &Work) {
|
|
self.work_row.set_title(&gettext("Work"));
|
|
self.work_row.set_subtitle(&work.get_title());
|
|
self.save_button.set_sensitive(true);
|
|
}
|
|
|
|
/// Save the recording and possibly upload it to the server.
|
|
async fn save(self: &Rc<Self>) -> Result<Recording> {
|
|
let recording = Recording {
|
|
id: self.id.clone(),
|
|
work: self
|
|
.work
|
|
.borrow()
|
|
.clone()
|
|
.expect("Tried to create recording without work!"),
|
|
comment: self.comment_entry.text().to_string(),
|
|
performances: self.performances.borrow().clone(),
|
|
};
|
|
|
|
let upload = self.upload_switch.state();
|
|
if upload {
|
|
self.handle.backend.cl().post_recording(&recording).await?;
|
|
}
|
|
|
|
self.handle
|
|
.backend
|
|
.db()
|
|
.update_recording(recording.clone())
|
|
.await
|
|
.unwrap();
|
|
|
|
self.handle.backend.library_changed();
|
|
|
|
Ok(recording)
|
|
}
|
|
}
|
|
|
|
impl Widget for RecordingEditor {
|
|
fn get_widget(&self) -> gtk::Widget {
|
|
self.widget.clone().upcast()
|
|
}
|
|
}
|