mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Merge recording editor and selector into one dialog
This commit is contained in:
parent
3e34658b25
commit
99bbd9e58f
22 changed files with 1167 additions and 1032 deletions
|
|
@ -10,6 +10,7 @@ diesel_migrations = "1.4.0"
|
|||
fragile = "1.0.0"
|
||||
futures = "0.3.6"
|
||||
futures-channel = "0.3.5"
|
||||
gdk = "0.13.2"
|
||||
gettext-rs = "0.5.0"
|
||||
gio = "0.9.1"
|
||||
glib = "0.10.2"
|
||||
|
|
|
|||
|
|
@ -37,12 +37,17 @@ src/dialogs/instrument_editor.rs
|
|||
src/dialogs/instrument_selector.rs
|
||||
src/dialogs/mod.rs
|
||||
src/dialogs/part_editor.rs
|
||||
src/dialogs/performance_editor.rs
|
||||
src/dialogs/person_editor.rs
|
||||
src/dialogs/person_selector.rs
|
||||
src/dialogs/preferences.rs
|
||||
src/dialogs/recording_editor.rs
|
||||
src/dialogs/recording_selector.rs
|
||||
src/dialogs/recording/mod.rs
|
||||
src/dialogs/recording/performance_editor.rs
|
||||
src/dialogs/recording/recording_dialog.rs
|
||||
src/dialogs/recording/recording_editor_dialog.rs
|
||||
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
|
||||
|
|
|
|||
58
po/de.po
58
po/de.po
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-11-08 02:37+0100\n"
|
||||
"POT-Creation-Date: 2020-11-08 12:11+0100\n"
|
||||
"PO-Revision-Date: 2020-11-08 02:37+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
|
|
@ -18,14 +18,14 @@ msgstr ""
|
|||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:197
|
||||
#: res/ui/performance_editor.ui:228
|
||||
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:196
|
||||
#: res/ui/performance_editor.ui:227
|
||||
msgid "Ensemble"
|
||||
msgstr "Ensemble"
|
||||
|
||||
#: res/ui/ensemble_editor.ui:23 res/ui/instrument_editor.ui:23
|
||||
#: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23
|
||||
#: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:25
|
||||
#: 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
|
||||
msgid "Cancel"
|
||||
|
|
@ -33,7 +33,7 @@ msgstr "Abbrechen"
|
|||
|
||||
#: res/ui/ensemble_editor.ui:31 res/ui/instrument_editor.ui:31
|
||||
#: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31
|
||||
#: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:33
|
||||
#: 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
|
||||
msgid "Save"
|
||||
|
|
@ -53,8 +53,9 @@ msgid "Recordings"
|
|||
msgstr "Aufnahmen"
|
||||
|
||||
#: res/ui/ensemble_screen.ui:188 res/ui/work_screen.ui:188
|
||||
#: src/dialogs/recording_selector.rs:205 src/screens/ensemble_screen.rs:53
|
||||
#: src/screens/person_screen.rs:77 src/screens/work_screen.rs:54
|
||||
#: src/dialogs/recording/recording_selector_work_screen.rs:38
|
||||
#: src/screens/ensemble_screen.rs:53 src/screens/person_screen.rs:77
|
||||
#: src/screens/work_screen.rs:54
|
||||
msgid "No recordings found."
|
||||
msgstr "Keine Aufnahmen gefunden."
|
||||
|
||||
|
|
@ -93,15 +94,16 @@ msgid "Title"
|
|||
msgstr "Titel"
|
||||
|
||||
#: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87
|
||||
#: res/ui/performance_editor.ui:171 res/ui/performance_editor.ui:215
|
||||
#: res/ui/recording_editor.ui:89 res/ui/tracks_editor.ui:93
|
||||
#: 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/performance_editor.rs:112 src/dialogs/performance_editor.rs:122
|
||||
#: src/dialogs/performance_editor.rs:136
|
||||
#: src/dialogs/recording/performance_editor.rs:150
|
||||
#: src/dialogs/recording/performance_editor.rs:160
|
||||
#: src/dialogs/recording/performance_editor.rs:170
|
||||
msgid "Select …"
|
||||
msgstr "Auswählen …"
|
||||
|
||||
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:126
|
||||
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:119
|
||||
#: res/ui/work_editor.ui:126
|
||||
msgid "Overview"
|
||||
msgstr "Überblick"
|
||||
|
|
@ -122,11 +124,11 @@ msgstr "Auftritt"
|
|||
msgid "Role"
|
||||
msgstr "Rolle"
|
||||
|
||||
#: res/ui/performance_editor.ui:129
|
||||
#: res/ui/performance_editor.ui:128
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
#: res/ui/performance_editor.ui:152 res/ui/performance_editor.ui:184
|
||||
#: res/ui/performance_editor.ui:151 res/ui/performance_editor.ui:183
|
||||
#: res/ui/person_editor.ui:20
|
||||
msgid "Person"
|
||||
msgstr "Person"
|
||||
|
|
@ -200,24 +202,20 @@ msgstr "Keiner ausgewählt"
|
|||
msgid "Select"
|
||||
msgstr "Auswählen"
|
||||
|
||||
#: res/ui/recording_editor.ui:22 res/ui/tracks_editor.ui:68
|
||||
#: res/ui/recording_editor.ui:14 res/ui/tracks_editor.ui:68
|
||||
msgid "Recording"
|
||||
msgstr "Aufnahme"
|
||||
|
||||
#: res/ui/recording_editor.ui:71
|
||||
#: res/ui/recording_editor.ui:63
|
||||
msgid "Comment"
|
||||
msgstr "Kommentar"
|
||||
|
||||
#: res/ui/recording_editor.ui:113 res/ui/tracks_editor.ui:109
|
||||
#: res/ui/recording_editor.ui:106 res/ui/tracks_editor.ui:109
|
||||
#: res/ui/work_editor.ui:22
|
||||
msgid "Work"
|
||||
msgstr "Werk"
|
||||
|
||||
#: res/ui/recording_editor.ui:157
|
||||
msgid "No performers added."
|
||||
msgstr "Keine Interpreten hinzugefügt."
|
||||
|
||||
#: res/ui/recording_editor.ui:251 res/ui/tracks_editor.ui:127
|
||||
#: res/ui/recording_editor.ui:226 res/ui/tracks_editor.ui:127
|
||||
msgid "Performers"
|
||||
msgstr "Interpreten"
|
||||
|
||||
|
|
@ -233,12 +231,7 @@ msgstr "Zur Wiedergabeliste hinzufügen"
|
|||
msgid "Select a composer on the left."
|
||||
msgstr "Wählen Sie einen Komponisten aus."
|
||||
|
||||
#: res/ui/recording_selector.ui:71 res/ui/recording_selector.ui:186
|
||||
#: src/dialogs/recording_selector.rs:109 src/screens/person_screen.rs:57
|
||||
msgid "No works found."
|
||||
msgstr "Keine Werke gefunden."
|
||||
|
||||
#: res/ui/recording_selector.ui:108
|
||||
#: res/ui/recording_selector.ui:51
|
||||
msgid "Select a recording"
|
||||
msgstr "Aufnahme auswählen"
|
||||
|
||||
|
|
@ -323,6 +316,15 @@ msgstr "Weitere Informationen und Quellcode"
|
|||
msgid "Select music library folder"
|
||||
msgstr "Ordner der Musikbibliothek auswählen"
|
||||
|
||||
#: src/dialogs/recording/recording_editor.rs:54
|
||||
msgid "No performers added."
|
||||
msgstr "Keine Interpreten hinzugefügt."
|
||||
|
||||
#: src/dialogs/recording/recording_selector_person_screen.rs:39
|
||||
#: src/screens/person_screen.rs:57
|
||||
msgid "No works found."
|
||||
msgstr "Keine Werke gefunden."
|
||||
|
||||
#: src/dialogs/tracks_editor.rs:60
|
||||
msgid "Add some tracks."
|
||||
msgstr "Fügen Sie Tracks hinzu."
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: musicus\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-11-08 02:37+0100\n"
|
||||
"POT-Creation-Date: 2020-11-08 12:11+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -17,14 +17,14 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:197
|
||||
#: res/ui/performance_editor.ui:228
|
||||
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:196
|
||||
#: res/ui/performance_editor.ui:227
|
||||
msgid "Ensemble"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/ensemble_editor.ui:23 res/ui/instrument_editor.ui:23
|
||||
#: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23
|
||||
#: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:25
|
||||
#: 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
|
||||
msgid "Cancel"
|
||||
|
|
@ -32,7 +32,7 @@ msgstr ""
|
|||
|
||||
#: res/ui/ensemble_editor.ui:31 res/ui/instrument_editor.ui:31
|
||||
#: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31
|
||||
#: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:33
|
||||
#: 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
|
||||
msgid "Save"
|
||||
|
|
@ -52,8 +52,9 @@ msgid "Recordings"
|
|||
msgstr ""
|
||||
|
||||
#: res/ui/ensemble_screen.ui:188 res/ui/work_screen.ui:188
|
||||
#: src/dialogs/recording_selector.rs:205 src/screens/ensemble_screen.rs:53
|
||||
#: src/screens/person_screen.rs:77 src/screens/work_screen.rs:54
|
||||
#: src/dialogs/recording/recording_selector_work_screen.rs:38
|
||||
#: src/screens/ensemble_screen.rs:53 src/screens/person_screen.rs:77
|
||||
#: src/screens/work_screen.rs:54
|
||||
msgid "No recordings found."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -92,15 +93,16 @@ msgid "Title"
|
|||
msgstr ""
|
||||
|
||||
#: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87
|
||||
#: res/ui/performance_editor.ui:171 res/ui/performance_editor.ui:215
|
||||
#: res/ui/recording_editor.ui:89 res/ui/tracks_editor.ui:93
|
||||
#: 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/performance_editor.rs:112 src/dialogs/performance_editor.rs:122
|
||||
#: src/dialogs/performance_editor.rs:136
|
||||
#: src/dialogs/recording/performance_editor.rs:150
|
||||
#: src/dialogs/recording/performance_editor.rs:160
|
||||
#: src/dialogs/recording/performance_editor.rs:170
|
||||
msgid "Select …"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:126
|
||||
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:119
|
||||
#: res/ui/work_editor.ui:126
|
||||
msgid "Overview"
|
||||
msgstr ""
|
||||
|
|
@ -121,11 +123,11 @@ msgstr ""
|
|||
msgid "Role"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/performance_editor.ui:129
|
||||
#: res/ui/performance_editor.ui:128
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/performance_editor.ui:152 res/ui/performance_editor.ui:184
|
||||
#: res/ui/performance_editor.ui:151 res/ui/performance_editor.ui:183
|
||||
#: res/ui/person_editor.ui:20
|
||||
msgid "Person"
|
||||
msgstr ""
|
||||
|
|
@ -199,24 +201,20 @@ msgstr ""
|
|||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_editor.ui:22 res/ui/tracks_editor.ui:68
|
||||
#: res/ui/recording_editor.ui:14 res/ui/tracks_editor.ui:68
|
||||
msgid "Recording"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_editor.ui:71
|
||||
#: res/ui/recording_editor.ui:63
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_editor.ui:113 res/ui/tracks_editor.ui:109
|
||||
#: res/ui/recording_editor.ui:106 res/ui/tracks_editor.ui:109
|
||||
#: res/ui/work_editor.ui:22
|
||||
msgid "Work"
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_editor.ui:157
|
||||
msgid "No performers added."
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_editor.ui:251 res/ui/tracks_editor.ui:127
|
||||
#: res/ui/recording_editor.ui:226 res/ui/tracks_editor.ui:127
|
||||
msgid "Performers"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -232,12 +230,7 @@ msgstr ""
|
|||
msgid "Select a composer on the left."
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_selector.ui:71 res/ui/recording_selector.ui:186
|
||||
#: src/dialogs/recording_selector.rs:109 src/screens/person_screen.rs:57
|
||||
msgid "No works found."
|
||||
msgstr ""
|
||||
|
||||
#: res/ui/recording_selector.ui:108
|
||||
#: res/ui/recording_selector.ui:51
|
||||
msgid "Select a recording"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -317,6 +310,15 @@ msgstr ""
|
|||
msgid "Select music library folder"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs/recording/recording_editor.rs:54
|
||||
msgid "No performers added."
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs/recording/recording_selector_person_screen.rs:39
|
||||
#: src/screens/person_screen.rs:57
|
||||
msgid "No works found."
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs/tracks_editor.rs:60
|
||||
msgid "Add some tracks."
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="reset_role_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
|
|
|
|||
|
|
@ -3,234 +3,183 @@
|
|||
<interface>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<requires lib="libhandy" version="0.0"/>
|
||||
<object class="HdyWindow" id="window">
|
||||
<object class="GtkBox" id="widget">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="default-width">500</property>
|
||||
<property name="default-height">450</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="HdyHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="title" translatable="yes">Recording</property>
|
||||
<child>
|
||||
<object class="HdyHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Recording</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="save_button">
|
||||
<property name="label" translatable="yes">Save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook">
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="save_button">
|
||||
<property name="label" translatable="yes">Save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<child>
|
||||
<!-- n-columns=2 n-rows=2 -->
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">18</property>
|
||||
<property name="row-spacing">12</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<child>
|
||||
<!-- n-columns=2 n-rows=2 -->
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">18</property>
|
||||
<property name="row-spacing">12</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Comment</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="work_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="work_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Select …</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="comment_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Work</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Overview</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Comment</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab-fill">False</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="work_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="work_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Select …</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="comment_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Work</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Overview</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">18</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scroll">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">18</property>
|
||||
<property name="border-width">0</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<object class="GtkButton" id="add_performer_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="performer_list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<child type="placeholder">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">No performers added.</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<property name="icon-name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkButton" id="edit_performer_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">0</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_performer_button">
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="edit_performer_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove_performer_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-remove-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
|
|
@ -239,30 +188,54 @@
|
|||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove_performer_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-remove-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Performers</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Performers</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.38.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.22" />
|
||||
<requires lib="libhandy" version="1.0" />
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<requires lib="libhandy" version="1.0"/>
|
||||
<object class="GtkBox" id="empty_screen">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
|
|
@ -33,168 +33,60 @@
|
|||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkBox" id="person_screen">
|
||||
<object class="HdyLeaflet" id="widget">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="visible-child">sidebar_box</property>
|
||||
<child>
|
||||
<object class="HdyHeaderBar" id="person_screen_header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="show-close-button">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="person_screen_stack">
|
||||
<object class="GtkBox" id="sidebar_box">
|
||||
<property name="width-request">250</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkSpinner">
|
||||
<object class="HdyHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">loading</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">No works found.</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">empty</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<object class="HdyWindow" id="window">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">424</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<child>
|
||||
<object class="HdyLeaflet" id="leaflet">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="visible-child">sidebar_box</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sidebar_box">
|
||||
<property name="width-request">250</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="title" translatable="yes">Select a recording</property>
|
||||
<property name="show-close-button" bind-source="widget" bind-property="folded" bind-flags="sync-create">False</property>
|
||||
<child>
|
||||
<object class="HdyHeaderBar">
|
||||
<object class="GtkButton" id="add_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Select a recording</property>
|
||||
<property name="show-close-button" bind-source="leaflet" bind-property="folded" bind-flags="sync-create">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_button">
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">sidebar</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<style>
|
||||
<class name="sidebar" />
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="navigatable">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkBox" id="work_screen">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="HdyHeaderBar" id="work_screen_header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="name">sidebar</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="work_screen_stack">
|
||||
<object class="GtkSeparator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkSpinner">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">loading</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">No works found.</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">empty</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<property name="orientation">vertical</property>
|
||||
<style>
|
||||
<class name="sidebar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="navigatable">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@ pub use instrument_selector::*;
|
|||
pub mod part_editor;
|
||||
pub use part_editor::*;
|
||||
|
||||
pub mod performance_editor;
|
||||
pub use performance_editor::*;
|
||||
|
||||
pub mod person_editor;
|
||||
pub use person_editor::*;
|
||||
|
||||
|
|
@ -28,11 +25,8 @@ pub use person_selector::*;
|
|||
pub mod preferences;
|
||||
pub use preferences::*;
|
||||
|
||||
pub mod recording_editor;
|
||||
pub use recording_editor::*;
|
||||
|
||||
pub mod recording_selector;
|
||||
pub use recording_selector::*;
|
||||
pub mod recording;
|
||||
pub use recording::*;
|
||||
|
||||
pub mod section_editor;
|
||||
pub use section_editor::*;
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
use super::*;
|
||||
use crate::backend::Backend;
|
||||
use crate::database::*;
|
||||
use gettextrs::gettext;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct PerformanceEditor<F>
|
||||
where
|
||||
F: Fn(PerformanceDescription) -> () + 'static,
|
||||
{
|
||||
backend: Rc<Backend>,
|
||||
window: libhandy::Window,
|
||||
callback: F,
|
||||
save_button: gtk::Button,
|
||||
person_label: gtk::Label,
|
||||
ensemble_label: gtk::Label,
|
||||
role_label: gtk::Label,
|
||||
person: RefCell<Option<Person>>,
|
||||
ensemble: RefCell<Option<Ensemble>>,
|
||||
role: RefCell<Option<Instrument>>,
|
||||
}
|
||||
|
||||
impl<F> PerformanceEditor<F>
|
||||
where
|
||||
F: Fn(PerformanceDescription) -> () + 'static,
|
||||
{
|
||||
pub fn new<P: IsA<gtk::Window>>(
|
||||
backend: Rc<Backend>,
|
||||
parent: &P,
|
||||
performance: Option<PerformanceDescription>,
|
||||
callback: F,
|
||||
) -> Rc<Self> {
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_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::Button, person_button);
|
||||
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, gtk::Label, person_label);
|
||||
get_widget!(builder, gtk::Label, ensemble_label);
|
||||
get_widget!(builder, gtk::Label, role_label);
|
||||
|
||||
let (person, ensemble, role) = match performance {
|
||||
Some(performance) => {
|
||||
match performance.person.clone() {
|
||||
Some(person) => {
|
||||
person_label.set_text(&person.name_fl());
|
||||
save_button.set_sensitive(true);
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
match performance.ensemble.clone() {
|
||||
Some(ensemble) => {
|
||||
ensemble_label.set_text(&ensemble.name);
|
||||
save_button.set_sensitive(true);
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
match performance.role.clone() {
|
||||
Some(role) => role_label.set_text(&role.name),
|
||||
None => (),
|
||||
}
|
||||
|
||||
(performance.person, performance.ensemble, performance.role)
|
||||
}
|
||||
None => (None, None, None),
|
||||
};
|
||||
|
||||
let result = Rc::new(PerformanceEditor {
|
||||
backend: backend,
|
||||
window: window,
|
||||
callback: callback,
|
||||
save_button: save_button,
|
||||
person_label: person_label,
|
||||
ensemble_label: ensemble_label,
|
||||
role_label: role_label,
|
||||
person: RefCell::new(person),
|
||||
ensemble: RefCell::new(ensemble),
|
||||
role: RefCell::new(role),
|
||||
});
|
||||
|
||||
cancel_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
result.window.close();
|
||||
}));
|
||||
|
||||
result
|
||||
.save_button
|
||||
.connect_clicked(clone!(@strong result => move |_| {
|
||||
(result.callback)(PerformanceDescription {
|
||||
person: result.person.borrow().clone(),
|
||||
ensemble: result.ensemble.borrow().clone(),
|
||||
role: result.role.borrow().clone(),
|
||||
});
|
||||
result.window.close();
|
||||
}));
|
||||
|
||||
person_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
PersonSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |person| {
|
||||
result.person.replace(Some(person.clone()));
|
||||
result.person_label.set_text(&person.name_fl());
|
||||
result.ensemble.replace(None);
|
||||
result.ensemble_label.set_text(&gettext("Select …"));
|
||||
result.save_button.set_sensitive(true);
|
||||
})).show();
|
||||
}));
|
||||
|
||||
ensemble_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
EnsembleSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |ensemble| {
|
||||
result.ensemble.replace(Some(ensemble.clone()));
|
||||
result.ensemble_label.set_text(&ensemble.name);
|
||||
result.person.replace(None);
|
||||
result.person_label.set_text(&gettext("Select …"));
|
||||
result.save_button.set_sensitive(true);
|
||||
})).show();
|
||||
}));
|
||||
|
||||
role_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
InstrumentSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |role| {
|
||||
result.role.replace(Some(role.clone()));
|
||||
result.role_label.set_text(&role.name);
|
||||
})).show();
|
||||
}));
|
||||
|
||||
reset_role_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
result.role.replace(None);
|
||||
result.role_label.set_text(&gettext("Select …"));
|
||||
}));
|
||||
|
||||
result.window.set_transient_for(Some(parent));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
self.window.show();
|
||||
}
|
||||
}
|
||||
11
src/dialogs/recording/mod.rs
Normal file
11
src/dialogs/recording/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pub mod recording_dialog;
|
||||
pub use recording_dialog::*;
|
||||
|
||||
pub mod recording_editor_dialog;
|
||||
pub use recording_editor_dialog::*;
|
||||
|
||||
mod performance_editor;
|
||||
mod recording_editor;
|
||||
mod recording_selector;
|
||||
mod recording_selector_person_screen;
|
||||
mod recording_selector_work_screen;
|
||||
174
src/dialogs/recording/performance_editor.rs
Normal file
174
src/dialogs/recording/performance_editor.rs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
use crate::backend::*;
|
||||
use crate::database::*;
|
||||
use crate::dialogs::*;
|
||||
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 editing a performance within a recording.
|
||||
pub struct PerformanceEditor {
|
||||
backend: Rc<Backend>,
|
||||
window: libhandy::Window,
|
||||
save_button: gtk::Button,
|
||||
person_label: gtk::Label,
|
||||
ensemble_label: gtk::Label,
|
||||
role_label: gtk::Label,
|
||||
reset_role_button: gtk::Button,
|
||||
person: RefCell<Option<Person>>,
|
||||
ensemble: RefCell<Option<Ensemble>>,
|
||||
role: RefCell<Option<Instrument>>,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(PerformanceDescription) -> ()>>>,
|
||||
}
|
||||
|
||||
impl PerformanceEditor {
|
||||
/// Create a new performance editor.
|
||||
pub fn new<P: IsA<gtk::Window>>(
|
||||
backend: Rc<Backend>,
|
||||
parent: &P,
|
||||
performance: Option<PerformanceDescription>,
|
||||
) -> Rc<Self> {
|
||||
// Create UI
|
||||
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_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::Button, person_button);
|
||||
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, gtk::Label, person_label);
|
||||
get_widget!(builder, gtk::Label, ensemble_label);
|
||||
get_widget!(builder, gtk::Label, role_label);
|
||||
|
||||
window.set_transient_for(Some(parent));
|
||||
|
||||
let this = Rc::new(PerformanceEditor {
|
||||
backend,
|
||||
window,
|
||||
save_button,
|
||||
person_label,
|
||||
ensemble_label,
|
||||
role_label,
|
||||
reset_role_button,
|
||||
person: RefCell::new(None),
|
||||
ensemble: RefCell::new(None),
|
||||
role: RefCell::new(None),
|
||||
selected_cb: RefCell::new(None),
|
||||
});
|
||||
|
||||
// Connect signals and callbacks
|
||||
|
||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.window.close();
|
||||
}));
|
||||
|
||||
this.save_button
|
||||
.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(PerformanceDescription {
|
||||
person: this.person.borrow().clone(),
|
||||
ensemble: this.ensemble.borrow().clone(),
|
||||
role: this.role.borrow().clone(),
|
||||
});
|
||||
|
||||
this.window.close();
|
||||
}
|
||||
}));
|
||||
|
||||
person_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
PersonSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |person| {
|
||||
this.show_person(Some(&person));
|
||||
this.person.replace(Some(person));
|
||||
this.show_ensemble(None);
|
||||
this.ensemble.replace(None);
|
||||
})).show();
|
||||
}));
|
||||
|
||||
ensemble_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
EnsembleSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |ensemble| {
|
||||
this.show_person(None);
|
||||
this.person.replace(None);
|
||||
this.show_ensemble(Some(&ensemble));
|
||||
this.ensemble.replace(Some(ensemble));
|
||||
})).show();
|
||||
}));
|
||||
|
||||
role_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
InstrumentSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |role| {
|
||||
this.show_role(Some(&role));
|
||||
this.role.replace(Some(role));
|
||||
})).show();
|
||||
}));
|
||||
|
||||
this.reset_role_button
|
||||
.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.show_role(None);
|
||||
this.role.replace(None);
|
||||
}));
|
||||
|
||||
// Initialize
|
||||
|
||||
if let Some(performance) = performance {
|
||||
if let Some(person) = performance.person {
|
||||
this.show_person(Some(&person));
|
||||
this.person.replace(Some(person));
|
||||
} else if let Some(ensemble) = performance.ensemble {
|
||||
this.show_ensemble(Some(&ensemble));
|
||||
this.ensemble.replace(Some(ensemble));
|
||||
}
|
||||
|
||||
if let Some(role) = performance.role {
|
||||
this.show_role(Some(&role));
|
||||
this.role.replace(Some(role));
|
||||
}
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Set a closure to be called when the user has chosen to save the performance.
|
||||
pub fn set_selected_cb<F: Fn(PerformanceDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
/// Show the performance editor.
|
||||
pub fn show(&self) {
|
||||
self.window.show();
|
||||
}
|
||||
|
||||
/// Update the UI according to person.
|
||||
fn show_person(&self, person: Option<&Person>) {
|
||||
if let Some(person) = person {
|
||||
self.person_label.set_text(&person.name_fl());
|
||||
self.save_button.set_sensitive(true);
|
||||
} else {
|
||||
self.person_label.set_text(&gettext("Select …"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the UI according to ensemble.
|
||||
fn show_ensemble(&self, ensemble: Option<&Ensemble>) {
|
||||
if let Some(ensemble) = ensemble {
|
||||
self.ensemble_label.set_text(&ensemble.name);
|
||||
self.save_button.set_sensitive(true);
|
||||
} else {
|
||||
self.ensemble_label.set_text(&gettext("Select …"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the UI according to role.
|
||||
fn show_role(&self, role: Option<&Instrument>) {
|
||||
if let Some(role) = role {
|
||||
self.role_label.set_text(&role.name);
|
||||
self.reset_role_button.show();
|
||||
} else {
|
||||
self.role_label.set_text(&gettext("Select …"));
|
||||
self.reset_role_button.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/dialogs/recording/recording_dialog.rs
Normal file
86
src/dialogs/recording/recording_dialog.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
use super::recording_editor::*;
|
||||
use super::recording_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 recording.
|
||||
pub struct RecordingDialog {
|
||||
pub window: libhandy::Window,
|
||||
stack: gtk::Stack,
|
||||
selector: Rc<RecordingSelector>,
|
||||
editor: Rc<RecordingEditor>,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||
}
|
||||
|
||||
impl RecordingDialog {
|
||||
/// Create a new recording dialog.
|
||||
pub fn new<W: IsA<gtk::Window>>(backend: Rc<Backend>, parent: &W) -> Rc<Self> {
|
||||
// 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 = RecordingSelector::new(backend.clone());
|
||||
let editor = RecordingEditor::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 |recording| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(recording);
|
||||
this.window.close();
|
||||
}
|
||||
}));
|
||||
|
||||
this.editor.set_back_cb(clone!(@strong this => move || {
|
||||
this.stack.set_visible_child(&this.selector.widget);
|
||||
}));
|
||||
|
||||
this.editor
|
||||
.set_selected_cb(clone!(@strong this => move |recording| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(recording);
|
||||
this.window.close();
|
||||
}
|
||||
}));
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Set the closure to be called when the user has selected or created a recording.
|
||||
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
/// Show the recording dialog.
|
||||
pub fn show(&self) {
|
||||
self.window.show();
|
||||
}
|
||||
}
|
||||
197
src/dialogs/recording/recording_editor.rs
Normal file
197
src/dialogs/recording/recording_editor.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use super::performance_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::rc::Rc;
|
||||
|
||||
/// A widget for creating or editing a recording.
|
||||
// TODO: Disable buttons if no performance is selected.
|
||||
pub struct RecordingEditor {
|
||||
pub widget: gtk::Box,
|
||||
backend: Rc<Backend>,
|
||||
parent: gtk::Window,
|
||||
save_button: gtk::Button,
|
||||
work_label: gtk::Label,
|
||||
comment_entry: gtk::Entry,
|
||||
performance_list: Rc<List<PerformanceDescription>>,
|
||||
id: i64,
|
||||
work: RefCell<Option<WorkDescription>>,
|
||||
performances: RefCell<Vec<PerformanceDescription>>,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||
back_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||
}
|
||||
|
||||
impl RecordingEditor {
|
||||
/// Create a new recording editor widget and optionally initialize it. The parent window is
|
||||
/// used as the parent for newly created dialogs.
|
||||
pub fn new<W: IsA<gtk::Window>>(
|
||||
backend: Rc<Backend>,
|
||||
parent: &W,
|
||||
recording: Option<RecordingDescription>,
|
||||
) -> Rc<Self> {
|
||||
// Create UI
|
||||
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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::Button, work_button);
|
||||
get_widget!(builder, gtk::Label, work_label);
|
||||
get_widget!(builder, gtk::Entry, comment_entry);
|
||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
||||
get_widget!(builder, gtk::Button, add_performer_button);
|
||||
get_widget!(builder, gtk::Button, edit_performer_button);
|
||||
get_widget!(builder, gtk::Button, remove_performer_button);
|
||||
|
||||
let performance_list = List::new(&gettext("No performers added."));
|
||||
scroll.add(&performance_list.widget);
|
||||
|
||||
let (id, work, performances) = match recording {
|
||||
Some(recording) => (recording.id, Some(recording.work), recording.performances),
|
||||
None => (rand::random::<u32>().into(), None, Vec::new()),
|
||||
};
|
||||
|
||||
let this = Rc::new(RecordingEditor {
|
||||
widget,
|
||||
backend,
|
||||
parent: parent.clone().upcast(),
|
||||
save_button,
|
||||
work_label,
|
||||
comment_entry,
|
||||
performance_list,
|
||||
id,
|
||||
work: RefCell::new(work),
|
||||
performances: RefCell::new(performances),
|
||||
selected_cb: RefCell::new(None),
|
||||
back_cb: RefCell::new(None),
|
||||
});
|
||||
|
||||
// Connect signals and callbacks
|
||||
|
||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(cb) = &*this.back_cb.borrow() {
|
||||
cb();
|
||||
}
|
||||
}));
|
||||
|
||||
this.save_button
|
||||
.connect_clicked(clone!(@strong this => move |_| {
|
||||
let recording = RecordingDescription {
|
||||
id: this.id,
|
||||
work: this.work.borrow().clone().expect("Tried to create recording without work!"),
|
||||
comment: this.comment_entry.get_text().to_string(),
|
||||
performances: this.performances.borrow().clone(),
|
||||
};
|
||||
|
||||
let c = glib::MainContext::default();
|
||||
let clone = this.clone();
|
||||
c.spawn_local(async move {
|
||||
clone.backend.update_recording(recording.clone().into()).await.unwrap();
|
||||
if let Some(cb) = &*clone.selected_cb.borrow() {
|
||||
cb(recording.clone());
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
work_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
WorkSelector::new(this.backend.clone(), &this.parent, clone!(@strong this => move |work| {
|
||||
this.work_selected(&work);
|
||||
this.work.replace(Some(work));
|
||||
})).show();
|
||||
}));
|
||||
|
||||
this.performance_list.set_make_widget(|performance| {
|
||||
let label = gtk::Label::new(Some(&performance.get_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()
|
||||
});
|
||||
|
||||
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let editor = PerformanceEditor::new(this.backend.clone(), &this.parent, None);
|
||||
|
||||
editor.set_selected_cb(clone!(@strong this => move |performance| {
|
||||
let mut performances = this.performances.borrow_mut();
|
||||
|
||||
let index = match this.performance_list.get_selected_index() {
|
||||
Some(index) => index + 1,
|
||||
None => performances.len(),
|
||||
};
|
||||
|
||||
performances.push(performance);
|
||||
this.performance_list.show_items(performances.clone());
|
||||
this.performance_list.select_index(index);
|
||||
}));
|
||||
|
||||
editor.show();
|
||||
}));
|
||||
|
||||
edit_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(index) = this.performance_list.get_selected_index() {
|
||||
let performance = &this.performances.borrow()[index];
|
||||
|
||||
let editor = PerformanceEditor::new(
|
||||
this.backend.clone(),
|
||||
&this.parent,
|
||||
Some(performance.clone()),
|
||||
);
|
||||
|
||||
editor.set_selected_cb(clone!(@strong this => move |performance| {
|
||||
let mut performances = this.performances.borrow_mut();
|
||||
performances[index] = performance;
|
||||
this.performance_list.show_items(performances.clone());
|
||||
this.performance_list.select_index(index);
|
||||
}));
|
||||
|
||||
editor.show();
|
||||
}
|
||||
}));
|
||||
|
||||
remove_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(index) = this.performance_list.get_selected_index() {
|
||||
let mut performances = this.performances.borrow_mut();
|
||||
performances.remove(index);
|
||||
this.performance_list.show_items(performances.clone());
|
||||
this.performance_list.select_index(index);
|
||||
}
|
||||
}));
|
||||
|
||||
// Initialize
|
||||
|
||||
if let Some(work) = &*this.work.borrow() {
|
||||
this.work_selected(work);
|
||||
}
|
||||
|
||||
this.performance_list.show_items(this.performances.borrow().clone());
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Set the closure to be called if the editor is canceled.
|
||||
pub fn set_back_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
||||
self.back_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
/// Set the closure to be called if the recording was created.
|
||||
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
/// Update the UI according to work.
|
||||
fn work_selected(&self, work: &WorkDescription) {
|
||||
self.work_label.set_text(&format!("{}: {}", work.composer.name_fl(), work.title));
|
||||
self.save_button.set_sensitive(true);
|
||||
}
|
||||
}
|
||||
63
src/dialogs/recording/recording_editor_dialog.rs
Normal file
63
src/dialogs/recording/recording_editor_dialog.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
use super::recording_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 recording.
|
||||
pub struct RecordingEditorDialog {
|
||||
pub window: libhandy::Window,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||
}
|
||||
|
||||
impl RecordingEditorDialog {
|
||||
/// Create a new recording editor dialog and optionally initialize it.
|
||||
pub fn new<W: IsA<gtk::Window>>(
|
||||
backend: Rc<Backend>,
|
||||
parent: &W,
|
||||
recording: Option<RecordingDescription>,
|
||||
) -> Rc<Self> {
|
||||
// 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 = RecordingEditor::new(backend.clone(), &window, recording);
|
||||
window.add(&editor.widget);
|
||||
window.show_all();
|
||||
|
||||
let this = Rc::new(Self {
|
||||
window,
|
||||
selected_cb: RefCell::new(None),
|
||||
});
|
||||
|
||||
// Connect signals and callbacks
|
||||
|
||||
editor.set_back_cb(clone!(@strong this => move || {
|
||||
this.window.close();
|
||||
}));
|
||||
|
||||
editor.set_selected_cb(clone!(@strong this => move |recording| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(recording);
|
||||
this.window.close();
|
||||
}
|
||||
}));
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Set the closure to be called when the user edited or created a recording.
|
||||
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
/// Show the recording editor dialog.
|
||||
pub fn show(&self) {
|
||||
self.window.show();
|
||||
}
|
||||
}
|
||||
89
src/dialogs/recording/recording_selector.rs
Normal file
89
src/dialogs/recording/recording_selector.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use super::recording_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 recording from a list of existing ones.
|
||||
pub struct RecordingSelector {
|
||||
pub widget: libhandy::Leaflet,
|
||||
backend: Rc<Backend>,
|
||||
sidebar_box: gtk::Box,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||
add_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||
navigator: Rc<Navigator>,
|
||||
}
|
||||
|
||||
impl RecordingSelector {
|
||||
/// Create a new recording selector.
|
||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||
// Create UI
|
||||
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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 = RecordingSelectorPersonScreen::new(
|
||||
this.backend.clone(),
|
||||
person.clone(),
|
||||
);
|
||||
|
||||
person_screen.set_selected_cb(clone!(@strong this => move |recording| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(recording);
|
||||
}
|
||||
}));
|
||||
|
||||
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 editor is user wants to add a new recording.
|
||||
pub fn set_add_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
||||
self.add_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
/// Set the closure to be called when the user has selected a recording.
|
||||
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
}
|
||||
126
src/dialogs/recording/recording_selector_person_screen.rs
Normal file
126
src/dialogs/recording/recording_selector_person_screen.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use super::recording_selector_work_screen::*;
|
||||
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 recording selector that presents a list of persons
|
||||
/// and switches to a work screen on selection.
|
||||
pub struct RecordingSelectorPersonScreen {
|
||||
backend: Rc<Backend>,
|
||||
widget: gtk::Box,
|
||||
stack: gtk::Stack,
|
||||
work_list: Rc<List<WorkDescription>>,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||
}
|
||||
|
||||
impl RecordingSelectorPersonScreen {
|
||||
/// Create a new recording selector person screen.
|
||||
pub fn new(backend: Rc<Backend>, person: Person) -> Rc<Self> {
|
||||
// Create UI
|
||||
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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 {
|
||||
let work_screen = RecordingSelectorWorkScreen::new(
|
||||
this.backend.clone(),
|
||||
work.clone(),
|
||||
);
|
||||
|
||||
work_screen.set_selected_cb(clone!(@strong this => move |recording| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(recording);
|
||||
}
|
||||
}));
|
||||
|
||||
navigator.push(work_screen);
|
||||
}
|
||||
}));
|
||||
|
||||
// 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 recording.
|
||||
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
}
|
||||
|
||||
impl NavigatorScreen for RecordingSelectorPersonScreen {
|
||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||
self.navigator.replace(Some(navigator));
|
||||
}
|
||||
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
|
||||
fn detach_navigator(&self) {
|
||||
self.navigator.replace(None);
|
||||
}
|
||||
}
|
||||
120
src/dialogs/recording/recording_selector_work_screen.rs
Normal file
120
src/dialogs/recording/recording_selector_work_screen.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
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 recording selector presenting a list of recordings for a work.
|
||||
pub struct RecordingSelectorWorkScreen {
|
||||
backend: Rc<Backend>,
|
||||
widget: gtk::Box,
|
||||
stack: gtk::Stack,
|
||||
recording_list: Rc<List<RecordingDescription>>,
|
||||
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||
}
|
||||
|
||||
impl RecordingSelectorWorkScreen {
|
||||
/// Create a new recording selector work screen.
|
||||
pub fn new(backend: Rc<Backend>, work: WorkDescription) -> Rc<Self> {
|
||||
// Create UI
|
||||
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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(&work.title));
|
||||
header.set_subtitle(Some(&work.composer.name_fl()));
|
||||
|
||||
let recording_list = List::new(&gettext("No recordings found."));
|
||||
stack.add_named(&recording_list.widget, "content");
|
||||
|
||||
let this = Rc::new(Self {
|
||||
backend,
|
||||
widget,
|
||||
stack,
|
||||
recording_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.recording_list.set_make_widget(|recording: &RecordingDescription| {
|
||||
let work_label = gtk::Label::new(Some(&recording.work.get_title()));
|
||||
work_label.set_ellipsize(pango::EllipsizeMode::End);
|
||||
work_label.set_halign(gtk::Align::Start);
|
||||
|
||||
let performers_label = gtk::Label::new(Some(&recording.get_performers()));
|
||||
performers_label.set_ellipsize(pango::EllipsizeMode::End);
|
||||
performers_label.set_opacity(0.5);
|
||||
performers_label.set_halign(gtk::Align::Start);
|
||||
|
||||
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
vbox.set_border_width(6);
|
||||
vbox.add(&work_label);
|
||||
vbox.add(&performers_label);
|
||||
|
||||
vbox.upcast()
|
||||
});
|
||||
|
||||
this.recording_list
|
||||
.set_selected(clone!(@strong this => move |recording| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(recording.clone());
|
||||
}
|
||||
}));
|
||||
|
||||
// Initialize
|
||||
|
||||
let context = glib::MainContext::default();
|
||||
let clone = this.clone();
|
||||
context.spawn_local(async move {
|
||||
let recordings = clone
|
||||
.backend
|
||||
.get_recordings_for_work(work.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
clone.recording_list.show_items(recordings);
|
||||
clone.stack.set_visible_child_name("content");
|
||||
});
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Sets a closure to be called when the user has selected a recording.
|
||||
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||
self.selected_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
}
|
||||
|
||||
impl NavigatorScreen for RecordingSelectorWorkScreen {
|
||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||
self.navigator.replace(Some(navigator));
|
||||
}
|
||||
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
|
||||
fn detach_navigator(&self) {
|
||||
self.navigator.replace(None);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
use super::*;
|
||||
use crate::backend::Backend;
|
||||
use crate::database::*;
|
||||
use crate::widgets::*;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub struct RecordingEditor<F>
|
||||
where
|
||||
F: Fn(RecordingDescription) -> () + 'static,
|
||||
{
|
||||
backend: Rc<Backend>,
|
||||
window: libhandy::Window,
|
||||
callback: F,
|
||||
id: i64,
|
||||
save_button: gtk::Button,
|
||||
work_label: gtk::Label,
|
||||
work: RefCell<Option<WorkDescription>>,
|
||||
comment_entry: gtk::Entry,
|
||||
performers: RefCell<Vec<PerformanceDescription>>,
|
||||
performer_list: gtk::ListBox,
|
||||
}
|
||||
|
||||
impl<F> RecordingEditor<F>
|
||||
where
|
||||
F: Fn(RecordingDescription) -> () + 'static,
|
||||
{
|
||||
pub fn new<P: IsA<gtk::Window>>(
|
||||
backend: Rc<Backend>,
|
||||
parent: &P,
|
||||
recording: Option<RecordingDescription>,
|
||||
callback: F,
|
||||
) -> Rc<Self> {
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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::Button, work_button);
|
||||
get_widget!(builder, gtk::Label, work_label);
|
||||
get_widget!(builder, gtk::Entry, comment_entry);
|
||||
get_widget!(builder, gtk::ListBox, performer_list);
|
||||
get_widget!(builder, gtk::Button, add_performer_button);
|
||||
get_widget!(builder, gtk::Button, edit_performer_button);
|
||||
get_widget!(builder, gtk::Button, remove_performer_button);
|
||||
|
||||
let (id, work, performers) = match recording {
|
||||
Some(recording) => {
|
||||
save_button.set_sensitive(true);
|
||||
work_label.set_text(&recording.work.get_title());
|
||||
comment_entry.set_text(&recording.comment);
|
||||
(recording.id, Some(recording.work), recording.performances)
|
||||
}
|
||||
None => (rand::random::<u32>().into(), None, Vec::new()),
|
||||
};
|
||||
|
||||
let result = Rc::new(RecordingEditor {
|
||||
backend: backend,
|
||||
window: window,
|
||||
callback: callback,
|
||||
id: id,
|
||||
save_button: save_button,
|
||||
work_label: work_label,
|
||||
work: RefCell::new(work),
|
||||
comment_entry: comment_entry,
|
||||
performers: RefCell::new(performers),
|
||||
performer_list: performer_list,
|
||||
});
|
||||
|
||||
cancel_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
result.window.close();
|
||||
}));
|
||||
|
||||
result
|
||||
.save_button
|
||||
.connect_clicked(clone!(@strong result => move |_| {
|
||||
let recording = RecordingDescription {
|
||||
id: result.id,
|
||||
work: result.work.borrow().clone().expect("Tried to create recording without work!"),
|
||||
comment: result.comment_entry.get_text().to_string(),
|
||||
performances: result.performers.borrow().to_vec(),
|
||||
};
|
||||
|
||||
let c = glib::MainContext::default();
|
||||
let clone = result.clone();
|
||||
c.spawn_local(async move {
|
||||
clone.backend.update_recording(recording.clone().into()).await.unwrap();
|
||||
clone.window.close();
|
||||
(clone.callback)(recording.clone());
|
||||
});
|
||||
}));
|
||||
|
||||
work_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
WorkSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |work| {
|
||||
result.work.replace(Some(work.clone()));
|
||||
result.work_label.set_text(&format!("{}: {}", work.composer.name_fl(), work.title));
|
||||
result.save_button.set_sensitive(true);
|
||||
})).show();
|
||||
}));
|
||||
|
||||
add_performer_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
PerformanceEditor::new(result.backend.clone(), &result.window, None, clone!(@strong result => move |performance| {
|
||||
{
|
||||
let mut performers = result.performers.borrow_mut();
|
||||
performers.push(performance);
|
||||
}
|
||||
|
||||
result.show_performers();
|
||||
})).show();
|
||||
}));
|
||||
|
||||
edit_performer_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
let row = result.get_selected_performer_row();
|
||||
match row {
|
||||
Some(row) => {
|
||||
let index = row.get_index();
|
||||
let index: usize = index.try_into().unwrap();
|
||||
let performer = result.performers.borrow()[index].clone();
|
||||
|
||||
PerformanceEditor::new(result.backend.clone(), &result.window, Some(performer), clone!(@strong result => move |performer| {
|
||||
result.performers.borrow_mut()[index] = performer;
|
||||
result.show_performers();
|
||||
})).show();
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}));
|
||||
|
||||
remove_performer_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
let row = result.get_selected_performer_row();
|
||||
match row {
|
||||
Some(row) => {
|
||||
let index = row.get_index();
|
||||
let index: usize = index.try_into().unwrap();
|
||||
result.performers.borrow_mut().remove(index);
|
||||
result.show_performers();
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}));
|
||||
|
||||
result.window.set_transient_for(Some(parent));
|
||||
|
||||
result.show_performers();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
self.window.show();
|
||||
}
|
||||
|
||||
fn show_performers(&self) {
|
||||
for child in self.performer_list.get_children() {
|
||||
self.performer_list.remove(&child);
|
||||
}
|
||||
|
||||
for (index, performer) in self.performers.borrow().iter().enumerate() {
|
||||
let label = gtk::Label::new(Some(&performer.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);
|
||||
|
||||
let row = SelectorRow::new(index.try_into().unwrap(), &label);
|
||||
row.show_all();
|
||||
self.performer_list.insert(&row, -1);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_performer_row(&self) -> Option<SelectorRow> {
|
||||
match self.performer_list.get_selected_rows().first() {
|
||||
Some(row) => match row.get_child() {
|
||||
Some(child) => Some(child.downcast().unwrap()),
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
use super::*;
|
||||
use crate::backend::Backend;
|
||||
use crate::database::*;
|
||||
use crate::widgets::*;
|
||||
use gettextrs::gettext;
|
||||
use gio::prelude::*;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use libhandy::prelude::*;
|
||||
use libhandy::HeaderBarExt;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct RecordingSelector {
|
||||
backend: Rc<Backend>,
|
||||
window: libhandy::Window,
|
||||
callback: Box<dyn Fn(RecordingDescription) -> () + 'static>,
|
||||
leaflet: libhandy::Leaflet,
|
||||
navigator: Rc<Navigator>,
|
||||
}
|
||||
|
||||
impl RecordingSelector {
|
||||
pub fn new<P, F>(backend: Rc<Backend>, parent: &P, callback: F) -> Rc<Self>
|
||||
where
|
||||
P: IsA<gtk::Window>,
|
||||
F: Fn(RecordingDescription) -> () + 'static,
|
||||
{
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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::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);
|
||||
leaflet.add(&navigator.widget);
|
||||
|
||||
navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || {
|
||||
leaflet.set_visible_child(&sidebar_box);
|
||||
}));
|
||||
|
||||
let result = Rc::new(Self {
|
||||
backend: backend,
|
||||
window: window,
|
||||
callback: Box::new(callback),
|
||||
leaflet: leaflet,
|
||||
navigator: navigator,
|
||||
});
|
||||
|
||||
add_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
let editor = RecordingEditor::new(
|
||||
result.backend.clone(),
|
||||
&result.window,
|
||||
None,
|
||||
clone!(@strong result => move |recording| {
|
||||
result.select(recording);
|
||||
}),
|
||||
);
|
||||
|
||||
editor.show();
|
||||
}));
|
||||
|
||||
person_list.set_selected(clone!(@strong result => move |person| {
|
||||
result.navigator.clone().replace(RecordingSelectorPersonScreen::new(result.backend.clone(), result.clone(), person.clone()));
|
||||
result.leaflet.set_visible_child(&result.navigator.widget);
|
||||
}));
|
||||
|
||||
result.window.set_transient_for(Some(parent));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
self.window.show();
|
||||
}
|
||||
|
||||
pub fn select(&self, recording: RecordingDescription) {
|
||||
self.window.close();
|
||||
(self.callback)(recording);
|
||||
}
|
||||
}
|
||||
|
||||
struct RecordingSelectorPersonScreen {
|
||||
backend: Rc<Backend>,
|
||||
selector: Rc<RecordingSelector>,
|
||||
widget: gtk::Box,
|
||||
stack: gtk::Stack,
|
||||
work_list: Rc<List<WorkDescription>>,
|
||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||
}
|
||||
|
||||
impl RecordingSelectorPersonScreen {
|
||||
pub fn new(backend: Rc<Backend>, selector: Rc<RecordingSelector>, person: Person) -> Rc<Self> {
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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."));
|
||||
|
||||
work_list.set_make_widget(|work: &WorkDescription| {
|
||||
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);
|
||||
label.upcast()
|
||||
});
|
||||
|
||||
stack.add_named(&work_list.widget, "content");
|
||||
|
||||
let result = Rc::new(Self {
|
||||
backend,
|
||||
selector,
|
||||
widget,
|
||||
stack,
|
||||
work_list,
|
||||
navigator: RefCell::new(None),
|
||||
});
|
||||
|
||||
back_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
let navigator = result.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
navigator.clone().pop();
|
||||
}
|
||||
}));
|
||||
|
||||
result
|
||||
.work_list
|
||||
.set_selected(clone!(@strong result => move |work| {
|
||||
let navigator = result.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
navigator.push(RecordingSelectorWorkScreen::new(result.backend.clone(), result.selector.clone(), work.clone()));
|
||||
}
|
||||
}));
|
||||
|
||||
let context = glib::MainContext::default();
|
||||
let clone = result.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");
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl NavigatorScreen for RecordingSelectorPersonScreen {
|
||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||
self.navigator.replace(Some(navigator));
|
||||
}
|
||||
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
|
||||
fn detach_navigator(&self) {
|
||||
self.navigator.replace(None);
|
||||
}
|
||||
}
|
||||
|
||||
struct RecordingSelectorWorkScreen {
|
||||
backend: Rc<Backend>,
|
||||
selector: Rc<RecordingSelector>,
|
||||
widget: gtk::Box,
|
||||
stack: gtk::Stack,
|
||||
recording_list: Rc<List<RecordingDescription>>,
|
||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||
}
|
||||
|
||||
impl RecordingSelectorWorkScreen {
|
||||
pub fn new(
|
||||
backend: Rc<Backend>,
|
||||
selector: Rc<RecordingSelector>,
|
||||
work: WorkDescription,
|
||||
) -> Rc<Self> {
|
||||
let builder =
|
||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_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(&work.title));
|
||||
header.set_subtitle(Some(&work.composer.name_fl()));
|
||||
|
||||
let recording_list = List::new(&gettext("No recordings found."));
|
||||
|
||||
recording_list.set_make_widget(|recording: &RecordingDescription| {
|
||||
let work_label = gtk::Label::new(Some(&recording.work.get_title()));
|
||||
|
||||
work_label.set_ellipsize(pango::EllipsizeMode::End);
|
||||
work_label.set_halign(gtk::Align::Start);
|
||||
|
||||
let performers_label = gtk::Label::new(Some(&recording.get_performers()));
|
||||
performers_label.set_ellipsize(pango::EllipsizeMode::End);
|
||||
performers_label.set_opacity(0.5);
|
||||
performers_label.set_halign(gtk::Align::Start);
|
||||
|
||||
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
vbox.set_border_width(6);
|
||||
vbox.add(&work_label);
|
||||
vbox.add(&performers_label);
|
||||
|
||||
vbox.upcast()
|
||||
});
|
||||
|
||||
stack.add_named(&recording_list.widget, "content");
|
||||
|
||||
let result = Rc::new(Self {
|
||||
backend,
|
||||
selector,
|
||||
widget,
|
||||
stack,
|
||||
recording_list,
|
||||
navigator: RefCell::new(None),
|
||||
});
|
||||
|
||||
back_button.connect_clicked(clone!(@strong result => move |_| {
|
||||
let navigator = result.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
navigator.clone().pop();
|
||||
}
|
||||
}));
|
||||
|
||||
result
|
||||
.recording_list
|
||||
.set_selected(clone!(@strong result => move |recording| {
|
||||
result.selector.select(recording.clone());
|
||||
}));
|
||||
|
||||
let context = glib::MainContext::default();
|
||||
let clone = result.clone();
|
||||
context.spawn_local(async move {
|
||||
let recordings = clone
|
||||
.backend
|
||||
.get_recordings_for_work(work.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
clone.recording_list.show_items(recordings);
|
||||
clone.stack.set_visible_child_name("content");
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl NavigatorScreen for RecordingSelectorWorkScreen {
|
||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||
self.navigator.replace(Some(navigator));
|
||||
}
|
||||
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
|
||||
fn detach_navigator(&self) {
|
||||
self.navigator.replace(None);
|
||||
}
|
||||
}
|
||||
|
|
@ -95,14 +95,14 @@ impl TracksEditor {
|
|||
}));
|
||||
|
||||
recording_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
RecordingSelector::new(
|
||||
this.backend.clone(),
|
||||
&this.window,
|
||||
clone!(@strong this => move |recording| {
|
||||
this.recording_selected(&recording);
|
||||
this.recording.replace(Some(recording));
|
||||
}),
|
||||
).show();
|
||||
let dialog = RecordingDialog::new(this.backend.clone(), &this.window);
|
||||
|
||||
dialog.set_selected_cb(clone!(@strong this => move |recording| {
|
||||
this.recording_selected(&recording);
|
||||
this.recording.replace(Some(recording));
|
||||
}));
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -45,12 +45,17 @@ sources = files(
|
|||
'dialogs/instrument_selector.rs',
|
||||
'dialogs/mod.rs',
|
||||
'dialogs/part_editor.rs',
|
||||
'dialogs/performance_editor.rs',
|
||||
'dialogs/person_editor.rs',
|
||||
'dialogs/person_selector.rs',
|
||||
'dialogs/preferences.rs',
|
||||
'dialogs/recording_editor.rs',
|
||||
'dialogs/recording_selector.rs',
|
||||
'dialogs/recording/mod.rs',
|
||||
'dialogs/recording/performance_editor.rs',
|
||||
'dialogs/recording/recording_dialog.rs',
|
||||
'dialogs/recording/recording_editor_dialog.rs',
|
||||
'dialogs/recording/recording_editor.rs',
|
||||
'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',
|
||||
|
|
|
|||
|
|
@ -166,9 +166,13 @@ impl Window {
|
|||
result.window,
|
||||
"add-recording",
|
||||
clone!(@strong result => move |_, _| {
|
||||
RecordingEditor::new(result.backend.clone(), &result.window, None, clone!(@strong result => move |_| {
|
||||
let dialog = RecordingDialog::new(result.backend.clone(), &result.window);
|
||||
|
||||
dialog.set_selected_cb(clone!(@strong result => move |_| {
|
||||
result.reload();
|
||||
})).show();
|
||||
}));
|
||||
|
||||
dialog.show();
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -292,9 +296,13 @@ impl Window {
|
|||
let c = glib::MainContext::default();
|
||||
c.spawn_local(async move {
|
||||
let recording = result.backend.get_recording_description(id).await.unwrap();
|
||||
RecordingEditor::new(result.backend.clone(), &result.window, Some(recording), clone!(@strong result => move |_| {
|
||||
let dialog = RecordingEditorDialog::new(result.backend.clone(), &result.window, Some(recording));
|
||||
|
||||
dialog.set_selected_cb(clone!(@strong result => move |_| {
|
||||
result.reload();
|
||||
})).show();
|
||||
}));
|
||||
|
||||
dialog.show();
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue