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

View file

@ -153,7 +153,7 @@ impl Screen<Arc<ImportSession>, ()> for ImportScreen {
add_button.connect_clicked(clone!(@weak this => 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);
}
});

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::widgets::{List, Widget};
use anyhow::Result;
@ -26,9 +26,9 @@ pub struct MediumEditor {
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.
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
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);
}));
// 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
}
}

View file

@ -1,12 +1,14 @@
use super::medium_editor::MediumEditor;
use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget;
use anyhow::Result;
use anyhow::{anyhow, Result};
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::Medium;
use musicus_backend::import::{ImportSession, State};
use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
@ -15,10 +17,12 @@ use std::sync::Arc;
pub struct MediumPreview {
handle: NavigationHandle<()>,
session: Arc<ImportSession>,
medium: Medium,
medium: RefCell<Option<Medium>>,
widget: gtk::Box,
import_button: gtk::Button,
done_stack: gtk::Stack,
name_label: gtk::Label,
medium_box: gtk::Box,
}
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::Button, back_button);
get_widget!(builder, gtk::Button, edit_button);
get_widget!(builder, gtk::Button, import_button);
get_widget!(builder, gtk::Stack, done_stack);
get_widget!(builder, gtk::Box, medium_box);
@ -38,10 +43,12 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
let this = Rc::new(Self {
handle,
session,
medium,
medium: RefCell::new(None),
widget,
import_button,
done_stack,
name_label,
medium_box,
});
// Connect signals and callbacks
@ -50,6 +57,15 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
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 |_| {
spawn!(@clone this, async move {
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_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 {
last_recording_id = &track.recording.id;
@ -88,9 +138,7 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
.build();
frame.set_child(Some(list));
medium_box.append(&frame);
last_list = None;
self.medium_box.append(&frame);
}
last_list = Some(list);
@ -125,27 +173,12 @@ impl Screen<(Arc<ImportSession>, Medium), ()> for MediumPreview {
.build();
frame.set_child(Some(list));
medium_box.append(&frame);
self.medium_box.append(&frame);
}
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,
_ => (),
self.medium.replace(Some(medium));
}
}
});
this
}
}
impl MediumPreview {
/// Handle a state change of the import process.
fn handle_state(&self, state: &State) {
match state {
@ -161,10 +194,13 @@ impl MediumPreview {
/// Copy the tracks to the music library and add the medium to the database.
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.
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))?;
// Copy the tracks to the music library.
@ -172,7 +208,7 @@ impl MediumPreview {
let mut tracks = Vec::new();
let import_tracks = self.session.tracks();
for track in &self.medium.tracks {
for track in &medium.tracks {
let mut track = track.clone();
// Set the track path to the new audio file location.
@ -190,9 +226,9 @@ impl MediumPreview {
// Add the modified medium to the database.
let medium = Medium {
id: self.medium.id.clone(),
name: self.medium.name.clone(),
discid: self.medium.discid.clone(),
id: medium.id.clone(),
name: medium.name.clone(),
discid: medium.discid.clone(),
tracks,
};