diff --git a/po/POTFILES.in b/po/POTFILES.in index ff61904..25efc7a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -24,6 +24,7 @@ res/ui/window.ui res/ui/work_editor.ui res/ui/work_screen.ui res/ui/work_selector.ui +res/ui/work_selector_screen.ui src/database/database.rs src/database/models.rs @@ -36,7 +37,6 @@ src/dialogs/ensemble_selector.rs src/dialogs/instrument_editor.rs src/dialogs/instrument_selector.rs src/dialogs/mod.rs -src/dialogs/part_editor.rs src/dialogs/person_editor.rs src/dialogs/person_selector.rs src/dialogs/preferences.rs @@ -48,11 +48,16 @@ src/dialogs/recording/recording_editor.rs src/dialogs/recording/recording_selector_person_screen.rs src/dialogs/recording/recording_selector.rs src/dialogs/recording/recording_selector_work_screen.rs -src/dialogs/section_editor.rs src/dialogs/track_editor.rs src/dialogs/tracks_editor.rs -src/dialogs/work_editor.rs -src/dialogs/work_selector.rs +src/dialogs/work/mod.rs +src/dialogs/work/part_editor.rs +src/dialogs/work/section_editor.rs +src/dialogs/work/work_dialog.rs +src/dialogs/work/work_editor_dialog.rs +src/dialogs/work/work_editor.rs +src/dialogs/work/work_selector_person_screen.rs +src/dialogs/work/work_selector.rs src/screens/ensemble_screen.rs src/screens/mod.rs src/screens/person_screen.rs diff --git a/po/de.po b/po/de.po index b33032e..795a1ad 100644 --- a/po/de.po +++ b/po/de.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-08 12:11+0100\n" -"PO-Revision-Date: 2020-11-08 02:37+0100\n" +"POT-Creation-Date: 2020-11-08 20:12+0100\n" +"PO-Revision-Date: 2020-11-08 20:13+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -27,7 +27,7 @@ msgstr "Ensemble" #: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23 #: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:17 #: res/ui/section_editor.ui:23 res/ui/track_editor.ui:24 -#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:25 +#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:17 msgid "Cancel" msgstr "Abbrechen" @@ -35,7 +35,7 @@ msgstr "Abbrechen" #: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31 #: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:25 #: res/ui/section_editor.ui:31 res/ui/track_editor.ui:32 -#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:33 +#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:25 msgid "Save" msgstr "Speichern" @@ -83,36 +83,32 @@ msgstr "Keine Instrumente gefunden." msgid "Work part" msgstr "Werkabschnitt" -#: res/ui/part_editor.ui:70 res/ui/work_editor.ui:91 +#: res/ui/part_editor.ui:70 res/ui/work_editor.ui:84 msgid "Composer" msgstr "Komponist" #: res/ui/part_editor.ui:93 res/ui/player_bar.ui:118 #: res/ui/player_screen.ui:160 res/ui/section_editor.ui:64 -#: res/ui/work_editor.ui:113 +#: res/ui/work_editor.ui:106 msgid "Title" msgstr "Titel" #: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87 #: res/ui/performance_editor.ui:170 res/ui/performance_editor.ui:214 #: res/ui/recording_editor.ui:81 res/ui/tracks_editor.ui:93 -#: res/ui/work_editor.ui:77 src/dialogs/part_editor.rs:99 -#: src/dialogs/recording/performance_editor.rs:150 +#: res/ui/work_editor.ui:69 src/dialogs/recording/performance_editor.rs:150 #: src/dialogs/recording/performance_editor.rs:160 #: src/dialogs/recording/performance_editor.rs:170 +#: src/dialogs/work/part_editor.rs:166 msgid "Select …" msgstr "Auswählen …" -#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:119 -#: res/ui/work_editor.ui:126 +#: res/ui/part_editor.ui:159 res/ui/recording_editor.ui:119 +#: res/ui/work_editor.ui:119 msgid "Overview" msgstr "Überblick" -#: res/ui/part_editor.ui:191 res/ui/work_editor.ui:157 -msgid "No instruments added." -msgstr "Keine Instrumente hinzugefügt." - -#: res/ui/part_editor.ui:266 res/ui/work_editor.ui:232 +#: res/ui/part_editor.ui:247 res/ui/work_editor.ui:207 msgid "Instruments" msgstr "Instrumente" @@ -141,7 +137,7 @@ msgstr "Vorname" msgid "Last name" msgstr "Nachname" -#: res/ui/person_list.ui:28 res/ui/work_selector.ui:69 +#: res/ui/person_list.ui:28 msgid "Search persons …" msgstr "Personen durchsuchen …" @@ -211,7 +207,7 @@ msgid "Comment" msgstr "Kommentar" #: res/ui/recording_editor.ui:106 res/ui/tracks_editor.ui:109 -#: res/ui/work_editor.ui:22 +#: res/ui/work_editor.ui:14 msgid "Work" msgstr "Werk" @@ -227,7 +223,7 @@ msgstr "Tracks" msgid "Add to playlist" msgstr "Zur Wiedergabeliste hinzufügen" -#: res/ui/recording_selector.ui:27 res/ui/work_selector.ui:180 +#: res/ui/recording_selector.ui:27 res/ui/work_selector.ui:26 msgid "Select a composer on the left." msgstr "Wählen Sie einen Komponisten aus." @@ -284,26 +280,14 @@ msgstr "Einstellungen" msgid "About Musicus" msgstr "Über Musicus" -#: res/ui/work_editor.ui:264 -msgid "No work parts added." -msgstr "Keine Werkabschnitte hinzugefügt." - -#: res/ui/work_editor.ui:416 +#: res/ui/work_editor.ui:373 msgid "Structure" msgstr "Struktur" -#: res/ui/work_selector.ui:113 src/widgets/person_list.rs:26 -msgid "No persons found." -msgstr "Keine Personen gefunden." - -#: res/ui/work_selector.ui:167 -msgid "Select work" +#: res/ui/work_selector.ui:51 +msgid "Select a work" msgstr "Werk auswählen" -#: res/ui/work_selector.ui:268 -msgid "Search works …" -msgstr "Werke durchsuchen …" - #: src/dialogs/about.rs:12 msgid "The classical music player and organizer." msgstr "Das Programm zum Abspielen und Organisieren von Klassik." @@ -321,6 +305,7 @@ msgid "No performers added." msgstr "Keine Interpreten hinzugefügt." #: src/dialogs/recording/recording_selector_person_screen.rs:39 +#: src/dialogs/work/work_selector_person_screen.rs:36 #: src/screens/person_screen.rs:57 msgid "No works found." msgstr "Keine Werke gefunden." @@ -338,6 +323,14 @@ msgstr "Audiodateien auswählen" msgid "Unknown" msgstr "Unbekannt" +#: src/dialogs/work/part_editor.rs:49 src/dialogs/work/work_editor.rs:69 +msgid "No instruments added." +msgstr "Keine Instrumente hinzugefügt." + +#: src/dialogs/work/work_editor.rs:72 +msgid "No work parts added." +msgstr "Keine Werkabschnitte hinzugefügt." + #: src/screens/ensemble_screen.rs:35 msgid "Edit ensemble" msgstr "Ensemble bearbeiten" @@ -382,6 +375,13 @@ msgstr "Werk bearbeiten" msgid "Delete work" msgstr "Werk löschen" +#: src/widgets/person_list.rs:26 +msgid "No persons found." +msgstr "Keine Personen gefunden." + #: src/widgets/poe_list.rs:41 msgid "No persons or ensembles found." msgstr "Keine Personen oder Ensembles gefunden." + +#~ msgid "Search works …" +#~ msgstr "Werke durchsuchen …" diff --git a/po/musicus.pot b/po/musicus.pot index 2bc2329..04c5725 100644 --- a/po/musicus.pot +++ b/po/musicus.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: musicus\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-08 12:11+0100\n" +"POT-Creation-Date: 2020-11-08 20:12+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -26,7 +26,7 @@ msgstr "" #: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23 #: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:17 #: res/ui/section_editor.ui:23 res/ui/track_editor.ui:24 -#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:25 +#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:17 msgid "Cancel" msgstr "" @@ -34,7 +34,7 @@ msgstr "" #: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31 #: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:25 #: res/ui/section_editor.ui:31 res/ui/track_editor.ui:32 -#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:33 +#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:25 msgid "Save" msgstr "" @@ -82,36 +82,32 @@ msgstr "" msgid "Work part" msgstr "" -#: res/ui/part_editor.ui:70 res/ui/work_editor.ui:91 +#: res/ui/part_editor.ui:70 res/ui/work_editor.ui:84 msgid "Composer" msgstr "" #: res/ui/part_editor.ui:93 res/ui/player_bar.ui:118 #: res/ui/player_screen.ui:160 res/ui/section_editor.ui:64 -#: res/ui/work_editor.ui:113 +#: res/ui/work_editor.ui:106 msgid "Title" msgstr "" #: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87 #: res/ui/performance_editor.ui:170 res/ui/performance_editor.ui:214 #: res/ui/recording_editor.ui:81 res/ui/tracks_editor.ui:93 -#: res/ui/work_editor.ui:77 src/dialogs/part_editor.rs:99 -#: src/dialogs/recording/performance_editor.rs:150 +#: res/ui/work_editor.ui:69 src/dialogs/recording/performance_editor.rs:150 #: src/dialogs/recording/performance_editor.rs:160 #: src/dialogs/recording/performance_editor.rs:170 +#: src/dialogs/work/part_editor.rs:166 msgid "Select …" msgstr "" -#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:119 -#: res/ui/work_editor.ui:126 +#: res/ui/part_editor.ui:159 res/ui/recording_editor.ui:119 +#: res/ui/work_editor.ui:119 msgid "Overview" msgstr "" -#: res/ui/part_editor.ui:191 res/ui/work_editor.ui:157 -msgid "No instruments added." -msgstr "" - -#: res/ui/part_editor.ui:266 res/ui/work_editor.ui:232 +#: res/ui/part_editor.ui:247 res/ui/work_editor.ui:207 msgid "Instruments" msgstr "" @@ -140,7 +136,7 @@ msgstr "" msgid "Last name" msgstr "" -#: res/ui/person_list.ui:28 res/ui/work_selector.ui:69 +#: res/ui/person_list.ui:28 msgid "Search persons …" msgstr "" @@ -210,7 +206,7 @@ msgid "Comment" msgstr "" #: res/ui/recording_editor.ui:106 res/ui/tracks_editor.ui:109 -#: res/ui/work_editor.ui:22 +#: res/ui/work_editor.ui:14 msgid "Work" msgstr "" @@ -226,7 +222,7 @@ msgstr "" msgid "Add to playlist" msgstr "" -#: res/ui/recording_selector.ui:27 res/ui/work_selector.ui:180 +#: res/ui/recording_selector.ui:27 res/ui/work_selector.ui:26 msgid "Select a composer on the left." msgstr "" @@ -278,24 +274,12 @@ msgstr "" msgid "About Musicus" msgstr "" -#: res/ui/work_editor.ui:264 -msgid "No work parts added." -msgstr "" - -#: res/ui/work_editor.ui:416 +#: res/ui/work_editor.ui:373 msgid "Structure" msgstr "" -#: res/ui/work_selector.ui:113 src/widgets/person_list.rs:26 -msgid "No persons found." -msgstr "" - -#: res/ui/work_selector.ui:167 -msgid "Select work" -msgstr "" - -#: res/ui/work_selector.ui:268 -msgid "Search works …" +#: res/ui/work_selector.ui:51 +msgid "Select a work" msgstr "" #: src/dialogs/about.rs:12 @@ -315,6 +299,7 @@ msgid "No performers added." msgstr "" #: src/dialogs/recording/recording_selector_person_screen.rs:39 +#: src/dialogs/work/work_selector_person_screen.rs:36 #: src/screens/person_screen.rs:57 msgid "No works found." msgstr "" @@ -332,6 +317,14 @@ msgstr "" msgid "Unknown" msgstr "" +#: src/dialogs/work/part_editor.rs:49 src/dialogs/work/work_editor.rs:69 +msgid "No instruments added." +msgstr "" + +#: src/dialogs/work/work_editor.rs:72 +msgid "No work parts added." +msgstr "" + #: src/screens/ensemble_screen.rs:35 msgid "Edit ensemble" msgstr "" @@ -376,6 +369,10 @@ msgstr "" msgid "Delete work" msgstr "" +#: src/widgets/person_list.rs:26 +msgid "No persons found." +msgstr "" + #: src/widgets/poe_list.rs:41 msgid "No persons or ensembles found." msgstr "" diff --git a/res/musicus.gresource.xml b/res/musicus.gresource.xml index d64423c..92e3c7d 100644 --- a/res/musicus.gresource.xml +++ b/res/musicus.gresource.xml @@ -27,5 +27,6 @@ ui/work_editor.ui ui/work_screen.ui ui/work_selector.ui + ui/work_selector_screen.ui diff --git a/res/ui/part_editor.ui b/res/ui/part_editor.ui index b876306..80a01b4 100644 --- a/res/ui/part_editor.ui +++ b/res/ui/part_editor.ui @@ -125,7 +125,6 @@ - True True True @@ -170,30 +169,12 @@ 18 6 - + True True in - - True - False - - - True - False - True - True - - - True - False - No instruments added. - - - - - + diff --git a/res/ui/work_editor.ui b/res/ui/work_editor.ui index d377bc4..0f01693 100644 --- a/res/ui/work_editor.ui +++ b/res/ui/work_editor.ui @@ -3,431 +3,386 @@ - + + True False - True - 500 - 450 - True - dialog + vertical - + True False - vertical + Work - - True - False - Work - - - Cancel - True - True - True - - - - - Save - True - False - True - True - - - - end - 1 - - - - - False - True - 0 - - - - + + Cancel True True - - - - True - False - 18 - 12 - 6 - - - True - True - True - True - - - True - False - start - Select … - - - - - 1 - 1 - - - - - True - False - end - Composer - - - 0 - 1 - - - - - True - True - - - 1 - 0 - - - - - True - False - end - Title - - - 0 - 0 - - - - - - - True - False - Overview - - - False - - - - - True - False - 18 - 6 - - - True - True - in - - - True - False - - - True - False - True - True - - - True - False - No instruments added. - - - - - - - - - True - True - 0 - - - - - True - False - 0 - vertical - 6 - - - True - True - True - - - True - False - list-add-symbolic - - - - - False - True - 0 - - - - - True - True - True - - - True - False - list-remove-symbolic - - - - - False - True - 1 - - - - - False - True - 1 - - - - - 1 - - - - - True - False - Instruments - - - 1 - False - - - - - True - False - 18 - 6 - - - True - True - in - - - True - False - - - True - False - True - True - - - True - False - No work parts added. - - - - - - - - - True - True - 0 - - - - - True - False - vertical - 6 - - - True - True - True - - - True - False - list-add-symbolic - - - - - False - True - 0 - - - - - True - True - True - - - True - False - folder-new-symbolic - - - - - False - True - 1 - - - - - True - True - True - - - True - False - edit-symbolic - - - - - False - True - 2 - - - - - True - True - True - - - True - False - list-remove-symbolic - - - - - False - True - 3 - - - - - True - True - True - - - True - False - go-down-symbolic - - - - - False - True - end - 4 - - - - - True - True - True - - - True - False - go-up-symbolic - - - - - False - True - end - 5 - - - - - False - True - 1 - - - - - 2 - - - - - True - False - Structure - - - 2 - False - - + True + + + + + Save + True + False + True + True + - False - True + end 1 + + False + True + 0 + + + + + True + True + + + + True + False + 18 + 12 + 6 + + + True + True + True + True + + + True + False + start + Select … + end + + + + + 1 + 1 + + + + + True + False + end + Composer + + + 0 + 1 + + + + + True + True + + + 1 + 0 + + + + + True + False + end + Title + + + 0 + 0 + + + + + + + True + False + Overview + + + False + + + + + True + False + 18 + 6 + + + True + True + in + + + + + + True + True + 0 + + + + + True + False + 0 + vertical + 6 + + + True + True + True + + + True + False + list-add-symbolic + + + + + False + True + 0 + + + + + True + True + True + + + True + False + list-remove-symbolic + + + + + False + True + 1 + + + + + False + True + 1 + + + + + 1 + + + + + True + False + Instruments + + + 1 + False + + + + + True + False + 18 + 6 + + + True + True + in + + + + + + True + True + 0 + + + + + True + False + vertical + 6 + + + True + True + True + + + True + False + list-add-symbolic + + + + + False + True + 0 + + + + + True + True + True + + + True + False + folder-new-symbolic + + + + + False + True + 1 + + + + + True + True + True + + + True + False + edit-symbolic + + + + + False + True + 2 + + + + + True + True + True + + + True + False + list-remove-symbolic + + + + + False + True + 3 + + + + + True + True + True + + + True + False + go-down-symbolic + + + + + False + True + end + 4 + + + + + True + True + True + + + True + False + go-up-symbolic + + + + + False + True + end + 5 + + + + + False + True + 1 + + + + + 2 + + + + + True + False + Structure + + + 2 + False + + + + + True + True + 1 + diff --git a/res/ui/work_selector.ui b/res/ui/work_selector.ui index a2d41f6..4396b8f 100644 --- a/res/ui/work_selector.ui +++ b/res/ui/work_selector.ui @@ -1,351 +1,92 @@ - - - + + + + True False - 600 - 424 - True - True - dialog + vertical - + True False - sidebar_box - True + True + True + + + False + True + 0 + + + + + True + False + Select a composer on the left. + + + True + True + 1 + + + + + True + False + sidebar_box + + + 250 + True + False + False + vertical - - 225 + True False - False - vertical + Select a work - + True - False - True + True + True - - True - True - True - - - True - False - list-add-symbolic - - - - - - - False - True - 0 - - - - - True - False - True - - + True False - 400 - - - True - True - edit-find-symbolic - False - False - Search persons … - - + list-add-symbolic - - False - True - 1 - - - - - True - False - crossfade - - - True - False - True - - - loading - - - - - True - True - - - True - False - none - - - True - False - - - True - False - No persons found. - - - - - - - - - persons_list - 1 - - - - - True - True - 2 - - sidebar - - - - - True - False - vertical - - - - False - - - - - True - False - True - crossfade - - - True - False - vertical - - - True - False - True - Select work - True - - - False - True - 0 - - - - - True - False - Select a composer on the left. - - - True - True - 1 - - - - - empty_screen - - - - - True - False - vertical - - - True - False - True - - - True - False - crossfade - 0 - False - - - True - True - True - - - True - False - go-previous-symbolic - - - - - - - - - True - True - True - - - True - False - edit-find-symbolic - - - - - end - 1 - - - - - False - True - 0 - - - - - True - False - False - - - True - False - 400 - - - True - True - edit-find-symbolic - False - False - Search works … - - - - - - - False - True - 1 - - - - - True - False - crossfade - - - True - False - True - - - loading - - - - - True - True - - - True - False - none - - - True - False - - - - - - - content - 1 - - - - - True - True - 2 - - - - - person_screen - 1 - - - - - content + False + True + 0 + + sidebar + + + + + True + False + vertical + + + + False + - - - - - - - - - - - - diff --git a/res/ui/work_selector_screen.ui b/res/ui/work_selector_screen.ui new file mode 100644 index 0000000..55f31fe --- /dev/null +++ b/res/ui/work_selector_screen.ui @@ -0,0 +1,59 @@ + + + + + + + True + False + vertical + + + True + False + True + True + + + True + True + True + + + True + False + go-previous-symbolic + + + + + + + False + True + 0 + + + + + True + False + + + True + False + True + + + loading + + + + + True + True + 1 + + + + diff --git a/src/dialogs/mod.rs b/src/dialogs/mod.rs index bac60df..ab86e94 100644 --- a/src/dialogs/mod.rs +++ b/src/dialogs/mod.rs @@ -13,9 +13,6 @@ pub use instrument_editor::*; pub mod instrument_selector; pub use instrument_selector::*; -pub mod part_editor; -pub use part_editor::*; - pub mod person_editor; pub use person_editor::*; @@ -28,17 +25,11 @@ pub use preferences::*; pub mod recording; pub use recording::*; -pub mod section_editor; -pub use section_editor::*; - pub mod track_editor; pub use track_editor::*; pub mod tracks_editor; pub use tracks_editor::*; -pub mod work_editor; -pub use work_editor::*; - -pub mod work_selector; -pub use work_selector::*; +pub mod work; +pub use work::*; diff --git a/src/dialogs/part_editor.rs b/src/dialogs/part_editor.rs deleted file mode 100644 index f7043d8..0000000 --- a/src/dialogs/part_editor.rs +++ /dev/null @@ -1,163 +0,0 @@ -use super::{InstrumentSelector, PersonSelector}; -use crate::backend::*; -use crate::database::*; -use crate::widgets::*; -use gettextrs::gettext; -use glib::clone; -use gtk::prelude::*; -use gtk_macros::get_widget; -use std::cell::RefCell; -use std::convert::TryInto; -use std::rc::Rc; - -pub struct PartEditor { - backend: Rc, - window: libhandy::Window, - title_entry: gtk::Entry, - composer: RefCell>, - composer_label: gtk::Label, - instruments: RefCell>, - instrument_list: gtk::ListBox, -} - -impl PartEditor { - pub fn new () + 'static, P: IsA>( - backend: Rc, - parent: &P, - part: Option, - callback: F, - ) -> Rc { - let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/part_editor.ui"); - - get_widget!(builder, libhandy::Window, window); - get_widget!(builder, gtk::Button, cancel_button); - get_widget!(builder, gtk::Button, save_button); - get_widget!(builder, gtk::Entry, title_entry); - get_widget!(builder, gtk::Button, composer_button); - get_widget!(builder, gtk::Label, composer_label); - get_widget!(builder, gtk::Button, reset_composer_button); - get_widget!(builder, gtk::ListBox, instrument_list); - get_widget!(builder, gtk::Button, add_instrument_button); - get_widget!(builder, gtk::Button, remove_instrument_button); - - match part.clone() { - Some(part) => { - title_entry.set_text(&part.title); - } - None => (), - }; - - let composer = RefCell::new(match part.clone() { - Some(work) => { - match work.composer.clone() { - Some(composer) => composer_label.set_text(&composer.name_fl()), - None => (), - } - - work.composer - }, - None => None, - }); - - let instruments = RefCell::new(match part.clone() { - Some(work) => work.instruments, - None => Vec::new(), - }); - - let result = Rc::new(PartEditor { - backend: backend, - window: window, - title_entry: title_entry, - composer: composer, - composer_label: composer_label, - instruments: instruments, - instrument_list: instrument_list, - }); - - cancel_button.connect_clicked(clone!(@strong result => move |_| { - result.window.close(); - })); - - save_button.connect_clicked(clone!(@strong result => move |_| { - result.window.close(); - callback(WorkPartDescription { - title: result.title_entry.get_text().to_string(), - composer: result.composer.borrow().clone(), - instruments: result.instruments.borrow().clone(), - }); - })); - - composer_button.connect_clicked(clone!(@strong result => move |_| { - PersonSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |person| { - result.composer.replace(Some(person.clone())); - result.composer_label.set_text(&person.name_fl()); - })).show(); - })); - - reset_composer_button.connect_clicked(clone!(@strong result => move |_| { - result.composer.replace(None); - result.composer_label.set_text(&gettext("Select …")); - })); - - add_instrument_button.connect_clicked(clone!(@strong result => move |_| { - InstrumentSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |instrument| { - { - let mut instruments = result.instruments.borrow_mut(); - instruments.push(instrument); - } - - result.show_instruments(); - })).show(); - })); - - remove_instrument_button.connect_clicked(clone!(@strong result => move |_| { - let row = result.get_selected_instrument_row(); - match row { - Some(row) => { - let index = row.get_index(); - let index: usize = index.try_into().unwrap(); - result.instruments.borrow_mut().remove(index); - result.show_instruments(); - } - None => (), - } - })); - - result.window.set_transient_for(Some(parent)); - - result - } - - pub fn show(&self) { - self.window.show(); - } - - fn show_instruments(&self) { - for child in self.instrument_list.get_children() { - self.instrument_list.remove(&child); - } - - for (index, instrument) in self.instruments.borrow().iter().enumerate() { - let label = gtk::Label::new(Some(&instrument.name)); - label.set_halign(gtk::Align::Start); - label.set_margin_start(6); - label.set_margin_end(6); - label.set_margin_top(6); - label.set_margin_bottom(6); - - let row = SelectorRow::new(index.try_into().unwrap(), &label); - row.show_all(); - self.instrument_list.insert(&row, -1); - } - } - - fn get_selected_instrument_row(&self) -> Option { - match self.instrument_list.get_selected_rows().first() { - Some(row) => match row.get_child() { - Some(child) => Some(child.downcast().unwrap()), - None => None, - }, - None => None, - } - } -} diff --git a/src/dialogs/recording/recording_editor.rs b/src/dialogs/recording/recording_editor.rs index 502fb89..2e6253a 100644 --- a/src/dialogs/recording/recording_editor.rs +++ b/src/dialogs/recording/recording_editor.rs @@ -105,10 +105,14 @@ impl RecordingEditor { })); work_button.connect_clicked(clone!(@strong this => move |_| { - WorkSelector::new(this.backend.clone(), &this.parent, clone!(@strong this => move |work| { + let dialog = WorkDialog::new(this.backend.clone(), &this.parent); + + dialog.set_selected_cb(clone!(@strong this => move |work| { this.work_selected(&work); this.work.replace(Some(work)); - })).show(); + })); + + dialog.show(); })); this.performance_list.set_make_widget(|performance| { diff --git a/src/dialogs/recording/recording_selector.rs b/src/dialogs/recording/recording_selector.rs index 4435034..4122d6b 100644 --- a/src/dialogs/recording/recording_selector.rs +++ b/src/dialogs/recording/recording_selector.rs @@ -77,7 +77,7 @@ impl RecordingSelector { this } - /// Set the closure to be called if the editor is user wants to add a new recording. + /// Set the closure to be called if the user wants to add a new recording. pub fn set_add_cb () + 'static>(&self, cb: F) { self.add_cb.replace(Some(Box::new(cb))); } diff --git a/src/dialogs/recording/recording_selector_person_screen.rs b/src/dialogs/recording/recording_selector_person_screen.rs index 98037ca..c144368 100644 --- a/src/dialogs/recording/recording_selector_person_screen.rs +++ b/src/dialogs/recording/recording_selector_person_screen.rs @@ -10,8 +10,8 @@ use libhandy::HeaderBarExt; use std::cell::RefCell; use std::rc::Rc; -/// A screen within the recording selector that presents a list of persons -/// and switches to a work screen on selection. +/// A screen within the recording selector that presents a list of works and switches to a work +/// screen on selection. pub struct RecordingSelectorPersonScreen { backend: Rc, widget: gtk::Box, diff --git a/src/dialogs/section_editor.rs b/src/dialogs/section_editor.rs deleted file mode 100644 index 78d725a..0000000 --- a/src/dialogs/section_editor.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::database::*; -use glib::clone; -use gtk::prelude::*; -use gtk_macros::get_widget; -use std::rc::Rc; - -pub struct SectionEditor { - window: libhandy::Window, - title_entry: gtk::Entry, -} - -impl SectionEditor { - pub fn new () + 'static, P: IsA>( - parent: &P, - section: Option, - callback: F, - ) -> Rc { - let builder = - gtk::Builder::from_resource("/de/johrpan/musicus/ui/section_editor.ui"); - - get_widget!(builder, libhandy::Window, window); - get_widget!(builder, gtk::Button, cancel_button); - get_widget!(builder, gtk::Button, save_button); - get_widget!(builder, gtk::Entry, title_entry); - - match section { - Some(section) => { - title_entry.set_text(§ion.title); - } - None => (), - } - - let result = Rc::new(SectionEditor { - window: window, - title_entry: title_entry, - }); - - cancel_button.connect_clicked(clone!(@strong result => move |_| { - result.window.close(); - })); - - save_button.connect_clicked(clone!(@strong result => move |_| { - result.window.close(); - - let section = WorkSectionDescription { - before_index: 0, - title: result.title_entry.get_text().to_string(), - }; - - callback(section); - })); - - result.window.set_transient_for(Some(parent)); - - result - } - - pub fn show(&self) { - self.window.show(); - } -} diff --git a/src/dialogs/work/mod.rs b/src/dialogs/work/mod.rs new file mode 100644 index 0000000..3c8e55d --- /dev/null +++ b/src/dialogs/work/mod.rs @@ -0,0 +1,11 @@ +pub mod work_dialog; +pub use work_dialog::*; + +pub mod work_editor_dialog; +pub use work_editor_dialog::*; + +mod part_editor; +mod section_editor; +mod work_editor; +mod work_selector; +mod work_selector_person_screen; diff --git a/src/dialogs/work/part_editor.rs b/src/dialogs/work/part_editor.rs new file mode 100644 index 0000000..e0e472f --- /dev/null +++ b/src/dialogs/work/part_editor.rs @@ -0,0 +1,170 @@ +use crate::backend::*; +use crate::database::*; +use crate::dialogs::*; +use crate::widgets::*; +use gettextrs::gettext; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use std::cell::RefCell; +use std::rc::Rc; + +/// A dialog for creating or editing a work part. +pub struct PartEditor { + backend: Rc, + window: libhandy::Window, + title_entry: gtk::Entry, + composer_label: gtk::Label, + reset_composer_button: gtk::Button, + instrument_list: Rc>, + composer: RefCell>, + instruments: RefCell>, + ready_cb: RefCell ()>>>, +} + +impl PartEditor { + /// Create a new part editor and optionally initialize it. + pub fn new>( + backend: Rc, + parent: &P, + part: Option, + ) -> Rc { + // Create UI + + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/part_editor.ui"); + + get_widget!(builder, libhandy::Window, window); + get_widget!(builder, gtk::Button, cancel_button); + get_widget!(builder, gtk::Button, save_button); + get_widget!(builder, gtk::Entry, title_entry); + get_widget!(builder, gtk::Button, composer_button); + get_widget!(builder, gtk::Label, composer_label); + get_widget!(builder, gtk::Button, reset_composer_button); + get_widget!(builder, gtk::ScrolledWindow, scroll); + get_widget!(builder, gtk::Button, add_instrument_button); + get_widget!(builder, gtk::Button, remove_instrument_button); + + window.set_transient_for(Some(parent)); + + let instrument_list = List::new(&gettext("No instruments added.")); + scroll.add(&instrument_list.widget); + + let (composer, instruments) = match part { + Some(part) => { + title_entry.set_text(&part.title); + (part.composer, part.instruments) + } + None => (None, Vec::new()), + }; + + let this = Rc::new(Self { + backend, + window, + title_entry, + composer_label, + reset_composer_button, + instrument_list, + composer: RefCell::new(composer), + instruments: RefCell::new(instruments), + ready_cb: RefCell::new(None), + }); + + // Connect signals and callbacks + + cancel_button.connect_clicked(clone!(@strong this => move |_| { + this.window.close(); + })); + + save_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(cb) = &*this.ready_cb.borrow() { + cb(WorkPartDescription { + title: this.title_entry.get_text().to_string(), + composer: this.composer.borrow().clone(), + instruments: this.instruments.borrow().clone(), + }); + } + + this.window.close(); + })); + + composer_button.connect_clicked(clone!(@strong this => move |_| { + PersonSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |person| { + this.show_composer(Some(&person)); + this.composer.replace(Some(person)); + })).show(); + })); + + this.reset_composer_button + .connect_clicked(clone!(@strong this => move |_| { + this.composer.replace(None); + this.show_composer(None); + })); + + this.instrument_list.set_make_widget(|instrument| { + let label = gtk::Label::new(Some(&instrument.name)); + label.set_ellipsize(pango::EllipsizeMode::End); + label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + label.upcast() + }); + + add_instrument_button.connect_clicked(clone!(@strong this => move |_| { + InstrumentSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |instrument| { + let mut instruments = this.instruments.borrow_mut(); + + let index = match this.instrument_list.get_selected_index() { + Some(index) => index + 1, + None => instruments.len(), + }; + + instruments.insert(index, instrument); + this.instrument_list.show_items(instruments.clone()); + this.instrument_list.select_index(index); + })).show(); + })); + + remove_instrument_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(index) = this.instrument_list.get_selected_index() { + let mut instruments = this.instruments.borrow_mut(); + instruments.remove(index); + this.instrument_list.show_items(instruments.clone()); + this.instrument_list.select_index(index); + } + })); + + // Initialize + + if let Some(composer) = &*this.composer.borrow() { + this.show_composer(Some(composer)); + } + + this.instrument_list + .show_items(this.instruments.borrow().clone()); + + this + } + + /// Set the closure to be called when the user wants to save the part. + pub fn set_ready_cb () + 'static>(&self, cb: F) { + self.ready_cb.replace(Some(Box::new(cb))); + } + + /// Show the part editor. + pub fn show(&self) { + self.window.show(); + } + + /// Update the UI according to person. + fn show_composer(&self, person: Option<&Person>) { + if let Some(person) = person { + self.composer_label.set_text(&person.name_fl()); + self.reset_composer_button.show(); + } else { + self.composer_label.set_text(&gettext("Select …")); + self.reset_composer_button.hide(); + } + } +} diff --git a/src/dialogs/work/section_editor.rs b/src/dialogs/work/section_editor.rs new file mode 100644 index 0000000..42a30db --- /dev/null +++ b/src/dialogs/work/section_editor.rs @@ -0,0 +1,73 @@ +use crate::database::*; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use std::cell::RefCell; +use std::rc::Rc; + +/// A dialog for creating or editing a work section. +pub struct SectionEditor { + window: libhandy::Window, + title_entry: gtk::Entry, + ready_cb: RefCell ()>>>, +} + +impl SectionEditor { + /// Create a new section editor and optionally initialize it. + pub fn new>( + parent: &P, + section: Option, + ) -> Rc { + // Create UI + + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/section_editor.ui"); + + get_widget!(builder, libhandy::Window, window); + get_widget!(builder, gtk::Button, cancel_button); + get_widget!(builder, gtk::Button, save_button); + get_widget!(builder, gtk::Entry, title_entry); + + window.set_transient_for(Some(parent)); + + if let Some(section) = section { + title_entry.set_text(§ion.title); + } + + let this = Rc::new(Self { + window, + title_entry, + ready_cb: RefCell::new(None), + }); + + // Connect signals and callbacks + + cancel_button.connect_clicked(clone!(@strong this => move |_| { + this.window.close(); + })); + + save_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(cb) = &*this.ready_cb.borrow() { + cb(WorkSectionDescription { + before_index: 0, + title: this.title_entry.get_text().to_string(), + }); + } + + this.window.close(); + })); + + this + } + + /// Set the closure to be called when the user wants to save the section. Note that the + /// resulting object will always have `before_index` set to 0. The caller is expected to + /// change that later before adding the section to the database. + pub fn set_ready_cb () + 'static>(&self, cb: F) { + self.ready_cb.replace(Some(Box::new(cb))); + } + + /// Show the section editor. + pub fn show(&self) { + self.window.show(); + } +} diff --git a/src/dialogs/work/work_dialog.rs b/src/dialogs/work/work_dialog.rs new file mode 100644 index 0000000..db5c260 --- /dev/null +++ b/src/dialogs/work/work_dialog.rs @@ -0,0 +1,86 @@ +use super::work_editor::*; +use super::work_selector::*; +use crate::backend::*; +use crate::database::*; +use glib::clone; +use gtk::prelude::*; +use std::cell::RefCell; +use std::rc::Rc; + +/// A dialog for selecting and creating a work. +pub struct WorkDialog { + pub window: libhandy::Window, + stack: gtk::Stack, + selector: Rc, + editor: Rc, + selected_cb: RefCell ()>>>, +} + +impl WorkDialog { + /// Create a new work dialog. + pub fn new>(backend: Rc, parent: &W) -> Rc { + // Create UI + + let window = libhandy::Window::new(); + window.set_type_hint(gdk::WindowTypeHint::Dialog); + window.set_modal(true); + window.set_transient_for(Some(parent)); + window.set_default_size(600, 424); + + let selector = WorkSelector::new(backend.clone()); + let editor = WorkEditor::new(backend.clone(), &window, None); + + let stack = gtk::Stack::new(); + stack.set_transition_type(gtk::StackTransitionType::Crossfade); + stack.add(&selector.widget); + stack.add(&editor.widget); + window.add(&stack); + window.show_all(); + + let this = Rc::new(Self { + window, + stack, + selector, + editor, + selected_cb: RefCell::new(None), + }); + + // Connect signals and callbacks + + this.selector.set_add_cb(clone!(@strong this => move || { + this.stack.set_visible_child(&this.editor.widget); + })); + + this.selector + .set_selected_cb(clone!(@strong this => move |work| { + if let Some(cb) = &*this.selected_cb.borrow() { + cb(work); + this.window.close(); + } + })); + + this.editor.set_cancel_cb(clone!(@strong this => move || { + this.stack.set_visible_child(&this.selector.widget); + })); + + this.editor + .set_saved_cb(clone!(@strong this => move |work| { + if let Some(cb) = &*this.selected_cb.borrow() { + cb(work); + this.window.close(); + } + })); + + this + } + + /// Set the closure to be called when the user has selected or created a work. + pub fn set_selected_cb () + 'static>(&self, cb: F) { + self.selected_cb.replace(Some(Box::new(cb))); + } + + /// Show the work dialog. + pub fn show(&self) { + self.window.show(); + } +} diff --git a/src/dialogs/work/work_editor.rs b/src/dialogs/work/work_editor.rs new file mode 100644 index 0000000..82d2745 --- /dev/null +++ b/src/dialogs/work/work_editor.rs @@ -0,0 +1,357 @@ +use super::part_editor::*; +use super::section_editor::*; +use crate::backend::*; +use crate::database::*; +use crate::dialogs::*; +use crate::widgets::*; +use gettextrs::gettext; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use std::cell::RefCell; +use std::convert::TryInto; +use std::rc::Rc; + +/// Either a work part or a work section. +#[derive(Clone)] +enum PartOrSection { + Part(WorkPartDescription), + Section(WorkSectionDescription), +} + +/// A widget for editing and creating works. +pub struct WorkEditor { + pub widget: gtk::Box, + backend: Rc, + parent: gtk::Window, + save_button: gtk::Button, + title_entry: gtk::Entry, + composer_label: gtk::Label, + instrument_list: Rc>, + part_list: Rc>, + id: i64, + composer: RefCell>, + instruments: RefCell>, + structure: RefCell>, + cancel_cb: RefCell ()>>>, + saved_cb: RefCell ()>>>, +} + +impl WorkEditor { + /// Create a new work editor widget and optionally initialize it. The parent window is used + /// as the parent for newly created dialogs. + pub fn new>( + backend: Rc, + parent: &P, + work: Option, + ) -> Rc { + // Create UI + + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_editor.ui"); + + get_widget!(builder, gtk::Box, widget); + get_widget!(builder, gtk::Button, cancel_button); + get_widget!(builder, gtk::Button, save_button); + get_widget!(builder, gtk::Entry, title_entry); + get_widget!(builder, gtk::Button, composer_button); + get_widget!(builder, gtk::Label, composer_label); + get_widget!(builder, gtk::ScrolledWindow, instruments_scroll); + get_widget!(builder, gtk::Button, add_instrument_button); + get_widget!(builder, gtk::Button, remove_instrument_button); + get_widget!(builder, gtk::ScrolledWindow, structure_scroll); + get_widget!(builder, gtk::Button, add_part_button); + get_widget!(builder, gtk::Button, remove_part_button); + get_widget!(builder, gtk::Button, add_section_button); + get_widget!(builder, gtk::Button, edit_part_button); + get_widget!(builder, gtk::Button, move_part_up_button); + get_widget!(builder, gtk::Button, move_part_down_button); + + let instrument_list = List::new(&gettext("No instruments added.")); + instruments_scroll.add(&instrument_list.widget); + + let part_list = List::new(&gettext("No work parts added.")); + structure_scroll.add(&part_list.widget); + + let (id, composer, instruments, structure) = match work { + Some(work) => { + title_entry.set_text(&work.title); + + let mut structure = Vec::new(); + + for part in work.parts { + structure.push(PartOrSection::Part(part)); + } + + for section in work.sections { + structure.insert( + section.before_index.try_into().unwrap(), + PartOrSection::Section(section), + ); + } + + (work.id, Some(work.composer), work.instruments, structure) + } + None => (rand::random::().into(), None, Vec::new(), Vec::new()), + }; + + let this = Rc::new(Self { + widget, + backend, + parent: parent.clone().upcast(), + save_button, + id, + title_entry, + composer_label, + instrument_list, + part_list, + composer: RefCell::new(composer), + instruments: RefCell::new(instruments), + structure: RefCell::new(structure), + cancel_cb: RefCell::new(None), + saved_cb: RefCell::new(None), + }); + + // Connect signals and callbacks + + cancel_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(cb) = &*this.cancel_cb.borrow() { + cb(); + } + })); + + this.save_button.connect_clicked(clone!(@strong this => move |_| { + let mut section_count = 0; + let mut parts = Vec::new(); + let mut sections = Vec::new(); + + for (index, pos) in this.structure.borrow().iter().enumerate() { + match pos { + PartOrSection::Part(part) => parts.push(part.clone()), + PartOrSection::Section(section) => { + let mut section = section.clone(); + let index: i64 = index.try_into().unwrap(); + section.before_index = index - section_count; + sections.push(section); + section_count += 1; + } + } + } + + let work = WorkDescription { + id: this.id, + title: this.title_entry.get_text().to_string(), + composer: this.composer.borrow().clone().expect("Tried to create work without composer!"), + instruments: this.instruments.borrow().clone(), + parts: parts, + sections: sections, + }; + + let c = glib::MainContext::default(); + let clone = this.clone(); + c.spawn_local(async move { + clone.backend.update_work(work.clone().into()).await.unwrap(); + if let Some(cb) = &*clone.saved_cb.borrow() { + cb(work); + } + }); + })); + + composer_button.connect_clicked(clone!(@strong this => move |_| { + PersonSelector::new(this.backend.clone(), &this.parent, clone!(@strong this => move |person| { + this.show_composer(&person); + this.composer.replace(Some(person)); + })).show(); + })); + + this.instrument_list.set_make_widget(|instrument| { + let label = gtk::Label::new(Some(&instrument.name)); + label.set_ellipsize(pango::EllipsizeMode::End); + label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + label.upcast() + }); + + add_instrument_button.connect_clicked(clone!(@strong this => move |_| { + InstrumentSelector::new(this.backend.clone(), &this.parent, clone!(@strong this => move |instrument| { + let mut instruments = this.instruments.borrow_mut(); + + let index = match this.instrument_list.get_selected_index() { + Some(index) => index + 1, + None => instruments.len(), + }; + + instruments.insert(index, instrument); + this.instrument_list.show_items(instruments.clone()); + this.instrument_list.select_index(index); + })).show(); + })); + + remove_instrument_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(index) = this.instrument_list.get_selected_index() { + let mut instruments = this.instruments.borrow_mut(); + instruments.remove(index); + this.instrument_list.show_items(instruments.clone()); + this.instrument_list.select_index(index); + } + })); + + this.part_list.set_make_widget(|pos| { + let label = gtk::Label::new(None); + label.set_ellipsize(pango::EllipsizeMode::End); + label.set_halign(gtk::Align::Start); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + + match pos { + PartOrSection::Part(part) => { + label.set_text(&part.title); + label.set_margin_start(12); + } + PartOrSection::Section(section) => { + let attrs = pango::AttrList::new(); + attrs.insert(pango::Attribute::new_weight(pango::Weight::Bold).unwrap()); + label.set_attributes(Some(&attrs)); + label.set_text(§ion.title); + label.set_margin_start(6); + } + } + + label.upcast() + }); + + add_part_button.connect_clicked(clone!(@strong this => move |_| { + let editor = PartEditor::new(this.backend.clone(), &this.parent, None); + + editor.set_ready_cb(clone!(@strong this => move |part| { + let mut structure = this.structure.borrow_mut(); + + let index = match this.part_list.get_selected_index() { + Some(index) => index + 1, + None => structure.len(), + }; + + structure.insert(index, PartOrSection::Part(part)); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index); + })); + + editor.show(); + })); + + add_section_button.connect_clicked(clone!(@strong this => move |_| { + let editor = SectionEditor::new(&this.parent, None); + + editor.set_ready_cb(clone!(@strong this => move |section| { + let mut structure = this.structure.borrow_mut(); + + let index = match this.part_list.get_selected_index() { + Some(index) => index + 1, + None => structure.len(), + }; + + structure.insert(index, PartOrSection::Section(section)); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index); + })); + + editor.show(); + })); + + edit_part_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(index) = this.part_list.get_selected_index() { + match this.structure.borrow()[index].clone() { + PartOrSection::Part(part) => { + let editor = PartEditor::new( + this.backend.clone(), + &this.parent, + Some(part), + ); + + editor.set_ready_cb(clone!(@strong this => move |part| { + let mut structure = this.structure.borrow_mut(); + structure[index] = PartOrSection::Part(part); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index); + })); + + editor.show(); + } + PartOrSection::Section(section) => { + let editor = SectionEditor::new(&this.parent, Some(section)); + + editor.set_ready_cb(clone!(@strong this => move |section| { + let mut structure = this.structure.borrow_mut(); + structure[index] = PartOrSection::Section(section); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index); + })); + + editor.show(); + } + } + } + })); + + remove_part_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(index) = this.part_list.get_selected_index() { + let mut structure = this.structure.borrow_mut(); + structure.remove(index); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index); + } + })); + + move_part_up_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(index) = this.part_list.get_selected_index() { + if index > 0 { + let mut structure = this.structure.borrow_mut(); + structure.swap(index - 1, index); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index - 1); + } + } + })); + + move_part_down_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(index) = this.part_list.get_selected_index() { + let mut structure = this.structure.borrow_mut(); + if index < structure.len() - 1 { + structure.swap(index, index + 1); + this.part_list.show_items(structure.clone()); + this.part_list.select_index(index + 1); + } + } + })); + + // Initialization + + if let Some(composer) = &*this.composer.borrow() { + this.show_composer(composer); + } + + this.instrument_list.show_items(this.instruments.borrow().clone()); + this.part_list.show_items(this.structure.borrow().clone()); + + this + } + + /// The closure to call when the editor is canceled. + pub fn set_cancel_cb () + 'static>(&self, cb: F) { + self.cancel_cb.replace(Some(Box::new(cb))); + } + + /// The closure to call when a work was created. + pub fn set_saved_cb () + 'static>(&self, cb: F) { + self.saved_cb.replace(Some(Box::new(cb))); + } + + /// Update the UI according to person. + fn show_composer(&self, person: &Person) { + self.composer_label.set_text(&person.name_fl()); + self.save_button.set_sensitive(true); + } +} diff --git a/src/dialogs/work/work_editor_dialog.rs b/src/dialogs/work/work_editor_dialog.rs new file mode 100644 index 0000000..6092c82 --- /dev/null +++ b/src/dialogs/work/work_editor_dialog.rs @@ -0,0 +1,63 @@ +use super::work_editor::*; +use crate::backend::*; +use crate::database::*; +use glib::clone; +use gtk::prelude::*; +use std::cell::RefCell; +use std::rc::Rc; + +/// A dialog for creating or editing a work. +pub struct WorkEditorDialog { + pub window: libhandy::Window, + saved_cb: RefCell ()>>>, +} + +impl WorkEditorDialog { + /// Create a new work editor dialog and optionally initialize it. + pub fn new>( + backend: Rc, + parent: &W, + work: Option, + ) -> Rc { + // Create UI + + let window = libhandy::Window::new(); + window.set_type_hint(gdk::WindowTypeHint::Dialog); + window.set_modal(true); + window.set_transient_for(Some(parent)); + + let editor = WorkEditor::new(backend.clone(), &window, work); + window.add(&editor.widget); + window.show_all(); + + let this = Rc::new(Self { + window, + saved_cb: RefCell::new(None), + }); + + // Connect signals and callbacks + + editor.set_cancel_cb(clone!(@strong this => move || { + this.window.close(); + })); + + editor.set_saved_cb(clone!(@strong this => move |work| { + if let Some(cb) = &*this.saved_cb.borrow() { + cb(work); + this.window.close(); + } + })); + + this + } + + /// Set the closure to be called when the user edited or created a work. + pub fn set_saved_cb () + 'static>(&self, cb: F) { + self.saved_cb.replace(Some(Box::new(cb))); + } + + /// Show the work editor dialog. + pub fn show(&self) { + self.window.show(); + } +} diff --git a/src/dialogs/work/work_selector.rs b/src/dialogs/work/work_selector.rs new file mode 100644 index 0000000..a727860 --- /dev/null +++ b/src/dialogs/work/work_selector.rs @@ -0,0 +1,89 @@ +use super::work_selector_person_screen::*; +use crate::backend::Backend; +use crate::database::*; +use crate::widgets::*; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use libhandy::prelude::*; +use std::cell::RefCell; +use std::rc::Rc; + +/// A widget for selecting a work from a list of existing ones. +pub struct WorkSelector { + pub widget: libhandy::Leaflet, + backend: Rc, + sidebar_box: gtk::Box, + selected_cb: RefCell ()>>>, + add_cb: RefCell ()>>>, + navigator: Rc, +} + +impl WorkSelector { + /// Create a new work selector. + pub fn new(backend: Rc) -> Rc { + // Create UI + + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_selector.ui"); + + get_widget!(builder, libhandy::Leaflet, widget); + get_widget!(builder, gtk::Button, add_button); + get_widget!(builder, gtk::Box, sidebar_box); + get_widget!(builder, gtk::Box, empty_screen); + + let person_list = PersonList::new(backend.clone()); + sidebar_box.pack_start(&person_list.widget, true, true, 0); + + let navigator = Navigator::new(&empty_screen); + widget.add(&navigator.widget); + + let this = Rc::new(Self { + widget, + backend, + sidebar_box, + selected_cb: RefCell::new(None), + add_cb: RefCell::new(None), + navigator, + }); + + // Connect signals and callbacks + + add_button.connect_clicked(clone!(@strong this => move |_| { + if let Some(cb) = &*this.add_cb.borrow() { + cb(); + } + })); + + person_list.set_selected(clone!(@strong this => move |person| { + let person_screen = WorkSelectorPersonScreen::new( + this.backend.clone(), + person.clone(), + ); + + person_screen.set_selected_cb(clone!(@strong this => move |work| { + if let Some(cb) = &*this.selected_cb.borrow() { + cb(work); + } + })); + + this.navigator.clone().push(person_screen); + this.widget.set_visible_child(&this.navigator.widget); + })); + + this.navigator.set_back_cb(clone!(@strong this => move || { + this.widget.set_visible_child(&this.sidebar_box); + })); + + this + } + + /// Set the closure to be called if the user wants to add a new work. + pub fn set_add_cb () + 'static>(&self, cb: F) { + self.add_cb.replace(Some(Box::new(cb))); + } + + /// Set the closure to be called when the user has selected a work. + pub fn set_selected_cb () + 'static>(&self, cb: F) { + self.selected_cb.replace(Some(Box::new(cb))); + } +} diff --git a/src/dialogs/work/work_selector_person_screen.rs b/src/dialogs/work/work_selector_person_screen.rs new file mode 100644 index 0000000..049b061 --- /dev/null +++ b/src/dialogs/work/work_selector_person_screen.rs @@ -0,0 +1,114 @@ +use crate::backend::*; +use crate::database::*; +use crate::widgets::*; +use gettextrs::gettext; +use glib::clone; +use gtk::prelude::*; +use gtk_macros::get_widget; +use libhandy::HeaderBarExt; +use std::cell::RefCell; +use std::rc::Rc; + +/// A screen within the work selector that presents a list of works by a person. +pub struct WorkSelectorPersonScreen { + backend: Rc, + widget: gtk::Box, + stack: gtk::Stack, + work_list: Rc>, + selected_cb: RefCell ()>>>, + navigator: RefCell>>, +} + +impl WorkSelectorPersonScreen { + /// Create a new work selector person screen. + pub fn new(backend: Rc, person: Person) -> Rc { + // Create UI + + let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_selector_screen.ui"); + + get_widget!(builder, gtk::Box, widget); + get_widget!(builder, libhandy::HeaderBar, header); + get_widget!(builder, gtk::Button, back_button); + get_widget!(builder, gtk::Stack, stack); + + header.set_title(Some(&person.name_fl())); + + let work_list = List::new(&gettext("No works found.")); + stack.add_named(&work_list.widget, "content"); + + let this = Rc::new(Self { + backend, + widget, + stack, + work_list, + selected_cb: RefCell::new(None), + navigator: RefCell::new(None), + }); + + // Connect signals and callbacks + + back_button.connect_clicked(clone!(@strong this => move |_| { + let navigator = this.navigator.borrow().clone(); + if let Some(navigator) = navigator { + navigator.pop(); + } + })); + + this.work_list.set_make_widget(|work: &WorkDescription| { + let label = gtk::Label::new(Some(&work.title)); + label.set_ellipsize(pango::EllipsizeMode::End); + label.set_halign(gtk::Align::Start); + label.set_margin_start(6); + label.set_margin_end(6); + label.set_margin_top(6); + label.set_margin_bottom(6); + label.upcast() + }); + + this.work_list + .set_selected(clone!(@strong this => move |work| { + let navigator = this.navigator.borrow().clone(); + if let Some(navigator) = navigator { + if let Some(cb) = &*this.selected_cb.borrow() { + cb(work.clone()); + } + } + })); + + // Initialize + + let context = glib::MainContext::default(); + let clone = this.clone(); + context.spawn_local(async move { + let works = clone + .backend + .get_work_descriptions(person.id) + .await + .unwrap(); + + clone.work_list.show_items(works); + clone.stack.set_visible_child_name("content"); + }); + + this + } + + /// Sets a closure to be called when the user has selected a work. + pub fn set_selected_cb () + 'static>(&self, cb: F) { + self.selected_cb.replace(Some(Box::new(cb))); + } +} + +impl NavigatorScreen for WorkSelectorPersonScreen { + fn attach_navigator(&self, navigator: Rc) { + self.navigator.replace(Some(navigator)); + } + + fn get_widget(&self) -> gtk::Widget { + self.widget.clone().upcast() + } + + fn detach_navigator(&self) { + self.navigator.replace(None); + } +} diff --git a/src/dialogs/work_editor.rs b/src/dialogs/work_editor.rs deleted file mode 100644 index 3deaf02..0000000 --- a/src/dialogs/work_editor.rs +++ /dev/null @@ -1,401 +0,0 @@ -use super::{InstrumentSelector, PersonSelector, PartEditor, SectionEditor}; -use crate::backend::*; -use crate::database::*; -use crate::widgets::*; -use glib::clone; -use gtk::prelude::*; -use gtk_macros::get_widget; -use std::cell::RefCell; -use std::convert::TryInto; -use std::rc::Rc; - -struct PartOrSection { - part: Option, - section: Option, -} - -impl PartOrSection { - pub fn part(part: WorkPartDescription) -> Self { - PartOrSection { - part: Some(part), - section: None, - } - } - - pub fn section(section: WorkSectionDescription) -> Self { - PartOrSection { - part: None, - section: Some(section), - } - } - - pub fn is_part(&self) -> bool { - self.part.is_some() - } - - pub fn unwrap_part(&self) -> WorkPartDescription { - self.part.as_ref().unwrap().clone() - } - - pub fn unwrap_section(&self) -> WorkSectionDescription { - self.section.as_ref().unwrap().clone() - } - - pub fn get_title(&self) -> String { - if self.is_part() { - self.unwrap_part().title - } else { - self.unwrap_section().title - } - } -} - -pub struct WorkEditor -where - F: Fn(WorkDescription) -> () + 'static, { - backend: Rc, - window: libhandy::Window, - callback: F, - save_button: gtk::Button, - id: i64, - title_entry: gtk::Entry, - composer: RefCell>, - composer_label: gtk::Label, - instruments: RefCell>, - instrument_list: gtk::ListBox, - structure: RefCell>, - part_list: gtk::ListBox, -} - -impl WorkEditor -where - F: Fn(WorkDescription) -> () + 'static, { - pub fn new>( - backend: Rc, - parent: &P, - work: Option, - callback: F, - ) -> Rc { - let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_editor.ui"); - - get_widget!(builder, libhandy::Window, window); - get_widget!(builder, gtk::Button, cancel_button); - get_widget!(builder, gtk::Button, save_button); - get_widget!(builder, gtk::Entry, title_entry); - get_widget!(builder, gtk::Button, composer_button); - get_widget!(builder, gtk::Label, composer_label); - get_widget!(builder, gtk::ListBox, instrument_list); - get_widget!(builder, gtk::Button, add_instrument_button); - get_widget!(builder, gtk::Button, remove_instrument_button); - get_widget!(builder, gtk::ListBox, part_list); - get_widget!(builder, gtk::Button, add_part_button); - get_widget!(builder, gtk::Button, remove_part_button); - get_widget!(builder, gtk::Button, add_section_button); - get_widget!(builder, gtk::Button, edit_part_button); - get_widget!(builder, gtk::Button, move_part_up_button); - get_widget!(builder, gtk::Button, move_part_down_button); - - let id = match work.clone() { - Some(work) => { - title_entry.set_text(&work.title); - work.id - } - None => rand::random::().into(), - }; - - let composer = RefCell::new(match work.clone() { - Some(work) => { - composer_label.set_text(&work.composer.name_fl()); - save_button.set_sensitive(true); - Some(work.composer) - } - None => None, - }); - - let instruments = RefCell::new(match work.clone() { - Some(work) => work.instruments, - None => Vec::new(), - }); - - let structure = RefCell::new(match work.clone() { - Some(work) => { - let mut result = Vec::new(); - - for part in work.parts { - result.push(PartOrSection::part(part)); - } - - for section in work.sections { - result.insert( - section - .before_index - .try_into() - .expect("Section with unrealistic before_index!"), - PartOrSection::section(section), - ); - } - - result - } - None => Vec::new(), - }); - - let result = Rc::new(WorkEditor { - backend: backend, - window: window, - callback: callback, - save_button: save_button, - id: id, - title_entry: title_entry, - composer: composer, - composer_label: composer_label, - instruments: instruments, - instrument_list: instrument_list, - structure: structure, - part_list: part_list, - }); - - cancel_button.connect_clicked(clone!(@strong result => move |_| { - result.window.close(); - })); - - result.save_button.connect_clicked(clone!(@strong result => move |_| { - let mut section_count: i64 = 0; - let mut parts: Vec = Vec::new(); - let mut sections: Vec = Vec::new(); - - for (index, pos) in result.structure.borrow().iter().enumerate() { - if pos.is_part() { - parts.push(pos.unwrap_part()); - } else { - let mut section = pos.unwrap_section(); - let index: i64 = index.try_into().unwrap(); - section.before_index = index - section_count; - sections.push(section); - section_count += 1; - } - } - - let work = WorkDescription { - id: result.id, - title: result.title_entry.get_text().to_string(), - composer: result.composer.borrow().clone().expect("Tried to create work without composer!"), - instruments: result.instruments.borrow().to_vec(), - parts: parts, - sections: sections, - }; - - let c = glib::MainContext::default(); - let clone = result.clone(); - c.spawn_local(async move { - clone.backend.update_work(work.clone().into()).await.unwrap(); - clone.window.close(); - (clone.callback)(work.clone()); - }); - })); - - composer_button.connect_clicked(clone!(@strong result => move |_| { - PersonSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |person| { - result.composer.replace(Some(person.clone())); - result.composer_label.set_text(&person.name_fl()); - result.save_button.set_sensitive(true); - })).show(); - })); - - add_instrument_button.connect_clicked(clone!(@strong result => move |_| { - InstrumentSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |instrument| { - { - let mut instruments = result.instruments.borrow_mut(); - instruments.push(instrument); - } - - result.show_instruments(); - })).show(); - })); - - remove_instrument_button.connect_clicked(clone!(@strong result => move |_| { - let row = result.get_selected_instrument_row(); - match row { - Some(row) => { - let index = row.get_index(); - let index: usize = index.try_into().unwrap(); - result.instruments.borrow_mut().remove(index); - result.show_instruments(); - } - None => (), - } - })); - - add_part_button.connect_clicked(clone!(@strong result => move |_| { - PartEditor::new(result.backend.clone(), &result.window, None, clone!(@strong result => move |part| { - { - let mut structure = result.structure.borrow_mut(); - structure.push(PartOrSection::part(part)); - } - - result.show_parts(); - })).show(); - })); - - add_section_button.connect_clicked(clone!(@strong result => move |_| { - SectionEditor::new(&result.window, None, clone!(@strong result => move |section| { - { - let mut structure = result.structure.borrow_mut(); - structure.push(PartOrSection::section(section)); - } - - result.show_parts(); - })).show(); - })); - - edit_part_button.connect_clicked(clone!(@strong result => move |_| { - let row = result.get_selected_part_row(); - match row { - Some(row) => { - let index = row.get_index(); - let index: usize = index.try_into().unwrap(); - let pos = &result.structure.borrow()[index]; - - if pos.is_part() { - let editor = - PartEditor::new(result.backend.clone(), &result.window, Some(pos.unwrap_part()), clone!(@strong result => move |part| { - result.structure.borrow_mut()[index] = PartOrSection::part(part); - result.show_parts(); - })); - - editor.show(); - } else { - let editor = - SectionEditor::new(&result.window, Some(pos.unwrap_section()), clone!(@strong result => move |section| { - result.structure.borrow_mut()[index] = PartOrSection::section(section); - result.show_parts(); - })); - - editor.show(); - } - } - None => (), - } - })); - - remove_part_button.connect_clicked(clone!(@strong result => move |_| { - let row = result.get_selected_part_row(); - match row { - Some(row) => { - let index = row.get_index(); - let index: usize = index.try_into().unwrap(); - result.structure.borrow_mut().remove(index); - result.show_parts(); - } - None => (), - } - })); - - move_part_up_button.connect_clicked(clone!(@strong result => move |_| { - let row = result.get_selected_part_row(); - match row { - Some(row) => { - let index = row.get_index(); - if index > 0 { - let index: usize = index.try_into().unwrap(); - result.structure.borrow_mut().swap(index - 1, index); - result.show_parts(); - } - } - None => (), - } - })); - - move_part_down_button.connect_clicked(clone!(@strong result => move |_| { - let row = result.get_selected_part_row(); - match row { - Some(row) => { - let index = row.get_index(); - let index: usize = index.try_into().unwrap(); - if index < result.structure.borrow().len() - 1 { - result.structure.borrow_mut().swap(index, index + 1); - result.show_parts(); - } - } - None => (), - } - })); - - result.window.set_transient_for(Some(parent)); - - result.show_instruments(); - result.show_parts(); - - result - } - - pub fn show(&self) { - self.window.show(); - } - - fn show_instruments(&self) { - for child in self.instrument_list.get_children() { - self.instrument_list.remove(&child); - } - - for (index, instrument) in self.instruments.borrow().iter().enumerate() { - let label = gtk::Label::new(Some(&instrument.name)); - label.set_halign(gtk::Align::Start); - label.set_margin_start(6); - label.set_margin_end(6); - label.set_margin_top(6); - label.set_margin_bottom(6); - - let row = SelectorRow::new(index.try_into().unwrap(), &label); - row.show_all(); - self.instrument_list.insert(&row, -1); - } - } - - fn get_selected_instrument_row(&self) -> Option { - match self.instrument_list.get_selected_rows().first() { - Some(row) => match row.get_child() { - Some(child) => Some(child.downcast().unwrap()), - None => None, - }, - None => None, - } - } - - fn show_parts(&self) { - for child in self.part_list.get_children() { - self.part_list.remove(&child); - } - - for (index, part) in self.structure.borrow().iter().enumerate() { - let label = gtk::Label::new(Some(&part.get_title())); - label.set_halign(gtk::Align::Start); - label.set_margin_start(6); - label.set_margin_end(6); - label.set_margin_top(6); - label.set_margin_bottom(6); - - if part.is_part() { - label.set_margin_start(6); - } else { - let attributes = pango::AttrList::new(); - attributes.insert(pango::Attribute::new_weight(pango::Weight::Bold).unwrap()); - label.set_attributes(Some(&attributes)); - } - - let row = SelectorRow::new(index.try_into().unwrap(), &label); - row.show_all(); - self.part_list.insert(&row, -1); - } - } - - fn get_selected_part_row(&self) -> Option { - match self.part_list.get_selected_rows().first() { - Some(row) => match row.get_child() { - Some(child) => Some(child.downcast().unwrap()), - None => None, - }, - None => None, - } - } -} diff --git a/src/dialogs/work_selector.rs b/src/dialogs/work_selector.rs deleted file mode 100644 index 3a0419f..0000000 --- a/src/dialogs/work_selector.rs +++ /dev/null @@ -1,267 +0,0 @@ -use super::*; -use crate::backend::Backend; -use crate::database::*; -use crate::widgets::*; -use gio::prelude::*; -use glib::clone; -use gtk::prelude::*; -use gtk_macros::get_widget; -use libhandy::prelude::*; -use libhandy::HeaderBarExt; -use std::cell::Cell; -use std::convert::TryInto; -use std::rc::Rc; - -enum WorkSelectorState { - Loading, - Persons(Vec), - PersonLoading(Person), - Person(Vec), -} - -pub struct WorkSelector -where - F: Fn(WorkDescription) -> () + 'static, -{ - window: libhandy::Window, - backend: Rc, - callback: F, - leaflet: libhandy::Leaflet, - sidebar_stack: gtk::Stack, - person_search_entry: gtk::SearchEntry, - person_list: gtk::ListBox, - stack: gtk::Stack, - header: libhandy::HeaderBar, - search_entry: gtk::SearchEntry, - content_stack: gtk::Stack, - work_list: gtk::ListBox, - person_list_row_activated_handler_id: Cell>, - work_list_row_activated_handler_id: Cell>, -} - -impl WorkSelector -where - F: Fn(WorkDescription) -> () + 'static, -{ - pub fn new>(backend: Rc, parent: &P, callback: F) -> Rc { - use WorkSelectorState::*; - - let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_selector.ui"); - - get_widget!(builder, libhandy::Window, window); - get_widget!(builder, libhandy::Leaflet, leaflet); - get_widget!(builder, gtk::Button, add_button); - get_widget!(builder, gtk::SearchEntry, person_search_entry); - get_widget!(builder, gtk::Stack, sidebar_stack); - get_widget!(builder, gtk::ListBox, person_list); - get_widget!(builder, gtk::Stack, stack); - get_widget!(builder, libhandy::HeaderBar, header); - get_widget!(builder, gtk::SearchEntry, search_entry); - get_widget!(builder, gtk::Button, back_button); - get_widget!(builder, gtk::Stack, content_stack); - get_widget!(builder, gtk::ListBox, work_list); - - let result = Rc::new(WorkSelector { - window: window, - backend: backend, - callback: callback, - leaflet: leaflet, - sidebar_stack: sidebar_stack, - person_list: person_list, - person_search_entry: person_search_entry, - stack: stack, - header: header, - search_entry: search_entry, - content_stack: content_stack, - work_list: work_list, - person_list_row_activated_handler_id: Cell::new(None), - work_list_row_activated_handler_id: Cell::new(None), - }); - - add_button.connect_clicked(clone!(@strong result => move |_| { - let editor = WorkEditor::new( - result.backend.clone(), - &result.window, - None, - clone!(@strong result => move |work| { - result.window.close(); - (result.callback)(work); - }), - ); - - editor.show(); - })); - - back_button.connect_clicked(clone!(@strong result => move |_| { - result.back(); - })); - - result - .person_search_entry - .connect_search_changed(clone!(@strong result => move |_| { - result.person_list.invalidate_filter(); - })); - - result - .search_entry - .connect_search_changed(clone!(@strong result => move |_| { - result.work_list.invalidate_filter(); - })); - - result.window.set_transient_for(Some(parent)); - result.clone().set_state(Loading); - - result - } - - pub fn show(&self) { - self.window.show(); - } - - fn set_state(self: Rc, state: WorkSelectorState) { - use WorkSelectorState::*; - - match state { - Loading => { - let c = glib::MainContext::default(); - let clone = self.clone(); - c.spawn_local(async move { - let persons = clone.backend.get_persons().await.unwrap(); - clone.clone().set_state(Persons(persons)); - }); - - self.sidebar_stack.set_visible_child_name("loading"); - self.stack.set_visible_child_name("empty_screen"); - self.leaflet.set_visible_child_name("sidebar"); - } - Persons(persons) => { - for child in self.person_list.get_children() { - self.person_list.remove(&child); - } - - for (index, person) in persons.iter().enumerate() { - let label = gtk::Label::new(Some(&person.name_lf())); - label.set_halign(gtk::Align::Start); - label.set_margin_start(6); - label.set_margin_end(6); - label.set_margin_top(6); - label.set_margin_bottom(6); - - let row = SelectorRow::new(index.try_into().unwrap(), &label); - row.show_all(); - self.person_list.insert(&row, -1); - } - - match self.person_list_row_activated_handler_id.take() { - Some(id) => self.person_list.disconnect(id), - None => (), - } - - let handler_id = self.person_list.connect_row_activated( - clone!(@strong self as self_, @strong persons => move |_, row| { - let row = row.get_child().unwrap().downcast::().unwrap(); - let index: usize = row.get_index().try_into().unwrap(); - let person = persons[index].clone(); - self_.clone().set_state(PersonLoading(person)); - }), - ); - - self.person_list_row_activated_handler_id - .set(Some(handler_id)); - - self.person_list.set_filter_func(Some(Box::new( - clone!(@strong self as self_, @strong persons => move |row| { - let row = row.get_child().unwrap().downcast::().unwrap(); - let index: usize = row.get_index().try_into().unwrap(); - let search = self_.person_search_entry.get_text().to_string().to_lowercase(); - - search.is_empty() || persons[index] - .name_lf() - .to_lowercase() - .contains(&search) - }), - ))); - - self.sidebar_stack.set_visible_child_name("persons_list"); - self.stack.set_visible_child_name("empty_screen"); - self.leaflet.set_visible_child_name("sidebar"); - } - PersonLoading(person) => { - self.header.set_title(Some(&person.name_fl())); - - let c = glib::MainContext::default(); - let clone = self.clone(); - c.spawn_local(async move { - let works = clone - .backend - .get_work_descriptions(person.id) - .await - .unwrap(); - clone.clone().set_state(Person(works)); - }); - - self.content_stack.set_visible_child_name("loading"); - self.stack.set_visible_child_name("person_screen"); - self.leaflet.set_visible_child_name("content"); - } - Person(works) => { - for child in self.work_list.get_children() { - self.work_list.remove(&child); - } - - for (index, work) in works.iter().enumerate() { - let label = gtk::Label::new(Some(&work.title)); - label.set_halign(gtk::Align::Start); - label.set_margin_start(6); - label.set_margin_end(6); - label.set_margin_top(6); - label.set_margin_bottom(6); - - let row = SelectorRow::new(index.try_into().unwrap(), &label); - row.show_all(); - self.work_list.insert(&row, -1); - } - - match self.work_list_row_activated_handler_id.take() { - Some(id) => self.work_list.disconnect(id), - None => (), - } - - let handler_id = self.work_list.connect_row_activated( - clone!(@strong self as self_, @strong works => move |_, row| { - let row = row.get_child().unwrap().downcast::().unwrap(); - let index: usize = row.get_index().try_into().unwrap(); - let work = works[index].clone(); - (self_.callback)(work); - self_.window.close(); - }), - ); - - self.work_list_row_activated_handler_id - .set(Some(handler_id)); - - self.work_list.set_filter_func(Some(Box::new( - clone!(@strong self as self_, @strong works => move |row| { - let row = row.get_child().unwrap().downcast::().unwrap(); - let index: usize = row.get_index().try_into().unwrap(); - let search = self_.search_entry.get_text().to_string().to_lowercase(); - - search.is_empty() || works[index] - .title - .to_lowercase() - .contains(&search) - }), - ))); - - self.content_stack.set_visible_child_name("content"); - self.stack.set_visible_child_name("person_screen"); - self.leaflet.set_visible_child_name("content"); - } - } - } - - fn back(&self) { - self.stack.set_visible_child_name("empty_screen"); - self.leaflet.set_visible_child_name("sidebar"); - } -} diff --git a/src/meson.build b/src/meson.build index e14b79b..bf0a04d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -44,7 +44,6 @@ sources = files( 'dialogs/instrument_editor.rs', 'dialogs/instrument_selector.rs', 'dialogs/mod.rs', - 'dialogs/part_editor.rs', 'dialogs/person_editor.rs', 'dialogs/person_selector.rs', 'dialogs/preferences.rs', @@ -56,11 +55,16 @@ sources = files( 'dialogs/recording/recording_selector_person_screen.rs', 'dialogs/recording/recording_selector.rs', 'dialogs/recording/recording_selector_work_screen.rs', - 'dialogs/section_editor.rs', 'dialogs/track_editor.rs', 'dialogs/tracks_editor.rs', - 'dialogs/work_editor.rs', - 'dialogs/work_selector.rs', + 'dialogs/work/mod.rs', + 'dialogs/work/part_editor.rs', + 'dialogs/work/section_editor.rs', + 'dialogs/work/work_dialog.rs', + 'dialogs/work/work_editor_dialog.rs', + 'dialogs/work/work_editor.rs', + 'dialogs/work/work_selector_person_screen.rs', + 'dialogs/work/work_selector.rs', 'screens/ensemble_screen.rs', 'screens/mod.rs', 'screens/person_screen.rs', diff --git a/src/window.rs b/src/window.rs index 6777f2e..0300994 100644 --- a/src/window.rs +++ b/src/window.rs @@ -146,9 +146,13 @@ impl Window { result.window, "add-work", clone!(@strong result => move |_, _| { - WorkEditor::new(result.backend.clone(), &result.window, None, clone!(@strong result => move |_| { + let dialog = WorkDialog::new(result.backend.clone(), &result.window); + + dialog.set_selected_cb(clone!(@strong result => move |_| { result.reload(); - })).show(); + })); + + dialog.show(); }) ); @@ -264,9 +268,13 @@ impl Window { let c = glib::MainContext::default(); c.spawn_local(async move { let work = result.backend.get_work_description(id).await.unwrap(); - WorkEditor::new(result.backend.clone(), &result.window, Some(work), clone!(@strong result => move |_| { + let dialog = WorkEditorDialog::new(result.backend.clone(), &result.window, Some(work)); + + dialog.set_saved_cb(clone!(@strong result => move |_| { result.reload(); - })).show(); + })); + + dialog.show(); }); }) );