mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Allow editing mediums from medium preview
This commit is contained in:
parent
2988d2ea0d
commit
e7cd5b9630
4 changed files with 114 additions and 37 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this
|
||||
self.medium.replace(Some(medium));
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue