diff --git a/Cargo.toml b/Cargo.toml index e687e65..b2ec831 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2018" [dependencies] anyhow = "1.0.33" +async-trait = "0.1.42" diesel = { version = "1.4.5", features = ["sqlite"] } diesel_migrations = "1.4.0" discid = "0.4.4" @@ -39,9 +40,9 @@ features = ["v2_64"] git = "https://github.com/gtk-rs/gtk4-rs" package = "gtk4" -[dependencies.libhandy] -git = "https://gitlab.gnome.org/bilelmoussaoui/libhandy4-rs" -package = "libhandy4" +[dependencies.libadwaita] +git = "https://gitlab.gnome.org/bilelmoussaoui/libadwaita-rs" +package = "libadwaita" [dependencies.pango] git = "https://github.com/gtk-rs/gtk-rs/" diff --git a/de.johrpan.musicus.json b/de.johrpan.musicus.json index 573f915..a6da167 100644 --- a/de.johrpan.musicus.json +++ b/de.johrpan.musicus.json @@ -93,7 +93,7 @@ ] }, { - "name" : "libhandy", + "name" : "libadwaita", "buildsystem" : "meson", "config-opts" : [ "-Dintrospection=enabled", @@ -109,8 +109,8 @@ "sources" : [ { "type" : "git", - "url" : "https://gitlab.gnome.org/exalm/libhandy", - "branch" : "gtk4" + "url" : "https://gitlab.gnome.org/exalm/libadwaita.git", + "branch" : "main" } ] }, diff --git a/meson.build b/meson.build index 8c1eb65..9f5f0a1 100644 --- a/meson.build +++ b/meson.build @@ -11,7 +11,7 @@ dependency('gstreamer-1.0', version: '>= 1.12') dependency('gtk+-3.0', version: '>= 3.24.7') dependency('libcurl', version: '>= 7.24.0') dependency('libdiscid', version: '>= 0.6.2') -dependency('libhandy-1', version: '>= 1.0.0') +dependency('libadwaita-1', version: '>= 1.0') dependency('pango', version: '>= 1.0') dependency('sqlite3', version: '>= 3.20') diff --git a/res/ui/ensemble_editor.ui b/res/ui/ensemble_editor.ui index a20e540..60d0ce7 100644 --- a/res/ui/ensemble_editor.ui +++ b/res/ui/ensemble_editor.ui @@ -1,7 +1,7 @@ - + crossfade @@ -11,8 +11,9 @@ vertical - - false + + false + false Ensemble @@ -45,7 +46,7 @@ true - + 12 12 18 @@ -59,7 +60,7 @@ none - + True Name name_entry @@ -72,7 +73,7 @@ - + True Publish to the server upload_switch @@ -103,8 +104,9 @@ vertical - - false + + false + false Ensemble diff --git a/res/ui/ensemble_screen.ui b/res/ui/ensemble_screen.ui index a6aa910..02cde11 100644 --- a/res/ui/ensemble_screen.ui +++ b/res/ui/ensemble_screen.ui @@ -1,11 +1,11 @@ - + vertical - + Ensemble @@ -36,7 +36,7 @@ False - + 400 true @@ -67,7 +67,7 @@ true - + 12 12 18 diff --git a/res/ui/instrument_editor.ui b/res/ui/instrument_editor.ui index b937ae7..ed33cce 100644 --- a/res/ui/instrument_editor.ui +++ b/res/ui/instrument_editor.ui @@ -1,7 +1,7 @@ - + crossfade @@ -11,8 +11,9 @@ vertical - - false + + false + false Instrument @@ -45,7 +46,7 @@ true - + 12 12 18 @@ -59,7 +60,7 @@ none - + True Name name_entry @@ -72,7 +73,7 @@ - + True Publish to the server upload_switch @@ -103,8 +104,9 @@ vertical - - false + + false + false Instrument diff --git a/res/ui/login_dialog.ui b/res/ui/login_dialog.ui index ffd6a9e..a870540 100644 --- a/res/ui/login_dialog.ui +++ b/res/ui/login_dialog.ui @@ -1,8 +1,8 @@ - - + + True 350 @@ -15,8 +15,9 @@ vertical - - false + + false + false Login @@ -56,7 +57,7 @@ none - + True Username username_entry @@ -69,7 +70,7 @@ - + True Password password_entry @@ -97,8 +98,9 @@ vertical - - false + + false + false Login diff --git a/res/ui/medium_editor.ui b/res/ui/medium_editor.ui index 4259d86..a07b16c 100644 --- a/res/ui/medium_editor.ui +++ b/res/ui/medium_editor.ui @@ -1,7 +1,7 @@ - + crossfade @@ -11,8 +11,9 @@ vertical - - false + + false + false Import music @@ -60,7 +61,7 @@ True - + 6 @@ -84,7 +85,7 @@ none - + True Name of the medium name_entry @@ -97,7 +98,7 @@ - + True Publish to the server publish_switch diff --git a/res/ui/performance_editor.ui b/res/ui/performance_editor.ui index 8031ce6..56f9062 100644 --- a/res/ui/performance_editor.ui +++ b/res/ui/performance_editor.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Performance @@ -35,7 +36,7 @@ true - + 12 12 18 @@ -49,7 +50,7 @@ none - + True Select a person person_button @@ -62,7 +63,7 @@ - + True Select an ensemble ensemble_button @@ -75,7 +76,7 @@ - + True Select a role role_button diff --git a/res/ui/person_editor.ui b/res/ui/person_editor.ui index 9b2e43a..78d3e45 100644 --- a/res/ui/person_editor.ui +++ b/res/ui/person_editor.ui @@ -1,7 +1,7 @@ - + crossfade @@ -11,8 +11,9 @@ vertical - - false + + false + false Person @@ -45,7 +46,7 @@ true - + 12 12 18 @@ -59,7 +60,7 @@ none - + True First name first_name_entry @@ -72,7 +73,7 @@ - + True Last name last_name_entry @@ -85,7 +86,7 @@ - + True Publish to the server upload_switch @@ -116,8 +117,9 @@ vertical - - false + + false + false Person diff --git a/res/ui/person_screen.ui b/res/ui/person_screen.ui index 5149dae..4a1aeda 100644 --- a/res/ui/person_screen.ui +++ b/res/ui/person_screen.ui @@ -1,11 +1,11 @@ - + vertical - + Person @@ -36,7 +36,7 @@ False - + 400 true @@ -71,7 +71,7 @@ true - + 12 12 18 diff --git a/res/ui/player_screen.ui b/res/ui/player_screen.ui index 9ec73e6..ce9134a 100644 --- a/res/ui/player_screen.ui +++ b/res/ui/player_screen.ui @@ -1,7 +1,7 @@ - + media-playback-start-symbolic @@ -13,7 +13,7 @@ vertical - + Player @@ -22,7 +22,6 @@ - True @@ -38,7 +37,7 @@ true - + 12 12 18 @@ -58,7 +57,6 @@ False - True media-skip-backward-symbolic diff --git a/res/ui/poe_list.ui b/res/ui/poe_list.ui index 3200fa3..8016452 100644 --- a/res/ui/poe_list.ui +++ b/res/ui/poe_list.ui @@ -1,14 +1,14 @@ - + vertical True - + 400 300 true @@ -44,8 +44,6 @@ content - True - True diff --git a/res/ui/preferences.ui b/res/ui/preferences.ui index 1f1fce5..e4bbf6d 100644 --- a/res/ui/preferences.ui +++ b/res/ui/preferences.ui @@ -1,19 +1,19 @@ - - + + True 400 400 - + General - + Music library - + Music library folder select_music_library_path_button None selected @@ -29,10 +29,10 @@ - + Server connection - + Server URL url_button Not set @@ -46,7 +46,7 @@ - + Login credentials login_button Not logged in diff --git a/res/ui/recording_editor.ui b/res/ui/recording_editor.ui index e9df94e..3748fd6 100644 --- a/res/ui/recording_editor.ui +++ b/res/ui/recording_editor.ui @@ -1,7 +1,7 @@ - + @@ -10,8 +10,9 @@ vertical - - false + + false + false Recording @@ -45,7 +46,7 @@ true - + 12 12 18 @@ -73,7 +74,7 @@ none - + True Select a work work_button @@ -86,7 +87,7 @@ - + True Comment comment_entry @@ -99,7 +100,7 @@ - + True Publish to the server upload_switch @@ -159,8 +160,9 @@ vertical - - false + + false + false Recording diff --git a/res/ui/recording_screen.ui b/res/ui/recording_screen.ui index 59de962..6a0a640 100644 --- a/res/ui/recording_screen.ui +++ b/res/ui/recording_screen.ui @@ -1,11 +1,11 @@ - + vertical - + vertical @@ -59,7 +59,7 @@ true - + 12 12 18 diff --git a/res/ui/selector.ui b/res/ui/selector.ui index 231d813..bc3fd41 100644 --- a/res/ui/selector.ui +++ b/res/ui/selector.ui @@ -1,14 +1,15 @@ - + 250 False vertical - - false + + false + false vertical @@ -46,7 +47,7 @@ True - + 500 300 true @@ -99,7 +100,7 @@ 200 true - + 500 300 diff --git a/res/ui/server_dialog.ui b/res/ui/server_dialog.ui index 98f9acd..6e481fd 100644 --- a/res/ui/server_dialog.ui +++ b/res/ui/server_dialog.ui @@ -1,15 +1,16 @@ - - + + True vertical - - false + + false + false Server @@ -38,7 +39,7 @@ none - + True URL url_entry diff --git a/res/ui/source_selector.ui b/res/ui/source_selector.ui index 7de7e90..3b7b711 100644 --- a/res/ui/source_selector.ui +++ b/res/ui/source_selector.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Import music @@ -48,49 +49,27 @@ - - True - center - center - 18 - 18 - 18 - 18 - vertical - 18 + + true + folder-music-symbolic + Import music + Select the source which contains the new audio files below. - - 0.5019607843137255 - 80 - media-optical-cd-audio-symbolic - - - - - 0.5019607843137255 - Import from audio CD - - - - - - - - 0.5019607843137255 - Insert an audio compact disc into your drive and click the button below. The disc will be copied in the background while you set up the metadata. - center - True - 40 - - - - - Import - True + + horizontal + true + 6 center - + + + Select folder + + + + + Copy audio CD + + diff --git a/res/ui/track_editor.ui b/res/ui/track_editor.ui index 63d35c1..9f138bb 100644 --- a/res/ui/track_editor.ui +++ b/res/ui/track_editor.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Track @@ -34,7 +35,7 @@ True - + start diff --git a/res/ui/track_selector.ui b/res/ui/track_selector.ui index 090966c..e8d14e9 100644 --- a/res/ui/track_selector.ui +++ b/res/ui/track_selector.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Select tracks @@ -35,7 +36,7 @@ True - + start diff --git a/res/ui/track_set_editor.ui b/res/ui/track_set_editor.ui index 4e822db..c0fc576 100644 --- a/res/ui/track_set_editor.ui +++ b/res/ui/track_set_editor.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Import music @@ -35,7 +36,7 @@ True - + 6 @@ -59,7 +60,7 @@ none - + True Select a recording select_recording_button diff --git a/res/ui/window.ui b/res/ui/window.ui index 8748236..433095f 100644 --- a/res/ui/window.ui +++ b/res/ui/window.ui @@ -1,13 +1,11 @@ - + vertical - - True - True + @@ -52,7 +50,7 @@ - + 800 566 @@ -65,7 +63,7 @@ vertical - + Musicus @@ -135,7 +133,7 @@ vertical - + @@ -158,18 +156,19 @@ vertical - + true - + 250 False vertical - - False + + false + false Musicus @@ -202,7 +201,7 @@ - + False diff --git a/res/ui/work_editor.ui b/res/ui/work_editor.ui index b8a2769..35a2489 100644 --- a/res/ui/work_editor.ui +++ b/res/ui/work_editor.ui @@ -1,19 +1,18 @@ - + content - True - False vertical - - false + + false + false Work @@ -47,7 +46,7 @@ true - + 12 12 18 @@ -75,7 +74,7 @@ none - + True Select a composer composer_button @@ -88,7 +87,7 @@ - + True Title title_entry @@ -101,7 +100,7 @@ - + True Publish to the server upload_switch @@ -194,8 +193,9 @@ vertical - - false + + false + false Work diff --git a/res/ui/work_part_editor.ui b/res/ui/work_part_editor.ui index c360b6e..dc4e434 100644 --- a/res/ui/work_part_editor.ui +++ b/res/ui/work_part_editor.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Work part @@ -39,7 +40,7 @@ true - + 12 12 18 @@ -53,7 +54,7 @@ none - + True Title title_entry @@ -66,7 +67,7 @@ - + True Select a composer composer_button diff --git a/res/ui/work_screen.ui b/res/ui/work_screen.ui index 2c5e5fe..44f4ca0 100644 --- a/res/ui/work_screen.ui +++ b/res/ui/work_screen.ui @@ -1,11 +1,11 @@ - + vertical - + vertical @@ -49,7 +49,7 @@ False - + 400 true @@ -80,7 +80,7 @@ true - + 12 12 18 diff --git a/res/ui/work_section_editor.ui b/res/ui/work_section_editor.ui index db9480d..46862dd 100644 --- a/res/ui/work_section_editor.ui +++ b/res/ui/work_section_editor.ui @@ -1,12 +1,13 @@ - + vertical - - false + + false + false Work section @@ -39,7 +40,7 @@ true - + 12 12 18 @@ -53,7 +54,7 @@ none - + True Title title_entry diff --git a/src/dialogs/login_dialog.rs b/src/dialogs/login_dialog.rs index 682c733..b1946be 100644 --- a/src/dialogs/login_dialog.rs +++ b/src/dialogs/login_dialog.rs @@ -8,7 +8,7 @@ use std::rc::Rc; /// A dialog for entering login credentials. pub struct LoginDialog { backend: Rc, - window: libhandy::Window, + window: libadwaita::Window, stack: gtk::Stack, info_bar: gtk::InfoBar, username_entry: gtk::Entry, @@ -22,7 +22,7 @@ impl LoginDialog { // Create UI let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/login_dialog.ui"); - get_widget!(builder, libhandy::Window, window); + get_widget!(builder, libadwaita::Window, window); get_widget!(builder, gtk::Stack, stack); get_widget!(builder, gtk::InfoBar, info_bar); get_widget!(builder, gtk::Button, cancel_button); diff --git a/src/dialogs/preferences.rs b/src/dialogs/preferences.rs index ccb94ed..3e7683d 100644 --- a/src/dialogs/preferences.rs +++ b/src/dialogs/preferences.rs @@ -4,16 +4,16 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::rc::Rc; /// A dialog for configuring the app. pub struct Preferences { backend: Rc, - window: libhandy::Window, - music_library_path_row: libhandy::ActionRow, - url_row: libhandy::ActionRow, - login_row: libhandy::ActionRow, + window: libadwaita::Window, + music_library_path_row: libadwaita::ActionRow, + url_row: libadwaita::ActionRow, + login_row: libadwaita::ActionRow, } impl Preferences { @@ -22,12 +22,12 @@ impl Preferences { // Create UI let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/preferences.ui"); - get_widget!(builder, libhandy::Window, window); - get_widget!(builder, libhandy::ActionRow, music_library_path_row); + get_widget!(builder, libadwaita::Window, window); + get_widget!(builder, libadwaita::ActionRow, music_library_path_row); get_widget!(builder, gtk::Button, select_music_library_path_button); - get_widget!(builder, libhandy::ActionRow, url_row); + get_widget!(builder, libadwaita::ActionRow, url_row); get_widget!(builder, gtk::Button, url_button); - get_widget!(builder, libhandy::ActionRow, login_row); + get_widget!(builder, libadwaita::ActionRow, login_row); get_widget!(builder, gtk::Button, login_button); window.set_transient_for(Some(parent)); @@ -66,6 +66,8 @@ impl Preferences { } } } + + dialog.hide(); })); dialog.show(); diff --git a/src/dialogs/server_dialog.rs b/src/dialogs/server_dialog.rs index 90d9d39..cb024ae 100644 --- a/src/dialogs/server_dialog.rs +++ b/src/dialogs/server_dialog.rs @@ -8,7 +8,7 @@ use std::rc::Rc; /// A dialog for setting up the server. pub struct ServerDialog { backend: Rc, - window: libhandy::Window, + window: libadwaita::Window, url_entry: gtk::Entry, selected_cb: RefCell ()>>>, } @@ -19,7 +19,7 @@ impl ServerDialog { // Create UI let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/server_dialog.ui"); - get_widget!(builder, libhandy::Window, window); + get_widget!(builder, libadwaita::Window, window); get_widget!(builder, gtk::Button, cancel_button); get_widget!(builder, gtk::Button, set_button); get_widget!(builder, gtk::Entry, url_entry); diff --git a/src/editors/performance.rs b/src/editors/performance.rs index e9cc0e0..b54e9d0 100644 --- a/src/editors/performance.rs +++ b/src/editors/performance.rs @@ -6,7 +6,7 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -15,9 +15,9 @@ pub struct PerformanceEditor { backend: Rc, widget: gtk::Box, save_button: gtk::Button, - person_row: libhandy::ActionRow, - ensemble_row: libhandy::ActionRow, - role_row: libhandy::ActionRow, + person_row: libadwaita::ActionRow, + ensemble_row: libadwaita::ActionRow, + role_row: libadwaita::ActionRow, reset_role_button: gtk::Button, person: RefCell>, ensemble: RefCell>, @@ -40,9 +40,9 @@ impl PerformanceEditor { get_widget!(builder, gtk::Button, ensemble_button); get_widget!(builder, gtk::Button, role_button); get_widget!(builder, gtk::Button, reset_role_button); - get_widget!(builder, libhandy::ActionRow, person_row); - get_widget!(builder, libhandy::ActionRow, ensemble_row); - get_widget!(builder, libhandy::ActionRow, role_row); + get_widget!(builder, libadwaita::ActionRow, person_row); + get_widget!(builder, libadwaita::ActionRow, ensemble_row); + get_widget!(builder, libadwaita::ActionRow, role_row); let this = Rc::new(PerformanceEditor { backend, diff --git a/src/editors/recording.rs b/src/editors/recording.rs index 706e780..d3a7677 100644 --- a/src/editors/recording.rs +++ b/src/editors/recording.rs @@ -8,7 +8,7 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -18,7 +18,7 @@ pub struct RecordingEditor { backend: Rc, save_button: gtk::Button, info_bar: gtk::InfoBar, - work_row: libhandy::ActionRow, + work_row: libadwaita::ActionRow, comment_entry: gtk::Entry, upload_switch: gtk::Switch, performance_list: Rc, @@ -40,7 +40,7 @@ impl RecordingEditor { get_widget!(builder, gtk::Button, back_button); get_widget!(builder, gtk::Button, save_button); get_widget!(builder, gtk::InfoBar, info_bar); - get_widget!(builder, libhandy::ActionRow, work_row); + get_widget!(builder, libadwaita::ActionRow, work_row); get_widget!(builder, gtk::Button, work_button); get_widget!(builder, gtk::Entry, comment_entry); get_widget!(builder, gtk::Switch, upload_switch); @@ -173,7 +173,7 @@ impl RecordingEditor { } })); - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&performance.get_title())); row.add_suffix(&delete_button); diff --git a/src/editors/work.rs b/src/editors/work.rs index c275887..1f3ebd2 100644 --- a/src/editors/work.rs +++ b/src/editors/work.rs @@ -9,7 +9,7 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::convert::TryInto; use std::rc::Rc; @@ -37,7 +37,7 @@ pub struct WorkEditor { save_button: gtk::Button, title_entry: gtk::Entry, info_bar: gtk::InfoBar, - composer_row: libhandy::ActionRow, + composer_row: libadwaita::ActionRow, upload_switch: gtk::Switch, instrument_list: Rc, part_list: Rc, @@ -62,7 +62,7 @@ impl WorkEditor { get_widget!(builder, gtk::InfoBar, info_bar); get_widget!(builder, gtk::Entry, title_entry); get_widget!(builder, gtk::Button, composer_button); - get_widget!(builder, libhandy::ActionRow, composer_row); + get_widget!(builder, libadwaita::ActionRow, composer_row); get_widget!(builder, gtk::Switch, upload_switch); get_widget!(builder, gtk::Frame, instrument_frame); get_widget!(builder, gtk::Button, add_instrument_button); @@ -179,7 +179,7 @@ impl WorkEditor { this.instrument_list.update(length); })); - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_title(Some(&instrument.name)); row.add_suffix(&delete_button); @@ -265,7 +265,7 @@ impl WorkEditor { } })); - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&pos.get_title())); row.add_suffix(&delete_button); diff --git a/src/editors/work_part.rs b/src/editors/work_part.rs index 9f48e34..86255ab 100644 --- a/src/editors/work_part.rs +++ b/src/editors/work_part.rs @@ -6,7 +6,7 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -15,7 +15,7 @@ pub struct WorkPartEditor { backend: Rc, widget: gtk::Box, title_entry: gtk::Entry, - composer_row: libhandy::ActionRow, + composer_row: libadwaita::ActionRow, reset_composer_button: gtk::Button, composer: RefCell>, ready_cb: RefCell ()>>>, @@ -34,7 +34,7 @@ impl WorkPartEditor { get_widget!(builder, gtk::Button, save_button); get_widget!(builder, gtk::Entry, title_entry); get_widget!(builder, gtk::Button, composer_button); - get_widget!(builder, libhandy::ActionRow, composer_row); + get_widget!(builder, libadwaita::ActionRow, composer_row); get_widget!(builder, gtk::Button, reset_composer_button); let composer = match part { diff --git a/src/import/disc_source.rs b/src/import/disc_source.rs index f1a33c3..49707e5 100644 --- a/src/import/disc_source.rs +++ b/src/import/disc_source.rs @@ -1,8 +1,12 @@ +use super::source::{Source, SourceTrack}; use anyhow::{anyhow, bail, Result}; +use async_trait::async_trait; use discid::DiscId; use futures_channel::oneshot; +use gettextrs::gettext; use gstreamer::prelude::*; use gstreamer::{Element, ElementFactory, Pipeline}; +use once_cell::sync::OnceCell; use std::path::{Path, PathBuf}; use std::thread; @@ -10,67 +14,27 @@ use std::thread; #[derive(Clone, Debug)] pub struct DiscSource { /// The MusicBrainz DiscID of the CD. - pub discid: String, - - /// The path to the temporary directory where the audio files will be. - pub path: PathBuf, + pub discid: OnceCell, /// The tracks on this disc. - pub tracks: Vec, -} - -/// Representation of a single track on an audio CD. -#[derive(Clone, Debug)] -pub struct TrackSource { - /// The track number. This is different from the index in the disc - /// source's tracks list, because it is not defined from which number the - /// the track numbers start. - pub number: u32, - - /// The path to the temporary file to which the track will be ripped. The - /// file will not exist until the track is actually ripped. - pub path: PathBuf, + tracks: OnceCell>, } impl DiscSource { - /// Try to create a new disc source by asynchronously reading the - /// information from the default disc drive. - pub async fn load() -> Result { - let (sender, receiver) = oneshot::channel(); + /// Create a new disc source. The source has to be initialized by calling + /// load() afterwards. + pub fn new() -> Result { + let result = Self { + discid: OnceCell::new(), + tracks: OnceCell::new(), + }; - thread::spawn(|| { - let disc = Self::load_priv(); - sender.send(disc).unwrap(); - }); - - let disc = receiver.await??; - - Ok(disc) + Ok(result) } - /// Rip the whole disc asynchronously. After this method has finished - /// successfully, the audio files will be available in the specified - /// location for each track source. - pub async fn rip(&self) -> Result<()> { - for track in &self.tracks { - let (sender, receiver) = oneshot::channel(); - - let number = track.number; - let path = track.path.clone(); - - thread::spawn(move || { - let result = Self::rip_track(&path, number); - sender.send(result).unwrap(); - }); - - receiver.await??; - } - - Ok(()) - } - - /// Load the disc from the default disc drive. - fn load_priv() -> Result { + /// Load the disc from the default disc drive and return the MusicBrainz + /// DiscID as well as descriptions of the contained tracks. + fn load_priv() -> Result<(String, Vec)> { let discid = DiscId::read(None)?; let id = discid.id(); @@ -82,26 +46,23 @@ impl DiscSource { let tmp_dir = Self::create_tmp_dir()?; for number in first_track..=last_track { + let name = gettext!("Track {}", number); + let file_name = format!("track_{:02}.flac", number); let mut path = tmp_dir.clone(); path.push(file_name); - let track = TrackSource { + let track = SourceTrack { number, + name, path, }; tracks.push(track); } - let disc = DiscSource { - discid: id, - tracks, - path: tmp_dir, - }; - - Ok(disc) + Ok((id, tracks)) } /// Create a new temporary directory and return its path. @@ -172,3 +133,57 @@ impl DiscSource { Ok(pipeline) } } + +#[async_trait] +impl Source for DiscSource { + async fn load(&self) -> Result<()> { + let (sender, receiver) = oneshot::channel(); + + thread::spawn(|| { + let result = Self::load_priv(); + sender.send(result).unwrap(); + }); + + let (discid, tracks) = receiver.await??; + + self.discid.set(discid); + self.tracks.set(tracks); + + Ok(()) + } + + fn tracks(&self) -> Option<&[SourceTrack]> { + match self.tracks.get() { + Some(tracks) => Some(tracks.as_slice()), + None => None, + } + } + + fn discid(&self) -> Option { + match self.discid.get() { + Some(discid) => Some(discid.to_owned()), + None => None, + } + } + + async fn copy(&self) -> Result<()> { + let tracks = self.tracks.get() + .ok_or_else(|| anyhow!("Tried to copy disc before loading has finished!"))?; + + for track in tracks { + let (sender, receiver) = oneshot::channel(); + + let number = track.number; + let path = track.path.clone(); + + thread::spawn(move || { + let result = Self::rip_track(&path, number); + sender.send(result).unwrap(); + }); + + receiver.await??; + } + + Ok(()) + } +} diff --git a/src/import/folder_source.rs b/src/import/folder_source.rs new file mode 100644 index 0000000..b0637d5 --- /dev/null +++ b/src/import/folder_source.rs @@ -0,0 +1,90 @@ +use super::source::{Source, SourceTrack}; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures_channel::oneshot; +use once_cell::sync::OnceCell; +use std::path::{Path, PathBuf}; +use std::thread; + +/// A folder outside of the music library that contains tracks to import. +#[derive(Clone, Debug)] +pub struct FolderSource { + /// The path to the folder. + path: PathBuf, + + /// The tracks within the folder. + tracks: OnceCell>, +} + +impl FolderSource { + /// Create a new folder source. + pub fn new(path: PathBuf) -> Self { + Self { + path, + tracks: OnceCell::new(), + } + } + + /// Load the contents of the folder as tracks. + fn load_priv(path: &Path) -> Result> { + let mut tracks = Vec::new(); + let mut number = 1; + + for entry in std::fs::read_dir(path)? { + let entry = entry?; + + if entry.file_type()?.is_file() { + let name = entry + .file_name() + .into_string() + .or_else(|_| Err(anyhow!("Failed to convert OsString to String!")))?; + + let path = entry.path(); + + let track = SourceTrack { + number, + name, + path, + }; + + tracks.push(track); + number += 1; + } + } + + Ok(tracks) + } +} + +#[async_trait] +impl Source for FolderSource { + async fn load(&self) -> Result<()> { + let (sender, receiver) = oneshot::channel(); + + let path = self.path.clone(); + thread::spawn(move || { + let result = Self::load_priv(&path); + sender.send(result).unwrap(); + }); + + let tracks = receiver.await??; + self.tracks.set(tracks); + + Ok(()) + } + + fn tracks(&self) -> Option<&[SourceTrack]> { + match self.tracks.get() { + Some(tracks) => Some(tracks.as_slice()), + None => None, + } + } + + fn discid(&self) -> Option { + None + } + + async fn copy(&self) -> Result<()> { + Ok(()) + } +} diff --git a/src/import/medium_editor.rs b/src/import/medium_editor.rs index 361ec04..740fb98 100644 --- a/src/import/medium_editor.rs +++ b/src/import/medium_editor.rs @@ -1,21 +1,21 @@ -use super::disc_source::DiscSource; +use super::source::Source; use super::track_set_editor::{TrackSetData, TrackSetEditor}; use crate::database::{generate_id, Medium, Track, TrackSet}; use crate::backend::Backend; use crate::widgets::{List, Navigator, NavigatorScreen}; -use anyhow::Result; +use anyhow::{anyhow, Result}; use glib::clone; use glib::prelude::*; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; /// A dialog for editing metadata while importing music into the music library. pub struct MediumEditor { backend: Rc, - source: Rc, + source: Rc>, widget: gtk::Stack, done_button: gtk::Button, done_stack: gtk::Stack, @@ -29,7 +29,7 @@ pub struct MediumEditor { impl MediumEditor { /// Create a new medium editor. - pub fn new(backend: Rc, source: DiscSource) -> Rc { + pub fn new(backend: Rc, source: Rc>) -> Rc { // Create UI let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/medium_editor.ui"); @@ -49,7 +49,7 @@ impl MediumEditor { let this = Rc::new(Self { backend, - source: Rc::new(source), + source, widget, done_button, done_stack, @@ -117,7 +117,7 @@ impl MediumEditor { edit_button.set_valign(gtk::Align::Center); edit_button.set_child(Some(&edit_image)); - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&title)); row.set_subtitle(Some(&subtitle)); @@ -131,14 +131,14 @@ impl MediumEditor { row.upcast() })); - // Start ripping the CD in the background. + // Copy the source in the background. let context = glib::MainContext::default(); let clone = this.clone(); context.spawn_local(async move { - match clone.source.rip().await { + match clone.source.copy().await { Err(error) => { // TODO: Present error. - println!("Failed to rip: {}", error); + println!("Failed to copy source: {}", error); }, Ok(_) => { clone.done_stack.set_visible_child(&clone.done); @@ -163,6 +163,7 @@ impl MediumEditor { // Convert the track set data to real track sets. let mut track_sets = Vec::new(); + let source_tracks = self.source.tracks().ok_or_else(|| anyhow!("Tracks not loaded!"))?; for track_set_data in &*self.track_sets.borrow() { let mut tracks = Vec::new(); @@ -170,11 +171,10 @@ impl MediumEditor { for track_data in &track_set_data.tracks { // Copy the corresponding audio file to the music library. - let track_source = &self.source.tracks[track_data.track_source]; - let file_name = format!("track_{:02}.flac", track_source.number); + let track_source = &source_tracks[track_data.track_source]; let mut track_path = path.clone(); - track_path.push(&file_name); + track_path.push(track_source.path.file_name().unwrap()); std::fs::copy(&track_source.path, &track_path)?; @@ -199,7 +199,7 @@ impl MediumEditor { let medium = Medium { id: generate_id(), name: self.name_entry.get_text().unwrap().to_string(), - discid: Some(self.source.discid.clone()), + discid: self.source.discid(), tracks: track_sets, }; diff --git a/src/import/mod.rs b/src/import/mod.rs index 2744611..f21da67 100644 --- a/src/import/mod.rs +++ b/src/import/mod.rs @@ -1,5 +1,7 @@ mod disc_source; +mod folder_source; mod medium_editor; +mod source; mod source_selector; mod track_editor; mod track_selector; diff --git a/src/import/source.rs b/src/import/source.rs new file mode 100644 index 0000000..dfcd4d0 --- /dev/null +++ b/src/import/source.rs @@ -0,0 +1,39 @@ +use anyhow::Result; +use async_trait::async_trait; +use std::path::PathBuf; + +/// A source for tracks that can be imported into the music library. +#[async_trait] +pub trait Source { + /// Load the source and discover the contained tracks. + async fn load(&self) -> Result<()>; + + /// Get a reference to the tracks within this source, if they are ready. + fn tracks(&self) -> Option<&[SourceTrack]>; + + /// Get the DiscID of the corresponging medium, if possible. + fn discid(&self) -> Option; + + /// Asynchronously copy the tracks to the files that are advertised within + /// their corresponding objects. + async fn copy(&self) -> Result<()>; +} + +/// Representation of a single track on a source. +#[derive(Clone, Debug)] +pub struct SourceTrack { + /// The track number. This is different from the index in the disc + /// source's tracks list, because it is not defined from which number the + /// the track numbers start. + pub number: u32, + + /// A human readable identifier for the track. This will be used to + /// present the track for selection. + pub name: String, + + /// The path to the file where the corresponding audio file is. This file + /// is only required to exist, once the source's copy method has finished. + /// This will not be the actual file within the user's music library, but + /// the location from which it can be copied to the music library. + pub path: PathBuf, +} diff --git a/src/import/source_selector.rs b/src/import/source_selector.rs index 0dbde5e..da95ba3 100644 --- a/src/import/source_selector.rs +++ b/src/import/source_selector.rs @@ -1,11 +1,15 @@ use super::medium_editor::MediumEditor; use super::disc_source::DiscSource; +use super::folder_source::FolderSource; +use super::source::Source; use crate::backend::Backend; use crate::widgets::{Navigator, NavigatorScreen}; +use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; use std::cell::RefCell; +use std::path::PathBuf; use std::rc::Rc; /// A dialog for starting to import music. @@ -28,7 +32,8 @@ impl SourceSelector { get_widget!(builder, gtk::Button, back_button); get_widget!(builder, gtk::Stack, stack); get_widget!(builder, gtk::InfoBar, info_bar); - get_widget!(builder, gtk::Button, import_button); + get_widget!(builder, gtk::Button, folder_button); + get_widget!(builder, gtk::Button, disc_button); let this = Rc::new(Self { backend, @@ -47,17 +52,68 @@ impl SourceSelector { } })); - import_button.connect_clicked(clone!(@strong this => move |_| { + folder_button.connect_clicked(clone!(@strong this => move |_| { + let window = this.navigator.borrow().clone().unwrap().window.clone(); + let dialog = gtk::FileChooserDialog::new( + Some(&gettext("Select folder")), + Some(&window), + gtk::FileChooserAction::SelectFolder, + &[ + (&gettext("Cancel"), gtk::ResponseType::Cancel), + (&gettext("Select"), gtk::ResponseType::Accept), + ]); + + dialog.connect_response(clone!(@strong this => move |dialog, response| { + this.stack.set_visible_child_name("loading"); + dialog.hide(); + + if let gtk::ResponseType::Accept = response { + if let Some(file) = dialog.get_file() { + if let Some(path) = file.get_path() { + let context = glib::MainContext::default(); + let clone = this.clone(); + context.spawn_local(async move { + let folder = FolderSource::new(PathBuf::from(path)); + match folder.load().await { + Ok(_) => { + let navigator = clone.navigator.borrow().clone(); + if let Some(navigator) = navigator { + let source = Rc::new(Box::new(folder) as Box); + let editor = MediumEditor::new(clone.backend.clone(), source); + navigator.push(editor); + } + + clone.info_bar.set_revealed(false); + clone.stack.set_visible_child_name("start"); + } + Err(_) => { + // TODO: Present error. + clone.info_bar.set_revealed(true); + clone.stack.set_visible_child_name("start"); + } + } + }); + } + } + } + })); + + dialog.show(); + })); + + disc_button.connect_clicked(clone!(@strong this => move |_| { this.stack.set_visible_child_name("loading"); let context = glib::MainContext::default(); let clone = this.clone(); context.spawn_local(async move { - match DiscSource::load().await { - Ok(disc) => { + let disc = DiscSource::new().unwrap(); + match disc.load().await { + Ok(_) => { let navigator = clone.navigator.borrow().clone(); if let Some(navigator) = navigator { - let editor = MediumEditor::new(clone.backend.clone(), disc); + let source = Rc::new(Box::new(disc) as Box); + let editor = MediumEditor::new(clone.backend.clone(), source); navigator.push(editor); } @@ -65,6 +121,7 @@ impl SourceSelector { clone.stack.set_visible_child_name("start"); } Err(_) => { + // TODO: Present error. clone.info_bar.set_revealed(true); clone.stack.set_visible_child_name("start"); } diff --git a/src/import/track_editor.rs b/src/import/track_editor.rs index be967ea..177b791 100644 --- a/src/import/track_editor.rs +++ b/src/import/track_editor.rs @@ -3,7 +3,7 @@ use crate::widgets::{Navigator, NavigatorScreen}; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -76,7 +76,7 @@ impl TrackEditor { } })); - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.add_prefix(&check); row.set_activatable_widget(Some(&check)); row.set_title(Some(&part.title)); diff --git a/src/import/track_selector.rs b/src/import/track_selector.rs index ae7621a..068fa73 100644 --- a/src/import/track_selector.rs +++ b/src/import/track_selector.rs @@ -1,15 +1,15 @@ -use super::disc_source::DiscSource; +use super::source::Source; use crate::widgets::{Navigator, NavigatorScreen}; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; -/// A screen for selecting tracks from a medium. +/// A screen for selecting tracks from a source. pub struct TrackSelector { - source: Rc, + source: Rc>, widget: gtk::Box, select_button: gtk::Button, selection: RefCell>, @@ -19,7 +19,7 @@ pub struct TrackSelector { impl TrackSelector { /// Create a new track selector. - pub fn new(source: Rc) -> Rc { + pub fn new(source: Rc>) -> Rc { // Create UI let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_selector.ui"); @@ -65,7 +65,9 @@ impl TrackSelector { } })); - for (index, track) in this.source.tracks.iter().enumerate() { + let tracks = this.source.tracks().unwrap(); + + for (index, track) in tracks.iter().enumerate() { let check = gtk::CheckButton::new(); check.connect_toggled(clone!(@strong this => move |check| { @@ -85,13 +87,11 @@ impl TrackSelector { } })); - let title = format!("Track {}", track.number); - - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.add_prefix(&check); row.set_activatable_widget(Some(&check)); row.set_activatable(true); - row.set_title(Some(&title)); + row.set_title(Some(&track.name)); track_list.append(&row); } diff --git a/src/import/track_set_editor.rs b/src/import/track_set_editor.rs index 852744e..ed79c70 100644 --- a/src/import/track_set_editor.rs +++ b/src/import/track_set_editor.rs @@ -1,4 +1,4 @@ -use super::disc_source::DiscSource; +use super::source::Source; use super::track_editor::TrackEditor; use super::track_selector::TrackSelector; use crate::backend::Backend; @@ -9,7 +9,7 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -33,10 +33,10 @@ pub struct TrackData { /// A screen for editing a set of tracks for one recording. pub struct TrackSetEditor { backend: Rc, - source: Rc, + source: Rc>, widget: gtk::Box, save_button: gtk::Button, - recording_row: libhandy::ActionRow, + recording_row: libadwaita::ActionRow, track_list: Rc, recording: RefCell>, tracks: RefCell>, @@ -46,7 +46,7 @@ pub struct TrackSetEditor { impl TrackSetEditor { /// Create a new track set editor. - pub fn new(backend: Rc, source: Rc) -> Rc { + pub fn new(backend: Rc, source: Rc>) -> Rc { // Create UI let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_set_editor.ui"); @@ -54,7 +54,7 @@ impl TrackSetEditor { get_widget!(builder, gtk::Box, widget); get_widget!(builder, gtk::Button, back_button); get_widget!(builder, gtk::Button, save_button); - get_widget!(builder, libhandy::ActionRow, recording_row); + get_widget!(builder, libadwaita::ActionRow, recording_row); get_widget!(builder, gtk::Button, select_recording_button); get_widget!(builder, gtk::Button, edit_tracks_button); get_widget!(builder, gtk::Frame, tracks_frame); @@ -174,8 +174,8 @@ impl TrackSetEditor { title_parts.join(", ") }; - let number = this.source.tracks[track.track_source].number; - let subtitle = format!("Track {}", number); + let tracks = this.source.tracks().unwrap(); + let track_name = &tracks[track.track_source].name; let edit_image = gtk::Image::from_icon_name(Some("document-edit-symbolic")); let edit_button = gtk::Button::new(); @@ -183,10 +183,10 @@ impl TrackSetEditor { edit_button.set_valign(gtk::Align::Center); edit_button.set_child(Some(&edit_image)); - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&title)); - row.set_subtitle(Some(&subtitle)); + row.set_subtitle(Some(track_name)); row.add_suffix(&edit_button); row.set_activatable_widget(Some(&edit_button)); diff --git a/src/main.rs b/src/main.rs index 49be0f5..4d1d79d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ fn main() { gstreamer::init().expect("Failed to initialize GStreamer!"); gtk::init().expect("Failed to initialize GTK!"); - libhandy::init(); + libadwaita::init(); resources::init().expect("Failed to initialize resources!"); let app = gtk::Application::new(Some("de.johrpan.musicus"), gio::ApplicationFlags::empty()) diff --git a/src/meson.build b/src/meson.build index 46c8738..0091060 100644 --- a/src/meson.build +++ b/src/meson.build @@ -66,8 +66,10 @@ sources = files( 'editors/work_part.rs', 'editors/work_section.rs', 'import/disc_source.rs', + 'import/folder_source.rs', 'import/medium_editor.rs', 'import/mod.rs', + 'import/source.rs', 'import/source_selector.rs', 'import/track_editor.rs', 'import/track_selector.rs', diff --git a/src/screens/ensemble_screen.rs b/src/screens/ensemble_screen.rs index 16101c7..f63aea1 100644 --- a/src/screens/ensemble_screen.rs +++ b/src/screens/ensemble_screen.rs @@ -7,7 +7,7 @@ use gio::prelude::*; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -72,7 +72,7 @@ impl EnsembleScreen { this.recording_list.set_make_widget_cb(clone!(@strong this => move |index| { let recording = &this.recordings.borrow()[index]; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&recording.work.get_title())); row.set_subtitle(Some(&recording.get_performers())); diff --git a/src/screens/person_screen.rs b/src/screens/person_screen.rs index 3955461..9937acc 100644 --- a/src/screens/person_screen.rs +++ b/src/screens/person_screen.rs @@ -7,7 +7,7 @@ use gio::prelude::*; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -86,7 +86,7 @@ impl PersonScreen { this.work_list.set_make_widget_cb(clone!(@strong this => move |index| { let work = &this.works.borrow()[index]; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&work.title)); @@ -111,7 +111,7 @@ impl PersonScreen { this.recording_list.set_make_widget_cb(clone!(@strong this => move |index| { let recording = &this.recordings.borrow()[index]; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&recording.work.get_title())); row.set_subtitle(Some(&recording.get_performers())); diff --git a/src/screens/player_screen.rs b/src/screens/player_screen.rs index c015d4e..cfe415d 100644 --- a/src/screens/player_screen.rs +++ b/src/screens/player_screen.rs @@ -4,7 +4,7 @@ use gettextrs::gettext; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::{Cell, RefCell}; use std::rc::Rc; @@ -155,7 +155,7 @@ impl PlayerScreen { let playlist_item = &this.playlist.borrow()[item_index]; let recording = &playlist_item.track_set.recording; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(false); row.set_selectable(false); row.set_title(Some(&recording.work.get_title())); @@ -179,7 +179,7 @@ impl PlayerScreen { parts.join(", ") }; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_selectable(false); row.set_activatable(true); row.set_title(Some(&title)); diff --git a/src/screens/recording_screen.rs b/src/screens/recording_screen.rs index d97aa69..85b9303 100644 --- a/src/screens/recording_screen.rs +++ b/src/screens/recording_screen.rs @@ -8,7 +8,7 @@ use gio::prelude::*; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -92,7 +92,7 @@ impl RecordingScreen { title_parts.join(", ") }; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_title(Some(&title)); row.upcast() diff --git a/src/screens/work_screen.rs b/src/screens/work_screen.rs index 9979e5a..2f8ce60 100644 --- a/src/screens/work_screen.rs +++ b/src/screens/work_screen.rs @@ -7,7 +7,7 @@ use gio::prelude::*; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -74,7 +74,7 @@ impl WorkScreen { this.recording_list.set_make_widget_cb(clone!(@strong this => move |index| { let recording = &this.recordings.borrow()[index]; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&recording.work.get_title())); row.set_subtitle(Some(&recording.get_performers())); diff --git a/src/selectors/ensemble.rs b/src/selectors/ensemble.rs index 76ecf6e..b3ec260 100644 --- a/src/selectors/ensemble.rs +++ b/src/selectors/ensemble.rs @@ -6,7 +6,7 @@ use crate::widgets::{Navigator, NavigatorScreen}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -65,7 +65,7 @@ impl EnsembleSelector { })); this.selector.set_make_widget(clone!(@strong this => move |ensemble| { - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&ensemble.name)); diff --git a/src/selectors/instrument.rs b/src/selectors/instrument.rs index 2bf835e..f4b41a5 100644 --- a/src/selectors/instrument.rs +++ b/src/selectors/instrument.rs @@ -6,7 +6,7 @@ use crate::widgets::{Navigator, NavigatorScreen}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -65,7 +65,7 @@ impl InstrumentSelector { })); this.selector.set_make_widget(clone!(@strong this => move |instrument| { - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&instrument.name)); diff --git a/src/selectors/person.rs b/src/selectors/person.rs index f991758..456a679 100644 --- a/src/selectors/person.rs +++ b/src/selectors/person.rs @@ -6,7 +6,7 @@ use crate::widgets::{Navigator, NavigatorScreen}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -65,7 +65,7 @@ impl PersonSelector { })); this.selector.set_make_widget(clone!(@strong this => move |person| { - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&person.name_lf())); diff --git a/src/selectors/recording.rs b/src/selectors/recording.rs index 7d66f20..8b8e400 100644 --- a/src/selectors/recording.rs +++ b/src/selectors/recording.rs @@ -6,7 +6,7 @@ use crate::widgets::{Navigator, NavigatorScreen}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -75,7 +75,7 @@ impl RecordingSelector { })); this.selector.set_make_widget(clone!(@strong this => move |recording| { - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&recording.get_performers())); diff --git a/src/selectors/work.rs b/src/selectors/work.rs index d937770..b163134 100644 --- a/src/selectors/work.rs +++ b/src/selectors/work.rs @@ -6,7 +6,7 @@ use crate::widgets::{Navigator, NavigatorScreen}; use gettextrs::gettext; use glib::clone; use gtk::prelude::*; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -75,7 +75,7 @@ impl WorkSelector { })); this.selector.set_make_widget(clone!(@strong this => move |work| { - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&work.title)); diff --git a/src/widgets/navigator.rs b/src/widgets/navigator.rs index c2c6a87..7e4a98b 100644 --- a/src/widgets/navigator.rs +++ b/src/widgets/navigator.rs @@ -128,6 +128,8 @@ impl Navigator { } pub fn reset(&self) { + self.widget.set_visible_child_name("empty_screen"); + for screen in self.screens.replace(Vec::new()) { screen.detach_navigator(); self.old_screens.borrow_mut().push(screen); diff --git a/src/widgets/navigator_window.rs b/src/widgets/navigator_window.rs index 297383b..70eb938 100644 --- a/src/widgets/navigator_window.rs +++ b/src/widgets/navigator_window.rs @@ -5,7 +5,7 @@ use std::rc::Rc; /// A window hosting a navigator. pub struct NavigatorWindow { - window: libhandy::Window, + window: libadwaita::Window, navigator: Rc, } @@ -14,11 +14,11 @@ impl NavigatorWindow { pub fn new(initial_screen: Rc) -> Rc { // Create UI - let window = libhandy::Window::new(); + let window = libadwaita::Window::new(); window.set_default_size(600, 424); let placeholder = gtk::Label::new(None); let navigator = Navigator::new(&window, &placeholder); - libhandy::WindowExt::set_child(&window, Some(&navigator.widget)); + libadwaita::WindowExt::set_child(&window, Some(&navigator.widget)); let this = Rc::new(Self { window, navigator }); diff --git a/src/widgets/poe_list.rs b/src/widgets/poe_list.rs index 3077726..d1fe7c6 100644 --- a/src/widgets/poe_list.rs +++ b/src/widgets/poe_list.rs @@ -4,7 +4,7 @@ use crate::database::*; use glib::clone; use gtk::prelude::*; use gtk_macros::get_widget; -use libhandy::prelude::*; +use libadwaita::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -64,7 +64,7 @@ impl PoeList { this.list.set_make_widget_cb(clone!(@strong this => move |index| { let poe = &this.data.borrow()[index]; - let row = libhandy::ActionRow::new(); + let row = libadwaita::ActionRow::new(); row.set_activatable(true); row.set_title(Some(&poe.get_title())); diff --git a/src/window.rs b/src/window.rs index b71bcd7..6129b16 100644 --- a/src/window.rs +++ b/src/window.rs @@ -13,9 +13,9 @@ use std::rc::Rc; pub struct Window { backend: Rc, - window: libhandy::ApplicationWindow, + window: libadwaita::ApplicationWindow, stack: gtk::Stack, - leaflet: libhandy::Leaflet, + leaflet: libadwaita::Leaflet, sidebar_box: gtk::Box, poe_list: Rc, navigator: Rc, @@ -27,11 +27,11 @@ impl Window { pub fn new(app: >k::Application) -> Rc { let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/window.ui"); - get_widget!(builder, libhandy::ApplicationWindow, window); + get_widget!(builder, libadwaita::ApplicationWindow, window); get_widget!(builder, gtk::Stack, stack); get_widget!(builder, gtk::Button, select_music_library_path_button); get_widget!(builder, gtk::Box, content_box); - get_widget!(builder, libhandy::Leaflet, leaflet); + get_widget!(builder, libadwaita::Leaflet, leaflet); get_widget!(builder, gtk::Button, add_button); get_widget!(builder, gtk::Box, sidebar_box); get_widget!(builder, gtk::Box, empty_screen); @@ -65,15 +65,17 @@ impl Window { result.window.set_application(Some(app)); select_music_library_path_button.connect_clicked(clone!(@strong result => move |_| { - let dialog = gtk::FileChooserNative::new( + let dialog = gtk::FileChooserDialog::new( Some(&gettext("Select music library folder")), Some(&result.window), gtk::FileChooserAction::SelectFolder, - None, - None); + &[ + (&gettext("Cancel"), gtk::ResponseType::Cancel), + (&gettext("Select"), gtk::ResponseType::Accept), + ]); dialog.connect_response(clone!(@strong result => move |dialog, response| { - if response == gtk::ResponseType::Accept { + if let gtk::ResponseType::Accept = response { if let Some(file) = dialog.get_file() { if let Some(path) = file.get_path() { let context = glib::MainContext::default(); @@ -84,6 +86,8 @@ impl Window { } } } + + dialog.hide(); })); dialog.show();