Allow editing mediums from medium preview

This commit is contained in:
Elias Projahn 2021-04-18 14:41:30 +02:00
parent 2988d2ea0d
commit e7cd5b9630
4 changed files with 114 additions and 37 deletions

View file

@ -53,6 +53,11 @@
</style> </style>
</object> </object>
</child> </child>
<child type="end">
<object class="GtkButton" id="edit_button">
<property name="icon-name">document-edit-symbolic</property>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
@ -61,7 +66,7 @@
<child> <child>
<object class="AdwClamp"> <object class="AdwClamp">
<child> <child>
<object class="GtkBox" id="medium_box"> <object class="GtkBox">
<property name="margin-start">6</property> <property name="margin-start">6</property>
<property name="margin-end">6</property> <property name="margin-end">6</property>
<property name="margin-bottom">6</property> <property name="margin-bottom">6</property>
@ -76,6 +81,11 @@
</attributes> </attributes>
</object> </object>
</child> </child>
<child>
<object class="GtkBox" id="medium_box">
<property name="orientation">vertical</property>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>

View file

@ -153,7 +153,7 @@ impl Screen<Arc<ImportSession>, ()> for ImportScreen {
add_button.connect_clicked(clone!(@weak this => move |_| { add_button.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move { spawn!(@clone this, async move {
if let Some(medium) = push!(this.handle, MediumEditor, Arc::clone(&this.session)).await { if let Some(medium) = push!(this.handle, MediumEditor, (Arc::clone(&this.session), None)).await {
this.select_medium(medium); this.select_medium(medium);
} }
}); });

View file

@ -1,4 +1,4 @@
use super::track_set_editor::{TrackSetData, TrackSetEditor}; use super::track_set_editor::{TrackData, TrackSetData, TrackSetEditor};
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::{List, Widget}; use crate::widgets::{List, Widget};
use anyhow::Result; use anyhow::Result;
@ -26,9 +26,9 @@ pub struct MediumEditor {
track_sets: RefCell<Vec<TrackSetData>>, track_sets: RefCell<Vec<TrackSetData>>,
} }
impl Screen<Arc<ImportSession>, Medium> for MediumEditor { impl Screen<(Arc<ImportSession>, Option<Medium>), Medium> for MediumEditor {
/// Create a new medium editor. /// Create a new medium editor.
fn new(session: Arc<ImportSession>, handle: NavigationHandle<Medium>) -> Rc<Self> { fn new((session, medium): (Arc<ImportSession>, Option<Medium>), handle: NavigationHandle<Medium>) -> Rc<Self> {
// Create UI // Create UI
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/medium_editor.ui"); let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/medium_editor.ui");
@ -126,6 +126,37 @@ impl Screen<Arc<ImportSession>, Medium> for MediumEditor {
this.handle.pop(None); this.handle.pop(None);
})); }));
// Initialize, if necessary.
if let Some(medium) = medium {
this.name_entry.set_text(&medium.name);
let mut track_sets: Vec<TrackSetData> = Vec::new();
for track in medium.tracks {
let track_data = TrackData {
track_source: track.source_index,
work_parts: track.work_parts,
};
if let Some(track_set) = track_sets.last_mut() {
if track.recording.id == track_set.recording.id {
track_set.tracks.push(track_data);
continue;
}
}
track_sets.push(TrackSetData {
recording: track.recording,
tracks: vec![track_data],
});
}
let length = track_sets.len();
this.track_sets.replace(track_sets);
this.track_set_list.update(length);
}
this this
} }
} }

View file

@ -1,12 +1,14 @@
use super::medium_editor::MediumEditor;
use crate::navigator::{NavigationHandle, Screen}; use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget; use crate::widgets::Widget;
use anyhow::Result; use anyhow::{anyhow, Result};
use gettextrs::gettext; use gettextrs::gettext;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_macros::get_widget; use gtk_macros::get_widget;
use musicus_backend::db::Medium; use musicus_backend::db::Medium;
use musicus_backend::import::{ImportSession, State}; use musicus_backend::import::{ImportSession, State};
use std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -15,10 +17,12 @@ use std::sync::Arc;
pub struct MediumPreview { pub struct MediumPreview {
handle: NavigationHandle<()>, handle: NavigationHandle<()>,
session: Arc<ImportSession>, session: Arc<ImportSession>,
medium: Medium, medium: RefCell<Option<Medium>>,
widget: gtk::Box, widget: gtk::Box,
import_button: gtk::Button, import_button: gtk::Button,
done_stack: gtk::Stack, done_stack: gtk::Stack,
name_label: gtk::Label,
medium_box: gtk::Box,
} }
impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview { impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
@ -30,6 +34,7 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
get_widget!(builder, gtk::Box, widget); get_widget!(builder, gtk::Box, widget);
get_widget!(builder, gtk::Button, back_button); get_widget!(builder, gtk::Button, back_button);
get_widget!(builder, gtk::Button, edit_button);
get_widget!(builder, gtk::Button, import_button); get_widget!(builder, gtk::Button, import_button);
get_widget!(builder, gtk::Stack, done_stack); get_widget!(builder, gtk::Stack, done_stack);
get_widget!(builder, gtk::Box, medium_box); get_widget!(builder, gtk::Box, medium_box);
@ -38,10 +43,12 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
let this = Rc::new(Self { let this = Rc::new(Self {
handle, handle,
session, session,
medium, medium: RefCell::new(None),
widget, widget,
import_button, import_button,
done_stack, done_stack,
name_label,
medium_box,
}); });
// Connect signals and callbacks // Connect signals and callbacks
@ -50,6 +57,15 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
this.handle.pop(None); this.handle.pop(None);
})); }));
edit_button.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move {
let old_medium = this.medium.borrow().clone().unwrap();
if let Some(medium) = push!(this.handle, MediumEditor, (this.session.clone(), Some(old_medium))).await {
this.set_medium(medium);
}
});
}));
this.import_button.connect_clicked(clone!(@weak this => move |_| { this.import_button.connect_clicked(clone!(@weak this => move |_| {
spawn!(@clone this, async move { spawn!(@clone this, async move {
this.import().await.unwrap(); this.import().await.unwrap();
@ -57,16 +73,50 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
}); });
})); }));
// Populate the widget this.set_medium(medium);
name_label.set_text(&this.medium.name); this.handle_state(&this.session.state());
spawn!(@clone this, async move {
loop {
let state = this.session.state_change().await;
this.handle_state(&state);
match state {
State::Ready | State::Error => break,
_ => (),
}
}
});
this
}
}
impl MediumPreview {
/// Set a new medium and update the view accordingly.
fn set_medium(&self, medium: Medium) {
self.name_label.set_text(&medium.name);
if let Some(widget) = self.medium_box.get_first_child() {
let mut child = widget;
loop {
let next_child = child.get_next_sibling();
self.medium_box.remove(&child);
match next_child {
Some(widget) => child = widget,
None => break,
}
}
}
let mut last_recording_id = ""; let mut last_recording_id = "";
let mut last_list = None::<gtk::ListBox>; let mut last_list = None::<gtk::ListBox>;
let import_tracks = this.session.tracks(); let import_tracks = self.session.tracks();
for track in &this.medium.tracks { for track in &medium.tracks {
if track.recording.id != last_recording_id { if track.recording.id != last_recording_id {
last_recording_id = &track.recording.id; last_recording_id = &track.recording.id;
@ -88,9 +138,7 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
.build(); .build();
frame.set_child(Some(list)); frame.set_child(Some(list));
medium_box.append(&frame); self.medium_box.append(&frame);
last_list = None;
} }
last_list = Some(list); last_list = Some(list);
@ -125,27 +173,12 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
.build(); .build();
frame.set_child(Some(list)); frame.set_child(Some(list));
medium_box.append(&frame); self.medium_box.append(&frame);
} }
this.handle_state(&this.session.state()); self.medium.replace(Some(medium));
spawn!(@clone this, async move {
loop {
let state = this.session.state_change().await;
this.handle_state(&state);
match state {
State::Ready | State::Error => break,
_ => (),
}
}
});
this
}
} }
impl MediumPreview {
/// Handle a state change of the import process. /// Handle a state change of the import process.
fn handle_state(&self, state: &State) { fn handle_state(&self, state: &State) {
match state { match state {
@ -161,10 +194,13 @@ impl MediumPreview {
/// Copy the tracks to the music library and add the medium to the database. /// Copy the tracks to the music library and add the medium to the database.
async fn import(&self) -> Result<()> { async fn import(&self) -> Result<()> {
let medium = self.medium.borrow();
let medium = medium.as_ref().ok_or(anyhow!("No medium set!"))?;
// Create a new directory in the music library path for the imported medium. // Create a new directory in the music library path for the imported medium.
let music_library_path = self.handle.backend.get_music_library_path().unwrap(); let music_library_path = self.handle.backend.get_music_library_path().unwrap();
let directory = PathBuf::from(&self.medium.id); let directory = PathBuf::from(&medium.id);
std::fs::create_dir(&music_library_path.join(&directory))?; std::fs::create_dir(&music_library_path.join(&directory))?;
// Copy the tracks to the music library. // Copy the tracks to the music library.
@ -172,7 +208,7 @@ impl MediumPreview {
let mut tracks = Vec::new(); let mut tracks = Vec::new();
let import_tracks = self.session.tracks(); let import_tracks = self.session.tracks();
for track in &self.medium.tracks { for track in &medium.tracks {
let mut track = track.clone(); let mut track = track.clone();
// Set the track path to the new audio file location. // Set the track path to the new audio file location.
@ -190,9 +226,9 @@ impl MediumPreview {
// Add the modified medium to the database. // Add the modified medium to the database.
let medium = Medium { let medium = Medium {
id: self.medium.id.clone(), id: medium.id.clone(),
name: self.medium.name.clone(), name: medium.name.clone(),
discid: self.medium.discid.clone(), discid: medium.discid.clone(),
tracks, tracks,
}; };