mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 19:57: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>
|
</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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue